{-# LANGUAGE StrictData #-}

-- | Record types behind 'Context': interaction, damage, overlay, animation,
-- scroll and drawing-cache state, theme scopes, and frame messages.
module NanoUI.Context.Types
  ( Context (..)
  , MeasureCacheKey
  , MetricSource (..)
  , TextInputMenu (..)
  , TextInputDrag (..)
  , TextFieldClickCell (..)
  , WindowResizeEdge (..)
  , WindowResizeDrag (..)
  , DamageRequest (..)
  , DamageState (..)
  , initialDamageState
  , OverlayState (..)
  , initialOverlayState
  , AnimationState (..)
  , initialAnimationState
  , ScrollTuning (..)
  , defaultScrollTuning
  , ScrollAxes (..)
  , ScrollGlide (..)
  , ScrollState (..)
  , initialScrollState
  , DrawFitCache (..)
  , DrawingEntry (..)
  , DrawingCacheState (..)
  , PopupConfig (..)
  , DrawOpCacheEntry (..)
  , CustomDrawingEntry (..)
  , CustomDrawOpCacheEntry (..)
  , SpanCacheEntry (..)
  , WidgetTextCacheEntry (..)
  , WidgetTextPlacement (..)
  , initialDrawingCacheState
  , InteractionState (..)
  , initialInteractionState
  , CustomMeasureFn
  , CustomDrawContext (..)
  , CustomDrawBuild
  , ThemeScopes (..)
  , FrameMsg (..)
  , decodeMessages
  , reduceMessages
  , reduceUpdates
  , intKey
  ) where

import Data.Dynamic (Dynamic)
import Data.HashMap.Strict (HashMap)
import Data.IORef (IORef)
import Data.IntMap.Strict (IntMap)
import Data.IntMap.Strict qualified as IM
import Data.IntSet (IntSet)
import Data.IntSet qualified as IS
import Data.Map.Strict (Map)
import Data.Primitive.PrimArray (MutablePrimArray)
import Data.Primitive.SmallArray (SmallArray, SmallMutableArray)
import Data.Word (Word64)
import Data.Text (Text)
import Data.Typeable (TypeRep, Typeable, cast)
import GHC.Exts (RealWorld)

import NanoUI.Animation (Animation)
import NanoUI.Atlas (ImageAtlas)
import NanoUI.Draw.Types (DrawArena, DrawOp, DrawingBuild)
import NanoUI.Font (CustomMeasureFn, FontMetrics)
import NanoUI.Frame.SpanArena (SpanArena)
import NanoUI.Id (IdContext, WidgetId, hashWidgetId)
import NanoUI.Input (UiCursorKind)
import NanoUI.Layout.Arena (DirTag, LayoutCache, NodeArena)
import NanoUI.Store (WidgetStore)
import NanoUI.Style (FontStyle, FontVariant, FontWeight, Layout, Theme)
import NanoUI.Widgets.TextCommand (TextCommand)
import NanoUI.Types
  ( Color
  , Damage (..)
  , DamageBounds
  , PopupAnchor
  , PopupPlacement
  , Rect
  , Size (..)
  , V2
  )

-- | Themes the view's @styled@ scopes pushed this frame, and last frame's, to
-- tell whether a frame changed only how its scopes look. A node's scope holds
-- an index into 'tsThemes' plus one; index 0 is the context theme.
data ThemeScopes = ThemeScopes
  { ThemeScopes -> Int
tsCount :: {-# UNPACK #-} !Int
  , ThemeScopes -> SmallMutableArray RealWorld Theme
tsThemes :: !(SmallMutableArray RealWorld Theme)
  -- ^ What each scope is drawn with.
  , ThemeScopes -> SmallMutableArray RealWorld Theme
tsRaw :: !(SmallMutableArray RealWorld Theme)
  -- ^ Each scope's theme before a disabled scope faded it, which nested
  -- @styled@ scopes modify.
  , ThemeScopes -> Int
tsPrevCount :: {-# UNPACK #-} !Int
  , ThemeScopes -> SmallMutableArray RealWorld Theme
tsPrev :: !(SmallMutableArray RealWorld Theme)
  , ThemeScopes -> SmallMutableArray RealWorld Theme
tsPrevRaw :: !(SmallMutableArray RealWorld Theme)
  , ThemeScopes -> Bool
tsDisabled :: !Bool
  -- ^ A disabled scope was entered this pass, so some widget may be disabled.
  , ThemeScopes -> Bool
tsChanged :: !Bool
  -- ^ A pushed theme differs from the one at its index last frame.
  , ThemeScopes -> Word64
tsPrevSig :: {-# UNPACK #-} !Word64
  -- ^ Last frame's scope signature ('NanoUI.Layout.Arena.getScopeSignature').
  }

data FrameMsg where
  FrameMsg :: Typeable a => a -> FrameMsg

decodeMessages :: (Foldable f, Typeable a) => f FrameMsg -> [a]
decodeMessages :: forall (f :: * -> *) a.
(Foldable f, Typeable a) =>
f FrameMsg -> [a]
decodeMessages = (FrameMsg -> [a] -> [a]) -> [a] -> f FrameMsg -> [a]
forall a b. (a -> b -> b) -> b -> f a -> b
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr (\(FrameMsg a
x) [a]
rest -> [a] -> (a -> [a]) -> Maybe a -> [a]
forall b a. b -> (a -> b) -> Maybe a -> b
maybe [a]
rest (a -> [a] -> [a]
forall a. a -> [a] -> [a]
: [a]
rest) (a -> Maybe a
forall a b. (Typeable a, Typeable b) => a -> Maybe b
cast a
x)) []

reduceMessages :: (Foldable f, Typeable msg) => (msg -> model -> model) -> model -> f FrameMsg -> model
reduceMessages :: forall (f :: * -> *) msg model.
(Foldable f, Typeable msg) =>
(msg -> model -> model) -> model -> f FrameMsg -> model
reduceMessages msg -> model -> model
update = (model -> FrameMsg -> model) -> model -> f FrameMsg -> model
forall b a. (b -> a -> b) -> b -> f a -> b
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' (\model
model (FrameMsg a
x) -> model -> (msg -> model) -> Maybe msg -> model
forall b a. b -> (a -> b) -> Maybe a -> b
maybe model
model (msg -> model -> model
`update` model
model) (a -> Maybe msg
forall a b. (Typeable a, Typeable b) => a -> Maybe b
cast a
x))

reduceUpdates :: (Foldable f, Typeable model) => model -> f FrameMsg -> model
reduceUpdates :: forall (f :: * -> *) model.
(Foldable f, Typeable model) =>
model -> f FrameMsg -> model
reduceUpdates = ((model -> model) -> model -> model)
-> model -> f FrameMsg -> model
forall (f :: * -> *) msg model.
(Foldable f, Typeable msg) =>
(msg -> model -> model) -> model -> f FrameMsg -> model
reduceMessages (model -> model) -> model -> model
forall a b. (a -> b) -> a -> b
($)

type MeasureCacheKey = (Text, Float)

-- | Identity of a font/measurement configuration. Pure Context modifiers
-- replace this value; the next frame invalidates shared caches if its identity
-- differs. Holding the current inputs (not a revision counter/history) also
-- distinguishes two differently configured Contexts derived from one parent.
data MetricSource
  = InitialMetricSource
  | MetricSource
      !FontMetrics
      !FontMetrics
      !(Text -> IO (Float, Float))
      !(Float -> FontWeight -> FontStyle -> FontVariant -> IO (FontMetrics, Bool))
      !(Float -> FontWeight -> FontStyle -> FontVariant -> Text -> IO (Float, Float))

-- | Explicit damage invalidation request queued during frame evaluation.
data DamageRequest
  = ReqWidget !WidgetId !DamageBounds      -- ^ Invalidate widget layout bounds (old & new)
  | ReqKey !Int !DamageBounds              -- ^ Invalidate widget bounds by integer key
  | ReqRect !Rect                          -- ^ Invalidate an explicit window-space rectangle
  | ReqPeers ![WidgetId] !DamageBounds     -- ^ Invalidate a collection of widgets
  | ReqFull                                -- ^ Force full window invalidation
  deriving (DamageRequest -> DamageRequest -> Bool
(DamageRequest -> DamageRequest -> Bool)
-> (DamageRequest -> DamageRequest -> Bool) -> Eq DamageRequest
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: DamageRequest -> DamageRequest -> Bool
== :: DamageRequest -> DamageRequest -> Bool
$c/= :: DamageRequest -> DamageRequest -> Bool
/= :: DamageRequest -> DamageRequest -> Bool
Eq, Int -> DamageRequest -> ShowS
[DamageRequest] -> ShowS
DamageRequest -> String
(Int -> DamageRequest -> ShowS)
-> (DamageRequest -> String)
-> ([DamageRequest] -> ShowS)
-> Show DamageRequest
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> DamageRequest -> ShowS
showsPrec :: Int -> DamageRequest -> ShowS
$cshow :: DamageRequest -> String
show :: DamageRequest -> String
$cshowList :: [DamageRequest] -> ShowS
showList :: [DamageRequest] -> ShowS
Show)

data TextInputMenu = TextInputMenu
  { TextInputMenu -> WidgetId
textInputMenuWidget :: WidgetId
  , TextInputMenu -> Rect
textInputMenuRect :: Rect
  }
  deriving (TextInputMenu -> TextInputMenu -> Bool
(TextInputMenu -> TextInputMenu -> Bool)
-> (TextInputMenu -> TextInputMenu -> Bool) -> Eq TextInputMenu
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: TextInputMenu -> TextInputMenu -> Bool
== :: TextInputMenu -> TextInputMenu -> Bool
$c/= :: TextInputMenu -> TextInputMenu -> Bool
/= :: TextInputMenu -> TextInputMenu -> Bool
Eq, Int -> TextInputMenu -> ShowS
[TextInputMenu] -> ShowS
TextInputMenu -> String
(Int -> TextInputMenu -> ShowS)
-> (TextInputMenu -> String)
-> ([TextInputMenu] -> ShowS)
-> Show TextInputMenu
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> TextInputMenu -> ShowS
showsPrec :: Int -> TextInputMenu -> ShowS
$cshow :: TextInputMenu -> String
show :: TextInputMenu -> String
$cshowList :: [TextInputMenu] -> ShowS
showList :: [TextInputMenu] -> ShowS
Show)

data TextInputDrag = TextInputDrag
  { TextInputDrag -> WidgetId
textInputDragWidget :: WidgetId
  , TextInputDrag -> Int
textInputDragAnchor :: {-# UNPACK #-} !Int
  , TextInputDrag -> Int
textInputDragAnchorRow :: {-# UNPACK #-} !Int
  , TextInputDrag -> Int
textInputDragAnchorCol :: {-# UNPACK #-} !Int
  , TextInputDrag -> Bool
textInputDragMultiline :: {-# UNPACK #-} !Bool
  , TextInputDrag -> Int
textInputDragClicks :: {-# UNPACK #-} !Int
  }
  deriving (TextInputDrag -> TextInputDrag -> Bool
(TextInputDrag -> TextInputDrag -> Bool)
-> (TextInputDrag -> TextInputDrag -> Bool) -> Eq TextInputDrag
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: TextInputDrag -> TextInputDrag -> Bool
== :: TextInputDrag -> TextInputDrag -> Bool
$c/= :: TextInputDrag -> TextInputDrag -> Bool
/= :: TextInputDrag -> TextInputDrag -> Bool
Eq, Int -> TextInputDrag -> ShowS
[TextInputDrag] -> ShowS
TextInputDrag -> String
(Int -> TextInputDrag -> ShowS)
-> (TextInputDrag -> String)
-> ([TextInputDrag] -> ShowS)
-> Show TextInputDrag
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> TextInputDrag -> ShowS
showsPrec :: Int -> TextInputDrag -> ShowS
$cshow :: TextInputDrag -> String
show :: TextInputDrag -> String
$cshowList :: [TextInputDrag] -> ShowS
showList :: [TextInputDrag] -> ShowS
Show)

data TextFieldClickCell = TextFieldClickCell
  { TextFieldClickCell -> WidgetId
textFieldClickWidget :: WidgetId
  , TextFieldClickCell -> Int
textFieldClickFlat :: {-# UNPACK #-} !Int
  , TextFieldClickCell -> Int
textFieldClickRow :: {-# UNPACK #-} !Int
  , TextFieldClickCell -> Int
textFieldClickCol :: {-# UNPACK #-} !Int
  , TextFieldClickCell -> Bool
textFieldClickMultiline :: {-# UNPACK #-} !Bool
  }
  deriving (TextFieldClickCell -> TextFieldClickCell -> Bool
(TextFieldClickCell -> TextFieldClickCell -> Bool)
-> (TextFieldClickCell -> TextFieldClickCell -> Bool)
-> Eq TextFieldClickCell
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: TextFieldClickCell -> TextFieldClickCell -> Bool
== :: TextFieldClickCell -> TextFieldClickCell -> Bool
$c/= :: TextFieldClickCell -> TextFieldClickCell -> Bool
/= :: TextFieldClickCell -> TextFieldClickCell -> Bool
Eq, Int -> TextFieldClickCell -> ShowS
[TextFieldClickCell] -> ShowS
TextFieldClickCell -> String
(Int -> TextFieldClickCell -> ShowS)
-> (TextFieldClickCell -> String)
-> ([TextFieldClickCell] -> ShowS)
-> Show TextFieldClickCell
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> TextFieldClickCell -> ShowS
showsPrec :: Int -> TextFieldClickCell -> ShowS
$cshow :: TextFieldClickCell -> String
show :: TextFieldClickCell -> String
$cshowList :: [TextFieldClickCell] -> ShowS
showList :: [TextFieldClickCell] -> ShowS
Show)

data WindowResizeEdge
  = ResizeN
  | ResizeS
  | ResizeE
  | ResizeW
  | ResizeNE
  | ResizeNW
  | ResizeSE
  | ResizeSW
  deriving (WindowResizeEdge -> WindowResizeEdge -> Bool
(WindowResizeEdge -> WindowResizeEdge -> Bool)
-> (WindowResizeEdge -> WindowResizeEdge -> Bool)
-> Eq WindowResizeEdge
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: WindowResizeEdge -> WindowResizeEdge -> Bool
== :: WindowResizeEdge -> WindowResizeEdge -> Bool
$c/= :: WindowResizeEdge -> WindowResizeEdge -> Bool
/= :: WindowResizeEdge -> WindowResizeEdge -> Bool
Eq, Int -> WindowResizeEdge -> ShowS
[WindowResizeEdge] -> ShowS
WindowResizeEdge -> String
(Int -> WindowResizeEdge -> ShowS)
-> (WindowResizeEdge -> String)
-> ([WindowResizeEdge] -> ShowS)
-> Show WindowResizeEdge
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> WindowResizeEdge -> ShowS
showsPrec :: Int -> WindowResizeEdge -> ShowS
$cshow :: WindowResizeEdge -> String
show :: WindowResizeEdge -> String
$cshowList :: [WindowResizeEdge] -> ShowS
showList :: [WindowResizeEdge] -> ShowS
Show)

data WindowResizeDrag = WindowResizeDrag
  { WindowResizeDrag -> WidgetId
wrdWidget :: WidgetId
  , WindowResizeDrag -> WindowResizeEdge
wrdEdge :: WindowResizeEdge
  , WindowResizeDrag -> Float
wrdGrabX :: {-# UNPACK #-} !Float
  , WindowResizeDrag -> Float
wrdGrabY :: {-# UNPACK #-} !Float
  , WindowResizeDrag -> Float
wrdStartX :: {-# UNPACK #-} !Float
  , WindowResizeDrag -> Float
wrdStartY :: {-# UNPACK #-} !Float
  , WindowResizeDrag -> Float
wrdStartW :: {-# UNPACK #-} !Float
  , WindowResizeDrag -> Float
wrdStartH :: {-# UNPACK #-} !Float
  , WindowResizeDrag -> Float
wrdMinW :: {-# UNPACK #-} !Float
  , WindowResizeDrag -> Float
wrdMinH :: {-# UNPACK #-} !Float
  , WindowResizeDrag -> Float
wrdMaxW :: {-# UNPACK #-} !Float
  , WindowResizeDrag -> Float
wrdMaxH :: {-# UNPACK #-} !Float
  }
  deriving (WindowResizeDrag -> WindowResizeDrag -> Bool
(WindowResizeDrag -> WindowResizeDrag -> Bool)
-> (WindowResizeDrag -> WindowResizeDrag -> Bool)
-> Eq WindowResizeDrag
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: WindowResizeDrag -> WindowResizeDrag -> Bool
== :: WindowResizeDrag -> WindowResizeDrag -> Bool
$c/= :: WindowResizeDrag -> WindowResizeDrag -> Bool
/= :: WindowResizeDrag -> WindowResizeDrag -> Bool
Eq, Int -> WindowResizeDrag -> ShowS
[WindowResizeDrag] -> ShowS
WindowResizeDrag -> String
(Int -> WindowResizeDrag -> ShowS)
-> (WindowResizeDrag -> String)
-> ([WindowResizeDrag] -> ShowS)
-> Show WindowResizeDrag
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> WindowResizeDrag -> ShowS
showsPrec :: Int -> WindowResizeDrag -> ShowS
$cshow :: WindowResizeDrag -> String
show :: WindowResizeDrag -> String
$cshowList :: [WindowResizeDrag] -> ShowS
showList :: [WindowResizeDrag] -> ShowS
Show)

data DamageState = DamageState
  { DamageState -> Bool
dsDirty :: !Bool
  , DamageState -> Damage
dsDamage :: !Damage
  , DamageState -> [DamageRequest]
dsRequests :: ![DamageRequest]
  , DamageState -> Size
dsLastWindowSize :: !Size
  , DamageState -> IntMap Rect
dsPrevRects :: !(IntMap Rect)
  , DamageState -> IntMap Rect
dsPrevClips :: !(IntMap Rect)
  , DamageState -> IntMap Text
dsPrevNodeTexts :: !(IntMap Text)
  }

initialDamageState :: DamageState
initialDamageState :: DamageState
initialDamageState = DamageState
  { dsDirty :: Bool
dsDirty = Bool
True
  , dsDamage :: Damage
dsDamage = Damage
DamageFull
  , dsRequests :: [DamageRequest]
dsRequests = []
  , dsLastWindowSize :: Size
dsLastWindowSize = Float -> Float -> Size
Size Float
0 Float
0
  , dsPrevRects :: IntMap Rect
dsPrevRects = IntMap Rect
forall a. IntMap a
IM.empty
  , dsPrevClips :: IntMap Rect
dsPrevClips = IntMap Rect
forall a. IntMap a
IM.empty
  , dsPrevNodeTexts :: IntMap Text
dsPrevNodeTexts = IntMap Text
forall a. IntMap a
IM.empty
  }

data OverlayState = OverlayState
  { OverlayState -> Bool
osModalWasActive :: {-# UNPACK #-} !Bool
  , OverlayState -> Bool
osModalActive :: {-# UNPACK #-} !Bool
  , OverlayState -> Int
osModalDepth :: {-# UNPACK #-} !Int
  , OverlayState -> Bool
osEscapeConsumed :: {-# UNPACK #-} !Bool
  , OverlayState -> IntMap Rect
osPrevFloatingRects :: !(IntMap Rect)
  , OverlayState -> [Int]
osPrevFloatingOrder :: ![Int]
  , OverlayState -> Maybe (V2, Maybe WidgetId)
osTopmostCache :: !(Maybe (V2, Maybe WidgetId))
  , OverlayState -> Maybe WidgetId
osCurrentFloatingId :: !(Maybe WidgetId)
  , OverlayState -> Bool
osLastPointerBlocked :: {-# UNPACK #-} !Bool
  }

initialOverlayState :: OverlayState
initialOverlayState :: OverlayState
initialOverlayState = OverlayState
  { osModalWasActive :: Bool
osModalWasActive = Bool
False
  , osModalActive :: Bool
osModalActive = Bool
False
  , osModalDepth :: Int
osModalDepth = Int
0
  , osEscapeConsumed :: Bool
osEscapeConsumed = Bool
False
  , osPrevFloatingRects :: IntMap Rect
osPrevFloatingRects = IntMap Rect
forall a. IntMap a
IM.empty
  , osPrevFloatingOrder :: [Int]
osPrevFloatingOrder = []
  , osTopmostCache :: Maybe (V2, Maybe WidgetId)
osTopmostCache = Maybe (V2, Maybe WidgetId)
forall a. Maybe a
Nothing
  , osCurrentFloatingId :: Maybe WidgetId
osCurrentFloatingId = Maybe WidgetId
forall a. Maybe a
Nothing
  , osLastPointerBlocked :: Bool
osLastPointerBlocked = Bool
False
  }

data AnimationState = AnimationState
  { AnimationState -> IntMap Animation
asAnimations :: !(IntMap Animation)
  , AnimationState -> IntMap Float
asAnimRest :: !(IntMap Float)
  , AnimationState -> Bool
asAnyAnimating :: {-# UNPACK #-} !Bool
  , AnimationState -> Bool
asAnimSettled :: {-# UNPACK #-} !Bool
  , AnimationState -> IntMap Int
asRectless :: !(IntMap Int)
  }

initialAnimationState :: AnimationState
initialAnimationState :: AnimationState
initialAnimationState = AnimationState
  { asAnimations :: IntMap Animation
asAnimations = IntMap Animation
forall a. IntMap a
IM.empty
  , asAnimRest :: IntMap Float
asAnimRest = IntMap Float
forall a. IntMap a
IM.empty
  , asAnyAnimating :: Bool
asAnyAnimating = Bool
False
  , asAnimSettled :: Bool
asAnimSettled = Bool
False
  , asRectless :: IntMap Int
asRectless = IntMap Int
forall a. IntMap a
IM.empty
  }

-- | How far one wheel notch scrolls, and how long a scroll takes to settle.
-- One setting for the whole context; a single scroller can take its own step
-- (see @setScrollStep@).
data ScrollTuning = ScrollTuning
  { ScrollTuning -> Float
scrollWheelStep :: Float
  -- ^ Pixels one wheel notch scrolls. The default is three text lines, which
  -- is what Windows and most desktops send a notch as.
  , ScrollTuning -> Float
scrollSmoothTime :: Float
  -- ^ Seconds a scroll takes to cover most of the distance to its target.
  -- @0@ (the default) lands on it in the same frame.
  }
  deriving (ScrollTuning -> ScrollTuning -> Bool
(ScrollTuning -> ScrollTuning -> Bool)
-> (ScrollTuning -> ScrollTuning -> Bool) -> Eq ScrollTuning
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: ScrollTuning -> ScrollTuning -> Bool
== :: ScrollTuning -> ScrollTuning -> Bool
$c/= :: ScrollTuning -> ScrollTuning -> Bool
/= :: ScrollTuning -> ScrollTuning -> Bool
Eq, Int -> ScrollTuning -> ShowS
[ScrollTuning] -> ShowS
ScrollTuning -> String
(Int -> ScrollTuning -> ShowS)
-> (ScrollTuning -> String)
-> ([ScrollTuning] -> ShowS)
-> Show ScrollTuning
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> ScrollTuning -> ShowS
showsPrec :: Int -> ScrollTuning -> ShowS
$cshow :: ScrollTuning -> String
show :: ScrollTuning -> String
$cshowList :: [ScrollTuning] -> ShowS
showList :: [ScrollTuning] -> ShowS
Show)

defaultScrollTuning :: ScrollTuning
defaultScrollTuning :: ScrollTuning
defaultScrollTuning =
  ScrollTuning
    { scrollWheelStep :: Float
scrollWheelStep = Float
60
    , scrollSmoothTime :: Float
scrollSmoothTime = Float
0
    }

-- | Which axes a scroller moves on, and how an offset in window axes (x
-- rightwards, y downwards) maps onto its stored offset. A 1D row scroller
-- keeps its offset in the main-axis slot, so its horizontal offset is the one
-- that needs swapping.
data ScrollAxes
  = ScrollAxisY
  | ScrollAxisX
  | ScrollAxisXY
  deriving (ScrollAxes -> ScrollAxes -> Bool
(ScrollAxes -> ScrollAxes -> Bool)
-> (ScrollAxes -> ScrollAxes -> Bool) -> Eq ScrollAxes
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: ScrollAxes -> ScrollAxes -> Bool
== :: ScrollAxes -> ScrollAxes -> Bool
$c/= :: ScrollAxes -> ScrollAxes -> Bool
/= :: ScrollAxes -> ScrollAxes -> Bool
Eq, Int -> ScrollAxes -> ShowS
[ScrollAxes] -> ShowS
ScrollAxes -> String
(Int -> ScrollAxes -> ShowS)
-> (ScrollAxes -> String)
-> ([ScrollAxes] -> ShowS)
-> Show ScrollAxes
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> ScrollAxes -> ShowS
showsPrec :: Int -> ScrollAxes -> ShowS
$cshow :: ScrollAxes -> String
show :: ScrollAxes -> String
$cshowList :: [ScrollAxes] -> ShowS
showList :: [ScrollAxes] -> ShowS
Show)

-- | A scroller on its way to an offset it has not reached yet. The target is
-- in window axes and already clamped to the scroller's range.
data ScrollGlide = ScrollGlide
  { ScrollGlide -> WidgetId
sgWidget :: WidgetId
  , ScrollGlide -> V2
sgTarget :: V2
  , ScrollGlide -> ScrollAxes
sgAxes :: ScrollAxes
  }
  deriving (ScrollGlide -> ScrollGlide -> Bool
(ScrollGlide -> ScrollGlide -> Bool)
-> (ScrollGlide -> ScrollGlide -> Bool) -> Eq ScrollGlide
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: ScrollGlide -> ScrollGlide -> Bool
== :: ScrollGlide -> ScrollGlide -> Bool
$c/= :: ScrollGlide -> ScrollGlide -> Bool
/= :: ScrollGlide -> ScrollGlide -> Bool
Eq, Int -> ScrollGlide -> ShowS
[ScrollGlide] -> ShowS
ScrollGlide -> String
(Int -> ScrollGlide -> ShowS)
-> (ScrollGlide -> String)
-> ([ScrollGlide] -> ShowS)
-> Show ScrollGlide
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> ScrollGlide -> ShowS
showsPrec :: Int -> ScrollGlide -> ShowS
$cshow :: ScrollGlide -> String
show :: ScrollGlide -> String
$cshowList :: [ScrollGlide] -> ShowS
showList :: [ScrollGlide] -> ShowS
Show)

data ScrollState = ScrollState
  { ScrollState -> ScrollTuning
ssTuning :: !ScrollTuning
  , ScrollState -> IntMap ScrollGlide
ssGlides :: !(IntMap ScrollGlide)
  , ScrollState -> IntSet
ssCached :: !IntSet
  -- ^ Scrollers whose geometry has been published this frame. Two scroll
  -- nodes can share a widget id (a table's frozen pane and its body), and
  -- without this the second would overwrite the first every frame, churning
  -- the store and flipping the geometry the commands read.
  }

initialScrollState :: ScrollState
initialScrollState :: ScrollState
initialScrollState =
  ScrollState
    { ssTuning :: ScrollTuning
ssTuning = ScrollTuning
defaultScrollTuning
    , ssGlides :: IntMap ScrollGlide
ssGlides = IntMap ScrollGlide
forall a. IntMap a
IM.empty
    , ssCached :: IntSet
ssCached = IntSet
IS.empty
    }

data DrawFitCache = DrawFitCache
  { DrawFitCache -> Double
dfcDw :: {-# UNPACK #-} !Double
  , DrawFitCache -> Double
dfcDh :: {-# UNPACK #-} !Double
  , DrawFitCache -> Float
dfcLh :: {-# UNPACK #-} !Float
  , DrawFitCache -> Int
dfcContent :: {-# UNPACK #-} !Int
  , DrawFitCache -> Layout
dfcIn :: !Layout
  , DrawFitCache -> Layout
dfcOut :: !Layout
  }

-- | Cached text-span layout for one arena node. Key fields are every input
-- that changes the produced spans; 'sceSpans' is the shared result. The whole
-- cache is dropped on theme or font-scale changes.
data SpanCacheEntry = SpanCacheEntry
  { SpanCacheEntry -> Text
sceText :: !Text
  , SpanCacheEntry -> Color
sceFg :: {-# UNPACK #-} !Color
  , SpanCacheEntry -> Color
sceBg :: {-# UNPACK #-} !Color
  , SpanCacheEntry -> Int
sceStyle :: {-# UNPACK #-} !Int
  , SpanCacheEntry -> Float
sceFontSize :: {-# UNPACK #-} !Float
  , SpanCacheEntry -> Int
sceAlign :: {-# UNPACK #-} !Int
  , SpanCacheEntry -> Int
sceWidthTag :: {-# UNPACK #-} !Int
  , SpanCacheEntry -> Rect
sceRect :: !Rect
  , SpanCacheEntry -> Float
sceEffMaxW :: {-# UNPACK #-} !Float
  , SpanCacheEntry -> Bool
sceRowChild :: {-# UNPACK #-} !Bool
  , SpanCacheEntry -> [(Rect, Text, Color, Color)]
sceSpans :: ![(Rect, Text, Color, Color)]
  }

-- | A cacheable widget label is a single line (or absent for close buttons).
-- Coordinates are relative to the node origin; paint translates them without
-- rebuilding a list or invalidating the cache when a widget scrolls.
data WidgetTextPlacement = WidgetTextPlacement
  !Text
  {-# UNPACK #-} !Float
  {-# UNPACK #-} !Float
  {-# UNPACK #-} !Float
  {-# UNPACK #-} !Float

data WidgetTextCacheEntry = WidgetTextCacheEntry
  { WidgetTextCacheEntry -> Int
wtcNodeType :: {-# UNPACK #-} !Int
  , WidgetTextCacheEntry -> Int
wtcStyle :: {-# UNPACK #-} !Int
  , WidgetTextCacheEntry -> Float
wtcFontSize :: {-# UNPACK #-} !Float
  , WidgetTextCacheEntry -> Text
wtcText :: !Text
  , WidgetTextCacheEntry -> Float
wtcWidth :: {-# UNPACK #-} !Float
  , WidgetTextCacheEntry -> Float
wtcHeight :: {-# UNPACK #-} !Float
  , WidgetTextCacheEntry -> Int
wtcAlign :: {-# UNPACK #-} !Int
  , WidgetTextCacheEntry -> Maybe WidgetTextPlacement
wtcPlacement :: {-# NOUNPACK #-} !(Maybe WidgetTextPlacement)
  }

data CustomDrawContext = CustomDrawContext
  { CustomDrawContext -> Bool
cdcHovered  :: {-# UNPACK #-} !Bool
  , CustomDrawContext -> Bool
cdcPressed  :: {-# UNPACK #-} !Bool
  , CustomDrawContext -> Bool
cdcFocused  :: {-# UNPACK #-} !Bool
  , CustomDrawContext -> Bool
cdcActive   :: {-# UNPACK #-} !Bool
  , CustomDrawContext -> Bool
cdcDisabled :: {-# UNPACK #-} !Bool
  , CustomDrawContext -> Theme
cdcTheme    :: !Theme
  , CustomDrawContext -> FontMetrics
cdcFont     :: !FontMetrics
  }

type CustomDrawBuild = CustomDrawContext -> Rect -> SmallArray DrawOp

-- | A registered custom drawing: its content key plus the op builder. A
-- non-zero key is the author's promise that the ops follow it, so a frame
-- whose key is unchanged neither rebuilds nor repaints them. Key 0 means the
-- drawing carries no key and is rebuilt every frame and compared.
data CustomDrawingEntry = CustomDrawingEntry
  { CustomDrawingEntry -> Int
cdrContent :: {-# UNPACK #-} !Int
  , CustomDrawingEntry -> CustomDrawBuild
cdrBuild :: !CustomDrawBuild
  }

-- | A registered drawing: content version plus the op builder. The version
-- participates in the draw-op cache key, so a builder whose output changes
-- without its size changing must bump the version to invalidate.
data DrawingEntry = DrawingEntry
  { DrawingEntry -> Int
deContent :: {-# UNPACK #-} !Int
  , DrawingEntry -> DrawingBuild
deBuild :: !DrawingBuild
  }

data DrawingCacheState = DrawingCacheState
  { DrawingCacheState -> IntMap PopupConfig
dcsPopupConfigs :: !(IntMap PopupConfig)
  , DrawingCacheState -> IntMap DrawingEntry
dcsDrawings :: !(IntMap DrawingEntry)
  , DrawingCacheState -> IntMap CustomDrawingEntry
dcsCustomDrawings :: !(IntMap CustomDrawingEntry)
  , DrawingCacheState -> IntMap CustomMeasureFn
dcsCustomMeasures :: !(IntMap CustomMeasureFn)
  , DrawingCacheState -> IntMap (CustomDrawContext -> UiCursorKind)
dcsCustomCursors :: !(IntMap (CustomDrawContext -> UiCursorKind))
  , DrawingCacheState -> IntMap Float
dcsCustomDamageSlop :: !(IntMap Float)
  , DrawingCacheState -> IntMap DrawOpCacheEntry
dcsDrawOpCache :: !(IntMap DrawOpCacheEntry)
  , DrawingCacheState -> IntMap CustomDrawOpCacheEntry
dcsCustomDrawOpCache :: !(IntMap CustomDrawOpCacheEntry)
  , DrawingCacheState -> IntMap DrawFitCache
dcsDrawFitCache :: !(IntMap DrawFitCache)
  }

-- | Strict cache entry for a popup's anchor configuration.
data PopupConfig = PopupConfig
  { PopupConfig -> PopupAnchor
pcAnchor :: !PopupAnchor
  , PopupConfig -> PopupPlacement
pcPlacement :: !PopupPlacement
  , PopupConfig -> Float
pcOffset :: {-# UNPACK #-} !Float
  }

-- | Strict cache entry for a drawing's compiled draw ops.
data DrawOpCacheEntry = DrawOpCacheEntry
  { DrawOpCacheEntry -> Int
doeContent :: {-# UNPACK #-} !Int
  , DrawOpCacheEntry -> Rect
doeBounds :: !Rect
  , DrawOpCacheEntry -> SmallArray DrawOp
doeOps :: !(SmallArray DrawOp)
  }

-- | Strict cache entry for a custom drawing's compiled draw ops. Every input
-- the ops can depend on is part of the key: the content key, the rect, the
-- interaction state the draw context exposes, and the metric generation, which
-- a theme or font change bumps.
data CustomDrawOpCacheEntry = CustomDrawOpCacheEntry
  { CustomDrawOpCacheEntry -> Int
cdeContent :: {-# UNPACK #-} !Int
  , CustomDrawOpCacheEntry -> Rect
cdeBounds :: !Rect
  , CustomDrawOpCacheEntry -> Bool
cdeHovered :: {-# UNPACK #-} !Bool
  , CustomDrawOpCacheEntry -> Bool
cdePressed :: {-# UNPACK #-} !Bool
  , CustomDrawOpCacheEntry -> Bool
cdeFocused :: {-# UNPACK #-} !Bool
  , CustomDrawOpCacheEntry -> Bool
cdeDisabled :: {-# UNPACK #-} !Bool
  , CustomDrawOpCacheEntry -> Int
cdeGen :: {-# UNPACK #-} !Int
  , CustomDrawOpCacheEntry -> SmallArray DrawOp
cdeOps :: !(SmallArray DrawOp)
  }

initialDrawingCacheState :: DrawingCacheState
initialDrawingCacheState :: DrawingCacheState
initialDrawingCacheState = DrawingCacheState
  { dcsPopupConfigs :: IntMap PopupConfig
dcsPopupConfigs = IntMap PopupConfig
forall a. IntMap a
IM.empty
  , dcsDrawings :: IntMap DrawingEntry
dcsDrawings = IntMap DrawingEntry
forall a. IntMap a
IM.empty
  , dcsCustomDrawings :: IntMap CustomDrawingEntry
dcsCustomDrawings = IntMap CustomDrawingEntry
forall a. IntMap a
IM.empty
  , dcsCustomMeasures :: IntMap CustomMeasureFn
dcsCustomMeasures = IntMap CustomMeasureFn
forall a. IntMap a
IM.empty
  , dcsCustomCursors :: IntMap (CustomDrawContext -> UiCursorKind)
dcsCustomCursors = IntMap (CustomDrawContext -> UiCursorKind)
forall a. IntMap a
IM.empty
  , dcsCustomDamageSlop :: IntMap Float
dcsCustomDamageSlop = IntMap Float
forall a. IntMap a
IM.empty
  , dcsDrawOpCache :: IntMap DrawOpCacheEntry
dcsDrawOpCache = IntMap DrawOpCacheEntry
forall a. IntMap a
IM.empty
  , dcsCustomDrawOpCache :: IntMap CustomDrawOpCacheEntry
dcsCustomDrawOpCache = IntMap CustomDrawOpCacheEntry
forall a. IntMap a
IM.empty
  , dcsDrawFitCache :: IntMap DrawFitCache
dcsDrawFitCache = IntMap DrawFitCache
forall a. IntMap a
IM.empty
  }

data InteractionState = InteractionState
  { InteractionState -> Maybe (WidgetId, DirTag, Float)
isScrollDrag :: !(Maybe (WidgetId, DirTag, Float))
  , InteractionState -> Maybe TextInputDrag
isTextInputDrag :: !(Maybe TextInputDrag)
  , InteractionState -> Maybe TextFieldClickCell
isTextFieldClickCell :: !(Maybe TextFieldClickCell)
  , InteractionState -> Maybe TextInputMenu
isTextInputMenu :: !(Maybe TextInputMenu)
  , InteractionState -> Maybe (WidgetId, TextCommand)
isTextEditLastAction :: !(Maybe (WidgetId, TextCommand))
  , InteractionState -> Bool
isSelectDropPress :: {-# UNPACK #-} !Bool
  , InteractionState -> Maybe (WidgetId, Rect)
isOpenSelectDrop :: !(Maybe (WidgetId, Rect))
  , InteractionState -> Bool
isMenuPointerGesture :: {-# UNPACK #-} !Bool
  , InteractionState -> Maybe (WidgetId, Float, Float)
isWindowDrag :: !(Maybe (WidgetId, Float, Float))
  , InteractionState -> Maybe WindowResizeDrag
isWindowResize :: !(Maybe WindowResizeDrag)
  }
  deriving (InteractionState -> InteractionState -> Bool
(InteractionState -> InteractionState -> Bool)
-> (InteractionState -> InteractionState -> Bool)
-> Eq InteractionState
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: InteractionState -> InteractionState -> Bool
== :: InteractionState -> InteractionState -> Bool
$c/= :: InteractionState -> InteractionState -> Bool
/= :: InteractionState -> InteractionState -> Bool
Eq, Int -> InteractionState -> ShowS
[InteractionState] -> ShowS
InteractionState -> String
(Int -> InteractionState -> ShowS)
-> (InteractionState -> String)
-> ([InteractionState] -> ShowS)
-> Show InteractionState
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> InteractionState -> ShowS
showsPrec :: Int -> InteractionState -> ShowS
$cshow :: InteractionState -> String
show :: InteractionState -> String
$cshowList :: [InteractionState] -> ShowS
showList :: [InteractionState] -> ShowS
Show)

initialInteractionState :: InteractionState
initialInteractionState :: InteractionState
initialInteractionState = InteractionState
  { isScrollDrag :: Maybe (WidgetId, DirTag, Float)
isScrollDrag = Maybe (WidgetId, DirTag, Float)
forall a. Maybe a
Nothing
  , isTextInputDrag :: Maybe TextInputDrag
isTextInputDrag = Maybe TextInputDrag
forall a. Maybe a
Nothing
  , isTextFieldClickCell :: Maybe TextFieldClickCell
isTextFieldClickCell = Maybe TextFieldClickCell
forall a. Maybe a
Nothing
  , isTextInputMenu :: Maybe TextInputMenu
isTextInputMenu = Maybe TextInputMenu
forall a. Maybe a
Nothing
  , isTextEditLastAction :: Maybe (WidgetId, TextCommand)
isTextEditLastAction = Maybe (WidgetId, TextCommand)
forall a. Maybe a
Nothing
  , isSelectDropPress :: Bool
isSelectDropPress = Bool
False
  , isOpenSelectDrop :: Maybe (WidgetId, Rect)
isOpenSelectDrop = Maybe (WidgetId, Rect)
forall a. Maybe a
Nothing
  , isMenuPointerGesture :: Bool
isMenuPointerGesture = Bool
False
  , isWindowDrag :: Maybe (WidgetId, Float, Float)
isWindowDrag = Maybe (WidgetId, Float, Float)
forall a. Maybe a
Nothing
  , isWindowResize :: Maybe WindowResizeDrag
isWindowResize = Maybe WindowResizeDrag
forall a. Maybe a
Nothing
  }

data Context = Context
  { Context -> NodeArena
ctxNodeArena :: NodeArena
  , Context -> DrawArena
ctxDrawArena :: DrawArena
  , Context -> IORef WidgetId
ctxHotId :: IORef WidgetId
  , Context -> IORef WidgetId
ctxLastHotId :: IORef WidgetId
  , Context -> IORef WidgetId
ctxActiveId :: IORef WidgetId
  , Context -> IORef WidgetId
ctxClickedId :: IORef WidgetId
  , Context -> IORef WidgetId
ctxReleaseClickedId :: IORef WidgetId
  -- | Where the held left and right buttons went down, cleared when they come
  -- up. A click belongs to the widget the press landed on, so a widget
  -- hit-tests this point as well as the release point. 'Nothing' (a release
  -- with no press behind it) lets the release stand on its own.
  , Context -> IORef (Maybe V2)
ctxPressPos :: IORef (Maybe V2)
  , Context -> IORef (Maybe V2)
ctxRightPressPos :: IORef (Maybe V2)
  , Context -> IORef WidgetId
ctxFocusId :: IORef WidgetId
  -- | Focus last moved by keyboard, so the focused widget shows its ring. A
  -- pointer press hides it again.
  , Context -> IORef Bool
ctxFocusVisible :: IORef Bool
  , Context -> IORef WidgetStore
ctxStore :: IORef WidgetStore
  , Context -> IORef DamageState
ctxDamageState :: IORef DamageState
  , Context -> IORef OverlayState
ctxOverlayState :: IORef OverlayState
  , Context -> IORef AnimationState
ctxAnimationState :: IORef AnimationState
  , Context -> IORef ScrollState
ctxScrollState :: !(IORef ScrollState)
  , Context -> IORef DrawingCacheState
ctxDrawingCache :: IORef DrawingCacheState
  , Context -> IORef IdContext
ctxIdContext :: IORef IdContext
  , Context -> FontMetrics
ctxFontMetrics :: FontMetrics
  , Context -> FontMetrics
ctxMonoFontMetrics :: FontMetrics
  , Context -> Text -> IO (Float, Float)
ctxMeasureText :: Text -> IO (Float, Float)
  , Context
-> Float
-> FontWeight
-> FontStyle
-> FontVariant
-> IO (FontMetrics, Bool)
ctxResolveFont :: !(Float -> FontWeight -> FontStyle -> FontVariant -> IO (FontMetrics, Bool))
  , Context
-> Float
-> FontWeight
-> FontStyle
-> FontVariant
-> Text
-> IO (Float, Float)
ctxResolveMeasure :: !(Float -> FontWeight -> FontStyle -> FontVariant -> Text -> IO (Float, Float))
  , Context -> Maybe (IORef (HashMap MeasureCacheKey (Float, Float)))
ctxMeasureCache :: Maybe (IORef (HashMap MeasureCacheKey (Float, Float)))
  , Context -> IORef (IntMap SpanCacheEntry)
ctxSpanCache :: !(IORef (IntMap SpanCacheEntry))
  , Context -> IORef (IntMap WidgetTextCacheEntry)
ctxWidgetTextCache :: !(IORef (IntMap WidgetTextCacheEntry))
  -- Whole-layout reuse cache (Phase 5A): cached signature + solved rects,
  -- with the window size and font/theme generation it was captured under.
  , Context -> IORef (Maybe (LayoutCache, Size, Int))
ctxLayoutCache :: !(IORef (Maybe (LayoutCache, Size, Int)))
  , Context -> IORef Int
ctxMetricGen :: !(IORef Int)
  , Context -> MetricSource
ctxMetricSource :: {-# NOUNPACK #-} !MetricSource
  , Context -> IORef (Maybe MetricSource)
ctxLastMetricSource :: !(IORef (Maybe MetricSource))
  -- True when the next present must repaint the whole window (fresh retain
  -- texture, forced full, continuous present, or window expose). When False,
  -- a DamageClip frame culls the paint pass to the damaged region.
  , Context -> IORef Bool
ctxPaintFull :: !(IORef Bool)
  , Context -> Bool
ctxExternalText :: Bool
  , Context -> IORef Theme
ctxTheme :: !(IORef Theme)
  , Context -> IORef ThemeScopes
ctxThemeScopes :: !(IORef ThemeScopes)
  , Context -> IORef [Int]
ctxContainerStack :: IORef [Int]
  , Context -> IORef [FrameMsg]
ctxMessages :: IORef [FrameMsg]
  , Context -> IORef (MutablePrimArray RealWorld WidgetId)
ctxFocusables :: IORef (MutablePrimArray RealWorld WidgetId)
  , Context -> IORef Int
ctxFocusablesCount :: IORef Int
  , Context -> SpanArena
ctxSpanBase :: SpanArena
  , Context -> SpanArena
ctxSpanOverlay :: SpanArena
  , Context -> IORef InteractionState
ctxInteractionState :: !(IORef InteractionState)
  , Context -> IO (Maybe Text)
ctxClipboardGet :: IO (Maybe Text)
  , Context -> Text -> IO Bool
ctxClipboardSet :: Text -> IO Bool
  , Context -> ImageAtlas
ctxImageAtlas :: ImageAtlas
  , Context -> IORef (Maybe (IO ()))
ctxWakeLoop :: IORef (Maybe (IO ()))
  , Context -> IORef (Map TypeRep Dynamic)
ctxHost :: IORef (Map TypeRep Dynamic)
  , Context -> IORef Layout
ctxDefaultLayout :: IORef Layout
  }

{-# INLINE intKey #-}
intKey :: WidgetId -> Int
intKey :: WidgetId -> Int
intKey = Word64 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Word64 -> Int) -> (WidgetId -> Word64) -> WidgetId -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. WidgetId -> Word64
hashWidgetId