packages feed

cattrap 0.2.0.0 → 0.3.0.0

raw patch · 12 files changed

+523/−312 lines, 12 filesdep +deepseqdep +directorydep +file-embeddep −GLUTdep −gldep ~balkondep ~fontconfig-puredep ~stylist-traitsnew-component:exe:cattrap-argonautnew-component:exe:cattrap-stylistPVP ok

version bump matches the API change (PVP)

Dependencies added: deepseq, directory, file-embed, html-conduit, hurl, hurl-xml, network-uri, parallel, sdl2, stylist, xml-conduit, xml-conduit-stylist

Dependencies removed: GLUT, gl

Dependency ranges changed: balkon, fontconfig-pure, stylist-traits, text

API changes (from Hackage documentation)

+ Graphics.Layout: instance (Graphics.Layout.Box.Zero m, Graphics.Layout.Box.CastDouble m, Control.DeepSeq.NFData m, Graphics.Layout.Box.Zero n, Graphics.Layout.Box.CastDouble n, Control.DeepSeq.NFData n) => Control.DeepSeq.NFData (Graphics.Layout.LayoutItem m n x)
+ Graphics.Layout.Box: instance (Control.DeepSeq.NFData m, Control.DeepSeq.NFData n) => Control.DeepSeq.NFData (Graphics.Layout.Box.Border m n)
+ Graphics.Layout.Box: instance (Control.DeepSeq.NFData m, Control.DeepSeq.NFData n) => Control.DeepSeq.NFData (Graphics.Layout.Box.PaddedBox m n)
+ Graphics.Layout.Box: instance (Control.DeepSeq.NFData m, Control.DeepSeq.NFData n) => Control.DeepSeq.NFData (Graphics.Layout.Box.Size m n)
+ Graphics.Layout.Box: instance (GHC.Read.Read m, GHC.Read.Read n) => GHC.Read.Read (Graphics.Layout.Box.Border m n)
+ Graphics.Layout.Box: instance (GHC.Read.Read n, GHC.Read.Read m) => GHC.Read.Read (Graphics.Layout.Box.PaddedBox m n)
+ Graphics.Layout.Box: instance (GHC.Read.Read n, GHC.Read.Read m) => GHC.Read.Read (Graphics.Layout.Box.Size m n)
+ Graphics.Layout.Box: instance (GHC.Show.Show m, GHC.Show.Show n) => GHC.Show.Show (Graphics.Layout.Box.Border m n)
+ Graphics.Layout.Box: instance (GHC.Show.Show n, GHC.Show.Show m) => GHC.Show.Show (Graphics.Layout.Box.PaddedBox m n)
+ Graphics.Layout.Box: instance Control.DeepSeq.NFData Graphics.Layout.Box.Length
+ Graphics.Layout.Box: instance GHC.Generics.Generic (Graphics.Layout.Box.Border m n)
+ Graphics.Layout.Box: instance GHC.Generics.Generic (Graphics.Layout.Box.PaddedBox m n)
+ Graphics.Layout.Box: instance GHC.Generics.Generic (Graphics.Layout.Box.Size m n)
+ Graphics.Layout.Box: instance GHC.Generics.Generic Graphics.Layout.Box.Length
+ Graphics.Layout.Box: instance GHC.Read.Read Graphics.Layout.Box.Length
+ Graphics.Layout.Box: instance GHC.Show.Show Graphics.Layout.Box.Length
+ Graphics.Layout.CSS: [innerParent] :: CSSBox a -> a
+ Graphics.Layout.CSS: [innerProperties] :: CSSBox a -> [(Text, [Token])]
+ Graphics.Layout.CSS: [paragraphOptions] :: CSSBox a -> ParagraphOptions
+ Graphics.Layout.CSS.Length: finalizeLengths :: Font' -> [Token] -> [Token]
+ Graphics.Layout.CSS.Length: units :: [Text]
+ Graphics.Layout.Grid: instance Control.DeepSeq.NFData Graphics.Layout.Grid.Alignment
+ Graphics.Layout.Grid: instance Control.DeepSeq.NFData Graphics.Layout.Grid.GridItem'
+ Graphics.Layout.Grid: instance GHC.Classes.Eq Graphics.Layout.Grid.Alignment
+ Graphics.Layout.Grid: instance GHC.Classes.Eq Graphics.Layout.Grid.GridItem'
+ Graphics.Layout.Grid: instance GHC.Classes.Ord Graphics.Layout.Grid.Alignment
+ Graphics.Layout.Grid: instance GHC.Classes.Ord Graphics.Layout.Grid.GridItem'
+ Graphics.Layout.Grid: instance GHC.Enum.Enum Graphics.Layout.Grid.Alignment
+ Graphics.Layout.Grid: instance GHC.Generics.Generic Graphics.Layout.Grid.Alignment
+ Graphics.Layout.Grid: instance GHC.Generics.Generic Graphics.Layout.Grid.GridItem'
+ Graphics.Layout.Grid: instance GHC.Read.Read Graphics.Layout.Grid.Alignment
+ Graphics.Layout.Grid: instance GHC.Read.Read Graphics.Layout.Grid.GridItem'
+ Graphics.Layout.Grid: instance GHC.Show.Show Graphics.Layout.Grid.Alignment
+ Graphics.Layout.Grid: instance GHC.Show.Show Graphics.Layout.Grid.GridItem'
- Graphics.Layout: boxMaxWidth :: CastDouble y => PaddedBox a Double -> LayoutItem y Length x -> LayoutItem y Length x
+ Graphics.Layout: boxMaxWidth :: (CastDouble y, Zero y, NFData y) => PaddedBox a Double -> LayoutItem y Length x -> LayoutItem y Length x
- Graphics.Layout: boxMinWidth :: (Zero y, CastDouble y) => Maybe Double -> LayoutItem y Length x -> LayoutItem y Length x
+ Graphics.Layout: boxMinWidth :: (Zero y, CastDouble y, NFData y) => Maybe Double -> LayoutItem y Length x -> LayoutItem y Length x
- Graphics.Layout: boxNatWidth :: (Zero y, CastDouble y) => Maybe Double -> LayoutItem y Length x -> LayoutItem y Length x
+ Graphics.Layout: boxNatWidth :: (Zero y, CastDouble y, NFData y) => Maybe Double -> LayoutItem y Length x -> LayoutItem y Length x
- Graphics.Layout: boxWidth :: (Zero y, CastDouble y) => PaddedBox b Double -> LayoutItem y Length x -> LayoutItem y Double x
+ Graphics.Layout: boxWidth :: (Zero y, CastDouble y, NFData y) => PaddedBox b Double -> LayoutItem y Length x -> LayoutItem y Double x
- Graphics.Layout: layoutGetInner :: LayoutItem m n c -> c
+ Graphics.Layout: layoutGetInner :: LayoutItem m n x -> x
- Graphics.Layout.Box: bottomSpace :: Num m => PaddedBox m n -> m
+ Graphics.Layout.Box: bottomSpace :: Num a => PaddedBox a n -> a
- Graphics.Layout.Box: hSpace :: Num n => PaddedBox m n -> n
+ Graphics.Layout.Box: hSpace :: Num a => PaddedBox m a -> a
- Graphics.Layout.Box: height :: Num m => PaddedBox m n -> m
+ Graphics.Layout.Box: height :: Num a => PaddedBox a n -> a
- Graphics.Layout.Box: leftSpace :: Num n => PaddedBox m n -> n
+ Graphics.Layout.Box: leftSpace :: Num a => PaddedBox m a -> a
- Graphics.Layout.Box: maxHeight :: Num m => PaddedBox m n -> m
+ Graphics.Layout.Box: maxHeight :: Num a => PaddedBox a n -> a
- Graphics.Layout.Box: maxWidth :: Num n => PaddedBox m n -> n
+ Graphics.Layout.Box: maxWidth :: Num a => PaddedBox m a -> a
- Graphics.Layout.Box: minHeight :: Num m => PaddedBox m n -> m
+ Graphics.Layout.Box: minHeight :: Num a => PaddedBox a n -> a
- Graphics.Layout.Box: minWidth :: Num n => PaddedBox m n -> n
+ Graphics.Layout.Box: minWidth :: Num a => PaddedBox m a -> a
- Graphics.Layout.Box: rightSpace :: Num n => PaddedBox m n -> n
+ Graphics.Layout.Box: rightSpace :: Num a => PaddedBox m a -> a
- Graphics.Layout.Box: topSpace :: Num m => PaddedBox m n -> m
+ Graphics.Layout.Box: topSpace :: Num a => PaddedBox a n -> a
- Graphics.Layout.Box: vSpace :: Num m => PaddedBox m n -> m
+ Graphics.Layout.Box: vSpace :: Num a => PaddedBox a n -> a
- Graphics.Layout.Box: width :: Num n => PaddedBox m n -> n
+ Graphics.Layout.Box: width :: Num a => PaddedBox m a -> a
- Graphics.Layout.CSS: CSSBox :: Display -> BoxSizing -> PaddedBox Unitted Unitted -> Pattern -> CSSFont -> a -> CSSGrid -> CSSCell -> CSSInline -> Bool -> PageOptions -> CSSBox a
+ Graphics.Layout.CSS: CSSBox :: Display -> BoxSizing -> PaddedBox Unitted Unitted -> Pattern -> CSSFont -> a -> [(Text, [Token])] -> a -> CSSGrid -> CSSCell -> CSSInline -> Bool -> PageOptions -> ParagraphOptions -> CSSBox a

Files

CHANGELOG.md view
@@ -3,3 +3,10 @@ ## 0.1.0.0 -- YYYY-mm-dd  * First version. Released on an unsuspecting world.++## 0.2.0.0 -- 2023-06-03++* Integrate richtext support.+* Add shorthand properties.+* Add logical properties based on text-direction.+* Propagate prioritized properties.
Graphics/Layout.hs view
@@ -14,6 +14,8 @@                                 ParagraphLayout(..), layoutRich) import Data.Text.ParagraphLayout (paginate, PageContinuity(..), PageOptions(..)) import Stylist (PropertyParser(temp))+import Control.Parallel.Strategies+import Control.DeepSeq (NFData(..))  import Graphics.Layout.Box as B import Graphics.Layout.Grid as G@@ -53,6 +55,10 @@ nullLayout :: (PropertyParser x, Zero m, Zero n) => LayoutItem m n x nullLayout = LayoutFlow temp zero [] +instance (Zero m, CastDouble m, NFData m, Zero n, CastDouble n, NFData n) =>+        NFData (LayoutItem m n x) where+    rnf = rnf . layoutGetBox -- Avoid auxiliary properties that don't cleanly `rnf`+ --- | Retrieve the surrounding box for a layout item. layoutGetBox :: (Zero m, Zero n, CastDouble m, CastDouble n) =>         LayoutItem m n x -> PaddedBox m n@@ -92,14 +98,14 @@ setCellBox' (child, cell) = setCellBox cell $ layoutGetBox child  -- | Update a (sub)tree to compute & cache minimum legible sizes.-boxMinWidth :: (Zero y, CastDouble y) =>+boxMinWidth :: (Zero y, CastDouble y, NFData y) =>         Maybe Double -> LayoutItem y Length x -> LayoutItem y Length x boxMinWidth parent (LayoutFlow val self childs) = LayoutFlow val self' childs'   where     self' = self { B.min = mapSizeX (B.mapAuto min') (B.min self) }     min' = flowMinWidth parent' self childs''     childs'' = map (mapX' $ lowerLength selfWidth) $ map layoutGetBox childs'-    childs' = map (boxMinWidth $ Just selfWidth) childs+    childs' = parMap' (boxMinWidth $ Just selfWidth) childs     selfWidth = width $ mapX' (lowerLength parent') self     parent' = fromMaybe 0 parent boxMinWidth parent (LayoutGrid val self cells0 childs) = LayoutGrid val self' cells' childs'@@ -108,7 +114,7 @@     cells = sizeTrackMins parent' (inline self) $ map inline cells'     cells' = map setCellBox' $ zip childs' cells0 -- Flatten subgrids     childs'' = map (mapX' $ lowerLength selfWidth) $ map layoutGetBox childs'-    childs' = map (boxMinWidth $ Just selfWidth) childs+    childs' = parMap' (boxMinWidth $ Just selfWidth) childs     selfWidth = trackNat (lowerLength parent') $ inline self     parent' = fromMaybe (gridEstWidth self cells0) parent     zeroBox :: PaddedBox Double Double@@ -119,14 +125,14 @@     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) =>+boxNatWidth :: (Zero y, CastDouble y, NFData y) =>         Maybe Double -> LayoutItem y Length x -> LayoutItem y Length x boxNatWidth parent (LayoutFlow val self childs) = LayoutFlow val self' childs'   where     self' = self { B.nat = Size size' $ block $ B.nat self }     size' = flowNatWidth parent' self childs''     childs'' = map (mapX' $ lowerLength selfWidth) $ map layoutGetBox childs'-    childs' = map (boxNatWidth $ Just selfWidth) childs+    childs' = parMap' (boxNatWidth $ Just selfWidth) childs     selfWidth = width $ mapX' (lowerLength parent') self     parent' = fromMaybe 0 parent boxNatWidth parent (LayoutGrid val self cells0 childs) = LayoutGrid val self' cells' childs'@@ -135,7 +141,7 @@     cells = sizeTrackNats parent' (inline $ self) $ map inline cells'     cells' = map setCellBox' $ zip childs' cells0 -- Flatten subgrids     childs'' = map (mapX' $ lowerLength selfWidth) $ map layoutGetBox childs'-    childs' = map (boxNatWidth $ Just selfWidth) childs+    childs' = parMap' (boxNatWidth $ Just selfWidth) childs     selfWidth = trackNat (lowerLength parent') $ inline self     parent' = fromMaybe (gridEstWidth self cells0) parent     zeroBox :: PaddedBox Double Double@@ -146,16 +152,17 @@     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+boxMaxWidth :: (CastDouble y, Zero y, NFData y) =>+        PaddedBox a Double -> LayoutItem y Length x -> LayoutItem y Length x boxMaxWidth parent (LayoutFlow val self childs) = LayoutFlow val self' childs'   where-    childs' = map (boxMaxWidth self'') childs+    childs' = parMap' (boxMaxWidth self'') childs     self'' = mapX' (lowerLength $ inline $ B.size parent) self'     self' = self { B.max = Size (Pixels max') (block $ B.max self) }     max' = flowMaxWidth parent self boxMaxWidth parent (LayoutGrid val self cells childs) = LayoutGrid val self cells childs'   where -- Propagate parent track as default.-    childs' = map inner $ zip cells childs+    childs' = parMap' inner $ zip cells childs     inner (Size cellx celly, child) =         boxMaxWidth (cellSize (inline self) cellx `size2box` cellSize (block self) celly) child     size2box x y = zeroBox { B.min = Size x y, B.max = Size x y, B.size = Size x y }@@ -165,18 +172,18 @@     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 ->-        LayoutItem y Double x+boxWidth :: (Zero y, CastDouble y, NFData y) =>+        PaddedBox b Double -> LayoutItem y Length x -> LayoutItem y Double x boxWidth parent (LayoutFlow val self childs) = LayoutFlow val self' childs'   where-    childs' = map (boxWidth self') childs+    childs' = parMap' (boxWidth self') childs     self' = (mapX' (lowerLength $ inline $ size parent) self) {         size = Size size' $ block $ B.max self       }     size' = flowWidth parent self boxWidth parent (LayoutGrid val self cells childs) = LayoutGrid val self' cells' childs'   where -- Propagate parent track as default-    (cells', childs') = unzip $ map recurse $ zip cells childs+    (cells', childs') = unzip $ parMap' recurse $ zip cells childs     recurse (cell, child) = (cell', child')       where         cell' = setCellBox cell $ layoutGetBox child'@@ -207,13 +214,13 @@     self' = self { size = mapSizeY (mapAuto size') (size self) }     size' = flowNatHeight parent self childs''     childs'' = map (mapY' (lowerLength parent)) $ map layoutGetBox childs'-    childs' = map (boxNatHeight $ inline $ size self) childs+    childs' = parMap' (boxNatHeight $ inline $ size self) childs boxNatHeight parent (LayoutGrid val self cells childs) = LayoutGrid val self' cells childs'   where     self' = Size (inline self) (block self) { trackNats = heights }     heights = sizeTrackNats parent (block self) $ map block cells'     cells' = map setCellBox' $ zip childs' cells -- Flatten subgrids-    childs' = map (boxNatHeight width) childs+    childs' = parMap' (boxNatHeight width) childs     width = trackNat id $ inline self boxNatHeight parent self@(LayoutInline _ _ _) = self boxNatHeight parent self@(LayoutInline' _ _ _) = self@@ -224,12 +231,12 @@ boxMinHeight :: Double -> LayoutItem Length Double x -> LayoutItem Length Double x boxMinHeight parent (LayoutFlow val self childs) = LayoutFlow val self' childs'   where-    childs' = map (boxMinHeight $ inline $ size self) childs+    childs' = parMap' (boxMinHeight $ inline $ size self) childs     self' = self { B.min = Size (inline $ B.min self) (Pixels min') }     min' = flowMinHeight parent self boxMinHeight parent (LayoutGrid val self cells childs) = LayoutGrid val self' cells' childs'   where-    (cells', childs') = unzip $ map recurse $ zip cells childs+    (cells', childs') = unzip $ parMap' recurse $ zip cells childs     recurse (cell, child) = (cell', child') -- Propagate track into subgrids.       where         cell' = setCellBox cell (layoutGetBox child')@@ -247,13 +254,13 @@         LayoutItem Length Double x boxMaxHeight parent (LayoutFlow val self childs) = LayoutFlow val self' childs'   where-    childs' = map (boxMaxHeight $ mapY' (lowerLength width) self') childs+    childs' = parMap' (boxMaxHeight $ mapY' (lowerLength width) self') childs     self' = self { B.max = Size (inline $ B.max self) (Pixels max') }     max' = flowMaxHeight (inline $ size parent) self     width = inline $ size self boxMaxHeight parent (LayoutGrid val self cells childs) = LayoutGrid val self cells' childs'   where-    (cells', childs') = unzip $ map recurse $ zip cells childs+    (cells', childs') = unzip $ parMap' recurse $ zip cells childs     recurse (cell, child) = (cell', child') -- Propagate track into subgrids       where         cell' = setCellBox cell (layoutGetBox child')@@ -269,7 +276,7 @@ boxHeight :: PaddedBox Double Double -> LayoutItem Length Double x -> LayoutItem Double Double x boxHeight parent (LayoutFlow val self childs) = LayoutFlow val self' childs'   where-    childs' = map (boxHeight self') childs+    childs' = parMap' (boxHeight self') childs     self' = (mapY' (lowerLength $ inline $ size parent) self) {         size = Size (inline $ size self) size'       }@@ -277,7 +284,7 @@     width = inline $ size self boxHeight parent (LayoutGrid val self cells0 childs) = LayoutGrid val self' cells' childs'   where-    (cells', childs') = unzip $ map recurse $ zip cells0 childs+    (cells', childs') = unzip $ parMap' recurse $ zip cells0 childs     recurse (cell, child) = (cell', child') -- Propagate track into subgrids.       where         cell' = setCellBox cell (layoutGetBox child')@@ -351,12 +358,12 @@     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+    childs' = parMap' recurse $ zip pos' childs     recurse ((Size x' y'), child) = boxPosition (x + x', y + y') child     pos' = positionFlow $ map layoutGetBox childs boxPosition pos@(x, y) (LayoutGrid val self cells childs) = LayoutGrid (pos, val) self cells childs'   where-    childs' = map recurse $ zip pos' childs+    childs' = parMap' recurse $ zip pos' childs     recurse ((x', y'), child) = boxPosition (x + x', y + y') child     pos' = gridPosition self cells boxPosition pos@(x, y) (LayoutInline val self paging) =@@ -364,7 +371,7 @@ 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+    LayoutConst (pos, val) self $ parMap' (boxPosition pos) childs boxPosition pos (LayoutSpan self) = LayoutSpan $ positionTree pos self -- | Compute sizes & position information for all nodes in the (sub)tree. boxLayout :: (PropertyParser x, Eq x) => PaddedBox Double Double ->@@ -392,3 +399,6 @@     (pattern font, fontSize font) `M.singleton` IS.fromList glyphs   where glyphs = map fromEnum $ map Hb.codepoint $ map fst $ fragmentGlyphs self glyphsPerFont node = M.unionsWith IS.union $ map glyphsPerFont $ layoutGetChilds node -}++parMap' :: NFData b => (a -> b) -> [a] -> [b]+parMap' = parMap rdeepseq
Graphics/Layout/Box.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE RecordWildCards, DeriveGeneric #-} -- | Datastructures representing the CSS box model, -- & utilities for operating on them. module Graphics.Layout.Box(Border(..), mapX, mapY,@@ -8,10 +8,14 @@         leftSpace, rightSpace, topSpace, bottomSpace, hSpace, vSpace,         Length(..), mapAuto, lowerLength, Zero(..), CastDouble(..)) where +import Control.DeepSeq (NFData)+import GHC.Generics (Generic)+ -- | Amount of space surrounding the box. data Border m n = Border {     top :: m, bottom :: m, left :: n, right :: n-} deriving Eq+} deriving (Eq, Read, Show, Generic)+instance (NFData m, NFData n) => NFData (Border m n) -- | Convert horizontal spacing via given callback. mapX :: (n -> nn) -> Border m n -> Border m nn -- | Convert vertical spacing via given callback.@@ -21,7 +25,8 @@  -- | 2D size of a box. Typically inline is width & block is height. -- This may change as support for vertical layout is added.-data Size m n = Size {inline :: n, block :: m} deriving (Eq, Show)+data Size m n = Size {inline :: n, block :: m} deriving (Eq, Show, Read, Generic)+instance (NFData m, NFData n) => NFData (Size m n) -- | Convert inline size via given callback mapSizeY :: (m -> mm) -> Size m n -> Size mm n mapSizeY cb self = Size (inline self) (cb $ block self)@@ -45,7 +50,8 @@     border :: Border m n,     -- | The amount of space between the border & anything else.     margin :: Border m n-} deriving Eq+} deriving (Eq, Read, Show, Generic)+instance (NFData m, NFData n) => NFData (PaddedBox m n) -- | An empty box, takes up nospace onscreen. zeroBox :: PaddedBox Double Double zeroBox = PaddedBox {@@ -124,7 +130,8 @@         | Auto -- ^ Use normal layout computations.         | Preferred -- ^ Use computed preferred width.         | Min -- ^ Use minimum legible width.-        deriving Eq+        deriving (Eq, Read, Show, Generic)+instance NFData Length  -- | Convert a length given the container's width. Filling in 0 for keywords. -- If you wish for keywords to be handled differently, callers need to compute
Graphics/Layout/CSS.hs view
@@ -9,8 +9,7 @@ import qualified Data.Text as Txt import Stylist (PropertyParser(..)) import Stylist.Tree (StyleTree(..))-import Data.Text.ParagraphLayout.Rich (paragraphLineHeight, constructParagraph,-        defaultParagraphOptions, defaultBoxOptions,+import Data.Text.ParagraphLayout.Rich (constructParagraph, defaultBoxOptions,         LineHeight(..), InnerNode(..), Box(..), RootNode(..))  import Graphics.Layout.Box as B@@ -22,41 +21,48 @@  import Data.Char (isSpace) import Graphics.Layout.CSS.Parse+import Data.Maybe (fromMaybe)  instance (PropertyParser x, Zero m, Zero n) => Default (UserData m n x) where     def = ((placeholderFont, 0), zero, temp) +inner' :: PropertyParser x => Font' -> CSSBox x -> x+inner' f self = foldr apply (inner self) $ innerProperties self+  where apply (k, v) ret = fromMaybe ret $+            longhand (innerParent self) ret k $ finalizeLengths f v+ -- | Desugar parsed CSS into more generic layout parameters. finalizeCSS :: PropertyParser x => Font' -> Font' -> StyleTree (CSSBox x) ->         LayoutItem Length Length x finalizeCSS root parent StyleTree { style = self'@CSSBox { display = None } } =-    LayoutFlow (inner self') lengthBox []+    LayoutFlow (inner' parent self') lengthBox [] finalizeCSS root parent self@StyleTree {-    style = self'@CSSBox { display = Grid, inner = val }, children = childs-  } = LayoutFlow val (finalizeBox self' font_) [+    style = self'@CSSBox { display = Grid }, children = childs+  } = LayoutFlow (inner' font_ self') (finalizeBox self' font_) [         finalizeGrid (gridStyles self') font_ (map cellStyles $ map style childs)             (finalizeChilds root font_ self' childs)]   where     font_ = pattern2font (font self') (font' self') parent root finalizeCSS root parent self@StyleTree {-        style = self'@CSSBox { display = Table, captionBelow = False }, children = childs-    } = LayoutFlow (inner self') (finalizeBox self' font_)+        style=self'@CSSBox {display=Table, captionBelow=False}, children=childs+    } = LayoutFlow (inner' font_ self') (finalizeBox self' font_)         ([finalizeCSS root font_ child { style = child' { display = Block } }             | child@StyleTree { style = child'@CSSBox { display = TableCaption } } <- childs] ++         [finalizeTable root font_ (inner self') childs])   where     font_ = pattern2font (font self') (font' self') parent root finalizeCSS root parent self@StyleTree {-        style = self'@CSSBox { display = Table, captionBelow = True }, children = childs-    } = LayoutFlow (inner self') (finalizeBox self' font_)+        style = self'@CSSBox {display=Table, captionBelow=True}, children = childs+    } = LayoutFlow (inner' font_ self') (finalizeBox self' font_)         (finalizeTable root font_ temp childs:         [finalizeCSS root font_ child { style = child' { display = Block } }             | 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 { inner = val }, children = childs-  } = LayoutFlow val (finalizeBox self' font_) (finalizeChilds root font_ self' childs)+    style = self', children = childs+  } = LayoutFlow (inner' font_ self') (finalizeBox self' font_)+        (finalizeChilds root font_ self' childs)   where     font_ = pattern2font (font self') (font' self') parent root finalizeCSS' sysfont self@StyleTree { style = self' } =@@ -69,10 +75,10 @@     finalizeChilds root parent style' childs finalizeChilds root parent style' childs@(child:childs')     | isInlineTree childs, Just self <- finalizeParagraph (flattenTree0 childs) =-        [LayoutInline (inherit $ inner style') self paging]+        [LayoutInline (inherit $ inner' parent style') self paging]     | (inlines@(_:_), blocks) <- spanInlines childs,         Just self <- finalizeParagraph (flattenTree0 inlines) =-            LayoutInline (inherit $ inner style') self paging :+            LayoutInline (inherit $ inner' parent style') self paging :                 finalizeChilds root parent style' blocks     | (StyleTree { style = CSSBox { display = Inline } }:childs') <- childs =         finalizeChilds root parent style' childs' -- Inline's all whitespace...@@ -100,19 +106,17 @@         buildInline f i self $ map (flattenTree f) $ enumerate child       where f = pattern2font (font self) (font' self) p root     flattenTree f (i,StyleTree {style=self@CSSBox {inlineStyles=CSSInline txt _ _}})-        = buildInline f i self [TextSequence ((f,0),zero,inherit $ inner self) txt]+        = buildInline f i self [+            TextSequence ((f, 0), zero, inherit $ inner' parent self) txt]     buildInline f i self childs =-        InlineBox ((f, i), finalizeBox self f, inner self)+        InlineBox ((f, i), finalizeBox self f, inner' parent self)                 (Box childs' $ flip applyFontInline f $ txtOpts self)                 defaultBoxOptions -- Fill in during layout.       where childs' = applyBidi (inlineStyles self) childs     finalizeParagraph (RootBox (Box [TextSequence _ txt] _))         | Txt.all isSpace txt = Nothing -- Discard isolated whitespace.     finalizeParagraph tree =-        Just $ constructParagraph "" tree "" defaultParagraphOptions {-            paragraphLineHeight = Absolute $ toEnum $ fromEnum-                    (lineheight parent * hbUnit)-          }+        Just $ constructParagraph "" tree "" $ paragraphOptions style'     enumerate = zip $ enumFrom 0 finalizeChilds _ _ _ [] = [] 
Graphics/Layout/CSS/Length.hs view
@@ -1,12 +1,12 @@ {-# LANGUAGE OverloadedStrings #-} -- | Infrastructure for parsing & desugaring length units & keywords, -- in reference to the selected font.-module Graphics.Layout.CSS.Length(Unitted, auto, parseLength, parseLength',-        n2f, finalizeLength, px2pt, Font'(..)) where+module Graphics.Layout.CSS.Length(Unitted, auto, parseLength, parseLength', units,+        n2f, finalizeLength, finalizeLengths, px2pt, Font'(..)) where  import Data.CSS.Syntax.Tokens (Token(..), NumericValue(..)) import qualified Data.Text as Txt-import Data.Scientific (toRealFloat)+import Data.Scientific (toRealFloat, fromFloatDigits) import Debug.Trace (trace) -- For warnings. import Data.Text.Glyphize (Font) import Graphics.Text.Font.Choose (Pattern(..))@@ -27,6 +27,7 @@ parseLength [Dimension _ x unit]     | n2f x == 0 && unit == "" = Just (0,"px")     | unit `elem` units = Just (n2f x,unit)+parseLength [Number _ x] | n2f x == 0 = Just (0,"px") parseLength [Ident "auto"] = Just (0,"auto") parseLength [Ident "initial"] = Just (0,"auto") parseLength _ = Nothing@@ -75,6 +76,15 @@ finalizeLength (_,unit) _ = trace ("Invalid unit " ++ Txt.unpack unit) $ Pixels 0 -- | Convert from a computed length to the "pt" unit. px2pt f x = x / scale f / 96 * 72++-- | Convert any length-units in the given CSS tokens to device pixels+finalizeLengths :: Font' -> [Token] -> [Token]+finalizeLengths f (Dimension _ x unit:toks)+    | unit `elem` units, Pixels y <- finalizeLength (n2f x,unit) f =+        Dimension "" (NVNumber $ fromFloatDigits y) "px":finalizeLengths f toks+finalizeLengths f (Number a b:ts)|n2f b==0=Dimension a b "px":finalizeLengths f ts+finalizeLengths f (tok:toks) = tok:finalizeLengths f toks+finalizeLengths _ [] = []  -- | A Harfbuzz font with sizing parameters. data Font' = Font' {
Graphics/Layout/CSS/Parse.hs view
@@ -5,18 +5,21 @@ import Stylist (PropertyParser(..), TrivialPropertyParser, parseOperands,                 parseUnorderedShorthand', parseUnorderedShorthand) import Data.Text.ParagraphLayout (PageOptions(..))-import Data.Text.ParagraphLayout.Rich (textDirection)+import Data.Text.ParagraphLayout.Rich (textDirection, ParagraphOptions,+            defaultParagraphOptions, paragraphAlignment, ParagraphAlignment(..)) import Data.Text.Glyphize (Direction(..))  import Graphics.Layout.Box as B import Graphics.Text.Font.Choose (Pattern, unset)-import Graphics.Layout.CSS.Length (Unitted, parseLength', parseLength, auto)+import Graphics.Layout.CSS.Length (Unitted, parseLength', parseLength, auto, units) import Graphics.Layout.CSS.Font (CSSFont) import Graphics.Layout.Grid.CSS (CSSGrid(..), CSSCell(..), Placement(..)) import Graphics.Layout.Inline.CSS (CSSInline(..))  import Data.Maybe (isJust, fromMaybe) import qualified Data.HashMap.Lazy as HM+import Data.Text (Text)+import Debug.Trace (trace) -- For debug warnings.  -- | Parsed CSS properties relevant to layout. data CSSBox a = CSSBox {@@ -33,6 +36,10 @@     font' :: CSSFont,     -- | Caller-specified data, to parse additional CSS properties.     inner :: a,+    -- | Properties to lower size units before passing onto to `inner`+    innerProperties :: [(Text, [Token])],+    -- | Parent to use when parsing length-expanded inner properties.+    innerParent :: a,     -- | Grid-related CSS properties.     gridStyles :: CSSGrid,     -- | Grid item related CSS properties.@@ -42,7 +49,9 @@     -- | Parsed CSS caption-side.     captionBelow :: Bool,     -- | Parsed widows & orphans controlling pagination.-    pageOptions :: PageOptions+    pageOptions :: PageOptions,+    -- | Parsed text-alignment & other options which applies per-paragraph.+    paragraphOptions :: ParagraphOptions } -- | Accessor for inlineStyle's `textDirection` attribute. direction CSSBox { inlineStyles = CSSInline _ opts _ } = textDirection opts@@ -77,11 +86,17 @@         font = temp,         font' = temp,         inner = temp,+        innerProperties = [],+        innerParent = trace ("Parent not overriden upon " +++            "buffering inner properties for length resolution!") temp,         gridStyles = temp,         cellStyles = temp,         inlineStyles = temp,         captionBelow = False,-        pageOptions = PageOptions 0 0 2 2+        pageOptions = PageOptions 0 0 2 2,+        paragraphOptions = defaultParagraphOptions {+            paragraphAlignment = AlignStart+        }       }     inherit parent = CSSBox {         boxSizing = boxSizing parent,@@ -90,11 +105,14 @@         font = inherit $ font parent,         font' = inherit $ font' parent,         inner = inherit $ inner parent,+        innerProperties = [],+        innerParent = inner parent,         gridStyles = inherit $ gridStyles parent,         cellStyles = inherit $ cellStyles parent,         inlineStyles = inherit $ inlineStyles parent,         captionBelow = captionBelow parent,-        pageOptions = pageOptions parent+        pageOptions = pageOptions parent,+        paragraphOptions = paragraphOptions parent       }     priority self = concat [x font, x font', x gridStyles, x cellStyles, x inner]       where x getter = priority $ getter self@@ -259,6 +277,12 @@         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+        | (d', _:_)<-testLengthProp d, Just _<-longhand (inner a) (inner b) c d' =+            Just b {+                innerProperties = (c, d):innerProperties b,+                innerParent = inner a+            }     longhand a b c d | Just inner' <- longhand (inner a) (inner b) c d = Just b {         inner = inner'       }@@ -274,6 +298,28 @@             }}       where p x = Numbered x Nothing +    longhand _ self@CSSBox {paragraphOptions=o} "text-align" [Ident "initial"] =+        Just self { paragraphOptions = o { paragraphAlignment = AlignStart } }+    longhand _ self@CSSBox {paragraphOptions=o} "text-align" [Ident "start"] =+        Just self { paragraphOptions = o { paragraphAlignment = AlignStart } }+    longhand _ self@CSSBox {paragraphOptions=o} "text-align" [Ident "end"] =+        Just self { paragraphOptions = o { paragraphAlignment = AlignEnd } }+    longhand _ self@CSSBox {paragraphOptions=o} "text-align" [Ident "left"] =+        Just self { paragraphOptions = o { paragraphAlignment = AlignLeft } }+    longhand _ self@CSSBox {paragraphOptions=o} "text-align" [Ident "right"] =+        Just self { paragraphOptions = o { paragraphAlignment = AlignRight } }+    longhand _ self@CSSBox {paragraphOptions=o} "text-align" [Ident "center"] =+        Just self { paragraphOptions = o { paragraphAlignment = AlignCentreH } }+    -- text-align: justify is unimplemented.+    longhand p self@CSSBox { paragraphOptions = o } "text-align"+            [Ident "match-parent"] = case paragraphAlignment$paragraphOptions p of+        AlignStart | DirLTR <- direction p -> ret AlignLeft+        AlignStart | DirRTL <- direction p -> ret AlignRight+        AlignEnd | DirLTR <- direction p -> ret AlignRight+        AlignEnd | DirRTL <- direction p -> ret AlignLeft+        x -> ret x+      where ret x = Just self { paragraphOptions = o { paragraphAlignment = x } }+     longhand _ _ _ _ = Nothing      shorthand self "font" toks = case parseOperands toks of@@ -317,88 +363,28 @@                 [("border-top-width", top), ("border-right-width", right),                  ("border-bottom-width", bottom), ("border-left-width", left)]       where x = parseOperands toks-    -- Define other border shorthands here to properly handle border-widths-    shorthand self "border" toks = parseUnorderedShorthand self [-        "border-color", "border-style", "border-width"] toks-    shorthand self "border-top" toks = parseUnorderedShorthand self [-        "border-top-color", "border-top-style", "border-top-width"] toks-    shorthand self "border-right" toks = parseUnorderedShorthand self [-        "border-right-color", "border-right-style", "border-right-width"] toks-    shorthand self "border-bottom" toks = parseUnorderedShorthand self [-        "border-bottom-color", "border-bottom-style", "border-bottom-width"] toks-    shorthand self "border-left" toks = parseUnorderedShorthand self [-        "border-left-color", "border-left-style", "border-left-width"] toks-    shorthand self "border-inline" toks = parseUnorderedShorthand self [-        "border-inline-color", "border-inline-style", "border-inline-width"] toks-    shorthand self "border-inline-start" toks = parseUnorderedShorthand self [-        "border-inline-start-color", "border-inline-start-style",-        "border-inline-start-width"] toks-    shorthand self "border-inline-end" toks = parseUnorderedShorthand self [-        "border-inline-end-color", "border-inline-end-style",-        "border-inline-end-width"] toks-    shorthand self "border-block" toks = parseUnorderedShorthand self [-        "border-block-color", "border-block-style", "border-block-width"] toks-    shorthand self "border-block-start" toks = parseUnorderedShorthand self [-        "border-block-start-color", "border-block-start-style",-        "border-block-start-width"] toks-    shorthand self "border-block-end" toks = parseUnorderedShorthand self [-        "border-block-end-color", "border-block-end-style",-        "border-block-end-width"] toks-    shorthand self "border-color" toks-        | length x > 0 && length x <= 4, (top:right:bottom:left:_) <- cycle x,-            all (validProp self "border-top-color") x =-                [("border-top-color", top), ("border-right-color", right),-                 ("border-bottom-color", bottom), ("border-left-color", left)]-      where x = parseOperands toks-    shorthand self "border-style" toks-        | length x > 0 && length x <= 4, (top:right:bottom:left:_) <- cycle x,-            all (validProp self "border-top-style") x =-                [("border-top-style", top), ("border-right-style", right),-                 ("border-bottom-style", bottom), ("border-left-style", left)]-      where x = parseOperands toks-    shorthand self "border-width" toks-        | length x > 0 && length x <= 4, (top:right:bottom:left:_) <- cycle x,-            all (validProp self "border-top-width") x =-                [("border-top-width", top), ("border-right-width", right),-                 ("border-bottom-width", bottom), ("border-left-width", left)]-      where x = parseOperands toks-    shorthand self "border-inline-color" toks-        | length x > 0 && length x <= 2, (s:e:_) <- cycle x,-            all (validProp self "border-inline-start-color") x =-                [("border-inline-start-color", s), ("border-inline-end-color", e)]-      where x = parseOperands toks-    shorthand self "border-inline-style" toks-        | length x > 0 && length x <= 2, (s:e:_) <- cycle x,-            all (validProp self "border-inline-start-style") x =-                [("border-inline-start-style", s), ("border-inline-end-style", e)]-      where x = parseOperands toks-    shorthand self "border-inline-width" toks-        | length x > 0 && length x <= 2, (s:e:_) <- cycle x,-            all (validProp self "border-inline-start-width") x =-                [("border-inline-start-width", s), ("border-inline-end-style", e)]-      where x = parseOperands toks-    shorthand self "border-block-color" toks-        | length x > 0 && length x <= 2, (s:e:_) <- cycle x,-            all (validProp self "border-block-start-color") x =-                [("border-block-start-color", s), ("border-block-end-color", e)]-      where x = parseOperands toks-    shorthand self "border-block-style" toks-        | length x > 0 && length x <= 2, (s:e:_) <- cycle x,-            all (validProp self "border-block-start-style") x =-                [("border-block-start-style", s), ("border-block-end-style", e)]-      where x = parseOperands toks-    shorthand self "border-block-width" toks-        | length x > 0 && length x <= 2, (s:e:_) <- cycle x,-            all (validProp self "border-block-start-width") x =-                [("border-block-start-width", s), ("border-block-end-width", e)]-      where x = parseOperands toks -    shorthand self k v | Just _ <- longhand self self k v = [(k, v)]     shorthand self k v | ret@(_:_) <- shorthand (font self) k v = ret     shorthand self k v | ret@(_:_) <- shorthand (font' self) k v = ret     shorthand self k v | ret@(_:_) <- shorthand (inlineStyles self) k v = ret     shorthand self k v | ret@(_:_) <- shorthand (gridStyles self) k v = ret     shorthand self k v | ret@(_:_) <- shorthand (cellStyles self) k v = ret-    shorthand self k v = shorthand (inner self) k v+    shorthand self k v | ret@(_:_) <- shorthand (inner self) k v = ret+    shorthand self k v+        | (v', ls)<-testLengthProp v, ret@(_:_)<-shorthand (inner self) k v' =+            [(key, map (restore ls) value) | (key, value) <- ret]+      where+        restore ls (Dimension _ (NVInteger x) "px") | x' < length ls = ls !! x'+          where x' = fromInteger x+        restore _ ret = ret+    shorthand self k v | Just _ <- longhand self self k v = [(k, v)]+        | otherwise = []  validProp self key value = isJust $ longhand self self key value++testLengthProp (tok@(Dimension _ _ unit):toks) | unit `elem` units =+    let (toks', lengths) = testLengthProp toks+    in (Dimension "" (NVInteger $ toInteger $ succ $ length lengths) "px":toks',+        tok:lengths)+testLengthProp (tok:toks) = let (toks',ls) = testLengthProp toks in (tok:toks',ls)+testLengthProp [] = ([], [])
Graphics/Layout/Grid.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE RecordWildCards, OverloadedStrings #-}+{-# LANGUAGE RecordWildCards, OverloadedStrings, DeriveGeneric #-} -- | Sizes grid cells & positions elements to them. module Graphics.Layout.Grid(Grid(..), Track(..), GridItem(..), GridItem'(..), Alignment(..),         buildTrack, buildGrid, setCellBox, enumerate, gridItemBox, cellSize,@@ -11,6 +11,8 @@ import Graphics.Layout.Box as B  import Debug.Trace (trace)+import GHC.Generics (Generic)+import Control.DeepSeq (NFData)  -- | An element which positions it's children within a grid. type Grid m n = Size (Track m) (Track n)@@ -40,9 +42,11 @@     minSize :: Double,     -- | The maximum aount of space to allocate to this child.     natSize :: Double-}+} deriving (Read, Show, Ord, Eq, Generic)+instance NFData GridItem' -- | How to redistribute excess space.-data Alignment = Start | Mid | End+data Alignment = Start | Mid | End deriving (Read, Show, Enum, Ord, Eq, Generic)+instance NFData Alignment  -- | Constructs a track with default (to-be-computed) values & given cell sizes. buildTrack :: CastDouble x => [Either x Double] -> Track x
Graphics/Layout/Inline.hs view
@@ -8,7 +8,8 @@ import Data.Text.ParagraphLayout.Rich (Paragraph(..), ParagraphOptions(..),                                 Fragment(..), ParagraphLayout(..), AncestorBox(..),                                 InnerNode(..), Box(..), RootNode(..),-                                layoutRich, boxSpacing, BoxSpacing(..))+                                layoutRich, boxSpacing, BoxSpacing(..),+                                activateBoxSpacing, paragraphSafeWidth) import Data.Text.ParagraphLayout.Rect (Rect(..),                                 width, height, x_max, x_min, y_min, y_max) import Data.Int (Int32)@@ -31,12 +32,12 @@ -- | Compute minimum width & height for some richtext. inlineMin :: (CastDouble x, CastDouble y) =>         Paragraph (a, PaddedBox x y, c) -> Size x y-inlineMin self = Size (c $ width rect) (c $ height rect)-    where rect = layoutRich' self 0+inlineMin = layoutSize' . flip layoutRich' 0 -- | Compute width & height of some richtext at configured width. inlineSize :: (CastDouble x, CastDouble y) =>         Paragraph (a, PaddedBox x y, c) -> Size x y-inlineSize self = layoutSize $ layoutRich $ lowerSpacing self+inlineSize self@(Paragraph _ _ _ opts) =+    layoutSize' . layoutRich' self $ paragraphMaxWidth opts -- | Retrieve children out of some richtext, -- associating given userdata with them. inlineChildren :: (CastDouble x, CastDouble y, Eq x, Eq y, Eq a, Eq c) =>@@ -45,17 +46,20 @@  -- | Retrieve a laid-out paragraph's rect & convert to CatTrap types. layoutSize :: (CastDouble x, CastDouble y) => ParagraphLayout a -> Size x y-layoutSize self = Size (c $ width r) (c $ height r)-  where r = paragraphRect self+layoutSize = layoutSize' . paragraphRect+layoutSize' r = Size (c $ width r) (c $ height r) -- | Retrieve a laid-out paragraph's children & associate with given userdata. layoutChildren :: Eq a => ParagraphLayout a -> [FragmentTree a] layoutChildren self = reconstructTree self  -- | Layout a paragraph at given width & retrieve resulting rect.+-- LEGACY. layoutRich' :: (CastDouble m, CastDouble n) =>         Paragraph (a, PaddedBox m n, c) -> Int32 -> Rect Int32-layoutRich' (Paragraph a b c d) width = paragraphRect $ layoutRich $-    lowerSpacing $ Paragraph a b c d { paragraphMaxWidth = width }+layoutRich' (Paragraph a b c d) width =+    (paragraphRect layout) { x_size = paragraphSafeWidth layout}+  where+    layout = layoutRich$lowerSpacing$Paragraph a b c d {paragraphMaxWidth=width}  -- | Copy surrounding whitespace into Balkon properties. lowerSpacing :: (CastDouble m, CastDouble n) =>@@ -63,9 +67,9 @@ lowerSpacing (Paragraph a b (RootBox c) d) = Paragraph a b (RootBox $ inner c) d   where     inner (Box childs opts) = flip Box opts $ map inner' childs-    inner' (InlineBox e@(_, f, _) child opts) = InlineBox e (inner child) opts {-            boxSpacing = BoxSpacingLeftRight (leftSpace box) (rightSpace box)-        }+    inner' (InlineBox e@(_, f, _) child opts) = InlineBox e (inner child) $+        flip activateBoxSpacing opts $+            BoxSpacingLeftRight (leftSpace box) (rightSpace box)       where box = mapX' unscale $ mapY' unscale f     inner' self@(TextSequence _ _) = self @@ -87,7 +91,7 @@ -- | Apply an operation to the 2nd field of a laid-out paragraph's userdata, -- for it's entire subtree. layoutMap :: (b -> b') -> ParagraphLayout (a, b, c) -> ParagraphLayout (a, b', c)-layoutMap cb (ParagraphLayout a b) = ParagraphLayout a $ map inner b+layoutMap cb (ParagraphLayout a b c) = ParagraphLayout a b $ map inner c   where     inner self@Fragment { fragmentUserData = (a, b, c) } = self {         fragmentUserData = (a, cb b, c),@@ -179,8 +183,8 @@   where     pos = (x + hbScale (x_min rect), y + hbScale (y_min rect))     rect = treeRect self-positionTree (x, y) self@(Leaf (Fragment (a, b, c) d _ f g h)) =-    Leaf (Fragment (a, b, (pos, c)) d [] f g h)+positionTree (x, y) self@(Leaf (Fragment (a, b, c) d _ f g h i)) =+    Leaf (Fragment (a, b, (pos, c)) d [] f g h i)   where     pos = (x + hbScale (x_min rect), y + hbScale (y_min rect))     rect = treeRect self
+ app/Integration.hs view
@@ -0,0 +1,198 @@+{-# LANGUAGE OverloadedStrings, TemplateHaskell #-}+module Main where++import System.Environment (getArgs)+import qualified Data.Map as M+import qualified Data.Set as S+import Data.Maybe (fromJust, fromMaybe)+import qualified Data.Text as Txt+import qualified Data.ByteString as BS+import System.Directory (getCurrentDirectory)+import qualified System.Directory as Dir++import Graphics.Layout.CSS (CSSBox(..), finalizeCSS')+import Graphics.Layout.CSS.Font (placeholderFont)+import Graphics.Layout (LayoutItem, boxLayout,+                        layoutGetBox, layoutGetChilds, layoutGetInner)+import Graphics.Layout.Box (zeroBox)+import qualified Graphics.Layout.Box as B++import Network.URI.Fetch.XML (Page(..), fetchDocument, applyCSScharset)+import Network.URI.Fetch (newSession, fetchURL)+import Network.URI.Charset (charsets)+import Network.URI (URI(..), nullURI, parseURIReference)+import Data.FileEmbed (makeRelativeToProject, embedStringFile)+import Data.HTML2CSS (el2stylist)++import Text.XML as X (Document(..), Element(..), Node(..), Prologue(..))+import Stylist.Tree (StyleTree(..), preorder, treeMap)+import Stylist (PropertyParser(..), cssPriorityAgent, cssPriorityUser)+import qualified Data.CSS.Style as Style+import qualified Data.CSS.Syntax.StyleSheet as CSS+import qualified Data.CSS.Preprocessor.Text as CSSTxt+import Data.CSS.Preprocessor.Conditions as CSSCond+        (ConditionalStyles, conditionalStyles, loadImports, Datum(..), resolve)+import qualified Data.CSS.Preprocessor.PsuedoClasses as CSSPseudo++import Control.Concurrent.MVar (putMVar, newEmptyMVar, tryReadMVar)+import Control.Concurrent (forkIO)+import Control.DeepSeq (NFData(..), ($!!))++import SDL hiding (rotate)+import Foreign.C.Types (CInt)+import Data.Function (fix)+import Control.Monad (unless)+import qualified Graphics.Text.Font.Choose as FC++initReferer :: IO (Page (CSSCond.ConditionalStyles (CSSBox Nil)))+initReferer = do+    cwd <- getCurrentDirectory+    return $ Page {+        -- Default to URIs being relative to CWD.+        pageURL = URI {uriScheme = "file:", uriPath = cwd,+            uriAuthority = Nothing, uriQuery = "", uriFragment = ""},+        -- Blank values:+        css = conditionalStyles nullURI "temp",+        domain = "temp",+        html = Document {+            documentPrologue = Prologue [] Nothing [],+            documentRoot = Element "temp" M.empty [],+            documentEpilogue = []+        },+        pageTitle = "", pageMIME = "", apps = [],+        backStack = [], forwardStack = [], visitedURLs = S.empty,+        initCSS = conditionalStyles,+        appName = "cattrap"+    }++stylize' style = preorder inner+  where+    inner parent _ el = Style.cascade style el [] $+            Style.inherit $ fromMaybe Style.temp parent++resolveCSS manager page = do+    let agentStyle = cssPriorityAgent (css page) `CSS.parse`+            $(makeRelativeToProject "app/useragent.css" >>= embedStringFile)+    userStyle <- loadUserStyles agentStyle+    CSSCond.loadImports loadURL lowerVars lowerToks userStyle []+  where+    loadURL url = do+        response <- fetchURL manager ["text/css"] url+        let charsets' = map Txt.unpack charsets+        return $ case response of+            ("text/css", Left text) -> text+            ("text/css", Right bytes) -> applyCSScharset charsets' $ BS.toStrict bytes+            (_, _) -> ""++loadUserStyles styles = do+    dir <- Dir.getXdgDirectory Dir.XdgConfig "rhapsode"+    exists <- Dir.doesDirectoryExist dir+    loadDirectory dir exists+  where+    loadDirectory _ False = return styles+    loadDirectory dir True = do+        files <- Dir.listDirectory dir+        loadFiles (cssPriorityUser styles) files+    loadFiles style (file:files) = do+        source <- readFile file+        CSS.parse style (Txt.pack source) `loadFiles` files+    loadFiles style [] = return style+-- FIXME: Support more media queries!+resolve' = CSSCond.resolve lowerVars lowerToks+lowerVars _ = CSSCond.B False+lowerToks _ = CSSCond.B False++main :: IO ()+main = do+    FC.init+    SDL.initializeAll++    let wcfg = defaultWindow {+            windowInitialSize = V2 1280 480,+            -- Simplify moving layout/download out-of-thread+            windowResizable = False+          }+    w <- createWindow "CatTrap" wcfg+    renderer <- createRenderer w (-1) defaultRenderer++    args <- getArgs+    let url = case args of+            (url:_) -> url+            [] -> "https://git.argonaut-constellation.org/~alcinnz/CatTrap"+    sess <- newSession+    ref <- initReferer+    xml <- fetchDocument sess ref $ fromMaybe nullURI $ parseURIReference url+    let pseudoFilter = CSSPseudo.htmlPsuedoFilter Style.queryableStyleSheet+    css' <- resolveCSS sess xml+    let css = CSSPseudo.inner $ resolve' pseudoFilter css'+    let styles = CSSTxt.resolve $ treeMap Style.innerParser $+            stylize' css $ el2stylist $ X.documentRoot $ html xml+    let layout = finalizeCSS' placeholderFont styles+    V2 x y <- get $ windowSize w+    pages' <- forkCompute $ boxLayout zeroBox {+            B.size = B.Size (fromIntegral x) (fromIntegral y)+          } layout False++    fix $ \loop -> do+        events <- fmap eventPayload <$> pollEvents+        rendererDrawColor renderer $= V4 255 255 255 255+        clear renderer++        pages <- tryReadMVar pages'+        case pages of+            Just (display:_) -> renderDisplay renderer display+            _ -> return ()++        present renderer+        unless (QuitEvent `elem` events) loop+    SDL.quit+    -- FC.fini -- FIXME: Need to free all Haskell data before freeing FontConfig's++data Nil = Nil deriving Eq+instance PropertyParser Nil where+    temp = Nil+    inherit _ = Nil+    longhand _ _ _ _ = Nothing+instance NFData Nil where rnf Nil = ()+++renderDisplay :: Renderer -> LayoutItem Double Double ((Double, Double), Nil)+        -> IO ()+renderDisplay renderer display = do+    let ((x, y), _) = layoutGetInner display+    let box = layoutGetBox display++    rendererDrawColor renderer $= V4 255 0 0 255+    drawBox renderer x y (B.width box) (B.height box)+    rendererDrawColor renderer $= V4 0 255 0 255+    drawBox renderer+        (x + B.left (B.margin box)) (y + B.top (B.margin box))+        (B.width box - B.left (B.margin box) - B.right (B.margin box))+        (B.height box - B.top (B.margin box) - B.bottom (B.margin box))+    rendererDrawColor renderer $= V4 0 0 255 255+    drawBox renderer+        (x + B.left (B.margin box) + B.left (B.border box))+        (y + B.top (B.margin box) + B.top (B.border box))+        (B.inline (B.size box) + B.left (B.padding box) + B.right (B.padding box))+        (B.block (B.size box) + B.top (B.padding box) + B.bottom (B.padding box))+    rendererDrawColor renderer $= V4 255 255 0 255+    drawBox renderer+        (x + B.left (B.margin box) + B.left (B.border box) + B.left (B.padding box))+        (y + B.top (B.margin box) + B.top (B.border box) + B.top (B.padding box))+        (B.inline $ B.size box) (B.block $ B.size box)++    mapM (renderDisplay renderer) $ layoutGetChilds display+    return ()++drawBox :: Renderer -> Double -> Double -> Double -> Double -> IO ()+drawBox renderer x y width height = do+    fillRect renderer $ Just $ Rectangle+        (P $ V2 (c x) (c y)) (V2 (c width) (c height))++c :: (Enum a, Enum b) => a -> b+c = toEnum . fromEnum++forkCompute dat = do+    ret <- newEmptyMVar+    forkIO $ putMVar ret $!! dat+    return ret
+ app/Integration2.hs view
@@ -0,0 +1,69 @@+module Main where++import Text.HTML.DOM as HTML+import Text.XML as X+import Data.HTML2CSS (html2css, el2stylist)+import Network.URI (nullURI)++import Data.CSS.Preprocessor.Conditions as CSSCond+import qualified Data.CSS.Preprocessor.PsuedoClasses as CSSPseudo+import qualified Data.CSS.Style as Style+import Stylist.Tree (StyleTree(..), preorder, treeMap)+import qualified Data.CSS.Preprocessor.Text as CSSTxt+import Data.Maybe (fromMaybe)++import Graphics.Layout.CSS.Font (placeholderFont)+import Graphics.Layout.CSS (finalizeCSS', CSSBox)+import Graphics.Layout (LayoutItem, boxLayout)+import Graphics.Layout.Box (Length, Size(..), PaddedBox(..), zeroBox)++import Control.Exception (evaluate)+import qualified Graphics.Text.Font.Choose as FC++import Control.Concurrent.MVar (putMVar, newEmptyMVar, readMVar)+import Control.Concurrent (forkIO)+import Control.DeepSeq (NFData(..), ($!!))+--import System.Mem (performGC)++resolve' = CSSCond.resolve lowerVars lowerToks+lowerVars _ = CSSCond.B False+lowerToks _ = CSSCond.B False++stylize' style = preorder inner+  where+    inner parent _ el = Style.cascade style el [] $+            Style.inherit $ fromMaybe Style.temp parent++main :: IO ()+main = do+    FC.init+    doc <- HTML.readFile "test.html"+    let css' :: CSSCond.ConditionalStyles (Style.VarParser (CSSTxt.TextStyle+                (CSSBox Nil)))+        css' = html2css doc nullURI $ CSSCond.conditionalStyles nullURI "temp"+    css' `seq` print "Parsed page with CSS!"+    let pseudoFilter = CSSPseudo.htmlPsuedoFilter Style.queryableStyleSheet+    let css = CSSPseudo.inner $ resolve' pseudoFilter css'+    let styles = CSSTxt.resolve $ treeMap Style.innerParser $+            stylize' css $ el2stylist $ X.documentRoot doc+    styles `seq` print "Styled page!"+    let layout :: LayoutItem Length Length Nil+        layout = finalizeCSS' placeholderFont styles+    layout `seq` print "Laying out page!"+    res <- forkCompute $ boxLayout zeroBox { size = Size 1280 480 } layout False+    readMVar res+    --performGC+    --FC.fini -- FIXME: GC still left FontConfig references...+    return ()++data Nil = Nil deriving Eq+instance Style.PropertyParser Nil where+    temp = Nil+    inherit _ = Nil+    longhand _ _ _ _ = Nothing+instance NFData Nil where rnf Nil = ()++forkCompute dat = do+    ret <- newEmptyMVar+    forkIO $ putMVar ret $!! dat+    return ret
app/Main.hs view
@@ -1,5 +1,7 @@+{-# LANGUAGE OverloadedStrings #-} module Main where +import System.Environment (getArgs) import Text.XML.Light.Input (parseXMLDoc) import qualified Text.XML.Light.Types as X import Data.Maybe (fromJust, fromMaybe)@@ -17,19 +19,23 @@ import Stylist (PropertyParser(..)) import Data.CSS.Syntax.Tokens (Token(..), tokenize) -import Graphics.UI.GLUT-import Graphics.GL.Core32--import Foreign.Ptr (castPtr, nullPtr)-import Foreign.Storable (Storable(..))-import Foreign.Marshal.Array (withArrayLen, allocaArray, peekArray)-import Foreign.Marshal.Alloc (alloca)-import Foreign.Marshal.Utils (with)-import Foreign.C.String (withCString)+import SDL hiding (rotate)+import Foreign.C.Types (CInt)+import Data.Function (fix)+import Control.Monad (unless)  main :: IO () main = do-    (progname, args) <- getArgsAndInitialize+    SDL.initializeAll++    let wcfg = defaultWindow {+            windowInitialSize = V2 640 480,+            windowResizable = True+          }+    w <- createWindow "CatTrap" wcfg+    renderer <- createRenderer w (-1) defaultRenderer++    args <- getArgs     source <- readFile $ case args of         (filename:_) -> filename         [] -> "styletree.xml"@@ -37,30 +43,19 @@     let styles = xml2styles temp xml     let layout = finalizeCSS' placeholderFont styles -    w <- createWindow progname--    vertexShader <- compileOGLShader vertexSource GL_VERTEX_SHADER-    fragmentShader <- compileOGLShader fragmentSource GL_FRAGMENT_SHADER-    shader <- compileOGLProgram [] [vertexShader, fragmentShader]-    glDetachShader shader vertexShader-    glDetachShader shader fragmentShader-    glDeleteShader vertexShader-    glDeleteShader fragmentShader+    fix $ \loop -> do+        events <- fmap eventPayload <$> pollEvents+        rendererDrawColor renderer $= V4 255 255 255 255+        clear renderer -    displayCallback $= do-        clear [ ColorBuffer ]-        Size x y <- get windowSize+        V2 x y <- get $ windowSize w         let (display:_) = boxLayout zeroBox {             B.size = B.Size (fromIntegral x) (fromIntegral y)           } layout False--        glUseProgram shader-        attribScale <- withCString "windowsize" $ glGetUniformLocation shader-        glUniform3f attribScale (realToFrac x) (realToFrac y) 1+        renderDisplay renderer display -        renderDisplay shader display-        flush-    mainLoop+        present renderer+        unless (QuitEvent `elem` events) loop  xml2styles :: CSSBox Nil -> X.Element -> StyleTree (CSSBox Nil) xml2styles parent el = StyleTree {@@ -78,138 +73,38 @@     inherit _ = Nil     longhand _ _ _ _ = Nothing -renderDisplay :: Eq a => GLuint -> LayoutItem Double Double ((Double, Double), a)+renderDisplay :: Renderer -> LayoutItem Double Double ((Double, Double), Nil)         -> IO ()-renderDisplay shader display = do+renderDisplay renderer display = do     let ((x, y), _) = layoutGetInner display     let box = layoutGetBox display-    attribColour <- withCString "fill" $ glGetUniformLocation shader -    glUniform3f attribColour 1 0 0-    drawBox x y (B.width box) (B.height box)-    glUniform3f attribColour 0 1 0-    drawBox (x + B.left (B.margin box)) (y + B.top (B.margin box))-            (B.width box - B.left (B.margin box) - B.right (B.margin box))-            (B.height box - B.top (B.margin box) - B.bottom (B.margin box))-    glUniform3f attribColour 0 0 1-    drawBox (x + B.left (B.margin box) + B.left (B.border box))-            (y + B.top (B.margin box) + B.top (B.border box))-            (B.inline (B.size box) + B.left (B.padding box) + B.right (B.padding box))-            (B.block (B.size box) + B.top (B.padding box) + B.bottom (B.padding box))-    glUniform3f attribColour 1 1 0-    drawBox (x + B.left (B.margin box) + B.left (B.border box) + B.left (B.padding box))-            (y + B.top (B.margin box) + B.top (B.border box) + B.top (B.padding box))-            (B.inline $ B.size box) (B.block $ B.size box)+    rendererDrawColor renderer $= V4 255 0 0 255+    drawBox renderer x y (B.width box) (B.height box)+    rendererDrawColor renderer $= V4 0 255 0 255+    drawBox renderer+        (x + B.left (B.margin box)) (y + B.top (B.margin box))+        (B.width box - B.left (B.margin box) - B.right (B.margin box))+        (B.height box - B.top (B.margin box) - B.bottom (B.margin box))+    rendererDrawColor renderer $= V4 0 0 255 255+    drawBox renderer+        (x + B.left (B.margin box) + B.left (B.border box))+        (y + B.top (B.margin box) + B.top (B.border box))+        (B.inline (B.size box) + B.left (B.padding box) + B.right (B.padding box))+        (B.block (B.size box) + B.top (B.padding box) + B.bottom (B.padding box))+    rendererDrawColor renderer $= V4 255 255 0 255+    drawBox renderer+        (x + B.left (B.margin box) + B.left (B.border box) + B.left (B.padding box))+        (y + B.top (B.margin box) + B.top (B.border box) + B.top (B.padding box))+        (B.inline $ B.size box) (B.block $ B.size box) -    mapM (renderDisplay shader) $ layoutGetChilds display+    mapM (renderDisplay renderer) $ layoutGetChilds display     return () -drawBox x y width height = do-    buf <- withPointer $ glGenBuffers 1-    glBindBuffer GL_ARRAY_BUFFER buf-    glBufferData' GL_ARRAY_BUFFER [-        x, y, 0,-        x + width, y, 0,-        x, y + height, 0,--        x + width, y, 0,-        x + width, y + height, 0,-        x, y + height, 0-      ] GL_STATIC_DRAW--    glEnableVertexAttribArray 0-    glBindBuffer GL_ARRAY_BUFFER buf-    glVertexAttribPointer 0 3 GL_FLOAT GL_FALSE 0 nullPtr--    glDrawArrays GL_TRIANGLES 0 6-    glDisableVertexAttribArray 0--withPointer cb = alloca $ \ret' -> do-    cb ret'-    peek ret'--glBufferData' _ [] _ = return ()-glBufferData' target dat usage =-    withArrayLen (map realToFrac dat :: [Float]) $ \len dat' -> do-        glBufferData target (toEnum $ len*sizeOf (head dat)) (castPtr dat') usage--compileOGLShader :: String -> GLenum -> IO GLuint-compileOGLShader src shType = do-  shader <- glCreateShader shType-  if shader == 0-    then error "Could not create shader"-    else do-      success <-do-        withCString (src) $ \ptr ->-          with ptr $ \ptrptr -> glShaderSource shader 1 ptrptr nullPtr--        glCompileShader shader-        with (0 :: GLint) $ \ptr -> do-          glGetShaderiv shader GL_COMPILE_STATUS ptr-          peek ptr--      if success == GL_FALSE-        then do-          err <- do-            infoLog <- with (0 :: GLint) $ \ptr -> do-                glGetShaderiv shader GL_INFO_LOG_LENGTH ptr-                logsize <- peek ptr-                allocaArray (fromIntegral logsize) $ \logptr -> do-                    glGetShaderInfoLog shader logsize nullPtr logptr-                    peekArray (fromIntegral logsize) logptr--            return $ unlines [ "Could not compile shader:"-                             , src-                             , map (toEnum . fromEnum) infoLog-                             ]-          error err-        else return shader--compileOGLProgram :: [(String, Integer)] -> [GLuint] -> IO GLuint-compileOGLProgram attribs shaders = do-  (program, success) <- do-     program <- glCreateProgram-     forM_ shaders (glAttachShader program)-     forM_ attribs-       $ \(name, loc) ->-         withCString name-           $ glBindAttribLocation program-           $ fromIntegral loc-     glLinkProgram program--     success <- with (0 :: GLint) $ \ptr -> do-       glGetProgramiv program GL_LINK_STATUS ptr-       peek ptr-     return (program, success)--  if success == GL_FALSE-  then with (0 :: GLint) $ \ptr -> do-    glGetProgramiv program GL_INFO_LOG_LENGTH ptr-    logsize <- peek ptr-    infoLog <- allocaArray (fromIntegral logsize) $ \logptr -> do-      glGetProgramInfoLog program logsize nullPtr logptr-      peekArray (fromIntegral logsize) logptr-    error $ unlines-          [ "Could not link program"-          , map (toEnum . fromEnum) infoLog-          ]-  else do-    forM_ shaders glDeleteShader-    return program+drawBox :: Renderer -> Double -> Double -> Double -> Double -> IO ()+drawBox renderer x y width height = do+    fillRect renderer $ Just $ Rectangle+        (P $ V2 (c x) (c y)) (V2 (c width) (c height)) -vertexSource = unlines [-    "#version 330 core",-    "layout(location = 0) in vec3 vertexPositionModelSpace;",-    "uniform vec3 windowsize;",-    "void main() {",-    "gl_Position.xyz = vertexPositionModelSpace/windowsize - 1;",-    "gl_Position.y = -gl_Position.y;",-    "gl_Position.w = 1.0;",-    "}"-  ]-fragmentSource = unlines [-    "#version 330 core",-    "uniform vec3 fill;",-    "out vec3 colour;",-    "void main() { colour = fill; }"-  ]+c :: (Enum a, Enum b) => a -> b+c = toEnum . fromEnum
cattrap.cabal view
@@ -2,7 +2,7 @@ -- documentation, see http://haskell.org/cabal/users-guide/  name:                cattrap-version:             0.2.0.0+version:             0.3.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/@@ -29,12 +29,12 @@                         Graphics.Layout.Inline, Graphics.Layout.Inline.CSS   other-modules:        Graphics.Layout.CSS.Parse   -- other-extensions:-  build-depends:       base >=4.12 && <4.16, containers,-                        css-syntax, scientific, text,+  build-depends:       base >=4.12 && <5, containers, parallel >= 3,+                        css-syntax, scientific, text, deepseq,                         stylist-traits >= 0.1.3.0 && < 1,-                        fontconfig-pure >= 0.2 && < 0.3,+                        fontconfig-pure >= 0.2 && < 0.5,                         harfbuzz-pure >= 1.0.3.2 && < 1.1, bytestring,-                        balkon >= 1.1 && <2, unordered-containers+                        balkon >= 1.2 && <2, unordered-containers   -- hs-source-dirs:   default-language:    Haskell2010   ghc-options:         -Wincomplete-patterns@@ -43,7 +43,24 @@   main-is:             Main.hs   -- other-modules:   -- other-extensions:-  build-depends:       base >=4.12 && <4.16, cattrap, xml, text, css-syntax, stylist-traits, GLUT, gl+  build-depends:       base >=4.12 && <5, cattrap, text, css-syntax, xml, stylist-traits, sdl2 >= 2.5.4+  hs-source-dirs:      app+  default-language:    Haskell2010++executable cattrap-argonaut+  main-is:             Integration.hs+  -- other-modules:+  -- other-extensions:+  build-depends:       base >=4.12 && <5, cattrap, text>=2.0.2, css-syntax, stylist-traits, stylist>=2.7.0.1, hurl-xml, hurl, sdl2 >= 2.5.4, containers, network-uri, xml-conduit, directory, xml-conduit-stylist, bytestring, file-embed, deepseq, fontconfig-pure+  hs-source-dirs:      app+  default-language:    Haskell2010+  ghc-options:	-threaded++executable cattrap-stylist+  main-is:             Integration2.hs+  -- other-modules:+  -- other-extensions:+  build-depends:       base >=4.12 && <5, cattrap, text>=2.0.2, css-syntax, stylist-traits, stylist>=2.7.0.1, network-uri, html-conduit, xml-conduit, xml-conduit-stylist, deepseq, fontconfig-pure   hs-source-dirs:      app   default-language:    Haskell2010