diff options
author | bptato <nincsnevem662@gmail.com> | 2024-03-25 15:05:52 +0100 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2024-03-25 15:05:52 +0100 |
commit | a3ab70686e8c890e2dbd86572f06f5c7ba202fb9 (patch) | |
tree | fd738bef524b9285296e7900e560d92c843993db /src | |
parent | 407ad7a1f04893a0598b2ea02e7d2eb118d1fa32 (diff) | |
download | chawan-a3ab70686e8c890e2dbd86572f06f5c7ba202fb9.tar.gz |
layout: rework inline box area model
Specifying the background area as three LayoutUnit pairs sounds clever, but breaks down for text-align: center. * store line Y offset in LineBox - this is necessary to pass down areas we need to paint * refactor addHorizontalLine loop; remove mystery "If necessary, update ictx's width" line that used to be labeled "TODO this seems meaningless" and indeed seems meaningless now (I am prepared to deeply regret this later) * replace broken & convoluted startOffset/endOffset/size with a simple seq of offsets + sizes
Diffstat (limited to 'src')
-rw-r--r-- | src/layout/box.nim | 30 | ||||
-rw-r--r-- | src/layout/engine.nim | 265 | ||||
-rw-r--r-- | src/layout/renderdocument.nim | 39 |
3 files changed, 180 insertions, 154 deletions
diff --git a/src/layout/box.nim b/src/layout/box.nim index 930ffa90..ab86f67b 100644 --- a/src/layout/box.nim +++ b/src/layout/box.nim @@ -12,17 +12,17 @@ type h*: LayoutUnit InlineAtomType* = enum - INLINE_SPACING, INLINE_WORD, INLINE_BLOCK + iatSpacing, iatWord, iatInlineBlock InlineAtom* = ref object offset*: Offset size*: Size case t*: InlineAtomType - of INLINE_SPACING: + of iatSpacing: discard - of INLINE_WORD: + of iatWord: str*: string - of INLINE_BLOCK: + of iatInlineBlock: innerbox*: BlockBox RootInlineFragment* = ref object @@ -41,23 +41,13 @@ type SplitType* = enum stSplitStart, stSplitEnd - InlineFragment* = ref object - # Say we have the following inline box: - # abcd - # efghij - # klm - # Then startOffset is x: 2ch, y: 1em, endOffset is x: 3ch, y: 2em, - # and size is w: 6ch, h: 3em. - # So the algorithm for painting a fragment is: - # if startOffset.y == endOffset.y: - # paint(startOffset, endOffset) - # else: - # paint(startOffset.x, 0, size.w, startOffset.y) - # paint(0, startOffset.y, size.w, endOffset.y) - # paint(0, endOffset.y, endOffset.x, size.h) - startOffset*: Offset - endOffset*: Offset + Area* = object + offset*: Offset size*: Size + + InlineFragment* = ref object + startOffset*: Offset # offset of the first word, for position: absolute + areas*: seq[Area] # background that should be painted by fragment children*: seq[InlineFragment] atoms*: seq[InlineAtom] computed*: CSSComputedValues diff --git a/src/layout/engine.nim b/src/layout/engine.nim index 1291c859..52b551b1 100644 --- a/src/layout/engine.nim +++ b/src/layout/engine.nim @@ -199,14 +199,14 @@ type # Set at the end of layoutText. It helps determine the beginning of the # next inline fragment. widthAfterWhitespace: LayoutUnit - # offset of line in root fragment - offsety: LayoutUnit # minimum height to fit all inline atoms minHeight: LayoutUnit LineBox = ref object atoms: seq[InlineAtom] size: Size + offsety: LayoutUnit # offset of line in root fragment + height: LayoutUnit # height used for painting; does not include padding InlineAtomState = object vertalign: CSSVerticalAlign @@ -269,11 +269,14 @@ template atoms(state: LineBoxState): untyped = template size(state: LineBoxState): untyped = state.line.size +template offsety(state: LineBoxState): untyped = + state.line.offsety + func size(ictx: var InlineContext): var Size = ictx.root.size # Whitespace between words -func computeShift(ictx: InlineContext, state: InlineState): LayoutUnit = +func computeShift(ictx: InlineContext; state: InlineState): LayoutUnit = if ictx.whitespacenum == 0: return 0 if ictx.whitespaceIsLF and state.lastrw == 2 and state.firstrw == 2: @@ -281,11 +284,11 @@ func computeShift(ictx: InlineContext, state: InlineState): LayoutUnit = return 0 if not state.computed.whitespacepre: if ictx.currentLine.atoms.len == 0 or - ictx.currentLine.atoms[^1].t == INLINE_SPACING: + ictx.currentLine.atoms[^1].t == iatSpacing: return 0 return ictx.cellwidth * ictx.whitespacenum -proc applyLineHeight(ictx: InlineContext, state: var LineBoxState, +proc applyLineHeight(ictx: InlineContext; state: var LineBoxState; computed: CSSComputedValues) = let lctx = ictx.lctx #TODO this should be computed during cascading. @@ -300,9 +303,9 @@ proc applyLineHeight(ictx: InlineContext, state: var LineBoxState, state.paddingBottom = max(paddingBottom, state.paddingBottom) state.lineheight = max(lineheight, state.lineheight) -proc newWord(ictx: var InlineContext, state: var InlineState) = +proc newWord(ictx: var InlineContext; state: var InlineState) = ictx.word = InlineAtom( - t: INLINE_WORD, + t: iatWord, size: Size(h: ictx.cellheight) ) ictx.wordstate = InlineAtomState( @@ -312,8 +315,7 @@ proc newWord(ictx: var InlineContext, state: var InlineState) = ictx.wrappos = -1 ictx.hasshy = false -proc horizontalAlignLine(ictx: var InlineContext, state: InlineState, - line: LineBox) = +proc horizontalAlignLines(ictx: var InlineContext; state: InlineState) = let width = case ictx.space.w.t of MIN_CONTENT, MAX_CONTENT: ictx.size.w @@ -327,19 +329,19 @@ proc horizontalAlignLine(ictx: var InlineContext, state: InlineState, discard of TEXT_ALIGN_END, TEXT_ALIGN_RIGHT, TEXT_ALIGN_CHA_RIGHT: # move everything - let x = max(width, line.size.w) - line.size.w - for atom in line.atoms.mitems: - atom.offset.x += x - ictx.size.w = max(atom.offset.x + atom.size.w, ictx.size.w) + for line in ictx.lines: + let x = max(width, line.size.w) - line.size.w + for atom in line.atoms: + atom.offset.x += x + ictx.size.w = max(atom.offset.x + atom.size.w, ictx.size.w) of TEXT_ALIGN_CENTER, TEXT_ALIGN_CHA_CENTER: # NOTE if we need line x offsets, use: #let width = width - line.offset.x - let x = max((max(width, line.size.w)) div 2 - line.size.w div 2, 0) - for atom in line.atoms.mitems: - atom.offset.x += x - ictx.size.w = max(atom.offset.x + atom.size.w, ictx.size.w) - # If necessary, update ictx's width. - ictx.size.w = max(line.size.w, ictx.size.w) + for line in ictx.lines: + let x = max((max(width, line.size.w)) div 2 - line.size.w div 2, 0) + for atom in line.atoms: + atom.offset.x += x + ictx.size.w = max(atom.offset.x + atom.size.w, ictx.size.w) # Align atoms (inline boxes, text, etc.) vertically (i.e. along the inline # axis) inside the line. @@ -354,7 +356,6 @@ proc verticalAlignLine(ictx: var InlineContext) = var marginTop: LayoutUnit = 0 var bottomEdge = baseline for i, atom in ictx.currentLine.atoms: - let atom = ictx.currentLine.atoms[i] let iastate = ictx.currentLine.atomstates[i] case iastate.vertalign.keyword of VERTICAL_ALIGN_BASELINE: @@ -374,7 +375,6 @@ proc verticalAlignLine(ictx: var InlineContext) = # The line height should be at least as high as the highest baseline used by # an atom plus that atom's height. for i, atom in ictx.currentLine.atoms: - let atom = ictx.currentLine.atoms[i] let iastate = ictx.currentLine.atomstates[i] # In all cases, the line's height must at least equal the atom's height. # (Where the atom is actually placed is irrelevant here.) @@ -440,6 +440,8 @@ proc verticalAlignLine(ictx: var InlineContext) = # baseline. ictx.currentLine.size.h = max(bottomEdge + marginTop, lineheight) ictx.currentLine.baseline = baseline + # Save line height that will be used for painting. + ictx.currentLine.line.height = ictx.currentLine.size.h # Add padding. ictx.currentLine.size.h += ictx.currentLine.paddingTop ictx.currentLine.size.h += ictx.currentLine.paddingBottom @@ -449,39 +451,33 @@ proc verticalAlignLine(ictx: var InlineContext) = ictx.currentLine.size.h = max(ictx.currentLine.size.h.round(ch), ictx.currentLine.minHeight) -proc addSpacing(ictx: var InlineContext, width, height: LayoutUnit, - hang = false) = +proc putAtom(state: var LineBoxState; atom: InlineAtom; + iastate: InlineAtomState; fragment: InlineFragment) = + state.atomstates.add(iastate) + state.atoms.add(atom) + fragment.atoms.add(atom) + +proc addSpacing(ictx: var InlineContext; width, height: LayoutUnit; + state: InlineState; hang = false) = let spacing = InlineAtom( - t: INLINE_SPACING, - size: Size( - w: width, - h: height - ), + t: iatSpacing, + size: Size(w: width, h: height), offset: Offset(x: ictx.currentLine.size.w) ) - let iastate = InlineAtomState( - baseline: height - #TODO vertalign? - ) + let iastate = InlineAtomState(baseline: height) if not hang: # In some cases, whitespace may "hang" at the end of the line. This means # it is written, but is not actually counted in the box's width. ictx.currentLine.size.w += width - ictx.currentLine.atomstates.add(iastate) - ictx.currentLine.atoms.add(spacing) - # whitespaceFragment's endOffset may already be set, in this case we must - # shift it by width. - # (If it is not set, then it will simply be overridden when endOffset is set.) - ictx.whitespaceFragment.endOffset.x += width - ictx.whitespaceFragment.atoms.add(spacing) - -proc flushWhitespace(ictx: var InlineContext, state: InlineState, + ictx.currentLine.putAtom(spacing, iastate, ictx.whitespaceFragment) + +proc flushWhitespace(ictx: var InlineContext; state: InlineState; hang = false) = let shift = ictx.computeShift(state) ictx.currentLine.charwidth += ictx.whitespacenum ictx.whitespacenum = 0 if shift > 0: - ictx.addSpacing(shift, ictx.cellheight, hang) + ictx.addSpacing(shift, ictx.cellheight, state, hang) # Prepare the next line's initial width and available width. # (If space on the left is excluded by floats, set the initial width to @@ -506,7 +502,7 @@ proc initLine(ictx: var InlineContext) = ictx.currentLine.line.size.w = left - bfcOffset.x ictx.currentLine.availableWidth = right - bfcOffset.x -proc finishLine(ictx: var InlineContext, state: var InlineState, wrap: bool, +proc finishLine(ictx: var InlineContext; state: var InlineState; wrap: bool; force = false) = if ictx.currentLine.atoms.len != 0 or force: let whitespace = state.computed{"white-space"} @@ -535,29 +531,70 @@ proc finishLine(ictx: var InlineContext, state: var InlineState, wrap: bool, x: state.startOffsetTop.x, y: y + ictx.currentLine.size.h ) - state.fragment.size.w = lineWidth - state.startOffsetTop.x state.firstLine = false - else: - state.fragment.size.w = max(lineWidth, state.fragment.size.w) ictx.size.w = max(ictx.size.w, lineWidth) ictx.lines.add(ictx.currentLine.line) ictx.currentLine = LineBoxState( - offsety: y + ictx.currentLine.size.h, - line: LineBox() + line: LineBox(offsety: y + ictx.currentLine.size.h) ) ictx.initLine() -proc finish(ictx: var InlineContext, state: var InlineState) = - ictx.finishLine(state, wrap = false) - for line in ictx.lines: - ictx.horizontalAlignLine(state, line) +proc addBackgroundAreas(ictx: var InlineContext; rootFragment: InlineFragment) = + var traverseStack: seq[InlineFragment] = @[rootFragment] + var currentStack: seq[InlineFragment] = @[] + template top: InlineFragment = currentStack[^1] + var atomIdx = 0 + for i, line in ictx.lines: + if line.atoms.len == 0: + continue + # extend current areas + for node in currentStack: + node.areas.add(Area( + offset: Offset(x: line.atoms[0].offset.x, y: line.offsety), + size: Size(w: line.atoms[0].size.w, h: line.height) + )) + var prevEnd: LayoutUnit = 0 + for atom in line.atoms: + if currentStack.len == 0 or atomIdx >= top.atoms.len: + atomIdx = 0 + while true: + let thisNode = traverseStack.pop() + if thisNode == nil: # sentinel found + let oldTop = currentStack.pop() + # finish oldTop area + # (must have at least one area on this line b/c we add it at start) + assert oldTop.areas[^1].offset.y == line.offsety + if prevEnd > 0: + oldTop.areas[^1].size.w = prevEnd - oldTop.areas[^1].offset.x + else: + # fragment got dropped without prevEnd moving anywhere; delete + # area + oldTop.areas.setLen(oldTop.areas.high) + continue + traverseStack.add(nil) # sentinel + for i in countdown(thisNode.children.high, 0): + traverseStack.add(thisNode.children[i]) + thisNode.areas.add(Area( + offset: Offset(x: atom.offset.x, y: line.offsety), + size: Size(w: atom.size.w, h: line.height) + )) + currentStack.add(thisNode) + if thisNode.atoms.len > 0: + break + prevEnd = atom.offset.x + atom.size.w + assert top.atoms[atomIdx] == atom + inc atomIdx + # finish areas of nodes currently on the stack + for node in currentStack: + assert node.areas[^1].offset.y == line.offsety + node.areas[^1].size.w = prevEnd - node.areas[^1].offset.x func minwidth(atom: InlineAtom): LayoutUnit = - if atom.t == INLINE_BLOCK: + if atom.t == iatInlineBlock: return atom.innerbox.xminwidth return atom.size.w -func shouldWrap(ictx: InlineContext, w: LayoutUnit, +func shouldWrap(ictx: InlineContext; w: LayoutUnit; pcomputed: CSSComputedValues): bool = if pcomputed != nil and pcomputed.nowrap: return false @@ -567,20 +604,20 @@ func shouldWrap(ictx: InlineContext, w: LayoutUnit, return true # always wrap with min-content return ictx.currentLine.size.w + w > ictx.currentLine.availableWidth -func shouldWrap2(ictx: InlineContext, w: LayoutUnit): bool = +func shouldWrap2(ictx: InlineContext; w: LayoutUnit): bool = if not ictx.currentLine.hasExclusion: return false return ictx.currentLine.size.w + w > ictx.currentLine.availableWidth # Start a new line, even if the previous one is empty -proc flushLine(ictx: var InlineContext, state: var InlineState) = +proc flushLine(ictx: var InlineContext; state: var InlineState) = ictx.applyLineHeight(ictx.currentLine, state.computed) ictx.finishLine(state, wrap = false, force = true) # Add an inline atom atom, with state iastate. # Returns true on newline. -proc addAtom(ictx: var InlineContext, state: var InlineState, - iastate: InlineAtomState, atom: InlineAtom): bool = +proc addAtom(ictx: var InlineContext; state: var InlineState; + iastate: InlineAtomState; atom: InlineAtom): bool = result = false var shift = ictx.computeShift(state) ictx.whitespacenum = 0 @@ -598,25 +635,23 @@ proc addAtom(ictx: var InlineContext, state: var InlineState, shift = ictx.computeShift(state) if atom.size.w > 0 and atom.size.h > 0: if shift > 0: - ictx.addSpacing(shift, ictx.cellheight) + ictx.addSpacing(shift, ictx.cellheight, state) ictx.minwidth = max(ictx.minwidth, atom.minwidth) ictx.applyLineHeight(ictx.currentLine, state.computed) - if atom.t != INLINE_WORD: + if atom.t != iatWord: ictx.currentLine.charwidth = 0 - ictx.currentLine.atoms.add(atom) - state.fragment.atoms.add(atom) - ictx.currentLine.atomstates.add(iastate) - ictx.currentLine.atoms[^1].offset.x += ictx.currentLine.size.w + ictx.currentLine.putAtom(atom, iastate, state.fragment) + atom.offset.x += ictx.currentLine.size.w ictx.currentLine.size.w += atom.size.w -proc addWord(ictx: var InlineContext, state: var InlineState): bool = +proc addWord(ictx: var InlineContext; state: var InlineState): bool = result = false if ictx.word.str != "": ictx.word.str.mnormalize() #TODO this may break on EOL. result = ictx.addAtom(state, ictx.wordstate, ictx.word) ictx.newWord(state) -proc addWordEOL(ictx: var InlineContext, state: var InlineState): bool = +proc addWordEOL(ictx: var InlineContext; state: var InlineState): bool = result = false if ictx.word.str != "": if ictx.wrappos != -1: @@ -632,7 +667,7 @@ proc addWordEOL(ictx: var InlineContext, state: var InlineState): bool = else: result = ictx.addWord(state) -proc checkWrap(ictx: var InlineContext, state: var InlineState, r: Rune) = +proc checkWrap(ictx: var InlineContext; state: var InlineState; r: Rune) = if state.computed.nowrap: return let shift = ictx.computeShift(state) @@ -664,7 +699,7 @@ proc checkWrap(ictx: var InlineContext, state: var InlineState, r: Rune) = ictx.finishLine(state, wrap = true) ictx.whitespacenum = 0 -proc processWhitespace(ictx: var InlineContext, state: var InlineState, +proc processWhitespace(ictx: var InlineContext; state: var InlineState; c: char) = discard ictx.addWord(state) case state.computed{"white-space"} @@ -700,8 +735,8 @@ proc processWhitespace(ictx: var InlineContext, state: var InlineState, # set the "last word's last rune width" to the previous rune width state.lastrw = state.prevrw -func initInlineContext(bctx: var BlockContext, space: AvailableSpace, - bfcOffset: Offset, root: RootInlineFragment): InlineContext = +func initInlineContext(bctx: var BlockContext; space: AvailableSpace; + bfcOffset: Offset; root: RootInlineFragment): InlineContext = var ictx = InlineContext( currentLine: LineBoxState( line: LineBox() @@ -715,7 +750,8 @@ func initInlineContext(bctx: var BlockContext, space: AvailableSpace, ictx.initLine() return ictx -proc layoutTextLoop(ictx: var InlineContext, state: var InlineState, str: string) = +proc layoutTextLoop(ictx: var InlineContext; state: var InlineState; + str: string) = var i = 0 while i < str.len: let c = str[i] @@ -749,7 +785,7 @@ proc layoutTextLoop(ictx: var InlineContext, state: var InlineState, str: string let shift = ictx.computeShift(state) ictx.currentLine.widthAfterWhitespace = ictx.currentLine.size.w + shift -proc layoutText(ictx: var InlineContext, state: var InlineState, str: string) = +proc layoutText(ictx: var InlineContext; state: var InlineState; str: string) = ictx.flushWhitespace(state) ictx.newWord(state) case state.computed{"text-transform"} @@ -1345,7 +1381,7 @@ proc addInlineBlock(ictx: var InlineContext, state: var InlineState, box.offset.y = 0 # Apply the block box's properties to the atom itself. let iblock = InlineAtom( - t: INLINE_BLOCK, + t: iatInlineBlock, innerbox: box, offset: Offset(x: sizes.margin.left), size: Size( @@ -1362,7 +1398,7 @@ proc addInlineBlock(ictx: var InlineContext, state: var InlineState, discard ictx.addAtom(state, iastate, iblock) ictx.whitespacenum = 0 -proc layoutInline(ictx: var InlineContext, box: InlineBoxBuilder): +proc layoutInline(ictx: var InlineContext; box: InlineBoxBuilder): InlineFragment = let lctx = ictx.lctx let fragment = InlineFragment( @@ -1403,10 +1439,11 @@ proc layoutInline(ictx: var InlineContext, box: InlineBoxBuilder): let child = ictx.layoutInline(InlineBoxBuilder(child)) state.fragment.children.add(child) of DISPLAY_INLINE_BLOCK, DISPLAY_INLINE_TABLE: - let child = BlockBoxBuilder(child) + # Note: we do not need a separate inline fragment here, because the tree + # generator already does an iflush() before adding inline blocks. let w = fitContent(ictx.space.w) let h = ictx.space.h - ictx.addInlineBlock(state, child, w, h) + ictx.addInlineBlock(state, BlockBoxBuilder(child), w, h) else: assert false, "child.t is " & $child.computed{"display"} @@ -1421,14 +1458,13 @@ proc layoutInline(ictx: var InlineContext, box: InlineBoxBuilder): if state.firstLine: fragment.startOffset = Offset( x: state.startOffsetTop.x, - y: ictx.currentLine.offsety + ictx.currentLine.size.h + y: ictx.currentLine.offsety + ) + else: + fragment.startOffset = Offset( + x: 0, + y: ictx.currentLine.offsety ) - fragment.endOffset = Offset( - x: ictx.currentLine.size.w, - y: ictx.currentLine.offsety - ) - fragment.size.h = ictx.currentLine.offsety + ictx.currentLine.size.h - - state.startOffsetTop.y return fragment proc layoutRootInline(bctx: var BlockContext, inlines: seq[BoxBuilder], @@ -1445,10 +1481,10 @@ proc layoutRootInline(bctx: var BlockContext, inlines: seq[BoxBuilder], let childFragment = ictx.layoutInline(InlineBoxBuilder(child)) root.fragment.children.add(childFragment) of DISPLAY_INLINE_BLOCK, DISPLAY_INLINE_TABLE: - #TODO ??? + # add an anonymous fragment to contain this var state = InlineState( computed: computed, - fragment: InlineFragment(computed: root.fragment.computed), + fragment: InlineFragment(computed: computed), firstLine: true ) let w = fitContent(ictx.space.w) @@ -1459,15 +1495,14 @@ proc layoutRootInline(bctx: var BlockContext, inlines: seq[BoxBuilder], assert false, "child.t is " & $child.computed{"display"} if ictx.firstTextFragment != nil: root.fragment.startOffset = ictx.firstTextFragment.startOffset - if ictx.lastTextFragment != nil: - root.fragment.endOffset = ictx.lastTextFragment.endOffset - root.fragment.size = ictx.size let lastFragment = if ictx.lastTextFragment != nil: ictx.lastTextFragment else: InlineFragment(computed: computed) var state = InlineState(computed: computed, fragment: lastFragment) - ictx.finish(state) + ictx.finishLine(state, wrap = false) + ictx.horizontalAlignLines(state) + ictx.addBackgroundAreas(root.fragment) root.xminwidth = ictx.minwidth return root @@ -2429,6 +2464,20 @@ proc flush(ctx: var InnerBlockContext) = ctx.flushTable() ctx.flushInherit() +proc reconstructInlineParents(ctx: var InnerBlockContext): InlineBoxBuilder = + let rootNode = ctx.inlineStack[0] + var parent = InlineBoxBuilder( + computed: rootNode.computed, + node: rootNode + ) + ctx.iroot = parent + for i in 1 ..< ctx.inlineStack.len: + let node = ctx.inlineStack[i] + let nbox = InlineBoxBuilder(computed: node.computed, node: node) + parent.children.add(nbox) + parent = nbox + return parent + proc generateFromElem(ctx: var InnerBlockContext, styledNode: StyledNode) = let box = ctx.blockgroup.parent @@ -2456,9 +2505,19 @@ proc generateFromElem(ctx: var InnerBlockContext, styledNode: StyledNode) = of DISPLAY_INLINE: ctx.generateInlineBoxes(styledNode) of DISPLAY_INLINE_BLOCK: + # create a new inline box that we can safely put our inline block into ctx.iflush() + let computed = styledNode.computed.inheritProperties() + ctx.ibox = InlineBoxBuilder(computed: computed, node: styledNode) + if ctx.inlineStack.len > 0: + let iparent = ctx.reconstructInlineParents() + iparent.children.add(ctx.ibox) + ctx.iroot = iparent + else: + ctx.iroot = ctx.ibox let childbox = ctx.generateBlockBox(styledNode) - ctx.blockgroup.add(childbox) + ctx.ibox.children.add(childbox) + ctx.iflush() of DISPLAY_TABLE: ctx.flush() let childbox = styledNode.generateTableBox(ctx.lctx, ctx) @@ -2502,9 +2561,19 @@ proc generateFromElem(ctx: var InnerBlockContext, styledNode: StyledNode) = ctx.anonRow = TableRowBoxBuilder(computed: wrappervals) ctx.anonRow.children.add(childbox) of DISPLAY_INLINE_TABLE: + # create a new inline box that we can safely put our inline block into ctx.iflush() + let computed = styledNode.computed.inheritProperties() + ctx.ibox = InlineBoxBuilder(computed: computed, node: styledNode) + if ctx.inlineStack.len > 0: + let iparent = ctx.reconstructInlineParents() + iparent.children.add(ctx.ibox) + ctx.iroot = iparent + else: + ctx.iroot = ctx.ibox let childbox = styledNode.generateTableBox(ctx.lctx, ctx) - ctx.blockgroup.add(childbox) + ctx.ibox.children.add(childbox) + ctx.iflush() of DISPLAY_TABLE_CAPTION: ctx.bflush() ctx.flushTableRow() @@ -2524,20 +2593,6 @@ proc generateFromElem(ctx: var InnerBlockContext, styledNode: StyledNode) = discard #TODO of DISPLAY_NONE: discard -proc reconstructInlineParents(ctx: var InnerBlockContext): InlineBoxBuilder = - let rootNode = ctx.inlineStack[0] - var parent = InlineBoxBuilder( - computed: rootNode.computed, - node: rootNode - ) - ctx.iroot = parent - for i in 1 ..< ctx.inlineStack.len: - let node = ctx.inlineStack[i] - let nbox = InlineBoxBuilder(computed: node.computed, node: node) - parent.children.add(nbox) - parent = nbox - return parent - proc generateAnonymousInlineText(ctx: var InnerBlockContext, text: string, styledNode: StyledNode) = if ctx.iroot == nil: diff --git a/src/layout/renderdocument.nim b/src/layout/renderdocument.nim index 2162776f..d4dc377f 100644 --- a/src/layout/renderdocument.nim +++ b/src/layout/renderdocument.nim @@ -333,31 +333,12 @@ proc renderBlockBox(grid: var FlexibleGrid; state: var RenderState; proc paintInlineFragment(grid: var FlexibleGrid; state: var RenderState; fragment: InlineFragment; offset: Offset; bgcolor: CellColor) = - let x = offset.x - let y = offset.y - let node = fragment.node - if fragment.startOffset.y - fragment.size.h == fragment.endOffset.y: - let x0 = toInt(x + fragment.startOffset.x) - let y0 = toInt(y + fragment.endOffset.y) - let x1 = toInt(x + fragment.endOffset.x) - let y1 = toInt(y + fragment.startOffset.y) - grid.paintBackground(state, bgcolor, x0, y0, x1, y1, node) - else: - let x0 = toInt(x + fragment.startOffset.x) - let y0 = toInt(y) - let x1 = toInt(x + fragment.size.w) - let y1 = toInt(y + fragment.startOffset.y) - grid.paintBackground(state, bgcolor, x0, y0, x1, y1, node) - let x2 = toInt(x) - let y2 = y1 - let x3 = x1 - let y3 = toInt(y + fragment.endOffset.y) - grid.paintBackground(state, bgcolor, x2, y2, x3, y3, node) - let x4 = x2 - let y4 = y3 - let x5 = toInt(x + fragment.endOffset.x) - let y5 = toInt(y + fragment.size.h) - grid.paintBackground(state, bgcolor, x4, y4, x5, y5, node) + for area in fragment.areas: + let x1 = toInt(offset.x + area.offset.x) + let y1 = toInt(offset.y + area.offset.y) + let x2 = toInt(offset.x + area.offset.x + area.size.w) + let y2 = toInt(offset.y + area.offset.y + area.size.h) + grid.paintBackground(state, bgcolor, x1, y1, x2, y2, fragment.node) proc renderInlineFragment(grid: var FlexibleGrid; state: var RenderState; fragment: InlineFragment; offset: Offset) = @@ -370,22 +351,22 @@ proc renderInlineFragment(grid: var FlexibleGrid; state: var RenderState; let format = fragment.computed.toFormat() for atom in fragment.atoms: case atom.t - of INLINE_BLOCK: + of iatInlineBlock: let offset = Offset( x: offset.x + atom.offset.x, y: offset.y + atom.offset.y ) grid.renderBlockBox(state, atom.innerbox, offset) - of INLINE_WORD: + of iatWord: grid.setRowWord(state, atom, offset, format, fragment.node) - of INLINE_SPACING: + of iatSpacing: grid.setSpacing(state, atom, offset, format, fragment.node) if fragment.computed{"position"} != POSITION_STATIC: if fragment.splitType != {stSplitStart, stSplitEnd}: if stSplitStart in fragment.splitType: state.absolutePos.add(Offset( x: offset.x + fragment.startOffset.x, - y: offset.y + fragment.endOffset.y + y: offset.y + fragment.startOffset.y )) if stSplitEnd in fragment.splitType: discard state.absolutePos.pop() |