about summary refs log tree commit diff stats
path: root/src/layout
diff options
context:
space:
mode:
Diffstat (limited to 'src/layout')
-rw-r--r--src/layout/box.nim13
-rw-r--r--src/layout/engine.nim168
2 files changed, 95 insertions, 86 deletions
diff --git a/src/layout/box.nim b/src/layout/box.nim
index 5acc7ef6..1167ca7b 100644
--- a/src/layout/box.nim
+++ b/src/layout/box.nim
@@ -23,7 +23,6 @@ type
 
   Viewport* = ref object
     window*: WindowAttributes
-    root*: seq[BlockBox]
     positioned*: seq[BlockBox]
 
   BoxBuilder* = ref object of RootObj
@@ -100,7 +99,7 @@ type
     width*: int
     contentWidth*: int
     contentHeight*: Option[int]
-    maxContentWidth*: int
+    contentWidthInfinite*: bool
 
     charwidth*: int
     whitespacenum*: int
@@ -139,14 +138,8 @@ type
     contentWidth*: int
     contentHeight*: Option[int]
     shrink*: bool
-    # The sole purpose of maxContentWidth is to stretch children of table
-    # cells to infinity in its maximum width calculation pass.
-    # For blocks with a specified width, maxContentWidth does nothing.
-    # This should never be used for anything other than setting width to
-    # min(maxContentWidth, width)! Failure to do so will almost certainly
-    # result in overflow errors (because maxContentWidth may be set to
-    # high(int).)
-    maxContentWidth*: int
+    # Whether to stretch content to infinity.
+    contentWidthInfinite*: bool
 
     positioned*: bool
     x_positioned*: bool
diff --git a/src/layout/engine.nim b/src/layout/engine.nim
index b5da155e..c3e6c3e0 100644
--- a/src/layout/engine.nim
+++ b/src/layout/engine.nim
@@ -255,8 +255,8 @@ proc addAtom(ictx: InlineContext, atom: InlineAtom, pcomputed: CSSComputedValues
   var shift = ictx.computeShift(pcomputed)
   ictx.whitespacenum = 0
   # Line wrapping
-  if not pcomputed.nowrap:
-    if ictx.currentLine.width + atom.width + shift > ictx.maxContentWidth:
+  if not pcomputed.nowrap and not ictx.contentWidthInfinite:
+    if ictx.currentLine.width + atom.width + shift > ictx.contentWidth:
       ictx.finishLine(pcomputed, false)
       # Recompute on newline
       shift = ictx.computeShift(pcomputed)
@@ -307,26 +307,30 @@ proc flushLine(ictx: InlineContext, computed: CSSComputedValues) =
 proc checkWrap(state: var InlineState, r: Rune) =
   if state.computed{"white-space"} in {WHITESPACE_NOWRAP, WHITESPACE_PRE}:
     return
+  if state.ictx.contentWidthInfinite:
+    return
   let shift = state.ictx.computeShift(state.computed)
   let rw = r.width()
+  let currWidth = state.ictx.currentLine.width + state.word.width + shift +
+    rw * state.ictx.cellwidth
   case state.computed{"word-break"}
   of WORD_BREAK_NORMAL:
     if rw == 2 or state.wrappos != -1: # break on cjk and wrap opportunities
-      if state.ictx.currentLine.width + state.word.width + shift + rw * state.ictx.cellwidth > state.ictx.maxContentWidth:
+      if currWidth > state.ictx.contentWidth:
         let l = state.ictx.currentLine
         state.addWordEOL()
         if l == state.ictx.currentLine: # no line wrapping occured in addAtom
           state.ictx.finishLine(state.computed)
           state.ictx.whitespacenum = 0
   of WORD_BREAK_BREAK_ALL:
-    if state.ictx.currentLine.width + state.word.width + shift + rw * state.ictx.cellwidth > state.ictx.maxContentWidth:
+    if currWidth > state.ictx.contentWidth:
       let l = state.ictx.currentLine
       state.addWordEOL()
       if l == state.ictx.currentLine: # no line wrapping occured in addAtom
         state.ictx.finishLine(state.computed)
         state.ictx.whitespacenum = 0
   of WORD_BREAK_KEEP_ALL:
-    if state.ictx.currentLine.width + state.word.width + shift + rw * state.ictx.cellwidth > state.ictx.maxContentWidth:
+    if currWidth > state.ictx.contentWidth:
       state.ictx.finishLine(state.computed)
       state.ictx.whitespacenum = 0
 
@@ -410,8 +414,10 @@ proc resolveContentWidth(box: BlockBox, widthpx, availableWidth: int, isauto = f
         box.margin_right = underflow div 2
 
 # Resolve percentage-based dimensions.
-# availableWidth: width of the containing box. availableHeight: ditto, but with height.
-proc resolveDimensions(box: BlockBox, availableWidth: int, availableHeight: Option[int], maxContentWidth: Option[int]) =
+# availableWidth: width of the containing box
+# availableHeight: ditto, but with height.
+proc resolveDimensions(box: BlockBox, availableWidth: int,
+    availableHeight: Option[int]) =
   let viewport = box.viewport
   let computed = box.computed
 
@@ -464,10 +470,8 @@ proc resolveDimensions(box: BlockBox, availableWidth: int, availableHeight: Opti
     if min_height.isSome and box.contentHeight.isSome and
         min_height.get > box.contentHeight.get:
       box.contentHeight = min_height
-  # if no max content width is supplied, just use regular content width.
-  box.maxContentWidth = maxContentWidth.get(box.contentWidth)
 
-proc resolveTableCellDimensions(box: BlockBox, availableWidth: int, availableHeight: Option[int], maxContentWidth: Option[int]) =
+proc resolveTableCellDimensions(box: BlockBox, availableWidth: int, availableHeight: Option[int]) =
   let viewport = box.viewport
   let computed = box.computed
 
@@ -490,9 +494,6 @@ proc resolveTableCellDimensions(box: BlockBox, availableWidth: int, availableHei
   let pheight = computed{"height"}
   if not pheight.auto and pheight.unit != UNIT_PERC:
     box.contentHeight = some(pheight.px(viewport))
-  # if no max content width is supplied, just use regular content width.
-  box.maxContentWidth = maxContentWidth.get(box.contentWidth)
-
 
 # Whether a width was specified on this block box.
 func isWidthSpecified(box: BlockBox): bool =
@@ -535,50 +536,72 @@ func isShrink(box: BlockBox, parent: BlockBox = nil, override = false): bool =
       return parent.shrink and not box.isWidthSpecified()
   else: discard
 
-proc newTableCellBox(viewport: Viewport, builder: BoxBuilder, parentWidth: int, parentHeight = none(int), shrink = true, maxContentWidth = none(int)): BlockBox {.inline.} =
+proc newTableCellBox(viewport: Viewport, builder: BoxBuilder,
+    parentWidth: int, parentHeight = none(int), shrink = true,
+    contentWidthInfinite = false): BlockBox =
   let box = BlockBox(
     viewport: viewport,
     computed: builder.computed,
     node: builder.node
   )
   box.shrink = box.isShrink(nil, shrink)
-  box.resolveTableCellDimensions(parentWidth, parentHeight, maxContentWidth)
+  box.contentWidthInfinite = contentWidthInfinite
+  box.resolveTableCellDimensions(parentWidth, parentHeight)
   return box
 
-proc newFlowRootBox(viewport: Viewport, builder: BoxBuilder, parentWidth: int, parentHeight = none(int), shrink = true, maxContentWidth = none(int)): BlockBox {.inline.} =
-  new(result)
-  result.viewport = viewport
-  result.computed = builder.computed
-  result.node = builder.node
-  result.positioned = builder.computed{"position"} != POSITION_STATIC
-  result.shrink = result.isShrink(nil, shrink)
-  result.resolveDimensions(parentWidth, parentHeight, maxContentWidth)
+proc newFlowRootBox(viewport: Viewport, builder: BoxBuilder,
+    parentWidth: int, parentHeight = none(int), shrink = true,
+    contentWidthInfinite = false): BlockBox =
+  let box = BlockBox(
+    viewport: viewport,
+    computed: builder.computed,
+    node: builder.node,
+    positioned: builder.computed{"position"} != POSITION_STATIC,
+    contentWidthInfinite: contentWidthInfinite
+  )
+  box.shrink = box.isShrink(nil, shrink)
+  box.resolveDimensions(parentWidth, parentHeight)
+  return box
 
 proc newBlockBox(parent: BlockBox, builder: BoxBuilder): BlockBox =
-  new(result)
-  result.viewport = parent.viewport
-  result.computed = builder.computed
-  result.shrink = result.isShrink(parent)
-  result.positioned = builder.computed{"position"} != POSITION_STATIC
-  let maxContentWidth = if result.shrink:
-    some(parent.maxContentWidth)
+  let box = BlockBox(
+    viewport: parent.viewport,
+    computed: builder.computed,
+    positioned: builder.computed{"position"} != POSITION_STATIC,
+    node: builder.node
+  )
+  box.shrink = box.isShrink(parent)
+  box.contentWidthInfinite = parent.contentWidthInfinite
+  let parentWidth = if box.positioned:
+    parent.viewport.positioned[^1].contentWidth
+  else:
+    parent.contentWidth
+  let parentHeight = if box.positioned:
+    parent.viewport.positioned[^1].contentHeight
   else:
-    none(int)
-  result.node = builder.node
-  result.resolveDimensions(parent.contentWidth, parent.contentHeight, maxContentWidth)
+    parent.contentHeight
+  box.resolveDimensions(parentWidth, parentHeight)
+  return box
 
 proc newListItem(parent: BlockBox, builder: ListItemBoxBuilder): ListItemBox =
-  new(result)
-  result.viewport = parent.viewport
-  result.computed = builder.content.computed
-  result.positioned = builder.computed{"position"} != POSITION_STATIC
-  result.shrink = result.isShrink(parent)
-  let maxContentWidth = if result.shrink:
-    some(parent.maxContentWidth)
+  let box = ListItemBox(
+    viewport: parent.viewport,
+    computed: builder.computed,
+    positioned: builder.computed{"position"} != POSITION_STATIC,
+    node: builder.node
+  )
+  box.shrink = box.isShrink(parent)
+  box.contentWidthInfinite = parent.contentWidthInfinite
+  let parentWidth = if box.positioned:
+    parent.viewport.positioned[^1].contentWidth
   else:
-    none(int)
-  result.node = builder.node
-  result.resolveDimensions(parent.contentWidth, parent.contentHeight, maxContentWidth)
+    parent.contentWidth
+  let parentHeight = if box.positioned:
+    parent.viewport.positioned[^1].contentHeight
+  else:
+    parent.contentHeight
+  box.resolveDimensions(parentWidth, parentHeight)
+  return box
 
 proc newInlineBlock(viewport: Viewport, builder: BoxBuilder, parentWidth: int, parentHeight = none(int)): InlineBlockBox =
   new(result)
@@ -592,7 +615,7 @@ proc newInlineContext(parent: BlockBox): InlineContext =
     shrink: parent.shrink,
     contentHeight: parent.contentHeight,
     contentWidth: parent.contentWidth,
-    maxContentWidth: parent.maxContentWidth
+    contentWidthInfinite: parent.contentWidthInfinite
   )
 
 proc buildBlock(builder: BlockBoxBuilder, parent: BlockBox): BlockBox
@@ -614,7 +637,10 @@ proc applyInlineDimensions(box: BlockBox) =
   box.width = if not box.isWidthSpecified():
     # We can make the box as small/large as the content's width.
     if box.shrink:
-      min(box.width, box.maxContentWidth)
+      if box.contentWidthInfinite:
+        box.width
+      else:
+        min(box.width, box.contentWidth)
     else:
       max(box.width, box.contentWidth)
   else:
@@ -761,27 +787,14 @@ proc buildListItem(builder: ListItemBoxBuilder, parent: BlockBox): ListItemBox =
     result.marker = buildMarker(builder.marker, result)
   result.buildLayout(builder.content)
 
-proc positionAbsolute(box: BlockBox, last: BlockBox = box.viewport.root[0]) =
-  # we use the viewport's dimensions if not parentPositioned.
-  let parentPositioned = box.viewport.positioned.len > 0
-  let last = if parentPositioned:
-    box.viewport.positioned[^1]
-  else:
-    box.viewport.root[0]
+proc positionAbsolute(box: BlockBox) =
+  let last = box.viewport.positioned[^1]
   let left = box.computed{"left"}
   let right = box.computed{"right"}
   let top = box.computed{"top"}
   let bottom = box.computed{"bottom"}
-  let parentHeight = if parentPositioned:
-    last.height
-  else:
-    box.viewport.window.height_px
-  let parentWidth = if parentPositioned:
-    last.width
-  else:
-    box.viewport.window.width_px
-  #TODO TODO TODO we should use parentWidth/parentHeight for size calculations
-  # too
+  let parentHeight = last.contentHeight.get(box.viewport.window.height_px)
+  let parentWidth = last.contentWidth
   box.x_positioned = not (left.auto and right.auto)
   box.y_positioned = not (top.auto and bottom.auto)
   if not left.auto:
@@ -824,7 +837,10 @@ proc applyChildPosition(parent, child: BlockBox, spec: bool, x, y: var int, marg
     y += child.height
     parent.height += child.height
     if not spec:
-      parent.width = min(parent.maxContentWidth, max(child.width, parent.width))
+      parent.width = if parent.contentWidthInfinite:
+        max(child.width, parent.width)
+      else:
+        min(parent.contentWidth, max(child.width, parent.width))
     parent.xminwidth = max(parent.xminwidth, child.xminwidth)
     margin_todo = Strut()
     margin_todo.append(child.margin_bottom)
@@ -846,7 +862,7 @@ proc positionBlocks(box: BlockBox) =
 
   # If content width has been specified, use it.
   # Otherwise, contentWidth is just the maximum width we can take up, so
-  # set width to min(maxContentWidth, box.contentWidth)
+  # set width to box.contentWidth
   let spec = box.isWidthSpecified()
   if spec:
     box.width = box.contentWidth
@@ -916,8 +932,11 @@ proc buildTableCaption(viewport: Viewport, builder: TableCaptionBoxBuilder, maxw
   result = viewport.newFlowRootBox(builder, maxwidth, maxheight, shrink)
   result.buildLayout(builder)
 
-proc buildTableCell(viewport: Viewport, builder: TableCellBoxBuilder, parentWidth: int, parentHeight: Option[int], shrink: bool, max_width = none(int)): BlockBox =
-  result = viewport.newTableCellBox(builder, parentWidth, parentHeight, shrink, max_width)
+proc buildTableCell(viewport: Viewport, builder: TableCellBoxBuilder,
+    parentWidth: int, parentHeight: Option[int], shrink: bool,
+    contentWidthInfinite = false): BlockBox =
+  result = viewport.newTableCellBox(builder, parentWidth, parentHeight,
+    shrink, contentWidthInfinite)
   result.buildLayout(builder)
 
 proc preBuildTableRow(pctx: var TableContext, box: TableRowBoxBuilder, parent: BlockBox, i: int): RowContext =
@@ -931,11 +950,8 @@ proc preBuildTableRow(pctx: var TableContext, box: TableRowBoxBuilder, parent: B
     let rowspan = cellbuilder.computed{"-cha-rowspan"}
     let computedWidth = cellbuilder.computed{"width"}
     let spec = (not computedWidth.auto) and computedWidth.unit != UNIT_PERC
-    let max_width = if spec:
-      none(int)
-    else:
-      some(high(int))
-    let box = parent.viewport.buildTableCell(cellbuilder, parent.contentWidth, parent.contentHeight, not spec, max_width)
+    let box = parent.viewport.buildTableCell(cellbuilder, parent.contentWidth,
+        parent.contentHeight, not spec, not spec)
     let wrapper = CellWrapper(box: box, builder: cellbuilder, colspan: colspan, rowspan: rowspan, rowi: i, coli: n)
     ctx.cells[i] = wrapper
     if rowspan != 1:
@@ -1211,14 +1227,15 @@ proc buildBlock(builder: BlockBoxBuilder, parent: BlockBox): BlockBox =
   result.buildLayout(builder)
 
 # Establish a new flow-root context and build a block box.
-proc buildRootBlock(viewport: Viewport, builder: BlockBoxBuilder) =
+proc buildRootBlock(viewport: Viewport, builder: BlockBoxBuilder): BlockBox =
   let box = viewport.newFlowRootBox(builder, viewport.window.width_px, shrink = false)
-  viewport.root.add(box)
+  viewport.positioned.add(box)
   box.buildLayout(builder)
   # Normally margin-top would be used by positionBlock, but the root block
   # doesn't get positioned by the parent, so we have to do it manually here.
   #TODO this is kind of ugly.
   box.offset.y += box.margin_top
+  return box
 
 # Generation phase
 
@@ -1656,8 +1673,7 @@ proc generateTableBox(styledNode: StyledNode, viewport: Viewport, parent: var In
   box.generateTableChildWrappers()
   return box
 
-proc renderLayout*(viewport: var Viewport, root: StyledNode) =
-  viewport.root.setLen(0)
+proc renderLayout*(viewport: var Viewport, root: StyledNode): BlockBox =
   viewport.positioned.setLen(0)
   let builder = root.generateBlockBox(viewport)
-  viewport.buildRootBlock(builder)
+  return viewport.buildRootBlock(builder)
blame/dwm.1?h=1.7.1&id=72655f0ce7abe85853aca98a051df2aab07116f9'>^
df74b26 ^


1549faf ^
ba59bc8 ^
3e06ede ^


4bb89e2 ^
df74b26 ^


2b35fae ^
0c3544d ^
0e5c819 ^
4bd0d33 ^
0e5c819 ^
df74b26 ^


0e5c819 ^
4bd0d33 ^




df74b26 ^


4bd0d33 ^
0e5c819 ^
4bd0d33 ^
0e5c819 ^
df74b26 ^


0c3544d ^

dc5c070 ^

4bb89e2 ^
60b3dce ^
3e06ede ^
4bb89e2 ^




3e06ede ^
5ef6ef1 ^

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150