packages feed

nano-ui-0.1.0.0: lib/NanoUI/Widgets/Drawing.hs

{-# LANGUAGE OverloadedStrings #-}

module NanoUI.Widgets.Drawing
  ( DrawOp (..)
  , DrawingBuild
  , drawing
  , drawingVersioned
  , drawingCached
  )
where

import Data.Text qualified as T
import Data.Primitive.SmallArray (SmallArray)
import Effectful (Eff, type (:>))
import NanoUI.Context (cachedWidgetLayout, registerDrawing)
import NanoUI.Draw (DrawOp (..), DrawingBuild)
import NanoUI.Layout.Arena (NodeType (NodeDrawing))
import NanoUI.Monad (Ui, askContext, nextId, uiIO)
import NanoUI.Style (Layout, defaultLayout)
import NanoUI.Types (Rect)
import NanoUI.Widgets.Node (Response, addWidget)

-- | Vector ops for a laid-out widget. Paint caches ops while width and height
-- stay the same, then translates when the widget moves. Unversioned: the cache
-- drops while the widget animates because the builder has no content key, and
-- a builder that draws something else at the same size neither rebuilds nor
-- repaints. Use 'drawingVersioned' for output that changes, or
-- 'NanoUI.Widgets.Custom.customWidget' without a key to have every frame
-- rebuild and compare.
{-# INLINE drawing #-}
drawing :: Ui :> es => (Layout -> Layout) -> (Rect -> SmallArray DrawOp) -> Eff es Response
drawing = drawingVersioned 0

-- | Like 'drawing', but the tessellated op cache is keyed by an explicit
-- content version. Change the version whenever the builder output changes
-- (a model pointer, dirty counter, or content hash): that rebuilds the ops and
-- repaints the widget. Frames with the same version replay cached ops without
-- rebuilding, even while the widget animates. Version 0 means unversioned, as
-- in 'drawing'.
drawingVersioned :: Ui :> es => Int -> (Layout -> Layout) -> (Rect -> SmallArray DrawOp) -> Eff es Response
drawingVersioned version f build = do
  wid <- nextId
  ctx <- askContext
  uiIO (registerDrawing ctx wid version build)
  addWidget wid NodeDrawing T.empty 0 (f defaultLayout)

-- | Like 'drawingVersioned', but the layout itself comes from @compute@, which
-- only reruns when the envelope, line height, content key, or modifier result
-- change.
drawingCached ::
  Ui :> es =>
  Double ->
  Double ->
  Float ->
  Int ->
  (Layout -> Layout) ->
  IO Layout ->
  DrawingBuild ->
  Eff es Response
drawingCached dw dh lh content f compute build = do
  wid <- nextId
  ctx <- askContext
  layout <- uiIO (cachedWidgetLayout ctx wid dw dh lh content (f defaultLayout) compute)
  uiIO (registerDrawing ctx wid content build)
  addWidget wid NodeDrawing T.empty 0 layout