packages feed

chiasma-0.12.2.0: lib/Chiasma/Ui/Measure.hs

module Chiasma.Ui.Measure where

import qualified Data.List.NonEmpty as NonEmpty (zip)

import qualified Chiasma.Data.Axis as Axis
import Chiasma.Data.Axis (Axis (Horizontal, Vertical))
import Chiasma.Ui.Data.Measure (MLayout (..), MPane (..), MeasureTree, MeasureTreeSub, Measured (..))
import Chiasma.Ui.Data.RenderableTree (RLayout (..), RPane (..), Renderable (..), RenderableNode, RenderableTree)
import Chiasma.Ui.Data.Tree (Tree (..))
import qualified Chiasma.Ui.Data.Tree as Tree (Node (..))
import Chiasma.Ui.Data.ViewGeometry (ViewGeometry (fixedSize, maxSize, minSize))
import Chiasma.Ui.Data.ViewState (ViewState (ViewState))
import Chiasma.Ui.Measure.Balance (balanceSizes)
import Chiasma.Ui.Measure.Weights (viewWeights)

minimizedSizeOrDefault :: ViewGeometry -> Float
minimizedSizeOrDefault = fromMaybe 2 . (.minSize)

effectiveFixedSize :: ViewState -> ViewGeometry -> Maybe Float
effectiveFixedSize (ViewState minimized) viewGeom =
  if minimized then Just (minimizedSizeOrDefault viewGeom) else viewGeom.fixedSize

actualSize :: (ViewGeometry -> Maybe Float) ->  ViewState -> ViewGeometry -> Maybe Float
actualSize getter viewState viewGeom =
  getter viewGeom <|> effectiveFixedSize viewState viewGeom

actualMinSizes :: NonEmpty (ViewState, ViewGeometry) -> NonEmpty Float
actualMinSizes =
  fmap (fromMaybe 0.0 . uncurry (actualSize (.minSize)))

actualMaxSizes :: NonEmpty (ViewState, ViewGeometry) -> NonEmpty (Maybe Float)
actualMaxSizes =
  fmap (uncurry $ actualSize (.maxSize))

isMinimized :: ViewState -> ViewGeometry -> Bool
isMinimized (ViewState minimized) _ = minimized

subMeasureData :: RenderableNode -> (ViewState, ViewGeometry)
subMeasureData (Tree.Sub (Tree (Renderable s g _) _)) = (s, g)
subMeasureData (Tree.Leaf (Renderable s g _)) = (s, g)

measureLayoutViews :: Float -> NonEmpty RenderableNode -> NonEmpty Int
measureLayoutViews total views =
  balanceSizes minSizes maxSizes weights minimized cells
  where
    measureData = fmap subMeasureData views
    paneSpacers = fromIntegral (length views) - 1.0
    cells = total - paneSpacers
    sizesInCells s = if s > 1 then s else s * cells
    minSizes = fmap sizesInCells (actualMinSizes measureData)
    maxSizes = fmap (fmap sizesInCells) (actualMaxSizes measureData)
    minimized = fmap (uncurry isMinimized) measureData
    weights = viewWeights measureData

measureSub :: Int -> Int -> Axis -> RenderableNode -> Int -> MeasureTreeSub
measureSub width height axis (Tree.Sub tree) size =
  Tree.Sub (measureLayout tree newWidth newHeight axis)
  where
    (newWidth, newHeight) =
      case axis of
        Vertical -> (width, size)
        Horizontal -> (size, height)
measureSub _ _ (Axis.vertical -> vertical) (Tree.Leaf (Renderable _ _ (RPane paneId top left))) size =
  Tree.Leaf (Measured size (MPane paneId (if vertical then top else left) (if vertical then left else top)))

measureLayout :: RenderableTree -> Int -> Int -> Axis -> MeasureTree
measureLayout (Tree (Renderable _ _ (RLayout (RPane refId refTop refLeft) axis)) sub) width height (Axis.vertical -> parentVertical) =
  Tree (Measured sizeInParent (MLayout refId mainPos offPos axis)) measuredSub
  where
    sizeInParent = if parentVertical then height else width
    mainPos = if parentVertical then refTop else refLeft
    offPos = if parentVertical then refLeft else refTop
    subTotalSize = if Axis.vertical axis then height else width
    sizes = measureLayoutViews (fromIntegral subTotalSize) sub
    measuredSub = uncurry (measureSub width height axis) <$> NonEmpty.zip sub sizes

measureTree :: RenderableTree -> Int -> Int -> MeasureTree
measureTree tree width height =
  measureLayout tree width height Horizontal