|
1 | 1 | module; |
2 | 2 |
|
3 | | -#include <karm-math/au.h> |
4 | 3 | #include <karm-core/macros.h> |
| 4 | +#include <karm-math/au.h> |
5 | 5 |
|
6 | 6 | export module Vaev.Engine:layout.block; |
7 | 7 |
|
@@ -122,6 +122,31 @@ Output fragmentEmptyBox(Tree& tree, Input input) { |
122 | 122 | } |
123 | 123 | } |
124 | 124 |
|
| 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 | + |
125 | 150 | // https://www.w3.org/TR/CSS22/visuren.html#normal-flow |
126 | 151 | struct BlockFormatingContext : FormatingContext { |
127 | 152 | Au _computeCapmin(Tree& tree, Box& box, Input input, Au inlineSize) { |
@@ -157,11 +182,6 @@ struct BlockFormatingContext : FormatingContext { |
157 | 182 | return fragmentEmptyBox(tree, input); |
158 | 183 | } |
159 | 184 |
|
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 | | - |
165 | 185 | Breakpoint currentBreakpoint; |
166 | 186 | BaselinePositionsSet firstBaselineSet, lastBaselineSet; |
167 | 187 |
|
@@ -195,36 +215,34 @@ struct BlockFormatingContext : FormatingContext { |
195 | 215 | .pendingVerticalSizes = input.pendingVerticalSizes, |
196 | 216 | }; |
197 | 217 |
|
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 | | - } |
| 218 | + UsedSpacings usedSpacings{ |
| 219 | + .padding = computePaddings(tree, c, childInput.containingBlock), |
| 220 | + .borders = computeBorders(tree, c), |
| 221 | + .margin = computeMargins(tree, c, childInput) |
| 222 | + }; |
204 | 223 |
|
205 | 224 | if (not impliesRemovingFromFlow(c.style->position)) { |
206 | 225 | // 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; |
| 226 | + blockSize += max(usedSpacings.margin.top, lastMarginBottom) - lastMarginBottom; |
210 | 227 | } |
211 | 228 |
|
212 | | - childInput.position = input.position + Vec2Au{margin.start, blockSize}; |
| 229 | + childInput.position = input.position + Vec2Au{usedSpacings.margin.start, blockSize}; |
213 | 230 |
|
214 | 231 | // HACK: Table Box mostly behaves like a block box, let's compute its capmin |
215 | 232 | // and avoid duplicating the layout code |
216 | 233 | if (c.style->display == Display::Internal::TABLE_BOX) { |
217 | 234 | childInput.capmin = _computeCapmin(tree, box, input, inlineSize); |
218 | 235 | } |
219 | 236 |
|
220 | | - auto output = layout( |
221 | | - tree, |
222 | | - c, |
223 | | - childInput |
224 | | - ); |
| 237 | + _populateChildSpecifiedSizes(tree, c, childInput, usedSpacings.margin.horizontal(), input.knownSize.x); |
| 238 | + |
| 239 | + auto output = input.fragment |
| 240 | + ? layoutAndCommitBorderBox(tree, c, childInput, *input.fragment, usedSpacings) |
| 241 | + : layoutBorderBox(tree, c, childInput, usedSpacings); |
| 242 | + |
225 | 243 | if (not impliesRemovingFromFlow(c.style->position)) { |
226 | | - blockSize += output.size.y + margin.bottom; |
227 | | - lastMarginBottom = margin.bottom; |
| 244 | + blockSize += output.size.y + usedSpacings.margin.bottom; |
| 245 | + lastMarginBottom = usedSpacings.margin.bottom; |
228 | 246 | } |
229 | 247 |
|
230 | 248 | maybeProcessChildBreakpoint( |
@@ -252,11 +270,14 @@ struct BlockFormatingContext : FormatingContext { |
252 | 270 | blockWasCompletelyLaidOut = output.completelyLaidOut and i + 1 == box.children().len(); |
253 | 271 | } |
254 | 272 |
|
255 | | - inlineSize = max(inlineSize, output.size.x + margin.horizontal()); |
| 273 | + inlineSize = max(inlineSize, output.size.x + usedSpacings.margin.horizontal()); |
256 | 274 | } |
257 | 275 |
|
258 | 276 | return { |
259 | | - .size = Vec2Au{inlineSize, blockSize}, |
| 277 | + .size = Vec2Au{ |
| 278 | + input.knownSize.x.unwrapOr(inlineSize), |
| 279 | + input.knownSize.y.unwrapOr(blockSize) |
| 280 | + }, |
260 | 281 | .completelyLaidOut = blockWasCompletelyLaidOut, |
261 | 282 | .breakpoint = currentBreakpoint, |
262 | 283 | .firstBaselineSet = firstBaselineSet, |
|
0 commit comments