about summary refs log tree commit diff stats
path: root/src/layout/engine.nim
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2023-07-07 15:08:06 +0200
committerbptato <nincsnevem662@gmail.com>2023-07-07 15:08:06 +0200
commitda3c7385d1e19ba97aee0b6220d26994eef985e8 (patch)
tree6de61d36e52ad61a67e958227b75ebc3826d5830 /src/layout/engine.nim
parent47f1056fb753bff832787de707b854fc01b514a0 (diff)
downloadchawan-da3c7385d1e19ba97aee0b6220d26994eef985e8.tar.gz
layout: refactor table layout
Split it into smaller functions.
Diffstat (limited to 'src/layout/engine.nim')
-rw-r--r--src/layout/engine.nim200
1 files changed, 108 insertions, 92 deletions
diff --git a/src/layout/engine.nim b/src/layout/engine.nim
index 2f926148..144f9102 100644
--- a/src/layout/engine.nim
+++ b/src/layout/engine.nim
@@ -1104,6 +1104,19 @@ iterator rows(builder: TableBoxBuilder): BoxBuilder {.inline.} =
   for child in footer:
     yield child
 
+proc preBuildTableRows(ctx: var TableContext, builder: TableBoxBuilder,
+    table: BlockBox) =
+  var i = 0
+  for row in builder.rows:
+    if unlikely(row.computed{"display"} == DISPLAY_TABLE_CAPTION):
+      ctx.caption = TableCaptionBoxBuilder(row)
+    else:
+      let row = TableRowBoxBuilder(row)
+      let rctx = ctx.preBuildTableRow(row, table, i)
+      ctx.rows.add(rctx)
+      ctx.maxwidth = max(rctx.width, ctx.maxwidth)
+      inc i
+
 proc calcUnspecifiedColIndices(ctx: var TableContext, W: var LayoutUnit,
     weight: var float64): seq[int] =
   # Spacing for each column:
@@ -1127,76 +1140,49 @@ proc calcUnspecifiedColIndices(ctx: var TableContext, W: var LayoutUnit,
     inc i
   return avail
 
-# 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)
-# 3. Calculate the maximum preferred row width. If this is
-# a) less than the specified table width, or
-# b) greater than the table's content width:
-#      Distribute the table's content width among cells with an unspecified
-#      width. If this would give any cell a width < min_width, set that
-#      cell's width to min_width, then re-do the distribution.
-proc buildTableLayout(table: BlockBox, builder: TableBoxBuilder) =
-  var ctx = TableContext(
-    collapse: table.computed{"border-collapse"} == BORDER_COLLAPSE_COLLAPSE
-  )
-  if not ctx.collapse:
-    ctx.inlinespacing = table.computed{"border-spacing"}.a.px(table.viewport)
-    ctx.blockspacing = table.computed{"border-spacing"}.b.px(table.viewport)
-  var i = 0
-  for row in builder.rows:
-    if unlikely(row.computed{"display"} == DISPLAY_TABLE_CAPTION):
-      ctx.caption = TableCaptionBoxBuilder(row)
-    else:
-      let row = TableRowBoxBuilder(row)
-      let rctx = ctx.preBuildTableRow(row, table, i)
-      ctx.rows.add(rctx)
-      ctx.maxwidth = max(rctx.width, ctx.maxwidth)
-      inc i
-  let spec = table.computed{"width"}.auto
-  var reflow = newSeq[bool](ctx.cols.len)
-  if (table.contentWidth > ctx.maxwidth and (not table.shrink or not spec)) or
-      table.contentWidth < ctx.maxwidth:
-    var W = table.contentWidth
-    # Remove inline spacing from distributable width.
-    W -= ctx.cols.len * ctx.inlinespacing * 2
-    var weight: float64
-    var avail = ctx.calcUnspecifiedColIndices(W, weight)
-    var redo = true
-    while redo and avail.len > 0 and weight != 0:
-      if weight == 0: break # zero weight; nothing to distribute
-      if W < 0:
-        W = 0
-      redo = false
-      # divide delta width by sum of sqrt(width) for all elem in avail
-      let unit = toFloat64(W) / weight
-      weight = 0
-      for i in countdown(avail.high, 0):
-        let j = avail[i]
-        let x = unit * ctx.cols[j].weight
-        let mw = ctx.cols[j].minwidth
-        ctx.cols[j].width = x
-        if mw > x:
-          W -= mw
-          ctx.cols[j].width = mw
-          avail.del(i)
-          redo = true
-        else:
-          weight += ctx.cols[j].weight
-        reflow[j] = true
-  for col in ctx.cols:
-    table.width += col.width
+proc redistributeWidth(ctx: var TableContext, table: BlockBox) =
+  var W = table.contentWidth
+  # Remove inline spacing from distributable width.
+  W -= ctx.cols.len * ctx.inlinespacing * 2
+  var weight: float64
+  var avail = ctx.calcUnspecifiedColIndices(W, weight)
+  var redo = true
+  while redo and avail.len > 0 and weight != 0:
+    if weight == 0: break # zero weight; nothing to distribute
+    if W < 0:
+      W = 0
+    redo = false
+    # divide delta width by sum of sqrt(width) for all elem in avail
+    let unit = toFloat64(W) / weight
+    weight = 0
+    for i in countdown(avail.high, 0):
+      let j = avail[i]
+      let x = unit * ctx.cols[j].weight
+      let mw = ctx.cols[j].minwidth
+      ctx.cols[j].width = x
+      if mw > x:
+        W -= mw
+        ctx.cols[j].width = mw
+        avail.del(i)
+        redo = true
+      else:
+        weight += ctx.cols[j].weight
+      ctx.reflow[j] = true
+
+proc reflowTableCells(ctx: var TableContext) =
   for i in countdown(ctx.rows.high, 0):
     var row = addr ctx.rows[i]
     var n = ctx.cols.len - 1
     for j in countdown(row.cells.high, 0):
       let m = n - row.cells[j].colspan
       while n > m:
-        if reflow[n]:
+        if ctx.reflow[n]:
           row.cells[j].reflow = true
         if n < row.reflow.len and row.reflow[n]:
-          reflow[n] = true
+          ctx.reflow[n] = true
         dec n
+
+proc buildTableRows(ctx: TableContext, table: BlockBox) =
   var y: LayoutUnit = 0
   for roww in ctx.rows:
     if roww.builder.computed{"visibility"} == VISIBILITY_COLLAPSE:
@@ -1212,38 +1198,68 @@ proc buildTableLayout(table: BlockBox, builder: TableBoxBuilder) =
     table.nested.add(row)
     table.width = max(row.width, table.width)
   table.height = table.contentHeight.get(y)
+
+proc addTableCaption(ctx: TableContext, table: BlockBox) =
+  case ctx.caption.computed{"caption-side"}
+  of CAPTION_SIDE_TOP, CAPTION_SIDE_BLOCK_START:
+    let caption = table.viewport.buildTableCaption(ctx.caption, table.width,
+      none(LayoutUnit), false)
+    for r in table.nested:
+      r.offset.y += caption.height
+    table.nested.insert(caption, 0)
+    table.height += caption.height
+    table.width = max(table.width, caption.width)
+  of CAPTION_SIDE_BOTTOM, CAPTION_SIDE_BLOCK_END:
+    let caption = table.viewport.buildTableCaption(ctx.caption, table.width,
+      none(LayoutUnit), false)
+    caption.offset.y += table.width
+    table.nested.add(caption)
+    table.height += caption.height
+    table.width = max(table.width, caption.width)
+  of CAPTION_SIDE_LEFT, CAPTION_SIDE_INLINE_START:
+    let caption = table.viewport.buildTableCaption(ctx.caption,
+      table.contentWidth, some(table.height), true)
+    for r in table.nested:
+      r.offset.x += caption.width
+    table.nested.insert(caption, 0)
+    table.width += caption.width
+    table.height = max(table.height, caption.height)
+  of CAPTION_SIDE_RIGHT, CAPTION_SIDE_INLINE_END:
+    let caption = table.viewport.buildTableCaption(ctx.caption,
+      table.contentWidth, some(table.height), true)
+    caption.offset.x += table.width
+    table.nested.add(caption)
+    table.width += caption.width
+    table.height = max(table.height, caption.height)
+
+# 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)
+# 3. Calculate the maximum preferred row width. If this is
+# a) less than the specified table width, or
+# b) greater than the table's content width:
+#      Distribute the table's content width among cells with an unspecified
+#      width. If this would give any cell a width < min_width, set that
+#      cell's width to min_width, then re-do the distribution.
+proc buildTableLayout(table: BlockBox, builder: TableBoxBuilder) =
+  var ctx = TableContext(
+    collapse: table.computed{"border-collapse"} == BORDER_COLLAPSE_COLLAPSE
+  )
+  if not ctx.collapse:
+    ctx.inlinespacing = table.computed{"border-spacing"}.a.px(table.viewport)
+    ctx.blockspacing = table.computed{"border-spacing"}.b.px(table.viewport)
+  ctx.preBuildTableRows(builder, table)
+  let spec = table.computed{"width"}.auto
+  ctx.reflow = newSeq[bool](ctx.cols.len)
+  if (table.contentWidth > ctx.maxwidth and (not table.shrink or not spec)) or
+      table.contentWidth < ctx.maxwidth:
+    ctx.redistributeWidth(table)
+  for col in ctx.cols:
+    table.width += col.width
+  ctx.reflowTableCells()
+  ctx.buildTableRows(table)
   if ctx.caption != nil:
-    case ctx.caption.computed{"caption-side"}
-    of CAPTION_SIDE_TOP, CAPTION_SIDE_BLOCK_START:
-      let caption = table.viewport.buildTableCaption(ctx.caption, table.width,
-        none(LayoutUnit), false)
-      for r in table.nested:
-        r.offset.y += caption.height
-      table.nested.insert(caption, 0)
-      table.height += caption.height
-      table.width = max(table.width, caption.width)
-    of CAPTION_SIDE_BOTTOM, CAPTION_SIDE_BLOCK_END:
-      let caption = table.viewport.buildTableCaption(ctx.caption, table.width,
-        none(LayoutUnit), false)
-      caption.offset.y += table.width
-      table.nested.add(caption)
-      table.height += caption.height
-      table.width = max(table.width, caption.width)
-    of CAPTION_SIDE_LEFT, CAPTION_SIDE_INLINE_START:
-      let caption = table.viewport.buildTableCaption(ctx.caption,
-        table.contentWidth, some(table.height), true)
-      for r in table.nested:
-        r.offset.x += caption.width
-      table.nested.insert(caption, 0)
-      table.width += caption.width
-      table.height = max(table.height, caption.height)
-    of CAPTION_SIDE_RIGHT, CAPTION_SIDE_INLINE_END:
-      let caption = table.viewport.buildTableCaption(ctx.caption,
-        table.contentWidth, some(table.height), true)
-      caption.offset.x += table.width
-      table.nested.add(caption)
-      table.width += caption.width
-      table.height = max(table.height, caption.height)
+    ctx.addTableCaption(table)
 
 proc buildTable(builder: TableBoxBuilder, parent: BlockBox): BlockBox =
   let table = parent.newBlockBox(builder)