@@ 5,6 5,7 @@ module Graphics.Layout.Box(Border(..), mapX, mapY,
Size(..), mapSizeX, mapSizeY,
PaddedBox(..), zeroBox, lengthBox, mapX', mapY',
width, height, minWidth, minHeight, maxWidth, maxHeight,
+ leftSpace, rightSpace, topSpace, bottomSpace, hSpace, vSpace,
Length(..), mapAuto, lowerLength, Zero(..), CastDouble(..)) where
-- | Amount of space surrounding the box.
@@ 110,6 111,13 @@ maxWidth PaddedBox {..} = left margin + left border + left padding +
maxHeight PaddedBox {..} = top margin + top border + top padding +
block max + bottom padding + bottom border + bottom margin
+leftSpace PaddedBox {..} = left margin + left border + left padding
+rightSpace PaddedBox {..} = right margin + right border + right padding
+topSpace PaddedBox {..} = top margin + top border + top padding
+bottomSpace PaddedBox {..} = bottom margin + bottom border + bottom padding
+hSpace self = leftSpace self + rightSpace self
+vSpace self = topSpace self + bottomSpace self
+
-- | A partially-computed length value.
data Length = Pixels Double -- ^ Absolute number of device pixels.
| Percent Double -- ^ Multiplier by container width.
@@ 4,11 4,11 @@
module Graphics.Layout.Inline(inlineMinWidth, inlineMin, inlineNatWidth, inlineHeight,
inlineSize, inlineChildren, layoutSize, layoutChildren, positionChildren,
fragmentSize, fragmentSize', fragmentPos, FragmentTree(..),
- positionSubtree, subtreeInner) where
+ positionSubtree, subtreeInner, paragraphMap, layoutMap, treeMap) where
import Data.Text.ParagraphLayout.Rich (Paragraph(..), ParagraphOptions(..),
Fragment(..), ParagraphLayout(..), AncestorBox(..),
- layoutRich)
+ layoutRich, InnerNode(..), Box(..), RootNode(..))
import Data.Text.ParagraphLayout.Rect (Rect(..),
width, height, x_max, x_min, y_min, y_max)
import Data.Text.Internal (Text(..))
@@ 16,10 16,11 @@ import qualified Data.Text as Txt
import Data.Char (isSpace)
import Data.Int (Int32)
-import Graphics.Layout.Box (PaddedBox, Length, Size(..), CastDouble(..), fromDouble)
+import Graphics.Layout.Box hiding (min, max, width, height)
import Graphics.Layout.CSS.Font (Font', hbUnit)
-- | Convert from Harfbuzz units to device pixels as a Double
+hbScale :: Int32 -> Double
hbScale = (/hbUnit) . fromIntegral
-- | Convert from Harfbuzz units to device pixels as a Double or Length.
c :: CastDouble a => Int32 -> a
@@ 61,23 62,63 @@ layoutRich' :: Paragraph a -> Int32 -> Rect Int32
layoutRich' (Paragraph a b c d) width =
paragraphRect $ layoutRich $ Paragraph a b c d { paragraphMaxWidth = width }
+-- | Apply an operation to the 2nd field of the paragraph's userdata,
+-- for it's entire subtree.
+paragraphMap :: (b -> b') -> Paragraph (a, b, c) -> Paragraph (a, b', c)
+paragraphMap cb (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, g) child opts) =
+ InlineBox (e, cb f, g) (inner child) opts
+ inner' (TextSequence (e, f, g) leaf) = TextSequence (e, cb f, g) leaf
+
+-- | 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
+ where
+ inner self@Fragment { fragmentUserData = (a, b, c) } = self {
+ fragmentUserData = (a, cb b, c),
+ fragmentAncestorBoxes = map inner' $ fragmentAncestorBoxes self
+ }
+ inner' self@AncestorBox { boxUserData = (a, b, c) } = self {
+ boxUserData = (a, cb b, c)
+ }
+
+-- | Apply an operation to the 2nd field of the tree extracted from a laid-out
+-- paragraph, for all nodes.
+treeMap :: (b -> b') -> FragmentTree (a, b, c) -> FragmentTree (a, b', c)
+treeMap cb (Branch self@AncestorBox { boxUserData = (a, b, c) } childs) =
+ Branch self { boxUserData = (a, cb b, c) } $ map (treeMap cb) childs
+treeMap cb (Leaf self@Fragment { fragmentUserData = (a, b, c) }) =
+ Leaf self { fragmentUserData = (a, cb b, c), fragmentAncestorBoxes = [] }
+
-- | Retrieve the rect for a fragment & convert to CatTrap types.
fragmentSize :: (CastDouble x, CastDouble y) =>
- FragmentTree (a, PaddedBox Length Length, c) -> Size x y
+ FragmentTree (a, PaddedBox x y, c) -> Size x y
fragmentSize self = Size (c $ width r) (c $ height r)
where r = treeRect self
-- | Compute the unioned rect for a subtree.
treeRect :: (CastDouble m, CastDouble n) =>
FragmentTree (a, PaddedBox m n, c) -> Rect Int32
-treeRect (Branch _ childs) = foldr unionRect (Rect 0 0 0 0) $ map treeRect childs
- where unionRect a b = Rect (x_min a `min` x_min b) (y_min a `min` y_min b)
- ((x_max a `max` x_max b) - (x_min a `min` x_min b))
- ((y_max a `max` y_max b) - (y_min a `min` x_min b))
+treeRect (Branch AncestorBox { boxUserData = (_, box', _)} childs) =
+ foldr unionRect (Rect 0 0 0 0) $ map treeRect childs
+ where
+ unionRect a b = Rect
+ (x_min a `min` x_min b - leftSpace box)
+ (y_min a `min` y_min b - topSpace box)
+ (x_max a `max` x_max b - x_min a `min` x_min b + hSpace box)
+ (y_max a `max` y_max b - y_min a `min` x_min b + vSpace box)
+ box :: PaddedBox Int32 Int32
+ box = mapX' unscale $ mapY' unscale box'
+ unscale :: CastDouble x => x -> Int32
+ unscale = floor . (*hbUnit) . toDouble
treeRect (Leaf self) = fragmentRect self
-- | Variant of `fragmentSize` asserting to the typesystem that both fields
-- of the resulting `Size` are of the same type.
-fragmentSize' :: CastDouble x => FragmentTree (a, PaddedBox Length Length, c) -> Size x x
+fragmentSize' :: CastDouble x => FragmentTree (a, PaddedBox x x, c) -> Size x x
fragmentSize' = fragmentSize -- Work around for typesystem.
-- | Retrieve the position of a fragment.
fragmentPos :: (Double, Double) -> Fragment a -> (Double, Double)
@@ 126,9 167,9 @@ reconstructTree' frags@(Fragment {
sameBranch Fragment { fragmentAncestorBoxes = [] } = False
reconstructTree' [] = []
-positionSubtree :: (Double, Double) ->
- FragmentTree (a, PaddedBox Length Length, c) ->
- FragmentTree (a, PaddedBox Length Length, ((Double, Double), c))
+positionSubtree :: (CastDouble m, CastDouble n) => (Double, Double) ->
+ FragmentTree (a, PaddedBox m n, c) ->
+ FragmentTree (a, PaddedBox m n, ((Double, Double), c))
positionSubtree (x, y) self@(Branch (AncestorBox (a, b, c) d e f g) childs) =
Branch (AncestorBox (a, b, (pos, c)) d e f g) $
map (positionSubtree pos) childs