~jaro/balkon

ref: 35a5cc75e3831fa3139ebb43dcf2e0948a76be16 balkon/src/Data/Text/ParagraphLayout/Internal/ProtoLine.hs -rw-r--r-- 2.9 KiB
35a5cc75Jaro Handle ancestor boxes in rich layout. 11 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
module Data.Text.ParagraphLayout.Internal.ProtoLine
    ( ProtoLine (..)
    , nonEmpty
    , width
    , applyBoxes
    )
where

import Data.Int (Int32)
import Data.List.NonEmpty (NonEmpty ((:|)))

import qualified Data.Text.ParagraphLayout.Internal.ApplyBoxes as Algo
import Data.Text.ParagraphLayout.Internal.ProtoFragment
import Data.Text.ParagraphLayout.Internal.ResolvedBox
import Data.Text.ParagraphLayout.Internal.WithSpan

-- | Contents of a line that have not been visually ordered or positioned yet.
data ProtoLine f d = ProtoLine

    { protoFragments :: f (WithSpan d ProtoFragment)
    -- ^ Fragments on the line, in logical order.
    -- Intended to be either a regular list or a non-empty list.

    , prevOpenBoxes :: [ResolvedBox d]
    -- ^ Boxes whose starts are located on preceding lines.
    -- The spacing for their start edge will not count towards this line.

    , nextOpenBoxes :: [ResolvedBox d]
    -- ^ Boxes whose ends are located on following lines.
    -- The spacing for their end edge will not count towards this line.

    }

-- | Covert a line with a regular list of fragments into `Just` a line with
-- a non-empty list of fragments, or `Nothing` if there are no fragments.
nonEmpty :: ProtoLine [] d -> Maybe (ProtoLine NonEmpty d)
nonEmpty (ProtoLine [] _ _) = Nothing
nonEmpty pl@(ProtoLine { protoFragments = (f : fs) }) =
    Just pl { protoFragments = f :| fs }

-- | Total width of the line (content and spacing).
width :: (Foldable f, Functor f) => ProtoLine f d -> Int32
width pl = totalAdvances pl + totalBoxesStart pl + totalBoxesEnd pl

-- | Determine which horizontal fragments are the leftmost and which are the
-- rightmost within their ancestor inline boxes.
--
-- The input must be ordered from left to right.
applyBoxes :: ProtoLine NonEmpty d ->
    NonEmpty (Algo.WithBoxes d (WithSpan d ProtoFragment))
applyBoxes pl = Algo.applyBoxes prevOpen nextOpen pfs
    where
        prevOpen = prevOpenBoxes pl
        nextOpen = nextOpenBoxes pl
        pfs = protoFragments pl

-- | Total glyph advances on the line.
totalAdvances :: (Foldable f, Functor f) => ProtoLine f d -> Int32
totalAdvances pl = sum $ fmap getAdvance $ protoFragments pl
    where getAdvance (WithSpan _ pf) = advance pf

-- | Total spacing for boxes starting on the line.
totalBoxesStart :: (Foldable f, Functor f) => ProtoLine f d -> Int32
totalBoxesStart pl = sum $ fmap boxStartSpacing $ boxesStart pl

-- | Total spacing for boxes ending on the line.
totalBoxesEnd :: (Foldable f, Functor f) => ProtoLine f d -> Int32
totalBoxesEnd pl = sum $ fmap boxEndSpacing $ boxesEnd pl

-- | Boxes that start on the given line.
boxesStart :: (Foldable f, Functor f) => ProtoLine f d -> [ResolvedBox d]
boxesStart pl = allBoxes (protoFragments pl) `diff` prevOpenBoxes pl

-- | Boxes that end on the given line.
boxesEnd :: (Foldable f, Functor f) => ProtoLine f d -> [ResolvedBox d]
boxesEnd pl = allBoxes (protoFragments pl) `diff` nextOpenBoxes pl