about summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2025-03-27 18:27:17 +0100
committerbptato <nincsnevem662@gmail.com>2025-03-27 18:27:17 +0100
commit0611a6b84a3c45ee65d90b48a7a40fd6e05571cd (patch)
tree69aaaebebabba2b55251f8a5930c8fd820fa9095 /src
parentd0797e696475784e4f104b4ee48050c51a69f501 (diff)
downloadchawan-0611a6b84a3c45ee65d90b48a7a40fd6e05571cd.tar.gz
layout: reimplement list items with out-of-flow markers
Instead of generating a separate container box for list items, just set
the marker's display to an internal value that is treated specially.

This fixes a bug where position: relative would not register the correct
block as the positioned ancestor.
Diffstat (limited to 'src')
-rw-r--r--src/css/csstree.nim7
-rw-r--r--src/css/cssvalues.nim10
-rw-r--r--src/css/layout.nim39
3 files changed, 20 insertions, 36 deletions
diff --git a/src/css/csstree.nim b/src/css/csstree.nim
index 6c2b78de..fea6dc43 100644
--- a/src/css/csstree.nim
+++ b/src/css/csstree.nim
@@ -259,10 +259,6 @@ proc getParent(frame: var TreeFrame; computed: CSSValues; display: CSSDisplay):
   of DisplayInnerTable:
     if frame.children.len > 0 and display != DisplayTableCaption:
       return frame.children[0].anonChildren
-  of DisplayListItem:
-    if frame.computed{"list-style-position"} == ListStylePositionOutside and
-        frame.children.len >= 2:
-      return frame.children[1].anonChildren
   of DisplayTableCell:
     if frame.anonComputed == nil:
       frame.anonComputed = frame.inheritFor(DisplayFlowRoot)
@@ -292,9 +288,8 @@ proc addListItem(frame: var TreeFrame; node: sink StyledNode) =
   of ListStylePositionOutside:
     # Generate separate boxes for the content and marker.
     let computed = node.computed.inheritProperties()
-    computed{"display"} = DisplayBlock
+    computed{"display"} = DisplayMarker
     node.anonChildren.add(initStyledAnon(node.element, computed, @[markerText]))
-    node.anonChildren.add(initStyledAnon(node.element, computed))
   of ListStylePositionInside:
     node.anonChildren.add(markerText)
   frame.getParent(node.computed, node.computed{"display"}).add(node)
diff --git a/src/css/cssvalues.nim b/src/css/cssvalues.nim
index 9d78f851..3cd22c2f 100644
--- a/src/css/cssvalues.nim
+++ b/src/css/cssvalues.nim
@@ -197,6 +197,7 @@ type
     DisplayInlineGrid = "inline-grid"
     # internal, for layout
     DisplayTableWrapper = ""
+    DisplayMarker = ""
 
   CSSWhiteSpace* = enum
     WhitespaceNormal = "normal"
@@ -558,9 +559,14 @@ const OverflowScrollLike* = {OverflowScroll, OverflowAuto, OverflowOverlay}
 const OverflowHiddenLike* = {OverflowHidden, OverflowClip}
 const FlexReverse* = {FlexDirectionRowReverse, FlexDirectionColumnReverse}
 const DisplayInlineBlockLike* = {
-  DisplayInlineTable, DisplayInlineBlock, DisplayInlineFlex, DisplayInlineGrid
+  DisplayInlineTable, DisplayInlineBlock, DisplayInlineFlex, DisplayInlineGrid,
+  DisplayMarker
 }
 const DisplayOuterInline* = DisplayInlineBlockLike + {DisplayInline}
+const DisplayInnerBlock* = {
+  DisplayBlock, DisplayFlowRoot, DisplayTableCaption, DisplayInlineBlock,
+  DisplayListItem, DisplayMarker
+}
 const DisplayInnerFlex* = {DisplayFlex, DisplayInlineFlex}
 const DisplayInnerGrid* = {DisplayGrid, DisplayInlineGrid}
 const RowGroupBox* = {
@@ -757,7 +763,7 @@ func inherited*(t: CSSPropertyType): bool =
 func blockify*(display: CSSDisplay): CSSDisplay =
   case display
   of DisplayBlock, DisplayTable, DisplayListItem, DisplayNone, DisplayFlowRoot,
-      DisplayFlex, DisplayTableWrapper, DisplayGrid:
+      DisplayFlex, DisplayTableWrapper, DisplayGrid, DisplayMarker:
     return display
   of DisplayInline, DisplayInlineBlock, DisplayTableRow,
       DisplayTableRowGroup, DisplayTableColumn,
diff --git a/src/css/layout.nim b/src/css/layout.nim
index 289c57a3..408a83f8 100644
--- a/src/css/layout.nim
+++ b/src/css/layout.nim
@@ -1735,7 +1735,7 @@ proc layoutOuterBlock(fstate: var FlowState; child: BlockBox;
         (fstate.lbstate.unpositionedFloats.len == 0 or
         not fstate.lbstate.unpositionedFloats[^1].newLine):
       # We can still cram floats into the line.
-      if child.computed{"float"} == FloatLeft:
+      if float == FloatLeft:
         fstate.lbstate.size.w += outerSize.w
         for iastate in fstate.lbstate.iastates.mitems:
           iastate.offset.x += outerSize.w
@@ -1763,6 +1763,15 @@ proc layoutInlineBlock(fstate: var FlowState; ibox: InlineBlockBox) =
       # Aligning min-content or max-content is nonsensical.
       textAlign = TextAlignLeft
     fstate.layoutOuterBlock(box, textAlign)
+  elif box.computed{"display"} == DisplayMarker:
+    # Marker box. This is a mixture of absolute and inline-block
+    # layout, where we don't care about the parent size but want to
+    # place ourselves outside the left edge of our parent box.
+    let lctx = fstate.lctx
+    var sizes = lctx.resolveFloatSizes(fstate.space, box.computed)
+    lctx.layoutRootBlock(box, sizes.margin.topLeft, sizes)
+    fstate.initLine(flag = ilfAbsolute)
+    box.state.offset.x = fstate.lbstate.size.w - box.state.size.w
   else:
     # A real inline block.
     let lctx = fstate.lctx
@@ -2111,30 +2120,6 @@ proc layoutFlow(bctx: var BlockContext; box: BlockBox; sizes: ResolvedSizes;
       size.h = max(size.h, bctx.maxFloatHeight)
     bctx.lctx.popPositioned(box, size)
 
-proc layoutListItem(bctx: var BlockContext; box: BlockBox;
-    sizes: ResolvedSizes) =
-  case box.computed{"list-style-position"}
-  of ListStylePositionOutside:
-    let marker = BlockBox(box.firstChild)
-    let content = BlockBox(marker.next)
-    content.resetState()
-    content.state.offset = box.state.offset
-    bctx.layoutFlow(content, sizes, canClear = true)
-    let markerSizes = ResolvedSizes(
-      space: availableSpace(w = fitContent(sizes.space.w), h = sizes.space.h),
-      bounds: DefaultBounds
-    )
-    bctx.lctx.layoutRootBlock(marker, offset(x = 0, y = 0), markerSizes)
-    #TODO put markers right before the first atom of the list item
-    # instead
-    marker.state.offset.x = -marker.state.size.w
-    # take inner box min width etc.
-    box.resetState()
-    box.state = content.state
-    content.state.offset = offset(x = 0, y = 0)
-  of ListStylePositionInside:
-    bctx.layoutFlow(box, sizes, canClear = true)
-
 # Table layout. We try to emulate w3m's behavior here:
 # 1. Calculate minimum and preferred width of each column
 # 2. If column width is not auto, set width to max(min_col_width, specified)
@@ -2625,10 +2610,8 @@ proc layoutTable(bctx: BlockContext; box: BlockBox; sizes: ResolvedSizes) =
 proc layout(bctx: var BlockContext; box: BlockBox; sizes: ResolvedSizes;
     canClear: bool) =
   case box.computed{"display"}
-  of DisplayBlock, DisplayFlowRoot, DisplayTableCaption, DisplayInlineBlock:
+  of DisplayInnerBlock:
     bctx.layoutFlow(box, sizes, canClear)
-  of DisplayListItem:
-    bctx.layoutListItem(box, sizes)
   of DisplayInnerTable:
     bctx.layoutTable(box, sizes)
   of DisplayInnerFlex: