~alcinnz/CatTrap

d549219c1de66809be3801517e80971b977fa42b — Adrian Cochrane 1 year, 8 months ago db049ad
Reference-document generic layout/tree-traversal, seperate natural from final size fields.
4 files changed, 40 insertions(+), 7 deletions(-)

M Graphics/Layout.hs
M Graphics/Layout/Box.hs
M Graphics/Layout/CSS.hs
M Graphics/Layout/Flow.hs
M Graphics/Layout.hs => Graphics/Layout.hs +31 -4
@@ 23,16 23,25 @@ import qualified Data.Map.Strict as M
import qualified Data.Text.Glyphize as Hb
import Graphics.Text.Font.Choose (Pattern)

-- | A tree of different layout algorithms.
-- More to come...
data LayoutItem m n x =
    -- | A block element. With margins, borders, & padding.
    LayoutFlow x (PaddedBox m n) [LayoutItem m n x]
    -- | A grid or table element.
    | LayoutGrid x (Grid m n) [GridItem] [LayoutItem m n x]
    -- | Some richtext.
    | LayoutInline x Font' Paragraph PageOptions [x] -- Balkon holds children.
    -- | Results laying out richtext, has fixed width.
    -- Generated from `LayoutInline` for the sake of pagination.
    | LayoutInline' x Font' ParagraphLayout PageOptions [x]
    -- | Children of a `LayoutInline` or `LayoutInline'`.
    | LayoutSpan x Font' Fragment
-- More to come...
-- | An empty box.
nullLayout :: (PropertyParser x, Zero m, Zero n) => LayoutItem m n x
nullLayout = LayoutFlow temp zero []

--- | Retrieve the surrounding box for a layout item.
layoutGetBox :: (Zero m, Zero n, CastDouble m, CastDouble n) =>
        LayoutItem m n x -> PaddedBox m n
layoutGetBox (LayoutFlow _ ret _) = ret


@@ 53,6 62,7 @@ layoutGetBox (LayoutInline' _ f self _ _) = zero {
layoutGetBox (LayoutSpan _ f self) = zero {
    B.min = fragmentSize f self, B.size = fragmentSize f self, B.max = fragmentSize f self
}
-- | Retrieve the subtree under a node.
layoutGetChilds (LayoutFlow _ _ ret) = ret
layoutGetChilds (LayoutGrid _ _ _ ret) = ret
layoutGetChilds (LayoutSpan _ _ _) = []


@@ 60,14 70,17 @@ layoutGetChilds (LayoutInline _ font self _ vals) = map inner $ inlineChildren v
  where inner (val, fragment) = LayoutSpan val font fragment
layoutGetChilds (LayoutInline' _ font self _ vals) = map inner $ layoutChildren vals self
  where inner (val, fragment) = LayoutSpan val font fragment
-- | Retrieve the caller-specified data attached to a layout node.
layoutGetInner (LayoutFlow ret _ _) = ret
layoutGetInner (LayoutGrid ret _ _ _) = ret
layoutGetInner (LayoutInline ret _ _ _ _) = ret
layoutGetInner (LayoutInline' ret _ _ _ _) = ret
layoutGetInner (LayoutSpan ret _ _) = ret

-- | map-ready wrapper around `setCellBox` sourcing from a child node.
setCellBox' (child, cell) = setCellBox cell $ layoutGetBox child

-- | Update a (sub)tree to compute & cache minimum legible sizes.
boxMinWidth :: (Zero y, CastDouble y) =>
        Maybe Double -> LayoutItem y Length x -> LayoutItem y Length x
boxMinWidth parent (LayoutFlow val self childs) = LayoutFlow val self' childs'


@@ 92,13 105,14 @@ boxMinWidth parent (LayoutGrid val self cells0 childs) = LayoutGrid val self' ce
boxMinWidth _ self@(LayoutInline _ _ _ _ _) = self
boxMinWidth _ self@(LayoutInline' _ _ _ _ _) = self
boxMinWidth _ self@(LayoutSpan _ _ _) = self
-- | Update a (sub)tree to compute & cache ideal width.
boxNatWidth :: (Zero y, CastDouble y) =>
        Maybe Double -> LayoutItem y Length x -> LayoutItem y Length x
boxNatWidth parent (LayoutFlow val self childs) = LayoutFlow val self childs'
boxNatWidth parent (LayoutFlow val self childs) = LayoutFlow val self' childs'
  where
    {-self' = self { size = mapSizeX (B.mapAuto size') (size self) }
    self' = self { B.nat = Size size' $ block $ B.nat self }
    size' = flowNatWidth parent' self childs''
    childs'' = map (mapX' $ lowerLength selfWidth) $ map layoutGetBox childs'-}
    childs'' = map (mapX' $ lowerLength selfWidth) $ map layoutGetBox childs'
    childs' = map (boxNatWidth $ Just selfWidth) childs
    selfWidth = width $ mapX' (lowerLength parent') self
    parent' = fromMaybe 0 parent


@@ 116,6 130,7 @@ boxNatWidth parent (LayoutGrid val self cells0 childs) = LayoutGrid val self' ce
boxNatWidth _ self@(LayoutInline _ _ _ _ _) = self
boxNatWidth _ self@(LayoutInline' _ _ _ _ _) = self
boxNatWidth _ self@(LayoutSpan _ _ _) = self
-- | Update a (sub)tree to compute & cache maximum legible width.
boxMaxWidth :: CastDouble y => PaddedBox a Double -> LayoutItem y Length x -> LayoutItem y Length x
boxMaxWidth parent (LayoutFlow val self childs) = LayoutFlow val self' childs'
  where


@@ 132,6 147,7 @@ boxMaxWidth parent (LayoutGrid val self cells childs) = LayoutGrid val self cell
boxMaxWidth parent self@(LayoutInline _ _ _ _ _) = self
boxMaxWidth parent self@(LayoutInline' _ _ _ _ _) = self
boxMaxWidth parent self@(LayoutSpan _ f self') = self
-- | Update a (sub)tree to compute & cache final width.
boxWidth :: (Zero y, CastDouble y) => PaddedBox b Double -> LayoutItem y Length x ->
        LayoutItem y Double x
boxWidth parent (LayoutFlow val self childs) = LayoutFlow val self' childs'


@@ 161,6 177,7 @@ boxWidth parent (LayoutInline val font (Paragraph a b c d) paging vals) =
boxWidth _ (LayoutInline' a b c d e) = LayoutInline' a b c d e
boxWidth parent (LayoutSpan val font self') = LayoutSpan val font self'

-- | Update a (sub)tree to compute & cache ideal legible height.
boxNatHeight :: Double -> LayoutItem Length Double x -> LayoutItem Length Double x
boxNatHeight parent (LayoutFlow val self childs) = LayoutFlow val self' childs'
  where


@@ 178,6 195,7 @@ boxNatHeight parent (LayoutGrid val self cells childs) = LayoutGrid val self' ce
boxNatHeight parent self@(LayoutInline _ _ _ _ _) = self
boxNatHeight parent self@(LayoutInline' _ _ _ _ _) = self
boxNatHeight parent self@(LayoutSpan _ _ _) = self
-- | Update a (sub)tree to compute & cache minimum legible height.
boxMinHeight :: Double -> LayoutItem Length Double x -> LayoutItem Length Double x
boxMinHeight parent (LayoutFlow val self childs) = LayoutFlow val self' childs'
  where


@@ 197,6 215,7 @@ boxMinHeight parent (LayoutGrid val self cells childs) = LayoutGrid val self' ce
boxMinHeight parent self@(LayoutInline _ _ _ _ _) = self
boxMinHeight _ self@(LayoutInline' _ _ _ _ _) = self
boxMinHeight parent self@(LayoutSpan _ font self') = self
-- | Update a subtree to compute & cache maximum legible height.
boxMaxHeight :: PaddedBox Double Double -> LayoutItem Length Double x ->
        LayoutItem Length Double x
boxMaxHeight parent (LayoutFlow val self childs) = LayoutFlow val self' childs'


@@ 219,6 238,7 @@ boxMaxHeight parent (LayoutInline val font self' paging vals) =
boxMaxHeight parent (LayoutInline' val font self' paging vals) =
    LayoutInline' val font self' paging vals
boxMaxHeight parent (LayoutSpan val font self') = LayoutSpan val font self'
-- | Update a (sub)tree to compute & cache final height.
boxHeight :: PaddedBox Double Double -> LayoutItem Length Double x -> LayoutItem Double Double x
boxHeight parent (LayoutFlow val self childs) = LayoutFlow val self' childs'
  where


@@ 250,6 270,8 @@ boxHeight _ (LayoutInline' val font self' paging vals) =
    LayoutInline' val font self' paging vals
boxHeight _ (LayoutSpan val font self') = LayoutSpan val font self'

-- | Split a (sub)tree to fit within max-height.
-- May take full page height into account.
boxSplit :: PropertyParser x => Double -> Double -> LayoutItem Double Double x ->
    (LayoutItem Double Double x, Maybe (LayoutItem Double Double x))
boxSplit maxheight _ node | height (layoutGetBox node) <= maxheight = (node, Nothing)


@@ 286,11 308,13 @@ boxSplit maxheight pageheight (LayoutInline' a b self paging c) =
  where
    wrap self' = LayoutInline' a b self' paging c
boxSplit _ _ self@(LayoutSpan _ _ _) = (self, Nothing) -- Can't split!
-- | Generate a list of pages from a node, splitting subtrees where necessary.
boxPaginate pageheight node
    | (page, Just overflow) <- boxSplit pageheight pageheight node =
        page:boxPaginate pageheight overflow
    | otherwise = [node]

-- | Compute position of all nodes in the (sub)tree relative to a base coordinate.
boxPosition :: PropertyParser x => (Double, Double) -> LayoutItem Double Double x ->
    LayoutItem Double Double ((Double, Double), x)
boxPosition pos@(x, y) (LayoutFlow val self childs) = LayoutFlow (pos, val) self childs'


@@ 310,6 334,7 @@ boxPosition pos@(x, y) (LayoutInline' val font self paging vals) =
    LayoutInline' (pos, val) font self paging $ map (\(x, y) -> (fragmentPos font pos y, x)) $
            layoutChildren vals self
boxPosition pos (LayoutSpan val f self) = LayoutSpan (pos, val) f self -- No children...
-- | Compute sizes & position information for all nodes in the (sub)tree.
boxLayout :: PropertyParser x => PaddedBox Double Double -> LayoutItem Length Length x ->
        Bool -> [LayoutItem Double Double ((Double, Double), x)]
boxLayout parent self paginate = self9


@@ 326,6 351,8 @@ boxLayout parent self paginate = self9
        | otherwise = [self7]
    self9 = map (boxPosition (0, 0)) self8

-- | Compute a mapping from a layout tree indicating which glyphs for which fonts
-- are required.
-- Useful for assembling glyph atlases.
glyphsPerFont :: LayoutItem x y z -> M.Map (Pattern, Double) IS.IntSet
glyphsPerFont (LayoutSpan _ font self) =

M Graphics/Layout/Box.hs => Graphics/Layout/Box.hs +7 -2
@@ 32,8 32,8 @@ data PaddedBox m n = PaddedBox {
    min :: Size m n,
    -- | The maximum amount of pixels this box should take.
    max :: Size m n,
    -- --| The ideal number of pixels this box should take.
    -- nat :: Size Double Double, -- FIXME Use seperate field for natsize.
    -- | The ideal number of pixels this box should take.
    nat :: Size Double Double,
    -- | The amount of pixels this box should take.
    size :: Size m n,
    -- | The amount of space between the box & the border.


@@ 48,6 48,7 @@ zeroBox :: PaddedBox Double Double
zeroBox = PaddedBox {
    min = Size 0 0,
    max = Size 0 0,
    nat = Size 0 0,
    size = Size 0 0,
    padding = Border 0 0 0 0,
    border = Border 0 0 0 0,


@@ 58,6 59,7 @@ lengthBox :: PaddedBox Length Length
lengthBox = PaddedBox {
    min = Size Auto Auto,
    max = Size Auto Auto,
    nat = Size 0 0,
    size = Size Auto Auto,
    padding = Border zero zero zero zero,
    border = Border zero zero zero zero,


@@ 69,6 71,7 @@ mapX' :: (n -> nn) -> PaddedBox m n -> PaddedBox m nn
mapX' cb PaddedBox {..} = PaddedBox {
    min = Size (cb $ inline min) (block min),
    size = Size (cb $ inline size) (block size),
    nat = Size 0 0,
    max = Size (cb $ inline max) (block max),
    padding = mapX cb padding,
    border = mapX cb border,


@@ 79,6 82,7 @@ mapY' :: (m -> mm) -> PaddedBox m n -> PaddedBox mm n
mapY' cb PaddedBox {..} = PaddedBox {
    min = Size (inline min) (cb $ block min),
    size = Size (inline size) (cb $ block size),
    nat = Size 0 0,
    max = Size (inline max) (cb $ block max),
    padding = mapY cb padding,
    border = mapY cb border,


@@ 137,6 141,7 @@ instance (Zero m, Zero n) => Zero (PaddedBox m n) where
    zero = PaddedBox {
        min = Size zero zero,
        max = Size zero zero,
        nat = Size 0 0,
        size = Size zero zero,
        padding = Border zero zero zero zero,
        border = Border zero zero zero zero,

M Graphics/Layout/CSS.hs => Graphics/Layout/CSS.hs +1 -0
@@ 62,6 62,7 @@ instance PropertyParser a => PropertyParser (CSSBox a) where
        cssBox = PaddedBox {
            B.min = Size auto auto,
            size = Size auto auto,
            nat = Size 0 0,
            B.max = Size auto auto,
            padding = noborder,
            border = noborder,

M Graphics/Layout/Flow.hs => Graphics/Layout/Flow.hs +1 -1
@@ 75,7 75,7 @@ flowHeight parent self
    | otherwise = small
  where
    small = flowMinHeight (block $ B.min parent) self
    natural = flowNatHeight (block $ size parent) self []
    natural = flowNatHeight (block $ B.nat parent) self []
    large = flowMaxHeight (block $ B.max parent) self

-- | Compute position of all children relative to this block element.