about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2025-01-02 18:24:40 +0100
committerbptato <nincsnevem662@gmail.com>2025-01-02 18:24:51 +0100
commit57d6a0d18d080b82b353a5451b5724807a5658b1 (patch)
treeee777cf2ffe31a9edc2b76399c4542a2217c6a07
parent9ef9d6a0615aa4166f905fc7b6527b8b380739b0 (diff)
downloadchawan-57d6a0d18d080b82b353a5451b5724807a5658b1.tar.gz
layout: implement layoutFlexItem as layoutRootBlock
This way, it benefits from sub-layout caching.  Which is great because
our flex layout likes to unnecessarily redo item layout...

Also, we now account for the bottom margin in flex items.
-rw-r--r--src/css/layout.nim31
-rw-r--r--test/layout/flex-item-margin-collapsing.color.expected17
-rw-r--r--test/layout/flex-item-margin-collapsing.html28
3 files changed, 60 insertions, 16 deletions
diff --git a/src/css/layout.nim b/src/css/layout.nim
index ce7127b5..d2538af8 100644
--- a/src/css/layout.nim
+++ b/src/css/layout.nim
@@ -1202,7 +1202,7 @@ proc layoutTableWrapper(bctx: BlockContext; box: BlockBox; sizes: ResolvedSizes)
 proc layoutFlex(bctx: var BlockContext; box: BlockBox; sizes: ResolvedSizes)
 proc layoutInline(ictx: var InlineContext; box: InlineBox)
 proc layoutRootBlock(lctx: LayoutContext; box: BlockBox; offset: Offset;
-  sizes: ResolvedSizes)
+  sizes: ResolvedSizes; includeMargin = false)
 
 # Note: padding must still be applied after this.
 proc applySize(box: BlockBox; sizes: ResolvedSizes;
@@ -2272,18 +2272,6 @@ proc layout(bctx: var BlockContext; box: BlockBox; sizes: ResolvedSizes;
   else:
     assert false
 
-proc layoutFlexItem(lctx: LayoutContext; box: BlockBox; sizes: ResolvedSizes) =
-  var bctx = BlockContext(lctx: lctx)
-  # note: we do not append margins here, since those belong to the flex item,
-  # not its inner BFC.
-  box.state = BoxLayoutState(offset: offset(x = sizes.margin.left, y = 0))
-  bctx.layout(box, sizes, canClear = false)
-  assert bctx.unpositionedFloats.len == 0
-  # If the highest float edge is higher than the box itself, set that as
-  # the box height.
-  box.state.size.h = max(box.state.size.h, bctx.maxFloatHeight)
-  box.state.intr.h = max(box.state.intr.h, bctx.maxFloatHeight)
-
 type
   FlexWeightType = enum
     fwtGrow, fwtShrink
@@ -2311,6 +2299,9 @@ type
     totalWeight: array[FlexWeightType, float64]
     pending: seq[FlexPendingItem]
 
+proc layoutFlexItem(lctx: LayoutContext; box: BlockBox; sizes: ResolvedSizes) =
+  lctx.layoutRootBlock(box, offset(x = 0, y = 0), sizes, includeMargin = true)
+
 const FlexRow = {FlexDirectionRow, FlexDirectionRowReverse}
 
 proc updateMaxSizes(mctx: var FlexMainContext; child: BlockBox;
@@ -2404,6 +2395,10 @@ proc flushMain(fctx: var FlexContext; mctx: var FlexMainContext;
       # instead. (if the box's available height is definite, then this will
       # change nothing, so we skip it as an optimization.)
       it.sizes.space[odim] = stretch(h - it.sizes.margin[odim].sum())
+      if odim == dtVertical:
+        # Do not include the bottom margin; space only applies to the
+        # actual height.
+        it.sizes.space[odim].u -= it.child.state.marginBottom
       lctx.layoutFlexItem(it.child, it.sizes)
     it.child.state.offset[dim] += offset[dim]
     # margins are added here, since they belong to the flex item.
@@ -2504,7 +2499,7 @@ proc layoutFlex(bctx: var BlockContext; box: BlockBox; sizes: ResolvedSizes) =
 # Returns the bottom margin for the box, collapsed with the appropriate
 # margins from its descendants.
 proc layoutRootBlock(lctx: LayoutContext; box: BlockBox; offset: Offset;
-    sizes: ResolvedSizes) =
+    sizes: ResolvedSizes; includeMargin = false) =
   if box.sizes == sizes:
     box.state.offset = offset
     return
@@ -2516,10 +2511,14 @@ proc layoutRootBlock(lctx: LayoutContext; box: BlockBox; offset: Offset;
   bctx.layout(box, sizes, canClear = false)
   assert bctx.unpositionedFloats.len == 0
   let marginBottom = bctx.marginTodo.sum()
+  if includeMargin:
+    box.state.size.h += marginBottom
+  else:
+    bctx.maxFloatHeight -= marginBottom
   # If the highest float edge is higher than the box itself, set that as
   # the box height.
-  box.state.size.h = max(box.state.size.h, bctx.maxFloatHeight - marginBottom)
-  box.state.intr.h = max(box.state.intr.h, bctx.maxFloatHeight - marginBottom)
+  box.state.size.h = max(box.state.size.h, bctx.maxFloatHeight)
+  box.state.intr.h = max(box.state.intr.h, bctx.maxFloatHeight)
   box.state.marginBottom = marginBottom
 
 proc initBlockPositionStates(state: var BlockState; bctx: var BlockContext;
diff --git a/test/layout/flex-item-margin-collapsing.color.expected b/test/layout/flex-item-margin-collapsing.color.expected
new file mode 100644
index 00000000..e38eebc4
--- /dev/null
+++ b/test/layout/flex-item-margin-collapsing.color.expected
@@ -0,0 +1,17 @@
+test 
+     
+     
+     
+test2
+     
+     
+     
+test3
+     
+     
+     
+test3
+     
+     
+     
+
diff --git a/test/layout/flex-item-margin-collapsing.html b/test/layout/flex-item-margin-collapsing.html
new file mode 100644
index 00000000..56c7260d
--- /dev/null
+++ b/test/layout/flex-item-margin-collapsing.html
@@ -0,0 +1,28 @@
+<div style="display: flex; background: red; width: 5ch">
+<div style="margin-bottom: 2em; background: blue">
+<div style="margin-bottom: 1em; background: green">
+test
+</div>
+</div>
+</div>
+<div style="display: flex; background: red; width: 5ch">
+<div style="margin-bottom: 1em; background: blue">
+<div style="margin-bottom: 2em; background: green">
+test2
+</div>
+</div>
+</div>
+<div style="display: flex; flex-direction: column; background: red; width: 5ch">
+<div style="margin-bottom: 2em; background: blue">
+<div style="margin-bottom: 1em; background: green">
+test3
+</div>
+</div>
+</div>
+<div style="display: flex; flex-direction: column; background: red; width: 5ch">
+<div style="margin-bottom: 1em; background: blue">
+<div style="margin-bottom: 2em; background: green">
+test3
+</div>
+</div>
+</div>