~jaro/balkon

ref: 2d69d190193e4edf1a137fc615b8eb0c895f1824 balkon/src/Data/Text/ParagraphLayout/Internal/ParagraphLine.hs -rw-r--r-- 2.3 KiB
2d69d190Jaro Mark some lists as explicitly non-empty. 1 year, 7 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
module Data.Text.ParagraphLayout.Internal.ParagraphLine (cutLines, mergeLines)
where

import Data.Int (Int32)
import qualified Data.List.NonEmpty as NonEmpty

import Data.Text.ParagraphLayout.Internal.Fragment
import Data.Text.ParagraphLayout.Internal.LinePagination
import Data.Text.ParagraphLayout.Internal.ParagraphLayout
import Data.Text.ParagraphLayout.Internal.Rect

-- | Represents one line of a `ParagraphLayout`.
newtype ParagraphLine = ParagraphLine ParagraphLayout

instance Line ParagraphLine where
    lineHeight (ParagraphLine pl) = height $ paragraphRect pl

-- | Split the given `ParagraphLayout` into individual lines.
cutLines :: ParagraphLayout -> [ParagraphLine]
cutLines pl = map (\y -> cutLine y pl) (lineOrigins pl)

-- | Reduce the given `ParagraphLayout` to fragments with the given `y_origin`.
--
-- This assumes that each line consists of fragments of equal height and that
-- there is no space between lines.
--
-- TODO: Use line numbers to support rich text.
cutLine :: Int32 -> ParagraphLayout -> ParagraphLine
cutLine y pl = ParagraphLine $ shiftFragments (-y) $ limitFragments y pl

lineOrigins :: ParagraphLayout -> [Int32]
lineOrigins pl = dedupe $ map (y_origin . fragmentRect) $ paragraphFragments pl

-- | Remove duplicates from a sorted list.
dedupe :: Eq a => [a] -> [a]
dedupe xs = map NonEmpty.head $ NonEmpty.group xs

-- | Combine the given `ParagraphLine`s into a `ParagraphLayout` by merging
-- their fragments.
mergeLines :: [ParagraphLine] -> ParagraphLayout
mergeLines lls = foldl mergeLine emptyParagraphLayout lls

mergeLine :: ParagraphLayout -> ParagraphLine -> ParagraphLayout
mergeLine pl (ParagraphLine nextLine) = pl'
    where
        -- Quadratic time complexity. TODO: Consider optimising.
        pl' = appendFragments pl $ shiftFragments y nextLine
        y = y_terminus $ paragraphRect pl

-- | Add @dy@ to each fragment's `y_origin`.
shiftFragments :: Int32 -> ParagraphLayout -> ParagraphLayout
shiftFragments dy = mapFragments (shiftFragment dy)

shiftFragment :: Int32 -> Fragment -> Fragment
shiftFragment dy f = f'
    where
        f' = f { fragmentRect = r' }
        r' = r { y_origin = y_origin r + dy }
        r = fragmentRect f

-- | Keep only fragments with the given `y_origin` value.
limitFragments :: Int32 -> ParagraphLayout -> ParagraphLayout
limitFragments y = filterFragments ((== y) . y_origin . fragmentRect)