From 8fb3f54afe3a41d83f48c35ea10a02daa1d250c4 Mon Sep 17 00:00:00 2001 From: Adrian Cochrane Date: Mon, 20 Nov 2023 13:10:59 +1300 Subject: [PATCH] Integrate FlexBox layout --- Graphics/Layout.hs | 38 ++++++- Graphics/Layout/Box.hs | 9 +- Graphics/Layout/CSS.hs | 8 +- Graphics/Layout/Flex.hs | 193 ++++++++++++++++++++++++++---------- Graphics/Layout/Flex/CSS.hs | 1 + 5 files changed, 192 insertions(+), 57 deletions(-) diff --git a/Graphics/Layout.hs b/Graphics/Layout.hs index 3083b76..faa3685 100644 --- a/Graphics/Layout.hs +++ b/Graphics/Layout.hs @@ -58,7 +58,7 @@ data LayoutItem m n x = | LayoutConst x (PaddedBox m n) [LayoutItem m n x] -- | Children of a `LayoutInline` or `LayoutInline'`. | LayoutSpan (FragmentTree (UserData m n x)) - | LayoutFlex x (PaddedBox m n) (FlexParent (LayoutItem m n x) m) + | LayoutFlex x (FlexParent (LayoutItem m n x) m) deriving (Show, Eq) -- | An empty box. nullLayout :: (PropertyParser x, Zero m, Zero n) => LayoutItem m n x @@ -88,6 +88,10 @@ layoutGetBox (LayoutInline' _ self _) = zero { } layoutGetBox (LayoutSpan self) = treeBox self layoutGetBox (LayoutConst _ ret _) = ret +layoutGetBox (LayoutFlex _ self) = flexGetBox layoutGetBox' self +layoutGetBox' :: (Zero m, Zero n, CastDouble m, CastDouble n) => + LayoutItem m n x -> PaddedBox Double Double +layoutGetBox' = mapX' toDouble . mapY' toDouble . layoutGetBox -- | Retrieve the subtree under a node. layoutGetChilds (LayoutFlow _ _ ret) = ret layoutGetChilds (LayoutGrid _ _ _ ret) = ret @@ -95,6 +99,7 @@ layoutGetChilds (LayoutSpan _) = [] layoutGetChilds (LayoutInline _ self _) = map LayoutSpan $ inlineChildren self layoutGetChilds (LayoutInline' _ self _) = map LayoutSpan $ layoutChildren self layoutGetChilds (LayoutConst _ _ childs) = childs +layoutGetChilds (LayoutFlex _ x) = map Fl.flexInner $ concat $ Fl.children x -- | Retrieve the caller-specified data attached to a layout node. layoutGetInner (LayoutFlow ret _ _) = ret layoutGetInner (LayoutGrid ret _ _ _) = ret @@ -102,6 +107,7 @@ layoutGetInner (LayoutInline ret _ _) = ret layoutGetInner (LayoutInline' ret _ _) = ret layoutGetInner (LayoutConst ret _ _) = ret layoutGetInner (LayoutSpan x) = treeInner x +layoutGetInner (LayoutFlex ret _ ) = ret -- | Retrieve the font associated with inline layout. fragmentFont x = let (ret, _, _) = treeInner' x in ret @@ -136,6 +142,7 @@ boxMinWidth _ self@(LayoutInline' _ _ _) = self boxMinWidth _ (LayoutConst val self' childs) = LayoutConst val self' $ map (boxMinWidth Nothing) childs boxMinWidth _ self@(LayoutSpan _) = self +boxMinWidth size self@(LayoutFlex a b) = LayoutFlex a $ flexMap (boxMinWidth size) b -- | Update a (sub)tree to compute & cache ideal width. boxNatWidth :: (Zero y, CastDouble y, NFData y) => Maybe Double -> LayoutItem y Length x -> LayoutItem y Length x @@ -163,6 +170,7 @@ boxNatWidth _ self@(LayoutInline' _ _ _) = self boxNatWidth _ (LayoutConst val self' childs) = LayoutConst val self' $ map (boxNatWidth Nothing) childs boxNatWidth _ self@(LayoutSpan _) = self +boxNatWidth size (LayoutFlex a b ) = LayoutFlex a $ flexMap (boxNatWidth size) b -- | Update a (sub)tree to compute & cache maximum legible width. boxMaxWidth :: (CastDouble y, Zero y, NFData y) => PaddedBox a Double -> LayoutItem y Length x -> LayoutItem y Length x @@ -183,6 +191,9 @@ 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 +boxMaxWidth parent (LayoutFlex a b) = LayoutFlex a $ (case Fl.direction b of + Fl.Row -> flip flexWrap (inline $ B.size parent) + Fl.Column -> id) $ flexMap (boxMaxWidth parent) b -- | Update a (sub)tree to compute & cache final width. boxWidth :: (Zero y, CastDouble y, NFData y) => PaddedBox b Double -> LayoutItem y Length x -> LayoutItem y Double x @@ -218,6 +229,7 @@ boxWidth p (LayoutConst val self childs) = LayoutConst val (mapX' cb self) $ where cb = lowerLength $ width p boxWidth parent (LayoutSpan self') = LayoutSpan $ treeMap (mapX' $ lowerLength $ width parent) self' +boxWidth parent (LayoutFlex a b) = LayoutFlex a $ flexMap (boxWidth parent) b -- | Update a (sub)tree to compute & cache ideal legible height. boxNatHeight :: Double -> LayoutItem Length Double x -> LayoutItem Length Double x @@ -239,6 +251,7 @@ 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 +boxNatHeight parent (LayoutFlex a b) = LayoutFlex a $ flexMap (boxNatHeight parent) b -- | 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' @@ -261,6 +274,7 @@ 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 +boxMinHeight parent self@(LayoutFlex a b) = LayoutFlex a $ flexMap (boxMinHeight parent) b -- | Update a subtree to compute & cache maximum legible height. boxMaxHeight :: PaddedBox Double Double -> LayoutItem Length Double x -> LayoutItem Length Double x @@ -284,6 +298,7 @@ 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' +boxMaxHeight parent (LayoutFlex a b) = LayoutFlex a $ flexMap (boxMaxHeight parent) b -- | 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' @@ -319,6 +334,8 @@ boxHeight p (LayoutConst val self childs) = in LayoutConst val self' $ map (boxHeight self') childs boxHeight p (LayoutSpan self') = LayoutSpan $ treeMap (mapY' $ lowerLength $ width p) self' +boxHeight p (LayoutFlex a b) = LayoutFlex a $ + flexResolve (innerMain . layoutGetBox) (width p) $ flexMap (boxHeight p) b -- | Split a (sub)tree to fit within max-height. -- May take full page height into account. @@ -359,6 +376,12 @@ boxSplit maxheight pageheight (LayoutInline' a self paging) = where wrap self' = LayoutInline' a self' paging boxSplit _ _ self@(LayoutSpan _) = (self, Nothing) -- Can't split! +boxSplit maxheight pageheight (LayoutFlex a self) = + -- FIXME: What if any children are too big for the page? + let (p0, p1) = flexSplit ( B.size . layoutGetBox ) maxheight pageheight self + in if null $ Fl.children p1 + then (LayoutFlex a p0, Nothing) + else (LayoutFlex a p0, Just $ LayoutFlex a p1) -- | Generate a list of pages from a node, splitting subtrees where necessary. boxPaginate pageheight node | (page, Just overflow) <- boxSplit pageheight pageheight node = @@ -368,6 +391,19 @@ boxPaginate pageheight node -- | Compute position of all nodes in the (sub)tree relative to a base coordinate. boxPosition :: (PropertyParser x, Eq x) => (Double, Double) -> LayoutItem Double Double x -> LayoutItem Double Double ((Double, Double), x) +boxPosition (x,y) (LayoutFlow val box [LayoutFlex val' self]) = + LayoutFlow ((x,y), val) box [ -- Obtaining size from parent + LayoutFlex (pos', val') $ + flexPosition boxPosition boxSize pos' (B.size box) self + ] + where + boxSize box' = let b = layoutGetBox box' in B.width b `Size` B.height b + pos' = (x + B.leftSpace box, y + B.rightSpace box) +boxPosition pos self@(LayoutFlex val self') = + LayoutFlex (pos, val) $ flexPosition boxPosition boxSize pos size self' + where + boxSize box' = let b = layoutGetBox box' in B.width b `Size` B.height b + size = B.size $ layoutGetBox self boxPosition pos@(x, y) (LayoutFlow val self childs) = LayoutFlow (pos, val) self childs' where childs' = parMap' recurse $ zip pos' childs diff --git a/Graphics/Layout/Box.hs b/Graphics/Layout/Box.hs index 5bdfb6d..5641111 100644 --- a/Graphics/Layout/Box.hs +++ b/Graphics/Layout/Box.hs @@ -181,10 +181,17 @@ class CastDouble a where fromDouble :: Double -> a -- | Convert a double or length to a double. toDouble :: a -> Double + toDouble = toDoubleWithin 0 + toDoubleWithin :: Double -> a -> Double + toDoubleWithin _ = toDouble + toDoubleWithinAuto :: Double -> Double -> a -> Double + toDoubleWithinAuto _ = toDoubleWithin instance CastDouble Double where fromDouble = id toDouble = id instance CastDouble Length where fromDouble = Pixels - toDouble = lowerLength 0 + toDoubleWithin = lowerLength + toDoubleWithinAuto x _ Auto = x + toDoubleWithinAuto _ x y = toDoubleWithin x y diff --git a/Graphics/Layout/CSS.hs b/Graphics/Layout/CSS.hs index e405b1f..8ea3530 100644 --- a/Graphics/Layout/CSS.hs +++ b/Graphics/Layout/CSS.hs @@ -56,7 +56,7 @@ finalizeCSS root parent self@StyleTree { (finalizeBox (collapseTBorders' self') font_) ([finalizeCSS root font_ child { style = child' { display = Block } } | child@StyleTree { style = child'@CSSBox { display = TableCaption } } <- childs] ++ - [finalizeTable root font_ (inner self') opts childs]) + [finalizeTable root font_ temp opts childs]) where font_ = pattern2font (font self') (font' self') parent root finalizeCSS root parent self@StyleTree { @@ -73,9 +73,9 @@ finalizeCSS root parent self@StyleTree { finalizeCSS root parent self@StyleTree { style = self'@CSSBox { display = Flex, flexOptions = flex }, children = childs - } = LayoutFlex (inner' font_ self' ) (finalizeBox self' font_) $ - lowerFlex flex font_ (map flexOptions childs') - (flip map childs $ finalizeCSS root font_) (map style2font childs') + } = LayoutFlow (inner' font_ self' ) (finalizeBox self' font_) [ + LayoutFlex temp $ lowerFlex flex font_ (map flexOptions childs') + (flip map childs $ finalizeCSS root font_) (map style2font childs')] where font_ = style2font self' style2font style = pattern2font (font style) (font' style) parent root diff --git a/Graphics/Layout/Flex.hs b/Graphics/Layout/Flex.hs index 74cc479..8f205bb 100644 --- a/Graphics/Layout/Flex.hs +++ b/Graphics/Layout/Flex.hs @@ -1,10 +1,10 @@ -module Graphics.Layout.Flex(FlexParent(..), FlexChild(..), - Direction(..), FlexWrapping(..), Justification(..), Alignment(..), - flexMaxBasis, flexSumBasis, flexWrap) where +module Graphics.Layout.Flex where -import Graphics.Layout.Box as B (Length, lowerLength, Size(..), PaddedBox(..), - maxWidth, width, minWidth, maxHeight, height, minHeight) +import Graphics.Layout.Box as B (Length(..), lowerLength, Size(..), PaddedBox(..), + maxWidth, width, minWidth, maxHeight, height, minHeight, CastDouble(..), Zero(..)) import Data.List (intersperse) +import GHC.Real (infinity) +import Data.Maybe (fromMaybe) data FlexParent a b = FlexParent { direction :: Direction, @@ -14,7 +14,8 @@ data FlexParent a b = FlexParent { alignLines :: Maybe Justification, -- `Nothing` is "stretch" baseGap :: b, crossGap :: b, - children :: [[FlexChild a b]] -- 2D list to store lines once split. + children :: [[FlexChild a b]], -- 2D list to store lines once split. + pageWidth :: Double -- Pagination argument } deriving (Eq, Show, Read) data FlexChild a b = FlexChild { grow :: Double, @@ -31,59 +32,81 @@ data Justification = JStart | JEnd | JCenter | JSpaceBetween | JSpaceAround | JS data Alignment = AlStretch | AlStart | AlEnd | AlCenter | AlBaseline deriving (Eq, Show, Read) -flexMaxBasis :: FlexParent a Length -> Double -> Double -flexMaxBasis self outersize = maximum [lowerLength outersize $ basis child | - row <- children self, child <- row] -flexSumBasis :: FlexParent a Length -> Double -> Double -flexSumBasis self size = maximum [Prelude.sum $ map (lowerLength size) $ +flexMap :: (a -> b) -> FlexParent a c -> FlexParent b c +flexMap cb self = FlexParent { + direction = direction self, reverseRows = reverseRows self, wrap = wrap self, + justify = justify self, alignLines = alignLines self, + baseGap = baseGap self, crossGap = crossGap self, pageWidth = pageWidth self, + children = [[FlexChild { + grow = grow kid, shrink = shrink kid, basis = basis kid, + alignment = alignment kid, + flexInner = cb $ flexInner kid -- The important line! + } | kid <- row] | row <- children self] + } +flexResolve :: CastDouble b => (a -> Direction -> Double) -> Double -> + FlexParent a b -> FlexParent a Double +flexResolve cb size self = FlexParent { + direction = direction self, reverseRows = reverseRows self, wrap = wrap self, + justify = justify self, alignLines = alignLines self, + baseGap = toDoubleWithin size $ baseGap self, + crossGap = toDoubleWithin size $ crossGap self, + pageWidth = pageWidth self, + children = [[FlexChild { + grow = grow kid, shrink = shrink kid, + basis = toDoubleWithinAuto (flexInner kid `cb` direction self) size $ basis kid, + alignment = alignment kid, flexInner = flexInner kid + } | kid <- row] | row <- children self] + } + +flexMaxBasis :: FlexParent a Double -> Double +flexMaxBasis self = maximum [basis child | row <- children self, child <- row] +flexSumBasis :: FlexParent a Double -> Double +flexSumBasis self = maximum [Prelude.sum $ intersperse (baseGap self) $ map basis row | row <- children self] -flexWrap :: FlexParent a Length -> Double -> FlexParent a Double +flexWrap :: CastDouble b => FlexParent a b -> Double -> FlexParent a b flexWrap self size - | NoWrap <- wrap self = post self' - | Wrap <- wrap self = post wrapped - | WrapReverse <- wrap self = post wrapped {children=reverse$children wrapped} + | NoWrap <- wrap self = post self + | Wrap <- wrap self = post self' + | WrapReverse <- wrap self = post self' { children=reverse $ children self' } where - self' = FlexParent { - direction = direction self, - reverseRows = reverseRows self, - wrap = wrap self, - justify = justify self, - alignLines = alignLines self, - baseGap = lowerLength size $ baseGap self, - crossGap = lowerLength size $ crossGap self, - children = map (map $ child' size) $ children self - } - child' size x = FlexChild { - grow = grow x, shrink = shrink x, - basis = lowerLength size $ basis x, - alignment = alignment x, flexInner = flexInner x - } - wrapped = self' { - children = concatMap wrapRow $ children self' + self' = self { + children = concatMap wrapRow $ children self } - wrapRow :: [FlexChild a Double] -> [[FlexChild a Double]] + wrapRow :: CastDouble b => [FlexChild a b] -> [[FlexChild a b]] wrapRow [] = [] - wrapRow kids@(kid:_) = let (row, rest) = splitRow kids $ basis kid - in (row):wrapRow rest + wrapRow kids@(kid:_) = let (row, rest) = splitRow kids $ basis' kid + in row:wrapRow rest + splitRow :: CastDouble b => [FlexChild a b] -> Double -> ([FlexChild a b], [FlexChild a b]) splitRow (kid:kids) end | end > size = ([], kid:kids) - | otherwise = let (kids', rest) = splitRow kids (end + baseGap self' + basis kid) + | otherwise = let (kids', rest) = splitRow kids (end + baseGap' self + basis' kid) in (kid:kids', rest) splitRow [] _ = ([], []) + post :: CastDouble b => FlexParent a b -> FlexParent a b post flex | reverseRows self = post' flex { children = map reverse $ children flex } | otherwise = post' flex + post' :: CastDouble b => FlexParent a b -> FlexParent a b post' flex = flex { children = map resizeRow $ children flex } + resizeRow :: CastDouble b => [FlexChild a b] -> [FlexChild a b] resizeRow row - | rowSize > size = [kid { basis = basis kid - shrink kid * sfr } | kid <- row] - | rowSize < size = [kid { basis = basis kid + grow kid * gfr } | kid <- row] + | rowSize > size = [kid { + basis = fromDouble $ basis' kid - shrink kid * sfr + } | kid <- row] + | rowSize < size = [kid { + basis = fromDouble $ basis' kid + grow kid * gfr + } | kid <- row] | otherwise = row where - rowSize = Prelude.sum $ intersperse (baseGap self') $ map basis row + rowSize = Prelude.sum $ intersperse (baseGap' self) $ map basis' row sfr = (rowSize - size)/(Prelude.sum $ map shrink row) gfr = (size - rowSize)/(Prelude.sum $ map grow row) + baseGap' :: CastDouble b => FlexParent a b -> Double + baseGap' = toDouble . baseGap + basis' :: CastDouble b => FlexChild a b -> Double + basis' = toDouble . basis flexRowSize :: (a -> Double) -> [FlexChild a b] -> Double flexRowSize cb row = maximum $ map (cb . flexInner) row @@ -98,12 +121,22 @@ justifyOffset outersize ks g JCenter = half $ outersize - innersize g ks justifyOffset _ _ _ JSpaceBetween = 0 justifyOffset outersize ks g JSpaceAround = half $ (outersize - innersize g ks)/length' ks +justifyOffset _ ks _ _ | length ks <= 1 = 0 -- No gaps to space, avoid numeric errors. justifyOffset size ks g JSpaceEvenly = (size - innersize g ks)/(length' ks + 1) justifySpacing size ks g JSpaceBetween = (size - innersize g ks)/(length' ks - 1) justifySpacing size ks g JSpaceAround = (size - innersize g ks)/length' ks justifySpacing size ks g JSpaceEvenly = (size - innersize g ks)/(length' ks + 1) justifySpacing _ _ _ _ = 0 +flexJustify :: (a -> Double) -> Double -> [a] -> Double -> Justification -> [(Double, a)] +flexJustify cb size kids gap just = inner kids offs + where + offs = justifyOffset size kids' gap just + spacing = justifySpacing size kids' gap just + kids' = map cb kids + inner (k:ks) start = (start, k):inner ks (start + cb k + gap) + inner [] _ = [] + alignOffset :: Double -> Double -> Alignment -> Double alignOffset _ _ AlStretch = 0 -- Needs special handling elsewhere alignOffset _ _ AlStart = 0 @@ -137,17 +170,75 @@ outerMaxCross box Row = maxHeight box outerMaxCross box Column = maxWidth box innerMinMain, innerMain, innerMaxMain :: Num m => PaddedBox m m -> Direction -> m -innerMinMain box = innerMain' $ B.min box -innerMain box = innerMain' $ B.size box -innerMaxMain box = innerMain' $ B.max box +innerMinMain box = sizeMain $ B.min box +innerMain box = sizeMain $ B.size box +innerMaxMain box = sizeMain $ B.max box innerMinCross, innerCross, innerMaxCross :: Num m => PaddedBox m m -> Direction -> m -innerMinCross box = innerCross' $ B.min box -innerCross box = innerCross' $ B.size box -innerMaxCross box = innerCross' $ B.max box - -innerMain', innerCross' :: Num m => Size m m -> Direction -> m -innerMain' self Row = inline self -innerMain' self Column = block self -innerCross' self Row = block self -innerCross' self Column = inline self +innerMinCross box = sizeCross $ B.min box +innerCross box = sizeCross $ B.size box +innerMaxCross box = sizeCross $ B.max box + +sizeMain, sizeCross :: Num m => Size m m -> Direction -> m +sizeMain self Row = inline self +sizeMain self Column = block self +sizeCross self Row = block self +sizeCross self Column = inline self + +flexGetBox :: (Zero m, CastDouble m, Zero n, CastDouble n) => + (a -> PaddedBox Double Double) -> FlexParent a m -> PaddedBox m n +flexGetBox cb self = zero { + B.min = flexMaxBasis self' `size` flexRowsSize (cb' innerMinCross) self', + B.max = fromRational infinity `size` fromRational infinity, + B.nat = flexSumBasis self' `size` flexRowsSize (cb' innerCross) self', + B.size = flexSumBasis self' `size` flexRowsSize (cb' innerCross) self' + } where + size main cross + | Row <- direction self = fromDouble main `Size` fromDouble cross + | otherwise = fromDouble cross `Size` fromDouble main + cb' cb_ = flip cb_ (direction self) . cb + self' = flexResolve (innerMain . cb) 0 self + +flexSplit :: (a -> Size Double Double) -> Double -> Double -> FlexParent a Double -> + (FlexParent a Double, FlexParent a Double) +flexSplit cb h _ self@FlexParent { direction = Row, pageWidth = w } = + (self' { children = page0 }, self' { children = page1 }) + where + self' = flexWrap self w + (page0, page1) = splitRows (-crossGap self) $ children self + splitRows start (row:rows) + | start > h = ([row], rows) + | otherwise = + let (rows', rest) = flip splitRows rows $ + start + crossGap self + flexRowSize (inline . cb) row + in (row:rows', rest) + splitRows _ [] = ([], []) +flexSplit cb h h' self@FlexParent { direction = Column, pageWidth = w } + | measure h = (flexWrap self h, self { children = [] }) + -- If it fits on neither page... Row-direction is more versatile! + | not $ measure h' = flexSplit cb h h' self { direction = Row } + | otherwise = (self { children = [] }, flexWrap self h') + where + measure space = (block . cb) `flexRowsSize` flexWrap self space <= w + +flexPosition :: ((Double, Double) -> a -> b) -> (a -> Size Double Double) -> + (Double, Double) -> Size Double Double -> + FlexParent a Double -> FlexParent b Double +flexPosition cb cb' (x,y) size self@FlexParent { direction = dir } = self { + children = map rowPosition $ flexJustify rowsize (sizeCross size dir) + (children self) (crossGap self) (justify self) + } where + rowsize = flexRowSize $ flip sizeCross dir . cb' + -- TODO: Handle stretch properly + rowPosition (rpos, row) = + let rsize = flexRowSize (flip sizeCross dir . cb') row + in map (alignChild rsize rpos) $ flexJustify basis rsize row + (baseGap self) (fromMaybe JSpaceAround $ alignLines self) + alignChild rsize rpos (kpos, kid@FlexChild { + flexInner = kid', alignment = align' + }) = kid { + flexInner = flip cb kid' $ sz kpos $ + rpos + alignOffset rsize (flip sizeCross dir $ cb' kid') align' + } + sz m c | Row <- direction self = (x + m, y + c) + | otherwise = (x + c, y + m) diff --git a/Graphics/Layout/Flex/CSS.hs b/Graphics/Layout/Flex/CSS.hs index 9d4bd37..a0fdfa2 100644 --- a/Graphics/Layout/Flex/CSS.hs +++ b/Graphics/Layout/Flex/CSS.hs @@ -183,6 +183,7 @@ lowerFlex self font kids kids' fonts' = FlexParent { crossGap = case directionCSS self of Row -> flip finalizeLength font $ columnGapCSS self F.Column -> flip finalizeLength font $ rowGapCSS self, + pageWidth = 0, children = [[FlexChild { grow = growCSS kid, shrink = shrinkCSS kid, -- 2.30.2