diff options
author | bptato <nincsnevem662@gmail.com> | 2024-05-24 20:50:42 +0200 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2024-05-24 20:57:48 +0200 |
commit | aca410c44a17146977f17ee12b7afbe99bd4ab2b (patch) | |
tree | 125ebda38d55835340bc950f678beb26d013126a /src | |
parent | 444a7084e7cb0b458c5cee47283b46ad0f38a959 (diff) | |
download | chawan-aca410c44a17146977f17ee12b7afbe99bd4ab2b.tar.gz |
layout: add wrapper box for table caption + misc stuff
Captions are no longer positioned inside tables, yay. Also, misc: * rename some things for consistency * clamp out of bounds rgb() values * remove inherited property lookup table
Diffstat (limited to 'src')
-rw-r--r-- | src/css/cssvalues.nim | 50 | ||||
-rw-r--r-- | src/layout/engine.nim | 247 |
2 files changed, 176 insertions, 121 deletions
diff --git a/src/css/cssvalues.nim b/src/css/cssvalues.nim index ec51e5de..010634be 100644 --- a/src/css/cssvalues.nim +++ b/src/css/cssvalues.nim @@ -160,6 +160,9 @@ type DisplayFlowRoot = "flow-root" DisplayFlex = "flex" DisplayInlineFlex = "inline-flex" + # internal, for layout + DisplayTableWrapper = "" + DisplayInlineTableWrapper = "" CSSWhiteSpace* = enum WhitespaceNormal = "normal" @@ -488,15 +491,6 @@ const InheritedProperties = { cptQuotes, cptVisibility, cptTextTransform } -func getPropInheritedArray(): array[CSSPropertyType, bool] = - for prop in CSSPropertyType: - if prop in InheritedProperties: - result[prop] = true - else: - result[prop] = false - -const InheritedArray = getPropInheritedArray() - func shorthandType(s: string): CSSShorthandType = return ShorthandNames.getOrDefault(s.toLowerAscii(), cstNone) @@ -582,7 +576,7 @@ macro `{}=`*(vals: CSSComputedValues; s: static string, val: typed) = ) func inherited(t: CSSPropertyType): bool = - return InheritedArray[t] + return t in InheritedProperties func em_to_px(em: float64; window: WindowAttributes): LayoutUnit = (em * float64(window.ppl)).toLayoutUnit() @@ -622,7 +616,7 @@ func px*(l: CSSLength; window: WindowAttributes; p: LayoutUnit): LayoutUnit func blockify*(display: CSSDisplay): CSSDisplay = case display of DisplayBlock, DisplayTable, DisplayListItem, DisplayNone, DisplayFlowRoot, - DisplayFlex: + DisplayFlex, DisplayTableWrapper, DisplayInlineTableWrapper: #TODO grid return display of DisplayInline, DisplayInlineBlock, DisplayTableRow, @@ -901,14 +895,14 @@ func parseARGB(value: openArray[CSSComponentValue]): Opt[CellColor] = check_err slash value.skipWhitespace(i) check_err false - let r = CSSToken(value[i]).nvalue + let r = clamp(CSSToken(value[i]).nvalue, 0, 255) next_value true - let g = CSSToken(value[i]).nvalue + let g = clamp(CSSToken(value[i]).nvalue, 0, 255) next_value - let b = CSSToken(value[i]).nvalue + let b = clamp(CSSToken(value[i]).nvalue, 0, 255) next_value false, true let a = if i < value.len: - CSSToken(value[i]).nvalue + clamp(CSSToken(value[i]).nvalue, 0, 1) else: 1 value.skipWhitespace(i) @@ -1602,6 +1596,32 @@ func rootProperties*(): CSSComputedValues = for prop in CSSPropertyType: result[prop] = getDefault(prop) +# Separate CSSComputedValues of a table into those of the wrapper and the actual +# table. +func splitTable*(computed: CSSComputedValues): + tuple[outerComputed, innnerComputed: CSSComputedValues] = + var outerComputed, innerComputed: CSSComputedValues + new(outerComputed) + new(innerComputed) + const props = { + cptPosition, cptFloat, cptMarginLeft, cptMarginRight, cptMarginTop, + cptMarginBottom, cptTop, cptRight, cptBottom, cptLeft, + # Note: the standard does not ask us to include padding or sizing, but the + # wrapper & actual table layouts share the same sizing from the wrapper, + # so we must add them here. + cptPaddingLeft, cptPaddingRight, cptPaddingTop, cptPaddingBottom, + cptWidth, cptHeight, cptBoxSizing + } + for prop in CSSPropertyType: + if prop in props: + outerComputed[prop] = computed[prop] + innerComputed[prop] = getDefault(prop) + else: + outerComputed[prop] = getDefault(prop) + innerComputed[prop] = computed[prop] + outerComputed{"display"} = computed{"display"} + return (outerComputed, innerComputed) + func hasValues*(builder: CSSComputedValuesBuilder): bool = for origin in CSSOrigin: if builder.normalProperties[origin].len > 0: diff --git a/src/layout/engine.nim b/src/layout/engine.nim index 179714cd..3a20eb44 100644 --- a/src/layout/engine.nim +++ b/src/layout/engine.nim @@ -1182,8 +1182,8 @@ proc layoutRootInline(bctx: var BlockContext; inlines: seq[BoxBuilder]; offset, bfcOffset: Offset): RootInlineFragment proc layoutBlock(bctx: var BlockContext; box: BlockBox; builder: BlockBoxBuilder; sizes: ResolvedSizes) -proc layoutTable(bctx: BlockContext; table: BlockBox; builder: BlockBoxBuilder; - sizes: ResolvedSizes) +proc layoutTableWrapper(bctx: BlockContext; box: BlockBox; + builder: BlockBoxBuilder; sizes: ResolvedSizes) proc layoutFlex(bctx: var BlockContext; box: BlockBox; builder: BlockBoxBuilder; sizes: ResolvedSizes) proc layoutInline(ictx: var InlineContext; box: InlineBoxBuilder): @@ -1448,8 +1448,8 @@ proc addInlineBlock(ictx: var InlineContext; state: var InlineState; case builder.computed{"display"} of DisplayInlineBlock: bctx.layoutFlow(box, builder, sizes) - of DisplayInlineTable: - bctx.layoutTable(box, builder, sizes) + of DisplayInlineTableWrapper: + bctx.layoutTableWrapper(box, builder, sizes) of DisplayInlineFlex: bctx.layoutFlex(box, builder, sizes) else: @@ -1615,9 +1615,9 @@ proc positionRelative(parent, box: BlockBox) = elif not box.computed{"bottom"}.auto: box.offset.y += parent.size.h - box.positioned.bottom - box.size.h +# Note: caption is not included here const ProperTableChild = RowGroupBox + { - DisplayTableRow, DisplayTableColumn, DisplayTableColumnGroup, - DisplayTableCaption + DisplayTableRow, DisplayTableColumn, DisplayTableColumnGroup } const ProperTableRowParent = RowGroupBox + { DisplayTable, DisplayInlineTable @@ -1654,7 +1654,6 @@ type TableContext = object lctx: LayoutState - caption: BlockBoxBuilder rows: seq[RowContext] cols: seq[ColumnContext] growing: seq[CellWrapper] @@ -1833,7 +1832,7 @@ proc alignTableCell(cell: BlockBox; availableHeight, baseline: LayoutUnit) = else: cell.offset.y = baseline - cell.firstBaseline -proc layoutTableRow(pctx: TableContext; ctx: RowContext; parent: BlockBox; +proc layoutTableRow(tctx: TableContext; ctx: RowContext; parent: BlockBox; builder: BlockBoxBuilder): BlockBox = var x: LayoutUnit = 0 var n = 0 @@ -1851,9 +1850,9 @@ proc layoutTableRow(pctx: TableContext; ctx: RowContext; parent: BlockBox; for cellw in ctx.cells: var w: LayoutUnit = 0 for i in n ..< n + cellw.colspan: - w += pctx.cols[i].width + w += tctx.cols[i].width # Add inline spacing for merged columns. - w += pctx.inlineSpacing * (cellw.colspan - 1) * 2 + w += tctx.inlineSpacing * (cellw.colspan - 1) * 2 if cellw.reflow and cellw.builder != nil: # Do not allow the table cell to make use of its specified width. # e.g. in the following table @@ -1867,13 +1866,13 @@ proc layoutTableRow(pctx: TableContext; ctx: RowContext; parent: BlockBox; # </TABLE> # the TD with a width of 5ch should be 9ch wide as well. let space = availableSpace(w = stretch(w), h = maxContent()) - cellw.box = pctx.lctx.layoutTableCell(cellw.builder, space) + cellw.box = tctx.lctx.layoutTableCell(cellw.builder, space) w = max(w, cellw.box.size.w) let cell = cellw.box - x += pctx.inlineSpacing + x += tctx.inlineSpacing if cell != nil: cell.offset.x += x - x += pctx.inlineSpacing + x += tctx.inlineSpacing x += w n += cellw.colspan const HasNoBaseline = { @@ -1905,14 +1904,14 @@ proc layoutTableRow(pctx: TableContext; ctx: RowContext; parent: BlockBox; row.size.w = x return row -proc preBuildTableRows(ctx: var TableContext; rows: seq[BlockBoxBuilder]; +proc preLayoutTableRows(tctx: var TableContext; rows: seq[BlockBoxBuilder]; table: BlockBox) = for i, row in rows: - let rctx = ctx.preBuildTableRow(row, table, i, rows.len) - ctx.rows.add(rctx) - ctx.maxwidth = max(rctx.width, ctx.maxwidth) + let rctx = tctx.preBuildTableRow(row, table, i, rows.len) + tctx.rows.add(rctx) + tctx.maxwidth = max(rctx.width, tctx.maxwidth) -proc preBuildTableRows(ctx: var TableContext; builder: BlockBoxBuilder; +proc preLayoutTableRows(tctx: var TableContext; builder: BlockBoxBuilder; table: BlockBox) = # Use separate seqs for different row groups, so that e.g. this HTML: # echo '<TABLE><TBODY><TR><TD>world<THEAD><TR><TD>hello'|cha -T text/html @@ -1922,7 +1921,6 @@ proc preBuildTableRows(ctx: var TableContext; builder: BlockBoxBuilder; var thead: seq[BlockBoxBuilder] = @[] var tbody: seq[BlockBoxBuilder] = @[] var tfoot: seq[BlockBoxBuilder] = @[] - var caption: BlockBoxBuilder = nil for child in builder.children: assert child.computed{"display"} in ProperTableChild case child.computed{"display"} @@ -1940,19 +1938,15 @@ proc preBuildTableRows(ctx: var TableContext; builder: BlockBoxBuilder; for child in child.children: assert child.computed{"display"} == DisplayTableRow tfoot.add(BlockBoxBuilder(child)) - of DisplayTableCaption: - if caption == nil: - caption = BlockBoxBuilder(child) else: discard - ctx.caption = caption - ctx.preBuildTableRows(thead, table) - ctx.preBuildTableRows(tbody, table) - ctx.preBuildTableRows(tfoot, table) + tctx.preLayoutTableRows(thead, table) + tctx.preLayoutTableRows(tbody, table) + tctx.preLayoutTableRows(tfoot, table) -func calcSpecifiedRatio(ctx: TableContext; W: LayoutUnit): LayoutUnit = +func calcSpecifiedRatio(tctx: TableContext; W: LayoutUnit): LayoutUnit = var totalSpecified: LayoutUnit = 0 var hasUnspecified = false - for col in ctx.cols: + for col in tctx.cols: if col.wspecified: totalSpecified += col.width else: @@ -1964,13 +1958,13 @@ func calcSpecifiedRatio(ctx: TableContext; W: LayoutUnit): LayoutUnit = return 1 return W / totalSpecified -proc calcUnspecifiedColIndices(ctx: var TableContext; W: var LayoutUnit; +proc calcUnspecifiedColIndices(tctx: var TableContext; W: var LayoutUnit; weight: var float64): seq[int] = - let specifiedRatio = ctx.calcSpecifiedRatio(W) + let specifiedRatio = tctx.calcSpecifiedRatio(W) # Spacing for each column: - var avail = newSeqUninitialized[int](ctx.cols.len) + var avail = newSeqUninitialized[int](tctx.cols.len) var j = 0 - for i, col in ctx.cols.mpairs: + for i, col in tctx.cols.mpairs: if not col.wspecified: avail[j] = i let w = if col.width < W: @@ -1988,21 +1982,22 @@ proc calcUnspecifiedColIndices(ctx: var TableContext; W: var LayoutUnit; avail.del(j) return avail -func needsRedistribution(ctx: TableContext; computed: CSSComputedValues): bool = - case ctx.space.w.t +func needsRedistribution(tctx: TableContext; computed: CSSComputedValues): + bool = + case tctx.space.w.t of scMinContent, scMaxContent: return false of scStretch: - return ctx.space.w.u != ctx.maxwidth + return tctx.space.w.u != tctx.maxwidth of scFitContent: - let u = ctx.space.w.u - return u > ctx.maxwidth and not computed{"width"}.auto or u < ctx.maxwidth + let u = tctx.space.w.u + return u > tctx.maxwidth and not computed{"width"}.auto or u < tctx.maxwidth -proc redistributeWidth(ctx: var TableContext) = +proc redistributeWidth(tctx: var TableContext) = # Remove inline spacing from distributable width. - var W = ctx.space.w.u - ctx.cols.len * ctx.inlineSpacing * 2 + var W = tctx.space.w.u - tctx.cols.len * tctx.inlineSpacing * 2 var weight = 0f64 - var avail = ctx.calcUnspecifiedColIndices(W, weight) + var avail = tctx.calcUnspecifiedColIndices(W, weight) var redo = true while redo and avail.len > 0 and weight != 0: if weight == 0: break # zero weight; nothing to distribute @@ -2014,73 +2009,74 @@ proc redistributeWidth(ctx: var TableContext) = weight = 0 for i in countdown(avail.high, 0): let j = avail[i] - let x = (unit * ctx.cols[j].weight).toLayoutUnit() - let mw = ctx.cols[j].minwidth - ctx.cols[j].width = x + let x = (unit * tctx.cols[j].weight).toLayoutUnit() + let mw = tctx.cols[j].minwidth + tctx.cols[j].width = x if mw > x: W -= mw - ctx.cols[j].width = mw + tctx.cols[j].width = mw avail.del(i) redo = true else: - weight += ctx.cols[j].weight - ctx.cols[j].reflow = true + weight += tctx.cols[j].weight + tctx.cols[j].reflow = 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 +proc reflowTableCells(tctx: var TableContext) = + for i in countdown(tctx.rows.high, 0): + var row = addr tctx.rows[i] + var n = tctx.cols.len - 1 for j in countdown(row.cells.high, 0): let m = n - row.cells[j].colspan while n > m: - if ctx.cols[n].reflow: + if tctx.cols[n].reflow: row.cells[j].reflow = true if n < row.reflow.len and row.reflow[n]: - ctx.cols[n].reflow = true + tctx.cols[n].reflow = true dec n -proc layoutTableRows(ctx: TableContext; table: BlockBox; sizes: ResolvedSizes) = +proc layoutTableRows(tctx: TableContext; table: BlockBox; + sizes: ResolvedSizes) = var y: LayoutUnit = 0 - for roww in ctx.rows: + for roww in tctx.rows: if roww.builder.computed{"visibility"} == VisibilityCollapse: continue - y += ctx.blockSpacing - let row = ctx.layoutTableRow(roww, table, roww.builder) + y += tctx.blockSpacing + let row = tctx.layoutTableRow(roww, table, roww.builder) row.offset.y += y row.offset.x += sizes.padding.left row.size.w += sizes.padding.left row.size.w += sizes.padding.right - y += ctx.blockSpacing + y += tctx.blockSpacing y += row.size.h table.nested.add(row) table.size.w = max(row.size.w, table.size.w) table.size.h = applySizeConstraint(y, sizes.space.h) -proc addTableCaption(ctx: TableContext; table: BlockBox) = - let percHeight = ctx.space.h.toPercSize() - let space = availableSpace(w = stretch(table.size.w), h = maxContent()) - let builder = ctx.caption - let sizes = ctx.lctx.resolveSizes(space, percHeight, builder.computed) +proc layoutCaption(tctx: TableContext; parent: BlockBox; + builder: BlockBoxBuilder) = + let percHeight = tctx.space.h.toPercSize() + let space = availableSpace(w = stretch(parent.size.w), h = maxContent()) + let sizes = tctx.lctx.resolveSizes(space, percHeight, builder.computed) let box = BlockBox( computed: builder.computed, node: builder.node, margin: sizes.margin, positioned: sizes.positioned ) - var bctx = BlockContext(lctx: ctx.lctx) + var bctx = BlockContext(lctx: tctx.lctx) bctx.layoutFlow(box, builder, sizes) assert bctx.unpositionedFloats.len == 0 let outerHeight = box.offset.y + box.size.h + bctx.marginTodo.sum() - table.size.h += outerHeight - table.size.w = max(table.size.w, box.size.w) + parent.size.h += outerHeight + parent.size.w = max(parent.size.w, box.size.w) case builder.computed{"caption-side"} of CaptionSideTop, CaptionSideBlockStart: - for r in table.nested: + for r in parent.nested: r.offset.y += outerHeight - table.nested.insert(box, 0) + parent.nested.insert(box, 0) of CaptionSideBottom, CaptionSideBlockEnd: box.offset.y += outerHeight - table.nested.add(box) + parent.nested.add(box) # Table layout. We try to emulate w3m's behavior here: # 1. Calculate minimum and preferred width of each column @@ -2091,22 +2087,35 @@ proc addTableCaption(ctx: TableContext; table: BlockBox) = # 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 layoutTable(bctx: BlockContext; table: BlockBox; builder: BlockBoxBuilder; - sizes: ResolvedSizes) = - let lctx = bctx.lctx - var ctx = TableContext(lctx: lctx, space: sizes.space) +proc layoutTable(tctx: var TableContext; table: BlockBox; + builder: BlockBoxBuilder; sizes: ResolvedSizes) = + let lctx = tctx.lctx if table.computed{"border-collapse"} != BorderCollapseCollapse: - ctx.inlineSpacing = table.computed{"border-spacing"}.a.px(lctx) - ctx.blockSpacing = table.computed{"border-spacing"}.b.px(lctx) - ctx.preBuildTableRows(builder, table) - if ctx.needsRedistribution(table.computed): - ctx.redistributeWidth() - for col in ctx.cols: + tctx.inlineSpacing = table.computed{"border-spacing"}.a.px(lctx) + tctx.blockSpacing = table.computed{"border-spacing"}.b.px(lctx) + tctx.preLayoutTableRows(builder, table) # first pass + if tctx.needsRedistribution(table.computed): + tctx.redistributeWidth() + for col in tctx.cols: table.size.w += col.width - ctx.reflowTableCells() - ctx.layoutTableRows(table, sizes) - if ctx.caption != nil: - ctx.addTableCaption(table) + tctx.reflowTableCells() + tctx.layoutTableRows(table, sizes) # second pass + +# As per standard, we must put the caption outside the actual table, inside a +# block-level wrapper box. +proc layoutTableWrapper(bctx: BlockContext; box: BlockBox; + builder: BlockBoxBuilder; sizes: ResolvedSizes) = + let tableBuilder = BlockBoxBuilder(builder.children[0]) + let table = BlockBox(computed: tableBuilder.computed, node: tableBuilder.node) + var tctx = TableContext(lctx: bctx.lctx, space: sizes.space) + tctx.layoutTable(table, tableBuilder, sizes) + box.nested.add(table) + box.size = table.size + if builder.children.len > 1: + # do it here, so that caption's box can stretch to our width + let caption = BlockBoxBuilder(builder.children[1]) + #TODO also count caption width in table width + tctx.layoutCaption(box, caption) proc postAlignChild(box, child: BlockBox; width: LayoutUnit) = case box.computed{"text-align"} @@ -2122,16 +2131,16 @@ proc postAlignChild(box, child: BlockBox; width: LayoutUnit) = proc layout(bctx: var BlockContext; box: BlockBox; builder: BoxBuilder; sizes: ResolvedSizes) = case builder.computed{"display"} - of DisplayBlock, DisplayFlowRoot: + of DisplayBlock, DisplayFlowRoot, DisplayTableCaption: bctx.layoutFlow(box, BlockBoxBuilder(builder), sizes) of DisplayListItem: bctx.layoutListItem(box, ListItemBoxBuilder(builder), sizes) - of DisplayTable: - bctx.layoutTable(box, BlockBoxBuilder(builder), sizes) + of DisplayTableWrapper: + bctx.layoutTableWrapper(box, BlockBoxBuilder(builder), sizes) of DisplayFlex: bctx.layoutFlex(box, BlockBoxBuilder(builder), sizes) else: - assert false, "builder.t is " & $builder.computed{"display"} + assert false, "Unexpected layout display " & $builder.computed{"display"} proc layoutFlexChild(lctx: LayoutState; builder: BoxBuilder; sizes: ResolvedSizes): BlockBox = @@ -2330,7 +2339,7 @@ proc layoutBlockChild(bctx: var BlockContext; builder: BoxBuilder; w = space.w, h = maxContent() #TODO fit-content when clip ) - if builder.computed{"display"} == DisplayTable: + if builder.computed{"display"} == DisplayTableWrapper: space.w = fitContent(space.w) let sizes = bctx.lctx.resolveSizes(space, percHeight, builder.computed) if appendMargins: @@ -2400,7 +2409,8 @@ func isParentResolved(state: BlockState; bctx: BlockContext): bool = func establishesBFC(computed: CSSComputedValues): bool = return computed{"float"} != FloatNone or computed{"position"} == PositionAbsolute or - computed{"display"} in {DisplayFlowRoot, DisplayTable, DisplayFlex} or + computed{"display"} in {DisplayFlowRoot, DisplayTable, DisplayTableWrapper, + DisplayFlex} or computed{"overflow"} notin {OverflowVisible, OverflowClip} #TODO contain, grid, multicol, column-span @@ -2647,7 +2657,7 @@ type InnerBlockContext = object ibox: InlineBoxBuilder iroot: InlineBoxBuilder anonRow: BlockBoxBuilder - anonTable: BlockBoxBuilder + anonTableWrapper: BlockBoxBuilder quoteLevel: int listItemCounter: int listItemReset: bool @@ -2706,11 +2716,23 @@ proc generateFlexBox(pctx: var InnerBlockContext; styledNode: StyledNode): BlockBoxBuilder = return generateFlexBox(styledNode, pctx.lctx, addr pctx) +func toTableWrapper(display: CSSDisplay): CSSDisplay = + if display == DisplayTable: + return DisplayTableWrapper + assert display == DisplayInlineTable + return DisplayInlineTableWrapper + proc createAnonTable(ctx: var InnerBlockContext; computed: CSSComputedValues) = - if ctx.anonTable == nil: - var wrappervals = computed.inheritProperties() - wrappervals{"display"} = DisplayTable - ctx.anonTable = BlockBoxBuilder(computed: wrappervals) + if ctx.anonTableWrapper == nil: + let inherited = computed.inheritProperties() + let (outerComputed, innerComputed) = inherited.splitTable() + #TODO this should be DisplayInlineTableWrapper inside inline contexts + outerComputed{"display"} = DisplayTableWrapper + let innerTable = BlockBoxBuilder(computed: innerComputed) + ctx.anonTableWrapper = BlockBoxBuilder( + computed: outerComputed, + children: @[BoxBuilder(innerTable)] + ) proc flushTableRow(ctx: var InnerBlockContext) = if ctx.anonRow != nil: @@ -2718,13 +2740,13 @@ proc flushTableRow(ctx: var InnerBlockContext) = ctx.blockgroup.parent.children.add(ctx.anonRow) else: ctx.createAnonTable(ctx.styledNode.computed) - ctx.anonTable.children.add(ctx.anonRow) + ctx.anonTableWrapper.children[0].children.add(ctx.anonRow) ctx.anonRow = nil proc flushTable(ctx: var InnerBlockContext) = ctx.flushTableRow() - if ctx.anonTable != nil: - ctx.blockgroup.parent.children.add(ctx.anonTable) + if ctx.anonTableWrapper != nil: + ctx.blockgroup.parent.children.add(ctx.anonTableWrapper) proc iflush(ctx: var InnerBlockContext) = if ctx.iroot != nil: @@ -2824,7 +2846,7 @@ proc generateFromElem(ctx: var InnerBlockContext; styledNode: StyledNode) = box.children.add(childbox) else: ctx.createAnonTable(box.computed) - ctx.anonTable.children.add(childbox) + ctx.anonTableWrapper.children[0].children.add(childbox) of DisplayTableRowGroup, DisplayTableHeaderGroup, DisplayTableFooterGroup: ctx.bflush() ctx.flushTableRow() @@ -2833,7 +2855,7 @@ proc generateFromElem(ctx: var InnerBlockContext; styledNode: StyledNode) = box.children.add(childbox) else: ctx.createAnonTable(box.computed) - ctx.anonTable.children.add(childbox) + ctx.anonTableWrapper.children[0].children.add(childbox) of DisplayTableCell: ctx.bflush() let childbox = styledNode.generateTableCellBox(ctx.lctx, ctx) @@ -2853,12 +2875,16 @@ proc generateFromElem(ctx: var InnerBlockContext; styledNode: StyledNode) = box.children.add(childbox) else: ctx.createAnonTable(box.computed) - ctx.anonTable.children.add(childbox) + # only add first caption + if ctx.anonTableWrapper.children.len == 1: + ctx.anonTableWrapper.children.add(childbox) of DisplayTableColumn: discard #TODO of DisplayTableColumnGroup: discard #TODO of DisplayNone: discard + of DisplayTableWrapper, DisplayInlineTableWrapper: + assert false proc generateAnonymousInlineText(ctx: var InnerBlockContext; text: string; styledNode: StyledNode; bmp: Bitmap = nil) = @@ -3098,27 +3124,36 @@ proc generateTableCaptionBox(styledNode: StyledNode; lctx: LayoutState; ctx.flush() return box -proc generateTableChildWrappers(box: BlockBoxBuilder) = - var newchildren = newSeqOfCap[BoxBuilder](box.children.len) +proc generateTableChildWrappers(box: BlockBoxBuilder; + computed: CSSComputedValues) = + let innerTable = BlockBoxBuilder(computed: computed, node: box.node) var wrappervals = box.computed.inheritProperties() wrappervals{"display"} = DisplayTableRow + var caption: BoxBuilder = nil for child in box.children: if child.computed{"display"} in ProperTableChild: - newchildren.add(child) + innerTable.children.add(child) + elif child.computed{"display"} == DisplayTableCaption: + if caption == nil: + caption = child else: let wrapper = BlockBoxBuilder(computed: wrappervals) wrapper.children.add(child) wrapper.generateTableRowChildWrappers() - newchildren.add(wrapper) - box.children = newchildren + innerTable.children.add(wrapper) + box.children = @[BoxBuilder(innerTable)] + if caption != nil: + box.children.add(caption) proc generateTableBox(styledNode: StyledNode; lctx: LayoutState; parent: var InnerBlockContext): BlockBoxBuilder = - let box = BlockBoxBuilder(computed: styledNode.computed, node: styledNode) + let (outerComputed, innerComputed) = styledNode.computed.splitTable() + let box = BlockBoxBuilder(computed: outerComputed, node: styledNode) var ctx = newInnerBlockContext(styledNode, box, lctx, addr parent) ctx.generateInnerBlockBox() ctx.flush() - box.generateTableChildWrappers() + outerComputed{"display"} = outerComputed{"display"}.toTableWrapper() + box.generateTableChildWrappers(innerComputed) return box proc layout*(root: StyledNode; attrsp: ptr WindowAttributes): BlockBox = |