diff options
author | bptato <nincsnevem662@gmail.com> | 2023-07-07 15:08:06 +0200 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2023-07-07 15:08:06 +0200 |
commit | da3c7385d1e19ba97aee0b6220d26994eef985e8 (patch) | |
tree | 6de61d36e52ad61a67e958227b75ebc3826d5830 /src/layout/engine.nim | |
parent | 47f1056fb753bff832787de707b854fc01b514a0 (diff) | |
download | chawan-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.nim | 200 |
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) |