~jaro/balkon

ref: 7dc45d1deab5d7d7e967fe2056f322f0dabac0bc balkon/src/Data/Text/ParagraphLayout/Internal/BoxOptions.hs -rw-r--r-- 5.9 KiB
7dc45d1dJaro Implement vertical alignment. 1 year, 4 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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
module Data.Text.ParagraphLayout.Internal.BoxOptions
    ( BoxCollapse (..)
    , BoxOptions (..)
    , BoxSpacing (..)
    , VerticalAlignment (..)
    , activateBoxSpacing
    , defaultBoxOptions
    )
where

import Data.Int (Int32)

-- | Style options to be applied to an inline box.
--
-- This record type is likely to be extended in the future.
-- Use `defaultBoxOptions` and update it with specific record selectors
-- instead of constructing `BoxOptions` directly.
--
-- In order to get CSS defaults, use:
--
-- > defaultBoxOptions { boxVerticalAlignment = AlignBaseline 0 }
data BoxOptions = BoxOptions

    { boxSpacing :: BoxSpacing
    -- ^ Determines amount of empty space to add before the first fragment
    -- and/or after the last fragment of this box.
    --
    -- This can be used to reserve space for a left margin, border,
    -- and/or padding.
    --
    -- In order to get CSS-compliant behaviour, consider using
    -- `activateBoxSpacing` instead.

    , boxCollapse :: BoxCollapse
    -- ^ Determines how this box affects the height of a line that would
    -- otherwise be empty.

    , boxVerticalAlignment :: VerticalAlignment
    -- ^ Determines how fragments are aligned vertically within a line.

    }
    deriving (Eq)

-- | Determines the amount of empty space to add around a box.
data BoxSpacing

    -- | Specification using absolute directions, unaffected by text direction.
    --
    -- (However, text direction is still used in case of fragmentation,
    -- to determine how to map the first (last) fragment to left (right).)
    = BoxSpacingLeftRight

        Int32
        -- ^ Amount of empty space to add to the left side of the
        -- first fragment (for LTR text) or last fragment (for RTL text)
        -- created from this box.

        Int32
        -- ^ Amount of empty space to add to the right side of the
        -- first fragment (for RTL text) or last fragment (for LTR text)
        -- created from this box.

    deriving (Eq, Show, Read)

-- | Determines how an inline box affects the height of a line that would
-- otherwise be empty.
--
-- Note that for this setting to have any effect, the box must be the ancestor
-- of at least one (possibly empty) text sequence, so that it can generate
-- a `Data.Text.ParagraphLayout.Rich.Fragment`.
--
-- For CSS-compliant behaviour:
--
-- - Ensure that empty boxes contain empty text sequences as mentioned above.
--   This can be achieved using the `Data.Text.ParagraphLayout.Rich.strut`
--   function.
--
-- - Set `AllowBoxCollapse` for boxes with zero margins, padding,
--   and borders.
--
-- - Set `AvoidBoxCollapse` for boxes with non-zero margins, padding,
--   or borders.
--
-- Note that `BoxSpacing` may contain zero values even when calculated from
-- non-zero CSS values, because of rounding to integer values and/or because
-- of adding together positive and negative values that cancel out. You should
-- therefore compare the individual computed values for margins, padding, and
-- borders with zero, and set `boxCollapse` to `AllowBoxCollapse` if and only
-- if all these values are equal to zero.
data BoxCollapse

    = AllowBoxCollapse
    -- ^ The box should disappear when possible to make empty lines invisible.

    | AvoidBoxCollapse
    -- ^ The box should be preserved when possible, making lines visible
    -- even when they would otherwise be empty.

    deriving (Eq, Read, Show)

-- | Determines how the contents of a line should be aligned vertically,
-- that is, how fragments should fill (and possibly extend) the vertical space
-- inside a line.
--
-- Vertical alignment takes place after line breaking.
data VerticalAlignment

    = AlignLineTop
    -- ^ Align the top of this box with the top of the line.
    --
    -- The /top of this box/ is the topmost coordinate of any fragment
    -- descended from this box, except for fragments that are descended from
    -- another box with `AlignLineTop` or `AlignLineBottom` closer to them.
    --
    -- t`Data.Text.ParagraphLayout.Rich.LineHeight` is included
    -- when considering the /top/ coordinates of descended fragments,
    -- and thus, leading is included.
    --
    -- This behaviour is equivalent to @vertical-align: top@ in CSS.

    | AlignLineBottom
    -- ^ Align the bottom of this box with the bottom of the line.
    --
    -- The /bottom of this box/ is the bottommost coordinate of any fragment
    -- descended from this box, except for fragments that are descended from
    -- another box with `AlignLineTop` or `AlignLineBottom` closer to them.
    --
    -- t`Data.Text.ParagraphLayout.Rich.LineHeight` is included
    -- when considering the /bottom/ coordinates of descended fragments,
    -- and thus, leading is included.
    --
    -- This behaviour is equivalent to @vertical-align: bottom@ in CSS.

    | AlignBaseline Int32
    -- ^ Align the baseline of this box with the baseline of its parent box,
    -- optionally shifting it by a specified amount upwards.
    --
    -- @`AlignBaseline` 0@ is equivalent to @vertical-align: baseline@ in CSS.
    --
    -- @`AlignBaseline` x@ is equivalent to @vertical-align: \<length\>@ in CSS,
    -- after an appropriate unit conversion.

    deriving (Eq, Read, Show)

-- | `BoxOptions` with backwards-compatible values.
--
-- Note that this sets `boxVerticalAlignment` to `AlignLineTop`,
-- whereas the CSS default should be `AlignBaseline 0`.
defaultBoxOptions :: BoxOptions
defaultBoxOptions = BoxOptions
    { boxSpacing = BoxSpacingLeftRight 0 0
    , boxCollapse = AllowBoxCollapse
    , boxVerticalAlignment = AlignLineTop
    }

-- | Shorthand for updating `boxSpacing` and setting `boxCollapse`
-- to `AvoidBoxCollapse`.
--
-- Can be used to activate CSS-compliant rendering of empty boxes whose
-- spacing is calculated from non-zero values.
activateBoxSpacing :: BoxSpacing -> BoxOptions -> BoxOptions
activateBoxSpacing spacing boxOptions = boxOptions
    { boxSpacing = spacing
    , boxCollapse = AvoidBoxCollapse
    }