Skip to content

Commit 818433e

Browse files
committed
vaev-engine: Refactor layout interface, shifting sizing and insets computation
to parent.
1 parent 67de658 commit 818433e

File tree

11 files changed

+681
-386
lines changed

11 files changed

+681
-386
lines changed

src/vaev-engine/driver/print.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ void _paintCornerMargin(Style::PageSpecifiedValues& pageStyle, Scene::Stack& sta
2727
.root = Layout::buildForPseudoElement(pageStyle.area(area)),
2828
.viewport = Layout::Viewport{.small = rect.size()}
2929
};
30-
auto [_, frag] = Layout::layoutCreateFragment(
30+
auto [_, frag] = Layout::layoutAndCommitRoot(
3131
tree,
3232
{
3333
.knownSize = rect.size().cast<Opt<Au>>(),
@@ -48,7 +48,7 @@ void _paintMainMargin(Style::PageSpecifiedValues& pageStyle, Scene::Stack& stack
4848
.root = std::move(box),
4949
.viewport = Layout::Viewport{.small = rect.size()}
5050
};
51-
auto [_, frag] = Layout::layoutCreateFragment(
51+
auto [_, frag] = Layout::layoutAndCommitRoot(
5252
tree,
5353
{
5454
.knownSize = rect.size().cast<Opt<Au>>(),
@@ -212,7 +212,7 @@ export Generator<Print::Page> print(Gc::Ref<Dom::Document> dom, Print::Settings
212212
};
213213

214214
contentTree.fc.enterDiscovery();
215-
auto outDiscovery = Layout::layout(
215+
auto outDiscovery = Layout::layoutRoot(
216216
contentTree,
217217
pageLayoutInput.withBreakpointTraverser(Layout::BreakpointTraverser(&prevBreakpoint))
218218
);
@@ -223,7 +223,7 @@ export Generator<Print::Page> print(Gc::Ref<Dom::Document> dom, Print::Settings
223223
: outDiscovery.breakpoint.unwrap();
224224

225225
contentTree.fc.leaveDiscovery();
226-
auto [outFragmentation, fragment] = Layout::layoutCreateFragment(
226+
auto [outFragmentation, fragment] = Layout::layoutAndCommitRoot(
227227
contentTree,
228228
pageLayoutInput
229229
.withBreakpointTraverser(Layout::BreakpointTraverser(&prevBreakpoint, &currBreakpoint))

src/vaev-engine/driver/render.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export RenderResult render(Gc::Ref<Dom::Document> dom, Style::Media const& media
3636

3737
auto canvasColor = fixupBackgrounds(computer, dom, tree);
3838

39-
auto [outDiscovery, root] = Layout::layoutCreateFragment(
39+
auto [outDiscovery, root] = Layout::layoutAndCommitRoot(
4040
tree,
4141
{
4242
.knownSize = {viewport.small.width, NONE},

src/vaev-engine/layout/block.cpp

Lines changed: 46 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
module;
22

3-
#include <karm-math/au.h>
43
#include <karm-core/macros.h>
4+
#include <karm-math/au.h>
55

66
export module Vaev.Engine:layout.block;
77

@@ -122,6 +122,31 @@ Output fragmentEmptyBox(Tree& tree, Input input) {
122122
}
123123
}
124124

125+
void _populateChildSpecifiedSizes(Tree& tree, Box& child, Input& childInput, Au horizontalMargins, Opt<Au> blockInlineSize) {
126+
if (childInput.intrinsic == IntrinsicSize::AUTO or child.style->display != Display::INLINE) {
127+
if (child.style->sizing->width.is<Keywords::Auto>()) {
128+
// https://www.w3.org/TR/css-tables-3/#layout-principles
129+
// Unlike other block-level boxes, tables do not fill their containing block by default.
130+
// When their width computes to auto, they behave as if they had fit-content specified instead.
131+
// This is different from most block-level boxes, which behave as if they had stretch instead.
132+
if (child.style->display == Display::TABLE_BOX) {
133+
// Do nothing. 'fit-content' is kinda intrinsic size, when we don't populate knownSize.
134+
} else if (blockInlineSize) {
135+
// When the inline size is not known, we cannot enforce it to the child. (?)
136+
childInput.knownSize.width = blockInlineSize.unwrap() - horizontalMargins;
137+
}
138+
} else {
139+
childInput.knownSize.width = computeSpecifiedWidth(
140+
tree, child, child.style->sizing->width, childInput.containingBlock
141+
);
142+
}
143+
144+
childInput.knownSize.height = computeSpecifiedHeight(
145+
tree, child, child.style->sizing->height, childInput.containingBlock
146+
);
147+
}
148+
}
149+
125150
// https://www.w3.org/TR/CSS22/visuren.html#normal-flow
126151
struct BlockFormatingContext : FormatingContext {
127152
Au _computeCapmin(Tree& tree, Box& box, Input input, Au inlineSize) {
@@ -157,11 +182,6 @@ struct BlockFormatingContext : FormatingContext {
157182
return fragmentEmptyBox(tree, input);
158183
}
159184

160-
// NOTE: Our parent has no clue about our width but wants us to commit,
161-
// we need to compute it first
162-
if (input.fragment and not input.knownSize.width)
163-
inlineSize = run(tree, box, input.withFragment(nullptr), startAt, stopAt).width();
164-
165185
Breakpoint currentBreakpoint;
166186
BaselinePositionsSet firstBaselineSet, lastBaselineSet;
167187

@@ -187,44 +207,41 @@ struct BlockFormatingContext : FormatingContext {
187207
// continue;
188208

189209
Input childInput = {
190-
.fragment = input.fragment,
191210
.intrinsic = input.intrinsic,
192211
.availableSpace = {input.availableSpace.x, 0_au},
193212
.containingBlock = {inlineSize, input.knownSize.y.unwrapOr(0_au)},
194213
.breakpointTraverser = input.breakpointTraverser.traverseInsideUsingIthChild(i),
195214
.pendingVerticalSizes = input.pendingVerticalSizes,
196215
};
197216

198-
auto margin = computeMargins(tree, c, childInput);
199-
200-
Opt<Au> childInlineSize = NONE;
201-
if (c.style->sizing->width.is<Keywords::Auto>()) {
202-
childInlineSize = inlineSize - margin.horizontal();
203-
}
217+
UsedSpacings usedSpacings{
218+
.padding = computePaddings(tree, c, childInput.containingBlock),
219+
.borders = computeBorders(tree, c),
220+
.margin = computeMargins(tree, c, childInput)
221+
};
204222

205223
if (not impliesRemovingFromFlow(c.style->position)) {
206224
// TODO: collapsed margins for sibling elements
207-
blockSize += max(margin.top, lastMarginBottom) - lastMarginBottom;
208-
if (input.fragment or input.knownSize.x)
209-
childInput.knownSize.width = childInlineSize;
225+
blockSize += max(usedSpacings.margin.top, lastMarginBottom) - lastMarginBottom;
210226
}
211227

212-
childInput.position = input.position + Vec2Au{margin.start, blockSize};
228+
childInput.position = input.position + Vec2Au{usedSpacings.margin.start, blockSize};
213229

214230
// HACK: Table Box mostly behaves like a block box, let's compute its capmin
215231
// and avoid duplicating the layout code
216232
if (c.style->display == Display::Internal::TABLE_BOX) {
217233
childInput.capmin = _computeCapmin(tree, box, input, inlineSize);
218234
}
219235

220-
auto output = layout(
221-
tree,
222-
c,
223-
childInput
224-
);
236+
_populateChildSpecifiedSizes(tree, c, childInput, usedSpacings.margin.horizontal(), input.knownSize.x);
237+
238+
auto output = input.fragment
239+
? layoutAndCommitBorderBox(tree, c, childInput, *input.fragment, usedSpacings)
240+
: layoutBorderBox(tree, c, childInput, usedSpacings);
241+
225242
if (not impliesRemovingFromFlow(c.style->position)) {
226-
blockSize += output.size.y + margin.bottom;
227-
lastMarginBottom = margin.bottom;
243+
blockSize += output.size.y + usedSpacings.margin.bottom;
244+
lastMarginBottom = usedSpacings.margin.bottom;
228245
}
229246

230247
maybeProcessChildBreakpoint(
@@ -252,11 +269,14 @@ struct BlockFormatingContext : FormatingContext {
252269
blockWasCompletelyLaidOut = output.completelyLaidOut and i + 1 == box.children().len();
253270
}
254271

255-
inlineSize = max(inlineSize, output.size.x + margin.horizontal());
272+
inlineSize = max(inlineSize, output.size.x + usedSpacings.margin.horizontal());
256273
}
257274

258275
return {
259-
.size = Vec2Au{inlineSize, blockSize},
276+
.size = Vec2Au{
277+
input.knownSize.x.unwrapOr(inlineSize),
278+
input.knownSize.y.unwrapOr(blockSize)
279+
},
260280
.completelyLaidOut = blockWasCompletelyLaidOut,
261281
.breakpoint = currentBreakpoint,
262282
.firstBaselineSet = firstBaselineSet,

src/vaev-engine/layout/flex.cpp

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,8 @@ struct FlexItem {
134134
FlexProps flexItemProps;
135135
FlexAxis fa;
136136

137-
// these 2 sizes do NOT account margins
137+
Math::Insets<Au> borders;
138+
Math::Insets<Au> padding;
138139
Vec2Au usedSize;
139140
Math::Insets<Opt<Au>> margin;
140141

@@ -152,19 +153,22 @@ struct FlexItem {
152153
// InsetsAu borders;
153154

154155
FlexItem(Tree& tree, Box& box, bool isRowOriented, Vec2Au containingBlock)
155-
: box(&box), flexItemProps(*box.style->flex), fa(isRowOriented) {
156+
: box(&box), flexItemProps(*box.style->flex), fa(isRowOriented),
157+
borders(computeBorders(tree, box)), padding(computePaddings(tree, box, containingBlock)) {
156158
// FIXME: check if really needed
157159
speculateValues(tree, Input{.containingBlock = containingBlock});
158160
// TODO: not always we will need min/max content sizes,
159161
// this can be lazy computed for performance gains
160162
computeContentSizes(tree, containingBlock);
161163
}
162164

163-
void commit(MutCursor<Frag> frag) {
164-
frag->metrics.margin.top = margin.top.unwrapOr(speculativeMargin.top);
165-
frag->metrics.margin.start = margin.start.unwrapOr(speculativeMargin.start);
166-
frag->metrics.margin.end = margin.end.unwrapOr(speculativeMargin.end);
167-
frag->metrics.margin.bottom = margin.bottom.unwrapOr(speculativeMargin.bottom);
165+
InsetsAu resolvedMargin() {
166+
return {
167+
margin.top.unwrapOr(speculativeMargin.top),
168+
margin.end.unwrapOr(speculativeMargin.end),
169+
margin.bottom.unwrapOr(speculativeMargin.bottom),
170+
margin.start.unwrapOr(speculativeMargin.start),
171+
};
168172
}
169173

170174
void computeContentSizes(Tree& tree, Vec2Au containingBlock) {
@@ -227,12 +231,21 @@ struct FlexItem {
227231
}
228232

229233
void speculateValues(Tree& t, Input input) {
230-
speculativeSize = layout(t, *box, input).size;
231234
speculativeMargin = computeMargins(
232235
t,
233236
*box,
234237
input
235238
);
239+
240+
input.knownSize.width = input.knownSize.width or computeSpecifiedWidth(
241+
t, *box, box->style->sizing->width, input.containingBlock
242+
);
243+
244+
input.knownSize.height = input.knownSize.height or computeSpecifiedHeight(
245+
t, *box, box->style->sizing->height, input.containingBlock
246+
);
247+
248+
speculativeSize = layoutBorderBox(t, *box, input, UsedSpacings{.padding = padding, .borders = borders}).size;
236249
}
237250

238251
// https://www.w3.org/TR/css-flexbox-1/#valdef-flex-basis-auto
@@ -1423,17 +1436,19 @@ struct FlexFormatingContext : FormatingContext {
14231436
for (auto& flexItem : flexLine.items) {
14241437
flexItem.position = flexItem.position + flexLine.position + input.position;
14251438

1426-
auto out = layout(
1427-
tree,
1428-
*flexItem.box,
1429-
{
1430-
.fragment = input.fragment,
1431-
.knownSize = {flexItem.usedSize.x, flexItem.usedSize.y},
1432-
.position = flexItem.position,
1433-
.availableSpace = availableSpace,
1434-
}
1435-
);
1436-
flexItem.commit(input.fragment);
1439+
UsedSpacings usedSpacings{
1440+
.padding = std::move(flexItem.padding),
1441+
.borders = std::move(flexItem.borders),
1442+
.margin = flexItem.resolvedMargin(),
1443+
};
1444+
1445+
Input childInput{
1446+
.knownSize = {flexItem.usedSize.x, flexItem.usedSize.y},
1447+
.position = flexItem.position,
1448+
.availableSpace = availableSpace,
1449+
};
1450+
1451+
layoutAndCommitBorderBox(tree, *flexItem.box, childInput, *input.fragment, usedSpacings);
14371452
}
14381453
}
14391454
}

src/vaev-engine/layout/inline.cpp

Lines changed: 43 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -56,18 +56,31 @@ struct InlineFormatingContext : FormatingContext {
5656

5757
auto& atomicBox = *inlineBox.atomicBoxes[boxStrutCell.id];
5858

59+
Input childInput{
60+
.availableSpace = {inlineSize, input.availableSpace.y},
61+
.containingBlock = {
62+
input.knownSize.x.unwrapOr(0_au),
63+
input.knownSize.y.unwrapOr(0_au)
64+
},
65+
};
66+
67+
childInput.knownSize.width = computeSpecifiedWidth(
68+
tree, atomicBox, atomicBox.style->sizing->width, childInput.containingBlock
69+
);
70+
71+
childInput.knownSize.height = computeSpecifiedHeight(
72+
tree, atomicBox, atomicBox.style->sizing->height, childInput.containingBlock
73+
);
74+
5975
// NOTE: We set the same availableSpace to child inline boxes since line wrapping is possible i.e. in the
6076
// worst case, they will take up the whole availableSpace, and a line break will be done right before them
61-
auto atomicBoxOutput = layout(
77+
auto atomicBoxOutput = layoutBorderBox(
6278
tree,
6379
atomicBox,
64-
Input{
65-
.knownSize = {NONE, NONE},
66-
.availableSpace = {inlineSize, input.availableSpace.y},
67-
.containingBlock = {
68-
input.knownSize.x.unwrapOr(0_au),
69-
input.knownSize.y.unwrapOr(0_au)
70-
},
80+
childInput,
81+
UsedSpacings{
82+
.padding = computePaddings(tree, atomicBox, childInput.containingBlock),
83+
.borders = computeBorders(tree, atomicBox),
7184
}
7285
);
7386

@@ -100,19 +113,24 @@ struct InlineFormatingContext : FormatingContext {
100113
};
101114
}
102115

103-
layout(
104-
tree,
105-
atomicBox,
106-
Input{
107-
.fragment = input.fragment,
108-
.knownSize = knownSize,
109-
.position = input.position + positionInProse,
110-
.containingBlock = {
111-
input.knownSize.x.unwrapOr(0_au),
112-
input.knownSize.y.unwrapOr(0_au)
113-
},
114-
}
115-
);
116+
Input childInput{
117+
.knownSize = knownSize,
118+
.position = input.position + positionInProse,
119+
.containingBlock = {
120+
input.knownSize.x.unwrapOr(0_au),
121+
input.knownSize.y.unwrapOr(0_au)
122+
},
123+
};
124+
125+
UsedSpacings usedSpacings{
126+
.padding = computePaddings(tree, atomicBox, childInput.containingBlock),
127+
.borders = computeBorders(tree, atomicBox),
128+
};
129+
130+
if (input.fragment)
131+
layoutAndCommitBorderBox(tree, atomicBox, childInput, *input.fragment, usedSpacings);
132+
else
133+
layoutBorderBox(tree, atomicBox, childInput, usedSpacings);
116134
}
117135

118136
if (tree.fc.allowBreak() and not tree.fc.acceptsFit(
@@ -128,7 +146,10 @@ struct InlineFormatingContext : FormatingContext {
128146
}
129147

130148
return {
131-
.size = size,
149+
.size = {
150+
input.knownSize.x.unwrapOr(size.x),
151+
input.knownSize.y.unwrapOr(size.y),
152+
},
132153
.completelyLaidOut = true,
133154
.firstBaselineSet = firstBaselineSet,
134155
.lastBaselineSet = lastBaselineSet,

0 commit comments

Comments
 (0)