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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
|
import options
import css/stylednode
import css/values
import display/window
import layout/layoutunit
import types/color
type
Offset* = object
x*: LayoutUnit
y*: LayoutUnit
Size* = object
width*: LayoutUnit
height*: LayoutUnit
# min-content: box width is longest word's width
# max-content: box width is content width without wrapping
# stretch: box width is n px wide
# fit-content: also known as shrink-to-fit, box width is
# min(max-content, max(stretch)
# in other words, as wide as needed, but wrap if wider than allowed
# (note: I write width here, but it can apply for any constraint)
SizeConstraintType* = enum
STRETCH, FIT_CONTENT, MIN_CONTENT, MAX_CONTENT
SizeConstraint* = object
t*: SizeConstraintType
u*: LayoutUnit
Strut* = object
pos*: LayoutUnit
neg*: LayoutUnit
Viewport* = ref object
window*: WindowAttributes
positioned*: seq[BlockBox]
BoxBuilder* = ref object of RootObj
children*: seq[BoxBuilder]
inlinelayout*: bool
computed*: CSSComputedValues
node*: StyledNode
InlineBoxBuilder* = ref object of BoxBuilder
text*: seq[string]
newline*: bool
splitstart*: bool
splitend*: bool
BlockBoxBuilder* = ref object of BoxBuilder
MarkerBoxBuilder* = ref object of InlineBoxBuilder
ListItemBoxBuilder* = ref object of BoxBuilder
marker*: MarkerBoxBuilder
content*: BlockBoxBuilder
TableRowGroupBoxBuilder* = ref object of BlockBoxBuilder
TableRowBoxBuilder* = ref object of BlockBoxBuilder
TableCellBoxBuilder* = ref object of BlockBoxBuilder
TableBoxBuilder* = ref object of BlockBoxBuilder
rowgroups*: seq[TableRowGroupBoxBuilder]
TableCaptionBoxBuilder* = ref object of BlockBoxBuilder
InlineAtom* = ref object of RootObj
offset*: Offset
width*: LayoutUnit
height*: LayoutUnit
vertalign*: CSSVerticalAlign
baseline*: LayoutUnit
top*: LayoutUnit
bottom*: LayoutUnit
ComputedFormat* = ref object
fontstyle*: CSSFontStyle
fontweight*: int
textdecoration*: set[CSSTextDecoration]
color*: RGBAColor
node*: StyledNode
#TODO: background color should not be stored in inline words. Instead,
# inline box fragments should be passed on to the renderer, which could
# then properly blend them.
bgcolor*: RGBAColor
InlineSpacing* = ref object of InlineAtom
format*: ComputedFormat
# Treated exactly the same as InlineSpacing, except it signifies padding.
InlinePadding* = ref object of InlineSpacing
InlineWord* = ref object of InlineAtom
str*: string
format*: ComputedFormat
LineBox* = ref object
atoms*: seq[InlineAtom]
offset*: Offset
width*: LayoutUnit
height*: LayoutUnit
baseline*: LayoutUnit
lineheight*: LayoutUnit #line-height property
InlineContext* = ref object
offset*: Offset
height*: LayoutUnit
lines*: seq[LineBox]
currentLine*: LineBox
width*: LayoutUnit
availableWidth*: SizeConstraint
availableHeight*: SizeConstraint
charwidth*: int
whitespacenum*: int
# this is actually xminwidth.
minwidth*: LayoutUnit
viewport*: Viewport
format*: ComputedFormat
BlockBox* = ref object of RootObj
inline*: InlineContext
node*: StyledNode
nested*: seq[BlockBox]
computed*: CSSComputedValues
viewport*: Viewport
offset*: Offset
# This is the padding width/height.
width*: LayoutUnit
height*: LayoutUnit
margin_top*: LayoutUnit
margin_bottom*: LayoutUnit
margin_left*: LayoutUnit
margin_right*: LayoutUnit
padding_top*: LayoutUnit
padding_bottom*: LayoutUnit
padding_left*: LayoutUnit
padding_right*: LayoutUnit
min_width*: Option[LayoutUnit]
max_width*: Option[LayoutUnit]
min_height*: Option[LayoutUnit]
max_height*: Option[LayoutUnit]
# width and height constraints
availableWidth*: SizeConstraint
availableHeight*: SizeConstraint
positioned*: bool
x_positioned*: bool
y_positioned*: bool
# very bad name. basically the minimum content width after the contents
# have been positioned (usually the width of the shortest word.) used
# in table cells.
xminwidth*: LayoutUnit
ListItemBox* = ref object of BlockBox
marker*: InlineContext
CellWrapper* = ref object
builder*: TableCellBoxBuilder
box*: BlockBox
coli*: int
colspan*: int
rowspan*: int
reflow*: bool
grown*: int # number of remaining rows
real*: CellWrapper # for filler wrappers
last*: bool # is this the last filler?
height*: LayoutUnit
baseline*: LayoutUnit
RowContext* = object
cells*: seq[CellWrapper]
reflow*: seq[bool]
width*: LayoutUnit
height*: LayoutUnit
builder*: TableRowBoxBuilder
ncols*: int
ColumnContext* = object
minwidth*: LayoutUnit
width*: LayoutUnit
wspecified*: bool
weight*: float64
TableContext* = object
caption*: TableCaptionBoxBuilder
rows*: seq[RowContext]
cols*: seq[ColumnContext]
growing*: seq[CellWrapper]
maxwidth*: LayoutUnit
blockspacing*: LayoutUnit
inlinespacing*: LayoutUnit
collapse*: bool
reflow*: seq[bool]
InlineBlockBox* = ref object of InlineAtom
innerbox*: BlockBox
margin_top*: LayoutUnit
margin_bottom*: LayoutUnit
proc append*(a: var Strut, b: LayoutUnit) =
if b < 0:
a.neg = min(b, a.neg)
else:
a.pos = max(b, a.pos)
func sum*(a: Strut): LayoutUnit =
return a.pos + a.neg
func minContent*(): SizeConstraint =
return SizeConstraint(t: MIN_CONTENT)
func maxContent*(): SizeConstraint =
return SizeConstraint(t: MAX_CONTENT)
func stretch*(u: LayoutUnit): SizeConstraint =
return SizeConstraint(t: STRETCH, u: u)
func fitContent*(u: LayoutUnit): SizeConstraint =
return SizeConstraint(t: FIT_CONTENT, u: u)
#TODO ?
func stretch*(sc: SizeConstraint): SizeConstraint =
case sc.t
of MIN_CONTENT, MAX_CONTENT:
return SizeConstraint(t: sc.t, u: sc.u)
of STRETCH, FIT_CONTENT:
return SizeConstraint(t: STRETCH, u: sc.u)
func fitContent*(sc: SizeConstraint): SizeConstraint =
case sc.t
of MIN_CONTENT, MAX_CONTENT:
return SizeConstraint(t: sc.t)
of STRETCH, FIT_CONTENT:
return SizeConstraint(t: FIT_CONTENT, u: sc.u)
func isDefinite*(sc: SizeConstraint): bool =
return sc.t in {STRETCH, FIT_CONTENT}
|