~alcinnz/CatTrap

15a1fd9420538aa265f04d60606636918ca1be07 — Adrian Cochrane 1 year, 6 months ago 8308c7a
Correctly assign positions, prepare to incorporate padding into size calc
2 files changed, 50 insertions(+), 14 deletions(-)

M Graphics/Layout.hs
M Graphics/Layout/Inline.hs
M Graphics/Layout.hs => Graphics/Layout.hs +33 -6
@@ 44,6 44,8 @@ data LayoutItem m n x =
    -- | Results laying out richtext, has fixed width.
    -- Generated from `LayoutInline` for the sake of pagination.
    | LayoutInline' x (ParagraphLayout (UserData x)) PageOptions
    -- | A branch with constant bounding box.
    | LayoutConst x (PaddedBox m n) [LayoutItem m n x]
    -- | Children of a `LayoutInline` or `LayoutInline'`.
    | LayoutSpan (FragmentTree (UserData x))
-- | An empty box.


@@ 71,17 73,20 @@ layoutGetBox (LayoutInline' _ self _) = zero {
layoutGetBox (LayoutSpan self) = zero {
    B.min = fragmentSize self, B.size = fragmentSize self, B.max = fragmentSize self
}
layoutGetBox (LayoutConst _ ret _) = ret
-- | Retrieve the subtree under a node.
layoutGetChilds (LayoutFlow _ _ ret) = ret
layoutGetChilds (LayoutGrid _ _ _ ret) = ret
layoutGetChilds (LayoutSpan _) = []
layoutGetChilds (LayoutInline _ self _) = map LayoutSpan $ inlineChildren self
layoutGetChilds (LayoutInline' _ self _) = map LayoutSpan $ layoutChildren self
layoutGetChilds (LayoutConst _ _ childs) = childs
-- | 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 (LayoutConst ret _ _) = ret
layoutGetInner (LayoutSpan x) = subtreeInner x

-- | map-ready wrapper around `setCellBox` sourcing from a child node.


@@ 111,6 116,8 @@ boxMinWidth parent (LayoutGrid val self cells0 childs) = LayoutGrid val self' ce
    zeroBox = zero
boxMinWidth _ self@(LayoutInline _ _ _) = self
boxMinWidth _ self@(LayoutInline' _ _ _) = self
boxMinWidth _ (LayoutConst val self' childs) =
    LayoutConst val self' $ map (boxMinWidth Nothing) childs
boxMinWidth _ self@(LayoutSpan _) = self
-- | Update a (sub)tree to compute & cache ideal width.
boxNatWidth :: (Zero y, CastDouble y) =>


@@ 136,6 143,8 @@ boxNatWidth parent (LayoutGrid val self cells0 childs) = LayoutGrid val self' ce
    zeroBox = zero
boxNatWidth _ self@(LayoutInline _ _ _) = self
boxNatWidth _ self@(LayoutInline' _ _ _) = self
boxNatWidth _ (LayoutConst val self' childs) =
    LayoutConst val self' $ map (boxNatWidth Nothing) childs
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


@@ 153,6 162,8 @@ boxMaxWidth parent (LayoutGrid val self cells childs) = LayoutGrid val self cell
    size2box x y = zeroBox { B.min = Size x y, B.max = Size x y, B.size = Size x y }
boxMaxWidth parent self@(LayoutInline _ _ _) = self
boxMaxWidth parent self@(LayoutInline' _ _ _) = self
boxMaxWidth _ (LayoutConst val self' childs) = LayoutConst val self' $
    map (boxMaxWidth $ mapY' toDouble $ mapX' toDouble self') childs
boxMaxWidth parent self@(LayoutSpan _) = self
-- | Update a (sub)tree to compute & cache final width.
boxWidth :: (Zero y, CastDouble y) => PaddedBox b Double -> LayoutItem y Length x ->


@@ 182,6 193,9 @@ boxWidth parent (LayoutInline val (Paragraph a b c d) paging) =
    LayoutInline val (Paragraph a b c d { paragraphMaxWidth = round width }) paging
  where width = B.inline $ B.size parent
boxWidth _ (LayoutInline' a b c) = LayoutInline' a b c
boxWidth p (LayoutConst val self childs) = LayoutConst val (mapX' cb self) $
    map (boxWidth $ mapY' toDouble $ mapX' cb self) childs
  where cb = lowerLength $ width p
boxWidth parent (LayoutSpan self') = LayoutSpan self'

-- | Update a (sub)tree to compute & cache ideal legible height.


@@ 201,6 215,8 @@ boxNatHeight parent (LayoutGrid val self cells childs) = LayoutGrid val self' ce
    width = trackNat id $ inline self
boxNatHeight parent self@(LayoutInline _ _ _) = self
boxNatHeight parent self@(LayoutInline' _ _ _) = self
boxNatHeight p (LayoutConst val self' childs) = LayoutConst val self' $
    map (boxNatHeight $ width $ mapY' (lowerLength p) self') childs
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


@@ 221,6 237,8 @@ boxMinHeight parent (LayoutGrid val self cells childs) = LayoutGrid val self' ce
    width = trackNat id $ inline self
boxMinHeight parent self@(LayoutInline _ _ _) = self
boxMinHeight _ self@(LayoutInline' _ _ _) = self
boxMinHeight p (LayoutConst val self' childs) = LayoutConst val self' $
    map (boxMinHeight $ width $ mapY' (lowerLength p) self') childs
boxMinHeight parent self@(LayoutSpan _) = self
-- | Update a subtree to compute & cache maximum legible height.
boxMaxHeight :: PaddedBox Double Double -> LayoutItem Length Double x ->


@@ 242,6 260,8 @@ boxMaxHeight parent (LayoutGrid val self cells childs) = LayoutGrid val self cel
    width = inline $ size parent
boxMaxHeight _ (LayoutInline val self' paging) = LayoutInline val self' paging
boxMaxHeight _ (LayoutInline' val self' paging) = LayoutInline' val self' paging
boxMaxHeight p (LayoutConst val self' childs) = LayoutConst val self' $
    map (boxMaxHeight $ mapY' (lowerLength $ width p) self') childs
boxMaxHeight parent (LayoutSpan self') = LayoutSpan self'
-- | Update a (sub)tree to compute & cache final height.
boxHeight :: PaddedBox Double Double -> LayoutItem Length Double x -> LayoutItem Double Double x


@@ 271,6 291,9 @@ boxHeight parent (LayoutGrid val self cells0 childs) = LayoutGrid val self' cell
    width = inline $ size parent
boxHeight _ (LayoutInline val self' paging) = LayoutInline val self' paging
boxHeight _ (LayoutInline' val self' paging) = LayoutInline' val self' paging
boxHeight p (LayoutConst val self childs) =
    let self' = mapY' (lowerLength $ width p) self
    in LayoutConst val self' $ map (boxHeight self') childs
boxHeight _ (LayoutSpan self') = LayoutSpan self'

-- | Split a (sub)tree to fit within max-height.


@@ 298,6 321,7 @@ boxSplit maxheight pageheight (LayoutFlow val self childs)
    inner start (child:childs) = (start', child):inner start' childs -- TODO margin collapse?
        where start' = start + height (layoutGetBox child)
    inner _ [] = []
boxSplit _ _ self@(LayoutConst _ _ _) = (self, Nothing) -- Doesn't split.
boxSplit _ _ self@(LayoutGrid _ _ _ _) = (self, Nothing) -- TODO
boxSplit maxheight pageheight (LayoutInline a self b) =
    boxSplit maxheight pageheight $ LayoutInline' a (layoutRich self) b


@@ 318,8 342,8 @@ boxPaginate pageheight node
    | 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 :: (PropertyParser x, Eq 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'
  where
    childs' = map recurse $ zip pos' childs


@@ 332,12 356,15 @@ boxPosition pos@(x, y) (LayoutGrid val self cells childs) = LayoutGrid (pos, val
    pos' = gridPosition self cells
boxPosition pos@(x, y) (LayoutInline val self paging) =
    boxPosition pos $ LayoutInline' val (layoutRich self) paging
boxPosition pos@(x, y) (LayoutInline' val self paging) =
    LayoutInline' (pos, val) (positionChildren pos self) paging
boxPosition pos@(x, y) self@(LayoutInline' val _ _) =
    boxPosition pos $ LayoutConst val (layoutGetBox self) $ layoutGetChilds self
boxPosition pos (LayoutConst val self childs) =
    LayoutConst (pos, val) self $ map (boxPosition pos) childs
boxPosition pos (LayoutSpan self) = LayoutSpan $ positionSubtree pos self
-- | 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 :: (PropertyParser x, Eq x) => PaddedBox Double Double ->
        LayoutItem Length Length x -> Bool -> 
        [LayoutItem Double Double ((Double, Double), x)]
boxLayout parent self paginate = self9
  where
    self0 = boxMinWidth Nothing self

M Graphics/Layout/Inline.hs => Graphics/Layout/Inline.hs +17 -8
@@ 16,7 16,7 @@ import qualified Data.Text as Txt
import Data.Char (isSpace)
import Data.Int (Int32)

import Graphics.Layout.Box (Size(..), CastDouble(..), fromDouble)
import Graphics.Layout.Box (PaddedBox, Length, Size(..), CastDouble(..), fromDouble)
import Graphics.Layout.CSS.Font (Font', hbUnit)

-- | Convert from Harfbuzz units to device pixels as a Double


@@ 62,11 62,13 @@ layoutRich' (Paragraph a b c d) width =
    paragraphRect $ layoutRich $ Paragraph a b c d { paragraphMaxWidth = width }

-- | Retrieve the rect for a fragment & convert to CatTrap types.
fragmentSize :: (CastDouble x, CastDouble y) => FragmentTree a -> Size x y
fragmentSize :: (CastDouble x, CastDouble y) =>
        FragmentTree (a, PaddedBox Length Length, c) -> Size x y
fragmentSize self = Size (c $ width r) (c $ height r)
    where r = treeRect self
-- | Compute the unioned rect for a subtree.
treeRect :: FragmentTree a -> Rect Int32
treeRect :: (CastDouble m, CastDouble n) =>
        FragmentTree (a, PaddedBox m n, c) -> Rect Int32
treeRect (Branch _ childs) = foldr unionRect (Rect 0 0 0 0) $ map treeRect childs
    where unionRect a b = Rect (x_min a `min` x_min b) (y_min a `min` y_min b)
            ((x_max a `max` x_max b) - (x_min a `min` x_min b))


@@ 75,7 77,7 @@ treeRect (Leaf self) = fragmentRect self

-- | Variant of `fragmentSize` asserting to the typesystem that both fields
-- of the resulting `Size` are of the same type.
fragmentSize' :: CastDouble x => FragmentTree a -> Size x x
fragmentSize' :: CastDouble x => FragmentTree (a, PaddedBox Length Length, c) -> Size x x
fragmentSize' = fragmentSize -- Work around for typesystem.
-- | Retrieve the position of a fragment.
fragmentPos :: (Double, Double) -> Fragment a -> (Double, Double)


@@ 124,13 126,20 @@ reconstructTree' frags@(Fragment {
    sameBranch Fragment { fragmentAncestorBoxes = [] } = False
reconstructTree' [] = []

positionSubtree :: (Double, Double) -> FragmentTree (a, b, c) ->
        FragmentTree (a, b, ((Double, Double), c))
positionSubtree pos (Branch (AncestorBox (a, b, c) d e f g) childs) =
positionSubtree :: (Double, Double) ->
        FragmentTree (a, PaddedBox Length Length, c) ->
        FragmentTree (a, PaddedBox Length Length, ((Double, Double), c))
positionSubtree (x, y) self@(Branch (AncestorBox (a, b, c) d e f g) childs) =
    Branch (AncestorBox (a, b, (pos, c)) d e f g) $
        map (positionSubtree pos) childs
positionSubtree pos (Leaf (Fragment (a, b, c) d _ f g h)) =
  where
    pos = (x + hbScale (x_min rect), y + hbScale (y_min rect))
    rect = treeRect self
positionSubtree (x, y) self@(Leaf (Fragment (a, b, c) d _ f g h)) =
    Leaf (Fragment (a, b, (pos, c)) d [] f g h)
  where
    pos = (x + hbScale (x_min rect), y + hbScale (y_min rect))
    rect = treeRect self
subtreeInner :: FragmentTree (a, b, c) -> c
subtreeInner (Branch AncestorBox { boxUserData = (_, _, ret) } _) = ret
subtreeInner (Leaf Fragment { fragmentUserData = (_, _, ret) }) = ret