11module ;
22
3- #include < karm-math/au.h>
43#include < karm-core/macros.h>
4+ #include < karm-math/au.h>
55
66export 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
126151struct 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,
0 commit comments