packages feed

cattrap 0.4.0.0 → 0.5.0.0

raw patch · 10 files changed

+1004/−20 lines, 10 filesdep ~xml-conduitPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: xml-conduit

API changes (from Hackage documentation)

+ Graphics.Layout: LayoutFlex :: x -> Flex (LayoutItem m n x) m -> LayoutItem m n x
+ Graphics.Layout.Box: toDoubleWithin :: CastDouble a => Double -> a -> Double
+ Graphics.Layout.Box: toDoubleWithinAuto :: CastDouble a => Double -> Double -> a -> Double
+ Graphics.Layout.CSS: Flex :: Display
+ Graphics.Layout.CSS: [flexOptions] :: CSSBox a -> CSSFlex
+ Graphics.Layout.Flex: AlBaseline :: Alignment
+ Graphics.Layout.Flex: AlCenter :: Alignment
+ Graphics.Layout.Flex: AlEnd :: Alignment
+ Graphics.Layout.Flex: AlStart :: Alignment
+ Graphics.Layout.Flex: AlStretch :: Alignment
+ Graphics.Layout.Flex: Column :: Direction
+ Graphics.Layout.Flex: Flex :: Direction -> Bool -> FlexWrapping -> Justification -> Maybe Justification -> b -> b -> [[FlexChild a b]] -> Double -> Flex a b
+ Graphics.Layout.Flex: FlexChild :: Double -> Double -> b -> Alignment -> a -> FlexChild a b
+ Graphics.Layout.Flex: JCenter :: Justification
+ Graphics.Layout.Flex: JEnd :: Justification
+ Graphics.Layout.Flex: JSpaceAround :: Justification
+ Graphics.Layout.Flex: JSpaceBetween :: Justification
+ Graphics.Layout.Flex: JSpaceEvenly :: Justification
+ Graphics.Layout.Flex: JStart :: Justification
+ Graphics.Layout.Flex: NoWrap :: FlexWrapping
+ Graphics.Layout.Flex: Row :: Direction
+ Graphics.Layout.Flex: Wrap :: FlexWrapping
+ Graphics.Layout.Flex: WrapReverse :: FlexWrapping
+ Graphics.Layout.Flex: [alignLines] :: Flex a b -> Maybe Justification
+ Graphics.Layout.Flex: [alignment] :: FlexChild a b -> Alignment
+ Graphics.Layout.Flex: [baseGap] :: Flex a b -> b
+ Graphics.Layout.Flex: [basis] :: FlexChild a b -> b
+ Graphics.Layout.Flex: [children] :: Flex a b -> [[FlexChild a b]]
+ Graphics.Layout.Flex: [crossGap] :: Flex a b -> b
+ Graphics.Layout.Flex: [direction] :: Flex a b -> Direction
+ Graphics.Layout.Flex: [flexInner] :: FlexChild a b -> a
+ Graphics.Layout.Flex: [grow] :: FlexChild a b -> Double
+ Graphics.Layout.Flex: [justify] :: Flex a b -> Justification
+ Graphics.Layout.Flex: [pageWidth] :: Flex a b -> Double
+ Graphics.Layout.Flex: [reverseRows] :: Flex a b -> Bool
+ Graphics.Layout.Flex: [shrink] :: FlexChild a b -> Double
+ Graphics.Layout.Flex: [wrap] :: Flex a b -> FlexWrapping
+ Graphics.Layout.Flex: alignOffset :: Double -> Double -> Alignment -> Double
+ Graphics.Layout.Flex: data Alignment
+ Graphics.Layout.Flex: data Direction
+ Graphics.Layout.Flex: data Flex a b
+ Graphics.Layout.Flex: data FlexChild a b
+ Graphics.Layout.Flex: data FlexWrapping
+ Graphics.Layout.Flex: data Justification
+ Graphics.Layout.Flex: flexGetBox :: (Zero m, CastDouble m, Zero n, CastDouble n) => (a -> PaddedBox Double Double) -> Flex a m -> PaddedBox m n
+ Graphics.Layout.Flex: flexJustify :: (a -> Double) -> Double -> [a] -> Double -> Justification -> [(Double, a)]
+ Graphics.Layout.Flex: flexMap :: (a -> b) -> Flex a c -> Flex b c
+ Graphics.Layout.Flex: flexMaxBasis :: Flex a Double -> Double
+ Graphics.Layout.Flex: flexPosition :: ((Double, Double) -> a -> b) -> (a -> Size Double Double) -> (Double, Double) -> Size Double Double -> Flex a Double -> Flex b Double
+ Graphics.Layout.Flex: flexResolve :: CastDouble b => (a -> Direction -> Double) -> Double -> Flex a b -> Flex a Double
+ Graphics.Layout.Flex: flexRowSize :: (a -> Double) -> [FlexChild a b] -> Double
+ Graphics.Layout.Flex: flexRowsSize :: (a -> Double) -> Flex a Double -> Double
+ Graphics.Layout.Flex: flexSplit :: (a -> Size Double Double) -> Double -> Double -> Flex a Double -> (Flex a Double, Flex a Double)
+ Graphics.Layout.Flex: flexSumBasis :: Flex a Double -> Double
+ Graphics.Layout.Flex: flexWrap :: CastDouble b => Flex a b -> Double -> Flex a b
+ Graphics.Layout.Flex: innerCross :: Num m => PaddedBox m m -> Direction -> m
+ Graphics.Layout.Flex: innerMain :: Num m => PaddedBox m m -> Direction -> m
+ Graphics.Layout.Flex: innerMaxCross :: Num m => PaddedBox m m -> Direction -> m
+ Graphics.Layout.Flex: innerMaxMain :: Num m => PaddedBox m m -> Direction -> m
+ Graphics.Layout.Flex: innerMinCross :: Num m => PaddedBox m m -> Direction -> m
+ Graphics.Layout.Flex: innerMinMain :: Num m => PaddedBox m m -> Direction -> m
+ Graphics.Layout.Flex: instance (GHC.Classes.Eq b, GHC.Classes.Eq a) => GHC.Classes.Eq (Graphics.Layout.Flex.Flex a b)
+ Graphics.Layout.Flex: instance (GHC.Classes.Eq b, GHC.Classes.Eq a) => GHC.Classes.Eq (Graphics.Layout.Flex.FlexChild a b)
+ Graphics.Layout.Flex: instance (GHC.Read.Read b, GHC.Read.Read a) => GHC.Read.Read (Graphics.Layout.Flex.Flex a b)
+ Graphics.Layout.Flex: instance (GHC.Read.Read b, GHC.Read.Read a) => GHC.Read.Read (Graphics.Layout.Flex.FlexChild a b)
+ Graphics.Layout.Flex: instance (GHC.Show.Show b, GHC.Show.Show a) => GHC.Show.Show (Graphics.Layout.Flex.Flex a b)
+ Graphics.Layout.Flex: instance (GHC.Show.Show b, GHC.Show.Show a) => GHC.Show.Show (Graphics.Layout.Flex.FlexChild a b)
+ Graphics.Layout.Flex: instance GHC.Classes.Eq Graphics.Layout.Flex.Alignment
+ Graphics.Layout.Flex: instance GHC.Classes.Eq Graphics.Layout.Flex.Direction
+ Graphics.Layout.Flex: instance GHC.Classes.Eq Graphics.Layout.Flex.FlexWrapping
+ Graphics.Layout.Flex: instance GHC.Classes.Eq Graphics.Layout.Flex.Justification
+ Graphics.Layout.Flex: instance GHC.Read.Read Graphics.Layout.Flex.Alignment
+ Graphics.Layout.Flex: instance GHC.Read.Read Graphics.Layout.Flex.Direction
+ Graphics.Layout.Flex: instance GHC.Read.Read Graphics.Layout.Flex.FlexWrapping
+ Graphics.Layout.Flex: instance GHC.Read.Read Graphics.Layout.Flex.Justification
+ Graphics.Layout.Flex: instance GHC.Show.Show Graphics.Layout.Flex.Alignment
+ Graphics.Layout.Flex: instance GHC.Show.Show Graphics.Layout.Flex.Direction
+ Graphics.Layout.Flex: instance GHC.Show.Show Graphics.Layout.Flex.FlexWrapping
+ Graphics.Layout.Flex: instance GHC.Show.Show Graphics.Layout.Flex.Justification
+ Graphics.Layout.Flex: justifyOffset :: Double -> [Double] -> Double -> Justification -> Double
+ Graphics.Layout.Flex: justifySpacing :: Double -> [Double] -> Double -> Justification -> Double
+ Graphics.Layout.Flex: outerCross :: Num m => PaddedBox m m -> Direction -> m
+ Graphics.Layout.Flex: outerMain :: Num m => PaddedBox m m -> Direction -> m
+ Graphics.Layout.Flex: outerMaxCross :: Num m => PaddedBox m m -> Direction -> m
+ Graphics.Layout.Flex: outerMaxMain :: Num m => PaddedBox m m -> Direction -> m
+ Graphics.Layout.Flex: outerMinCross :: Num m => PaddedBox m m -> Direction -> m
+ Graphics.Layout.Flex: outerMinMain :: Num m => PaddedBox m m -> Direction -> m
+ Graphics.Layout.Flex.CSS: CSSFlex :: Direction -> Bool -> FlexWrapping -> Maybe Justification -> Alignment -> Maybe Justification -> Unitted -> Unitted -> Integer -> Double -> Double -> Unitted -> Alignment -> Bool -> CSSFlex
+ Graphics.Layout.Flex.CSS: [alignItemsCSS] :: CSSFlex -> Alignment
+ Graphics.Layout.Flex.CSS: [alignLinesCSS] :: CSSFlex -> Maybe Justification
+ Graphics.Layout.Flex.CSS: [alignSelfCSS] :: CSSFlex -> Alignment
+ Graphics.Layout.Flex.CSS: [basisCSS] :: CSSFlex -> Unitted
+ Graphics.Layout.Flex.CSS: [columnGapCSS] :: CSSFlex -> Unitted
+ Graphics.Layout.Flex.CSS: [directionCSS] :: CSSFlex -> Direction
+ Graphics.Layout.Flex.CSS: [growCSS] :: CSSFlex -> Double
+ Graphics.Layout.Flex.CSS: [justifyCSS] :: CSSFlex -> Maybe Justification
+ Graphics.Layout.Flex.CSS: [orderCSS] :: CSSFlex -> Integer
+ Graphics.Layout.Flex.CSS: [reverseRowsCSS] :: CSSFlex -> Bool
+ Graphics.Layout.Flex.CSS: [rowGapCSS] :: CSSFlex -> Unitted
+ Graphics.Layout.Flex.CSS: [shrinkCSS] :: CSSFlex -> Double
+ Graphics.Layout.Flex.CSS: [textRTL] :: CSSFlex -> Bool
+ Graphics.Layout.Flex.CSS: [wrapCSS] :: CSSFlex -> FlexWrapping
+ Graphics.Layout.Flex.CSS: data CSSFlex
+ Graphics.Layout.Flex.CSS: instance Stylist.PropertyParser Graphics.Layout.Flex.CSS.CSSFlex
+ Graphics.Layout.Flex.CSS: lowerFlex :: CSSFlex -> Font' -> [CSSFlex] -> [a] -> [Font'] -> Flex a Length
- Graphics.Layout: layoutGetChilds :: (CastDouble m, CastDouble n, Eq m, Eq n, Eq x) => LayoutItem m n x -> [LayoutItem m n x]
+ Graphics.Layout: layoutGetChilds :: (CastDouble b, CastDouble n, Eq b, Eq n, Eq x) => LayoutItem b n x -> [LayoutItem b n x]
- Graphics.Layout.CSS: CSSBox :: Display -> BoxSizing -> PaddedBox Unitted Unitted -> Pattern -> CSSFont -> a -> [(Text, [Token])] -> a -> CSSGrid -> CSSCell -> CSSInline -> PageOptions -> ParagraphOptions -> TableOptions -> CSSBox a
+ Graphics.Layout.CSS: CSSBox :: Display -> BoxSizing -> PaddedBox Unitted Unitted -> Pattern -> CSSFont -> a -> [(Text, [Token])] -> a -> CSSGrid -> CSSCell -> CSSInline -> PageOptions -> ParagraphOptions -> TableOptions -> CSSFlex -> CSSBox a

Files

Graphics/Layout.hs view
@@ -23,6 +23,7 @@ import Graphics.Layout.Flow as F import Graphics.Layout.Inline as I import Graphics.Layout.CSS.Font (Font'(..))+import Graphics.Layout.Flex as Fl  import Data.Maybe (fromMaybe) @@ -57,6 +58,7 @@     | LayoutConst x (PaddedBox m n) [LayoutItem m n x]     -- | Children of a `LayoutInline` or `LayoutInline'`.     | LayoutSpan (FragmentTree (UserData m n x))+    | LayoutFlex x (Flex (LayoutItem m n x) m)     deriving (Show, Eq) -- | An empty box. nullLayout :: (PropertyParser x, Zero m, Zero n) => LayoutItem m n x@@ -86,13 +88,19 @@ } 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-layoutGetChilds (LayoutSpan _) = []+layoutGetChilds (LayoutSpan (Leaf _)) = []+layoutGetChilds (LayoutSpan (Branch _ ret)) = map LayoutSpan ret 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@@ -100,6 +108,7 @@ 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@@ -134,6 +143,7 @@ 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@@ -161,6 +171,7 @@ 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@@ -181,6 +192,9 @@ 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@@ -216,6 +230,7 @@   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@@ -237,6 +252,7 @@ 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'@@ -259,6 +275,7 @@ 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@@ -282,6 +299,7 @@ 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'@@ -317,6 +335,8 @@     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.@@ -357,6 +377,12 @@   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 =@@ -366,6 +392,19 @@ -- | 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@@ -406,7 +445,7 @@ -- Useful for assembling glyph atlases. glyphsPerFont :: (CastDouble x, CastDouble y, Eq x, Eq y, Eq z) =>         LayoutItem x y z -> M.Map (Pattern, Double) IS.IntSet-glyphsPerFont (LayoutSpan self) | (_:_) <- glyphs =+glyphsPerFont (LayoutSpan self@(Leaf _)) | (_:_) <- glyphs =         (pattern font, fontSize font) `M.singleton` IS.fromList glyphs     | otherwise = M.empty   where
Graphics/Layout/Box.hs view
@@ -181,10 +181,17 @@     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
Graphics/Layout/CSS.hs view
@@ -20,6 +20,7 @@ import Graphics.Layout.Grid import Graphics.Layout.Grid.Table import Graphics.Layout.Inline.CSS+import Graphics.Layout.Flex.CSS  import Data.Char (isSpace) import Graphics.Layout.CSS.Parse@@ -55,7 +56,7 @@         (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 {@@ -69,6 +70,16 @@             | child@StyleTree { style = child'@CSSBox { display = TableCaption } } <- childs])   where     font_ = pattern2font (font self') (font' self') parent root+finalizeCSS root parent self@StyleTree {+        style = self'@CSSBox { display = Flex, flexOptions = flex },+        children = 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+    childs' = map style childs finalizeCSS root parent self@StyleTree {     style = self', children = childs   } = LayoutFlow (inner' font_ self') (finalizeBox self' font_)
Graphics/Layout/CSS/Parse.hs view
@@ -16,6 +16,7 @@ import Graphics.Layout.Grid.CSS (CSSGrid(..), CSSCell(..), Placement(..)) import Graphics.Layout.Grid.Table (TableOptions) import Graphics.Layout.Inline.CSS (CSSInline(..))+import Graphics.Layout.Flex.CSS (CSSFlex(..))  import Data.Maybe (isJust, fromMaybe) import Text.Read (readMaybe)@@ -54,8 +55,12 @@     paragraphOptions :: ParagraphOptions,     -- | (Semi-)parsed CSS properties & HTML attributes relating to laying out     -- HTML table elements.-    tableOptions :: TableOptions+    tableOptions :: TableOptions,+    -- | Semi-parsed CSS properties relating to FlexBox layouts.+    flexOptions :: CSSFlex }+-- | FlexOptions getter with `textLTR` set+flexOpts' self@CSSBox { flexOptions = ret } = ret { textRTL = direction self == DirRTL } -- | Accessor for inlineStyle's `textDirection` attribute. direction CSSBox { inlineStyles = CSSInline _ opts _ } = textDirection opts -- | Accessor for inlineStyle's options.@@ -68,7 +73,7 @@ -- | Possibly values for CSS display property. data Display = Block | Grid | Inline | Table | None |     TableRow | TableHeaderGroup | TableRowGroup | TableFooterGroup | TableCell |-    TableColumn | TableColumnGroup | TableCaption deriving Eq+    TableColumn | TableColumnGroup | TableCaption | Flex deriving Eq -- | Can the display value contain table-rows? rowContainer CSSBox { display = d } =     d `elem` [Table, TableHeaderGroup, TableRowGroup, TableFooterGroup]@@ -99,7 +104,8 @@         paragraphOptions = defaultParagraphOptions {             paragraphAlignment = AlignStart         },-        tableOptions = temp+        tableOptions = temp,+        flexOptions = temp       }     inherit parent = CSSBox {         boxSizing = boxSizing parent,@@ -115,9 +121,11 @@         inlineStyles = inherit $ inlineStyles parent,         pageOptions = pageOptions parent,         paragraphOptions = paragraphOptions parent,-        tableOptions = inherit $ tableOptions parent+        tableOptions = inherit $ tableOptions parent,+        flexOptions = inherit $ flexOptions parent       }-    priority self = concat [x font, x font', x gridStyles, x cellStyles, x inner]+    priority self = concat [x inlineStyles, x font, x font', x gridStyles,+        x cellStyles, x flexOptions, x inner]       where x getter = priority $ getter self      -- Wasn't sure how to implement in FontConfig-Pure@@ -254,6 +262,7 @@     longhand CSSBox { display = Table } self "display" [Ident "table-caption"] =         Just self { display=TableCaption }     longhand _ self "display" [Ident "inline"] = Just self { display = Inline }+    longhand _ self "display" [Ident "flex"] = Just self { display = Flex }     longhand _ self "display" [Ident "initial"] = Just self { display = Inline }      longhand _ self "orphans" [Number _ (NVInteger x)] =@@ -294,12 +303,20 @@       }     longhand a b c d | Just inline' <- longhand (inlineStyles a) (inlineStyles b) c d =         Just b { inlineStyles = inline' }+    longhand a b c d | Just grid' <- longhand (gridStyles a) (gridStyles b) c d,+            Just flex' <- longhand (flexOpts' a) (flexOpts' b) c d =+        Just b { gridStyles = grid', flexOptions = flex' }+    longhand a b c d | Just cell' <- longhand (cellStyles a) (cellStyles b) c d,+            Just flex' <- longhand (flexOpts' a) (flexOpts' b) c d =+        Just b { cellStyles = cell', flexOptions = flex' }     longhand a b c d | Just grid' <- longhand (gridStyles a) (gridStyles b) c d =         Just b { gridStyles = grid' }     longhand a b c d | Just cell' <- longhand (cellStyles a) (cellStyles b) c d =         Just b { cellStyles = cell' }     longhand a b c d | Just table'<-longhand (tableOptions a) (tableOptions b) c d         = Just b { tableOptions = table' }+    longhand a b c d | Just flex' <- longhand (flexOpts' a) (flexOpts' b) c d =+        Just b { flexOptions = flex' }     longhand a b c d         | (d', _:_)<-testLengthProp d, Just _<-longhand (inner a) (inner b) c d' =             Just b {
+ Graphics/Layout/Flex.hs view
@@ -0,0 +1,293 @@+-- | Layout formula positioning children horizontally or vertically, with or without wrapping.+module Graphics.Layout.Flex(+        Flex(..), FlexChild(..), Direction(..), FlexWrapping(..), Justification(..), Alignment(..),+        flexMap, flexResolve, flexMaxBasis, flexSumBasis, flexWrap, flexRowSize, flexRowsSize,+        justifyOffset, justifySpacing, flexJustify, alignOffset,+        outerMinMain, outerMain, outerMaxMain, outerMinCross, outerCross, outerMaxCross,+        innerMinMain, innerMain, innerMaxMain, innerMinCross, innerCross, innerMaxCross,+        flexGetBox, flexSplit, flexPosition) where++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)++-- | Parameters to flexbox layout+data Flex a b = Flex {+    -- | On which axis to position children+    direction :: Direction,+    -- | Whether to reverse each row+    reverseRows :: Bool,+    -- | Whether to wrap children to multiple lines+    wrap :: FlexWrapping,+    -- | How to justify children within lines+    justify :: Justification,+    -- | How to justify lines within the flexbox. Nothing is CSS "stretch".+    alignLines :: Maybe Justification,+    -- | Gap between children+    baseGap :: b,+    -- | Gap between lines+    crossGap :: b,+    -- | The children to layout, 2D list so as to store lines once split.+    children :: [[FlexChild a b]],+    -- | How wide to consider the page when paginating.+    pageWidth :: Double+} deriving (Eq, Show, Read)+-- | Properties for positioning a child of a flexbox+data FlexChild a b = FlexChild {+    grow :: Double,+    shrink :: Double,+    basis :: b,+    alignment :: Alignment,+    flexInner :: a+} deriving (Eq, Show, Read)++-- | Which axis to position children along+data Direction = Row | Column deriving (Eq, Show, Read)+-- | Whether to wrap or reverse the wrapped lines+data FlexWrapping = NoWrap | Wrap | WrapReverse deriving (Eq, Show, Read)+-- | How to position lines within a flexbox, or children within a line+data Justification = JStart | JEnd | JCenter | JSpaceBetween | JSpaceAround | JSpaceEvenly+    deriving (Eq, Show, Read)+-- | How to align children along the cross-axis (opposite axis from which they're laid out)+data Alignment = AlStretch | AlStart | AlEnd | AlCenter | AlBaseline+    deriving (Eq, Show, Read)++-- | Apply an operation to every child+flexMap :: (a -> b) -> Flex a c -> Flex b c+flexMap cb self = Flex {+    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]+  }+-- | Resolve lengths in the flexbox to doubles.+flexResolve :: CastDouble b => (a -> Direction -> Double) -> Double ->+        Flex a b -> Flex a Double+flexResolve cb size self = Flex {+    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]+  }++-- | The minimum size of the flexbox along `direction`, i.e. maximum size of a child.+flexMaxBasis :: Flex a Double -> Double+flexMaxBasis self = maximum [basis child | row <- children self, child <- row]+-- | The maximum width of each row of the flexbox.+flexSumBasis :: Flex a Double -> Double+flexSumBasis self = maximum [Prelude.sum $+        intersperse (baseGap self) $ map basis row | row <- children self]++-- | Wrap all lines to a given size reassigning overflow or underflow space.+-- NOTE: shrink propery may yield negative sizes. Caller will want to enforce min-sizes.+flexWrap :: CastDouble b => Flex a b -> Double -> Flex a b+flexWrap self size+    | NoWrap <- wrap self = post self+    | Wrap <- wrap self = post self'+    | WrapReverse <- wrap self = post self' { children=reverse $ children self' }+  where+    self' = self {+        children = concatMap wrapRow $ children self+    }+    wrapRow :: CastDouble b => [FlexChild a b] -> [[FlexChild a b]]+    wrapRow [] = []+    wrapRow kids@(kid:_) = let (row, rest) = splitRow' kids $ basis' kid+        in row:wrapRow rest+    splitRow, splitRow' :: CastDouble b => [FlexChild a b] -> Double ->+            ([FlexChild a b], [FlexChild a b])+    -- This wrapper function ensures we don't end up with empty rows, or infinite loops.+    splitRow' (kid:kids) end =+        let (kids', rest) = splitRow kids (end + baseGap' self + basis' kid)+        in (kid:kids', rest)+    splitRow' [] _ = ([], [])+    splitRow (kid:kids) end+        | end > size = ([], kid:kids)+        | otherwise = let (kids', rest) = splitRow kids (end + baseGap' self + basis' kid)+            in (kid:kids', rest)+    splitRow [] _ = ([], [])++    post :: CastDouble b => Flex a b -> Flex a b+    post flex+        | reverseRows self = post' flex { children = map reverse $ children flex }+        | otherwise = post' flex+    post' :: CastDouble b => Flex a b -> Flex 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 = fromDouble $ basis' kid - shrink kid * nanguard sfr+            } | kid <- row]+        | rowSize < size = [kid {+                basis = fromDouble $ basis' kid + grow kid * nanguard gfr+            } | kid <- row]+        | otherwise = row+      where+        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)+        nanguard x | isNaN x = 0+            | isInfinite x = 0+            | otherwise = x+    baseGap' :: CastDouble b => Flex a b -> Double+    baseGap' = toDouble . baseGap+    basis' :: CastDouble b => FlexChild a b -> Double+    basis' = toDouble . basis++-- | The cross (opposite from `direction` axis) size of a row.+flexRowSize :: (a -> Double) -> [FlexChild a b] -> Double+flexRowSize cb row = maximum $ map (cb . flexInner) row+-- | The cross (opposite from `direction` axis) size of all rows.+flexRowsSize :: (a -> Double) -> Flex a Double -> Double+flexRowsSize cb Flex { crossGap = gap, children = kids } =+    sum $ intersperse gap $ flexRowSize cb `map` kids++justifyOffset, justifySpacing :: Double -> [Double] -> Double -> Justification -> Double+-- | How far right to shift some elements to achieve desired justification.+justifyOffset _ _ _ JStart = 0+justifyOffset outersize ks g JEnd = outersize - innersize g ks+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)+-- | How much space to add between elements.+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++-- | Position new positions for the given items according to the given justification.+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 [] _ = []++-- | How far right to shift some elements to achieve desired alignment.+alignOffset :: Double -> Double -> Alignment -> Double+alignOffset _ _ AlStretch = 0 -- Needs special handling elsewhere+alignOffset _ _ AlStart = 0+alignOffset outer inner AlEnd = outer - inner+alignOffset outer inner AlCenter = half $ outer - inner+alignOffset outer inner AlBaseline = half $ outer - inner -- FIXME: Implement properly!++-- | Sum given sizes with a specified gap between them.+innersize gap = sum . intersperse gap+-- | divide by 2, can be syntactically in certain circumstances.+half = (/2)+-- | length of an list, as a double.+length' :: [a] -> Double+length' = toEnum . length++------+--- Mapping Box Model axes <-> Flex Box axes+------++outerMinMain, outerMain, outerMaxMain :: Num m => PaddedBox m m -> Direction -> m+outerMinMain box Row = minWidth box+outerMinMain box Column = minHeight box+outerMain box Row = width box+outerMain box Column = height box+outerMaxMain box Row = maxWidth box+outerMaxMain box Column = maxHeight box++outerMinCross, outerCross, outerMaxCross :: Num m => PaddedBox m m -> Direction -> m+outerMinCross box Row = minHeight box+outerMinCross box Column = minWidth box+outerCross box Row = height box+outerCross box Column = width box+outerMaxCross box Row = maxHeight box+outerMaxCross box Column = maxWidth box++innerMinMain, innerMain, innerMaxMain :: Num m => PaddedBox m m -> Direction -> m+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 = 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++-- | Compute the size bounds of a flexbox.+flexGetBox :: (Zero m, CastDouble m, Zero n, CastDouble n) =>+    (a -> PaddedBox Double Double) -> Flex 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++-- | Split a flexbox over multiple pages.+flexSplit :: (a -> Size Double Double) -> Double -> Double -> Flex a Double ->+    (Flex a Double, Flex a Double)+flexSplit cb h _ self@Flex { 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@Flex { 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++-- | Compute final position of a flexbox's children.+flexPosition :: ((Double, Double) -> a -> b) -> (a -> Size Double Double) ->+        (Double, Double) -> Size Double Double ->+        Flex a Double -> Flex b Double+flexPosition cb cb' (x,y) size self@Flex { 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)
+ Graphics/Layout/Flex/CSS.hs view
@@ -0,0 +1,212 @@+{-# LANGUAGE OverloadedStrings #-}+-- | Parse FlexBox-related CSS properties+module Graphics.Layout.Flex.CSS(CSSFlex(..), lowerFlex) where++import Data.CSS.Syntax.Tokens (Token(..), NumericValue(..))+import Stylist (PropertyParser(..), TrivialPropertyParser, parseOperands,+                parseUnorderedShorthand', parseUnorderedShorthand)+import Graphics.Layout.Flex as F+import Graphics.Layout.CSS.Length (parseLength, finalizeLength, n2f, Unitted, Font')+import Graphics.Layout.Box (Length)+import Data.Maybe (isJust)++-- | Parsed FlexBox-related CSS properties.+data CSSFlex = CSSFlex {+    -- | Parsed CSS flex-direction, axis component.+    directionCSS :: Direction,+    -- | Parsed CSS flex-direction, reversed flag+    reverseRowsCSS :: Bool,+    -- | Parsed CSS flex-wrap+    wrapCSS :: FlexWrapping,+    -- | Parsed CSS justify-content+    justifyCSS :: Maybe Justification,+    -- | Parsed CSS align-items+    alignItemsCSS :: Alignment,+    -- | Parsed CSS align-content, `Nothing` is "stretch"+    alignLinesCSS :: Maybe Justification,+    -- | Parsed CSS row-gap+    rowGapCSS :: Unitted,+    -- | Parsed CSS column-gap+    columnGapCSS :: Unitted,++    -- flex children+    -- | Parsed CSS order+    orderCSS :: Integer,+    -- | Parsed CSS flex-grow+    growCSS :: Double,+    -- | Parsed CSS flex-shrink+    shrinkCSS :: Double,+    -- | Parsed CSS flex-basis+    basisCSS :: Unitted,+    -- | Parsed CSS align-self+    alignSelfCSS :: Alignment,+    -- | Whether justification or alignment properties should be parsed as right-to-left or left-to-right.+    textRTL :: Bool -- Extra parameter from caller.+}++setDir self dir rev = Just self { directionCSS = dir, reverseRowsCSS = rev }++parseJustify self "flex-start" | reverseRowsCSS self = Just JEnd+    | otherwise = Just JStart+parseJustify self "flex-end" | reverseRowsCSS self = Just JStart+    | otherwise = Just JEnd+parseJustify self "start" | textRTL self = Just JEnd+    | otherwise = Just JStart+parseJustify self "end" | textRTL self = Just JStart+    | otherwise = Just JEnd+parseJustify _ "left" = Just JStart+parseJustify _ "right" = Just JEnd+parseJustify _ "center" = Just JCenter+parseJustify _ "space-between" = Just JSpaceBetween+parseJustify _ "space-around" = Just JSpaceAround+parseJustify _ "space-evenly" = Just JSpaceEvenly+parseJustify _ _ = Nothing++parseAlign _ "stretch" = Just AlStretch+parseAlign self "flex-start" | reverseRowsCSS self = Just AlEnd+    | otherwise = Just AlStart+parseAlign _ "start" = Just AlStart+parseAlign self "self-start" | textRTL self = Just AlEnd+    | otherwise = Just AlStart+parseAlign self "flex-end" | reverseRowsCSS self = Just AlStart+    | otherwise = Just AlEnd+parseAlign _ "end" = Just AlEnd+parseAlign self "self-end" | textRTL self = Just AlStart+    | otherwise = Just AlEnd+parseAlign _ "center" = Just AlCenter+parseAlign _ "baseline" = Just AlBaseline+parseAlign _ _ = Nothing++instance PropertyParser CSSFlex where+    temp = CSSFlex {+        directionCSS = Row,+        reverseRowsCSS = False,+        wrapCSS = NoWrap,+        justifyCSS = Nothing, -- flex-start, conditional on directionCSS+        alignItemsCSS = AlStretch,+        alignLinesCSS = Just JStart,+        rowGapCSS = (0,"px"),+        columnGapCSS = (0,"px"),+        orderCSS = 0,+        growCSS = 0,+        shrinkCSS = 1,+        basisCSS = (0,"auto"),+        alignSelfCSS = AlStretch, -- Should be auto, but we're implementing that in `inherit`.+        textRTL = False+      }+    inherit parent = temp { alignSelfCSS = alignItemsCSS parent }+    priority _ = ["flex-direction"]++    longhand _ self "flex-direction" [Ident "row"] = setDir self Row False+    longhand _ self "flex-direction" [Ident "row-reverse"] = setDir self Row True+    longhand _ self "flex-direction" [Ident "column"] = setDir self F.Column False+    longhand _ self "flex-direction" [Ident "column-reverse"] =+        setDir self F.Column True+    longhand _ self "flex-direction" [Ident "initial"] = setDir self Row False++    longhand _ self "flex-wrap" [Ident "no-wrap"] = Just self { wrapCSS = NoWrap }+    longhand _ self "flex-wrap" [Ident "wrap"] = Just self { wrapCSS = Wrap }+    longhand _ self "flex-wrap" [Ident "wrap-reverse"] =+        Just self { wrapCSS = WrapReverse }+    longhand _ self "flex-wrap" [Ident "initial"] = Just self { wrapCSS = NoWrap }++    longhand _ self "justify-content" [Ident x]+        | x == "initial" = Just self { justifyCSS = parseJustify self "flex-start" }+        | y@(Just _) <- parseJustify self x = Just self { justifyCSS = y }++    longhand _ self "align-items" [Ident x]+        | x == "initial" = Just self { alignItemsCSS = AlStretch }+        | Just y <- parseAlign self x = Just self { alignItemsCSS = y }++    longhand _ self "align-content" [Ident x] | x `elem` ["initial", "normal"] =+            Just self { alignLinesCSS = parseJustify self "start" }+        | x == "stretch" = Just self { alignLinesCSS = Nothing }+        | y@(Just _) <- parseJustify self x = Just self { alignLinesCSS = y }++    longhand _ self "row-gap" [Ident x]+        | x `elem` ["initial", "normal"] = Just self { rowGapCSS = (0,"px") }+        | otherwise = Nothing+    longhand _ self "row-gap" toks+        | Just x <- parseLength toks = Just self { rowGapCSS = x}+    longhand _ self "column-gap" [Ident x]+        | x `elem` ["initial", "normal"] = Just self { columnGapCSS = (0,"px") }+        | otherwise = Nothing+    longhand _ self "column-gap" toks+        | Just x <- parseLength toks = Just self { columnGapCSS = x }++    longhand _ self "order" [Number _ (NVInteger x)] = Just self { orderCSS = x }+    longhand _ self "order" [Ident "initial"] = Just self { orderCSS = 0 }++    longhand _ self "flex-grow" [Number _ x] | n2f x>0 = Just self {growCSS=n2f x}+    longhand _ self "flex-grow" [Ident "initial"] = Just self { growCSS = 0 }+    longhand _ self "flex-shrink" [Number _ x] | n2f x > 0 =+        Just self { shrinkCSS = n2f x }+    longhand _ self "flex-shrink" [Ident "initial"] = Just self { shrinkCSS = 1 }++    longhand _ self "flex-basis" toks | Just x <- parseLength toks =+        Just self { basisCSS = x }++    longhand parent self "align-self" [Ident x] | x `elem` ["initial", "auto"] =+            Just self { alignSelfCSS = alignItemsCSS parent }+        | Just y <- parseAlign self x = Just self { alignSelfCSS = y }++    longhand _ _ _ _ = Nothing++    shorthand self "flex-flow" toks =+        parseUnorderedShorthand self ["flex-direction", "flex-wrap"] toks+    shorthand self "gap" toks | [x] <- parseOperands toks =+            parseUnorderedShorthand' self ["row-gap", "column-gap"] [x, x]+        | otherwise = parseUnorderedShorthand self ["row-gap", "column-gap"] toks+    shorthand self "flex" toks+        | [Ident "initial"] <- toks =+            [("flex-grow", init), ("flex-shrink", init), ("flex-basis", px0)]+        | [Ident "auto"] <- toks =+            [("flex-grow", n1), ("flex-shrink", n1), ("flex-basis", init)]+        | [Ident "none"] <- toks =+            [("flex-grow", n0), ("flex-shrink", n0), ("flex-basis", init)]+        | [a] <- operands, test "flex-grow" a =+            [("flex-grow", a), ("flex-shrink", init), ("flex-basis", px0)]+        | [a] <- operands, test "flex-basis" a =+            [("flex-grow", n1), ("flex-shrink", init), ("flex-basis", a)]+        | [a, b] <- operands, test "flex-grow" a, test "flex-shrink" b =+            [("flex-grow", a), ("flex-shrink", b), ("flex-basis", px0)]+        | [a, b] <- operands, test "flex-grow" a, test "flex-basis" b =+            [("flex-grow", a), ("flex-shrink", init), ("flex-basis", b)]+        | [a, b, c] <- operands, test "flex-grow" a, test "flex-shrink" b, test "flex-basis" c =+            [("flex-grow", a), ("flex-shrink", b), ("flex-basis", c)]+      where+        operands = parseOperands toks+        test a = isJust . longhand self self a+        init = [Ident "initial"]+        px0 = [Dimension "0" (NVInteger 0) "px"]+        n1 = [Number "1" (NVInteger 1)]+        n0 = [Number "0" (NVInteger 0)]+    shorthand self k v | Just _ <- longhand self self k v = [(k, v)]+        | otherwise = []++-- | Lower the Flexbox styling tree to the Layout tree.+lowerFlex :: CSSFlex -> Font' -> [CSSFlex] -> [a] -> [Font'] -> Flex a Length+lowerFlex self font kids kids' fonts' = Flex {+    direction = directionCSS self,+    reverseRows = reverseRowsCSS self,+    wrap = wrapCSS self,+    justify = case justifyCSS self of+        Just x -> x+        Nothing | reverseRowsCSS self -> JEnd+        Nothing -> JStart,+    alignLines = alignLinesCSS self,+    baseGap = case directionCSS self of+        Row -> flip finalizeLength font $ rowGapCSS self+        F.Column -> flip finalizeLength font $ columnGapCSS self,+    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,+        basis = flip finalizeLength font' $ rowGapCSS kid,+        alignment = alignSelfCSS kid,+        flexInner = kid'+      } | (kid, kid', font') <- zip3 kids kids' fonts']]+  }
app/Integration.hs view
@@ -12,7 +12,7 @@  import Graphics.Layout.CSS (CSSBox(..), finalizeCSS') import Graphics.Layout.CSS.Font (placeholderFont)-import Graphics.Layout (LayoutItem, boxLayout,+import Graphics.Layout (LayoutItem, boxLayout, glyphsPerFont,                         layoutGetBox, layoutGetChilds, layoutGetInner) import Graphics.Layout.Box (zeroBox) import qualified Graphics.Layout.Box as B@@ -42,6 +42,7 @@ import Foreign.C.Types (CInt) import Data.Function (fix) import Control.Monad (unless)+import Control.Exception (evaluate) import qualified Graphics.Text.Font.Choose as FC  initReferer :: IO (Page (CSSCond.ConditionalStyles (CSSBox Nil)))@@ -140,7 +141,9 @@          pages <- tryReadMVar pages'         case pages of-            Just (display:_) -> renderDisplay renderer display+            Just (display:_) -> do+                evaluate $ glyphsPerFont display +                renderDisplay renderer display             _ -> return ()          present renderer
app/Integration2.hs view
@@ -14,7 +14,7 @@  import Graphics.Layout.CSS.Font (placeholderFont) import Graphics.Layout.CSS (finalizeCSS', CSSBox)-import Graphics.Layout (LayoutItem, boxLayout)+import Graphics.Layout (LayoutItem, boxLayout, glyphsPerFont) import Graphics.Layout.Box (Length, Size(..), PaddedBox(..), zeroBox)  import Control.Exception (evaluate)@@ -51,7 +51,13 @@         layout = finalizeCSS' placeholderFont styles     layout `seq` print "Laying out page!"     res <- forkCompute $ boxLayout zeroBox { size = Size 1280 480 } layout False-    readMVar res+    res' <- readMVar res+    case res' of+        (page:_) -> do+            print "Gathering atlas"+            evaluate $ glyphsPerFont page+            return ()+        _ -> return ()     --performGC     --FC.fini -- FIXME: GC still left FontConfig references...     return ()
cattrap.cabal view
@@ -2,7 +2,7 @@ -- documentation, see http://haskell.org/cabal/users-guide/  name:                cattrap-version:             0.4.0.0+version:             0.5.0.0 synopsis:            Lays out boxes according to the CSS Box Model. description:         Computes where to place e.g. images, paragraphs, containers, tables, etc onscreen given desired amounts of whitespace. homepage:            https://argonaut-constellation.org/@@ -27,7 +27,8 @@                         Graphics.Layout.Box, Graphics.Layout.Arithmetic,                         Graphics.Layout.CSS.Length, Graphics.Layout.CSS.Font,                         Graphics.Layout.Inline, Graphics.Layout.Inline.CSS,-                        Graphics.Layout.Grid.Table+                        Graphics.Layout.Grid.Table,+                        Graphics.Layout.Flex, Graphics.Layout.Flex.CSS   other-modules:        Graphics.Layout.CSS.Parse   -- other-extensions:   build-depends:       base >=4.12 && <5, containers >= 0.6 && < 1, parallel >= 3 && <4,@@ -58,7 +59,7 @@                         stylist-traits , stylist>=2.7.0.1 && <3,                         hurl-xml >= 0.2 && < 1, hurl >= 2.3 && < 3,                         sdl2 >= 2.5.4 && < 2.6, containers, network-uri >=2.6 && <3,-                        xml-conduit, directory >= 1.3 && < 2, xml-conduit-stylist,+                        xml-conduit >= 1.8 && < 2, directory >= 1.3 && < 2, xml-conduit-stylist,                         bytestring, file-embed >= 0.0.15 && < 0.1, deepseq, fontconfig-pure   hs-source-dirs:      app   default-language:    Haskell2010@@ -69,8 +70,9 @@   -- other-modules:   -- other-extensions:   build-depends:       base >=4.12 && <5, cattrap, text>=2.0.2 && <3, css-syntax,-                        stylist-traits, stylist>=2.7.0.1 && <3, network-uri, html-conduit >=1.3 && <2,-                        xml-conduit, xml-conduit-stylist >= 3 && < 4, deepseq, fontconfig-pure+                        stylist-traits, stylist>=2.7.0.1 && <3, network-uri,+                        html-conduit >=1.3 && <2, xml-conduit >= 1.8 && < 2,+                        xml-conduit-stylist >= 3 && < 4, deepseq, fontconfig-pure   hs-source-dirs:      app   default-language:    Haskell2010 
test/Test.hs view
@@ -10,7 +10,7 @@ import Data.Maybe (fromJust)  import Graphics.Layout.Box as B-import Graphics.Layout.Grid+import Graphics.Layout.Grid as Grid import Graphics.Layout.Flow import Graphics.Layout @@ -18,6 +18,7 @@ import Graphics.Layout.Grid.Table import Graphics.Layout.Inline.CSS import Graphics.Layout.CSS.Font (placeholderFont)+import Graphics.Layout.Flex as Flex  import Data.Text.ParagraphLayout.Rich (constructParagraph,         defaultParagraphOptions, defaultTextOptions,@@ -360,7 +361,7 @@             let defaultPageOptions = PageOptions 0 0 2 2             let gridItem x y = GridItem {                 cellStart = x, cellEnd = y,-                alignment = Start,+                Grid.alignment = Start,                 minSize = 0, natSize = 0               }             let track cells' = Track {@@ -410,6 +411,399 @@                             LayoutFlow () lengthBox [inline "H"]                         ]                     ]+    describe "Flexbox" $ do+        it "wraps properly" $ do+            let child l = FlexChild {+                grow = 0,+                shrink = 0,+                basis = l,+                Flex.alignment = AlStart,+                flexInner = ()+              }+            let baseFlex = Flex.Flex {+                direction = Row,+                reverseRows = False,+                wrap = NoWrap,+                justify = JStart,+                alignLines = Just JStart,+                baseGap = 2,+                crossGap = 2,+                Flex.children = [[(child 10) { grow = 1 }, (child 20) { shrink = 1 },+                        (child 30) { grow = 2 }, (child 40) { shrink = 2 }]],+                pageWidth = 0+              } :: Flex.Flex () Double+            -- These test results don't look right...+            flexWrap baseFlex 50 `shouldBe` baseFlex {+                Flex.children = [[(child 10) { grow = 1 },+                        (child 1.3333333333333321) { shrink = 1 }, (child 30) { grow = 2 },+                        (child 2.6666666666666643) { shrink = 2}]]+              }+            flexWrap baseFlex 40 `shouldBe` baseFlex {+                Flex.children = [[(child 10) { grow = 1 },+                        (child $ -2) { shrink = 1 }, (child 30) { grow = 2 },+                        (child $ -4) { shrink = 2}]]+              }+            flexWrap baseFlex 30 `shouldBe` baseFlex {+                Flex.children = [[(child 10) { grow = 1 },+                        (child $ -5.333333333333332) { shrink = 1 }, (child 30) { grow = 2 },+                        (child $ -10.666666666666664) { shrink = 2}]]+              }+            flexWrap baseFlex 20 `shouldBe` baseFlex {+                Flex.children = [[(child 10) { grow = 1 },+                        (child $ -8.666666666666668) { shrink = 1 }, (child 30) { grow = 2 },+                        (child $ -17.333333333333336) { shrink = 2}]]+              }+            flexWrap baseFlex 10 `shouldBe` baseFlex {+                Flex.children = [[(child 10) { grow = 1 },+                        (child $ -12) { shrink = 1 }, (child 30) { grow = 2 },+                        (child $ -24) { shrink = 2}]]+              }+            flexWrap baseFlex { direction = Flex.Column } 50 `shouldBe` baseFlex {+                direction = Flex.Column,+                Flex.children = [[(child 10) { grow = 1 },+                        (child 1.3333333333333321) { shrink = 1 }, (child 30) { grow = 2 },+                        (child 2.6666666666666643) { shrink = 2}]]+              }+            flexWrap baseFlex { direction = Flex.Column } 40 `shouldBe` baseFlex {+                direction = Flex.Column,+                Flex.children = [[(child 10) { grow = 1 },+                        (child $ -2) { shrink = 1 }, (child 30) { grow = 2 },+                        (child $ -4) { shrink = 2}]]+              }+            flexWrap baseFlex { direction = Flex.Column } 30 `shouldBe` baseFlex {+                direction = Flex.Column,+                Flex.children = [[(child 10) { grow = 1 },+                        (child $ -5.333333333333332) { shrink = 1 }, (child 30) { grow = 2 },+                        (child $ -10.666666666666664) { shrink = 2}]]+              }+            flexWrap baseFlex { direction = Flex.Column } 20 `shouldBe` baseFlex {+                direction = Flex.Column,+                Flex.children = [[(child 10) { grow = 1 },+                        (child $ -8.666666666666668) { shrink = 1 }, (child 30) { grow = 2 },+                        (child $ -17.333333333333336) { shrink = 2}]]+              }+            flexWrap baseFlex { direction = Flex.Column } 10 `shouldBe` baseFlex {+                direction = Flex.Column,+                Flex.children = [[(child 10) { grow = 1 },+                        (child $ -12) { shrink = 1 }, (child 30) { grow = 2 },+                        (child $ -24) { shrink = 2}]]+              }+            flexWrap baseFlex { reverseRows = True } 50 `shouldBe` baseFlex {+                reverseRows = True,+                Flex.children = [[+                    FlexChild {+                        grow = 0.0,+                        shrink = 2.0,+                        basis = 2.6666666666666643,+                        Flex.alignment = AlStart,+                        flexInner = ()+                    },+                    FlexChild {+                        grow = 2.0,+                        shrink = 0.0,+                        basis = 30.0,+                        Flex.alignment = AlStart,+                        flexInner = ()+                    },+                    FlexChild {+                        grow = 0.0,+                        shrink = 1.0,+                        basis = 1.3333333333333321,+                        Flex.alignment = AlStart,+                        flexInner = ()+                    },+                    FlexChild {+                       grow = 1.0,+                       shrink = 0.0,+                       basis = 10.0,+                       Flex.alignment = AlStart,+                       flexInner = ()+                    }+                ]]+              }+            flexWrap baseFlex { reverseRows = True } 40 `shouldBe` baseFlex {+                reverseRows = True,+                Flex.children = [[+                    FlexChild {+                        grow = 0.0,+                        shrink = 2.0,+                        basis = -4,+                        Flex.alignment = AlStart,+                        flexInner = ()+                    },+                    FlexChild {+                        grow = 2.0,+                        shrink = 0.0,+                        basis = 30.0,+                        Flex.alignment = AlStart,+                        flexInner = ()+                    },+                    FlexChild {+                        grow = 0.0,+                        shrink = 1.0,+                        basis = -2,+                        Flex.alignment = AlStart,+                        flexInner = ()+                    },+                    FlexChild {+                       grow = 1.0,+                       shrink = 0.0,+                       basis = 10.0,+                       Flex.alignment = AlStart,+                       flexInner = ()+                    }+                ]]+            }+            flexWrap baseFlex { reverseRows = True } 30 `shouldBe` baseFlex {+                reverseRows = True,+                Flex.children = [[+                    FlexChild {+                        grow = 0.0,+                        shrink = 2.0,+                        basis = -10.666666666666664,+                        Flex.alignment = AlStart,+                        flexInner = ()+                    },+                    FlexChild {+                        grow = 2.0,+                        shrink = 0.0,+                        basis = 30.0,+                        Flex.alignment = AlStart,+                        flexInner = ()+                    },+                    FlexChild {+                        grow = 0.0,+                        shrink = 1.0,+                        basis = -5.333333333333332,+                        Flex.alignment = AlStart,+                        flexInner = ()+                    },+                    FlexChild {+                       grow = 1.0,+                       shrink = 0.0,+                       basis = 10.0,+                       Flex.alignment = AlStart,+                       flexInner = ()+                    }+                ]]+            }+            flexWrap baseFlex { reverseRows = True } 20 `shouldBe` baseFlex {+                reverseRows = True,+                Flex.children = [[+                    FlexChild {+                        grow = 0.0,+                        shrink = 2.0,+                        basis = -17.333333333333336,+                        Flex.alignment = AlStart,+                        flexInner = ()+                    },+                    FlexChild {+                        grow = 2.0,+                        shrink = 0.0,+                        basis = 30.0,+                        Flex.alignment = AlStart,+                        flexInner = ()+                    },+                    FlexChild {+                        grow = 0.0,+                        shrink = 1.0,+                        basis = -8.666666666666668,+                        Flex.alignment = AlStart,+                        flexInner = ()+                    },+                    FlexChild {+                       grow = 1.0,+                       shrink = 0.0,+                       basis = 10.0,+                       Flex.alignment = AlStart,+                       flexInner = ()+                    }+                ]]+              }+            flexWrap baseFlex { reverseRows = True } 10 `shouldBe` baseFlex {+                reverseRows = True,+                Flex.children = [[+                    FlexChild {+                        grow = 0.0,+                        shrink = 2.0,+                        basis = -24,+                        Flex.alignment = AlStart,+                        flexInner = ()+                    },+                    FlexChild {+                        grow = 2.0,+                        shrink = 0.0,+                        basis = 30.0,+                        Flex.alignment = AlStart,+                        flexInner = ()+                    },+                    FlexChild {+                        grow = 0.0,+                        shrink = 1.0,+                        basis = -12,+                        Flex.alignment = AlStart,+                        flexInner = ()+                    },+                    FlexChild {+                       grow = 1.0,+                       shrink = 0.0,+                       basis = 10.0,+                       Flex.alignment = AlStart,+                       flexInner = ()+                    }+                ]]+              }+            flexWrap baseFlex { wrap = Wrap } 50 `shouldBe` baseFlex {+                wrap = Wrap,+                Flex.children = [[(child 10) { grow = 1 },+                        (child 6) { shrink = 1 }, (child 30) { grow = 2 }], [+                        (child 40) { shrink = 2}]]+              }+            flexWrap baseFlex { wrap = Wrap } 40 `shouldBe` baseFlex {+                wrap = Wrap,+                Flex.children = [[(child 18) { grow = 1 },+                        (child 20) { shrink = 1 }], [(child 40) { grow = 2 }],+                        [(child 40) { shrink = 2}]]+              }+            flexWrap baseFlex { wrap = Wrap } 30 `shouldBe` baseFlex {+                wrap = Wrap,+                Flex.children = [[(child 10) { grow = 1 },+                        (child 18) { shrink = 1 }], [(child 30) { grow = 2 }],+                        [(child 30) { shrink = 2}]]+              }+            flexWrap baseFlex { wrap = Wrap } 20 `shouldBe` baseFlex {+                wrap = Wrap,+                Flex.children = [[(child 20) { grow = 1 }],+                        [(child 20) { shrink = 1 }], [(child 30) { grow = 2 }],+                        [(child 20) { shrink = 2}]]+              }+            flexWrap baseFlex { wrap = Wrap } 10 `shouldBe` baseFlex {+                wrap = Wrap,+                Flex.children = [[(child 10) { grow = 1 }], [(child 10) { shrink = 1 }],+                        [(child 30) { grow = 2 }], [(child 10) { shrink = 2}]]+              }+            flexWrap baseFlex { wrap = WrapReverse } 50 `shouldBe` baseFlex {+                wrap = WrapReverse,+                Flex.children = [[(child 40) { shrink = 2}], [(child 10) { grow = 1 },+                        (child 6) { shrink = 1 }, (child 30) { grow = 2 }]]+              }+            flexWrap baseFlex { wrap = WrapReverse } 40 `shouldBe` baseFlex {+                wrap = WrapReverse,+                Flex.children = [[(child 40) { shrink = 2}], [(child 40) { grow = 2 }],+                        [(child 18) { grow = 1 }, (child 20) { shrink = 1 }]]+              }+            flexWrap baseFlex { wrap = WrapReverse } 30 `shouldBe` baseFlex {+                wrap = WrapReverse,+                Flex.children = [[(child 30) { shrink = 2}], [(child 30) { grow = 2 }],+                        [(child 10) { grow = 1 }, (child 18) { shrink = 1 }]]+              }+            flexWrap baseFlex { wrap = WrapReverse } 20 `shouldBe` baseFlex {+                wrap = WrapReverse,+                Flex.children = [[(child 20) { shrink = 2}], [(child 30) { grow = 2 }],+                        [(child 20) { shrink = 1 }], [(child 20) { grow = 1 }]]+              }+            flexWrap baseFlex { wrap = WrapReverse } 10 `shouldBe` baseFlex {+                wrap = WrapReverse,+                Flex.children = [[(child 10) { shrink = 2}], [(child 30) { grow = 2 }],+                        [(child 10) { shrink = 1 }], [(child 10) { grow = 1 }]]+              }+        it "Paginates properly" $ do+            let child l align = FlexChild {+                grow = 0,+                shrink = 0,+                basis = l,+                Flex.alignment = align,+                flexInner = l+              }+            let baseFlex = Flex.Flex {+                direction = Row,+                reverseRows = False,+                wrap = Wrap,+                justify = JStart,+                alignLines = Just JStart,+                baseGap = 2,+                crossGap = 2,+                Flex.children = [[(child 10 AlStart) { grow = 1 },+                        (child 20 AlCenter) { shrink = 1 },+                        (child 30 AlEnd) { grow = 2 },+                        (child 40 AlStretch) { shrink = 2 }]],+                pageWidth = 80+              } :: Flex.Flex Double Double+            let self = flexWrap baseFlex 50+            self `shouldBe` baseFlex {+                Flex.children = [[(child 10 AlStart) { grow = 1 },+                        (child 6 AlCenter) { shrink = 1, flexInner = 20 },+                        (child 30 AlEnd) { grow = 2 }], [+                        (child 40 AlStretch) { shrink = 2}]]+              }+            flexSplit (\x -> Size x x) 30 50 self `shouldBe` (baseFlex {+                Flex.children = [[(child 10 AlStart) { grow = 1 },+                        (child 6 AlCenter) { shrink = 1, flexInner = 20 },+                        (child 30 AlEnd) { grow = 2 }]]+              }, baseFlex {+                Flex.children = [[(child 40 AlStretch) { shrink = 2}]]+              })+            print self { direction = Flex.Column }+            flexSplit (\x -> Size x x) 40 50 self { direction = Flex.Column } `shouldBe` (baseFlex {+                direction = Flex.Column,+                Flex.children = [[(child 10 AlStart) { grow = 1 },+                        (child (-4) AlCenter) { shrink = 1, flexInner = 20 },+                        (child 30 AlEnd) { grow = 2 }], [(child 40 AlStretch) { shrink = 2}]]+              }, baseFlex {+                direction = Flex.Column,+                Flex.children = []+              })+            flexSplit (\x -> Size x x) 10 50 self { direction = Flex.Column } `shouldBe` (baseFlex {+                direction = Flex.Column,+                Flex.children = []+              }, baseFlex {+                direction = Flex.Column,+                Flex.children = [[(child 10 AlStart) { grow = 1 },+                        (child 6 AlCenter) { shrink = 1, flexInner = 20 },+                        (child 30 AlEnd) { grow = 2 }], [(child 40 AlStretch) { shrink = 2}]]+              })+            flexSplit (\x -> Size x x) 10 50 self { direction = Flex.Column, pageWidth = 40 } `shouldBe` (baseFlex {+                Flex.children = [[(child 10 AlStart) { grow = 1 },+                        (child 6 AlCenter) { shrink = 1, flexInner = 20 },+                        (child 30 AlEnd) { grow = 2 }]],+                pageWidth = 40+              }, baseFlex {+                Flex.children = [[(child 40 AlStretch) { shrink = 2}]],+                pageWidth = 40+              })+        it "positions correctly" $ do+            let child l align = FlexChild {+                grow = 0,+                shrink = 0,+                basis = l,+                Flex.alignment = align,+                flexInner = l+              }+            let child' l align pos = FlexChild {+                grow = 0, shrink = 0, basis = l,+                Flex.alignment = align, flexInner = (pos, l)+              }+            let flex childs = Flex.Flex {+                direction = Row,+                reverseRows = False,+                wrap = Wrap,+                justify = JStart,+                alignLines = Just JStart,+                baseGap = 2,+                crossGap = 2,+                Flex.children = childs,+                pageWidth = 80+              }+            let baseFlex = flex [[(child 10 AlStart) { grow = 1 },+                    (child 20 AlCenter) { shrink = 1 },+                    (child 30 AlEnd) { grow = 2 },+                    (child 40 AlStretch) { shrink = 2 }]] :: Flex.Flex Double Double+            flexPosition (,) (\a -> Size a a) (10, 10) (Size 50 50) baseFlex `shouldBe` flex [[+                (child' 10 AlStart (10, 10)) { grow = 1 },+                (child' 20 AlCenter (22, 20)) { shrink = 1 },+                (child' 30 AlEnd (44, 20)) { grow = 2 },+                (child' 40 AlStretch (76, 10)) { shrink = 2 }+              ]]   runMath = flip evalCalc [] . mapCalc fst . flip parseCalc [] . filter (/= Whitespace) . tokenize