diff options
-rw-r--r-- | src/layout/box.nim | 22 | ||||
-rw-r--r-- | src/layout/engine.nim | 133 | ||||
-rw-r--r-- | src/render/renderdocument.nim | 17 |
3 files changed, 85 insertions, 87 deletions
diff --git a/src/layout/box.nim b/src/layout/box.nim index 3403c465..2d82537e 100644 --- a/src/layout/box.nim +++ b/src/layout/box.nim @@ -20,18 +20,9 @@ type pos*: int neg*: int - Box* = ref object of RootObj - - BlockBox* = ref object of Box - flowRoot*: bool - - FlowRoot* = ref object of BlockBox - - #InlineRoot* = ref object of - Viewport* = ref object term*: TermAttributes - root*: BlockContext + root*: BlockBox BoxBuilder* = ref object of RootObj children*: seq[BoxBuilder] @@ -53,7 +44,6 @@ type MarkerBoxBuilder* = ref object of InlineBoxBuilder ordinalvalue*: int - inside*: bool ListItemBoxBuilder* = ref object of BoxBuilder marker*: MarkerBoxBuilder @@ -103,9 +93,9 @@ type shrink*: bool format*: ComputedFormat - BlockContext* = ref object of RootObj + BlockBox* = ref object of RootObj inline*: InlineContext - nested*: seq[BlockContext] + nested*: seq[BlockBox] computed*: CSSComputedValues viewport*: Viewport offset*: Offset @@ -127,11 +117,11 @@ type compheight*: Option[int] shrink*: bool - ListItem* = ref object of BlockContext + ListItemBox* = ref object of BlockBox marker*: InlineContext - InlineBlock* = ref object of InlineAtom - bctx*: BlockContext + InlineBlockBox* = ref object of InlineAtom + bctx*: BlockBox margin_top*: int margin_bottom*: int diff --git a/src/layout/engine.nim b/src/layout/engine.nim index d194deba..0a89c338 100644 --- a/src/layout/engine.nim +++ b/src/layout/engine.nim @@ -181,8 +181,8 @@ proc verticalAlignLine(ictx: InlineContext) = var margin_bottom = 0 for atom in line.atoms: - if atom of InlineBlock: - let atom = InlineBlock(atom) + if atom of InlineBlockBox: + let atom = InlineBlockBox(atom) margin_top = max(atom.margin_top, margin_top) margin_bottom = max(atom.margin_bottom, margin_bottom) @@ -353,7 +353,7 @@ proc preferredDimensions(computed: CSSComputedValues, viewport: Viewport, width: elif height.issome: result.compheight = pheight.px(viewport, height.get).some -proc setPreferredDimensions(bctx: BlockContext, width: int, height: Option[int]) = +proc setPreferredDimensions(bctx: BlockBox, width: int, height: Option[int]) = let preferred = preferredDimensions(bctx.computed, bctx.viewport, width, height) bctx.compwidth = preferred.compwidth bctx.compheight = preferred.compheight @@ -366,46 +366,45 @@ proc setPreferredDimensions(bctx: BlockContext, width: int, height: Option[int]) bctx.margin_left = preferred.margin_left bctx.margin_right = preferred.margin_right -proc newBlockContext_common2(bctx: BlockContext, parent: BlockContext, box: BoxBuilder) {.inline.} = +proc newBlockBox_common2(bctx: BlockBox, parent: BlockBox, box: BoxBuilder) {.inline.} = bctx.viewport = parent.viewport bctx.computed = box.computed bctx.setPreferredDimensions(parent.compwidth, parent.compheight) -proc newBlockContext_common(parent: BlockContext, box: BoxBuilder): BlockContext {.inline.} = +proc newBlockBox_common(parent: BlockBox, box: BoxBuilder): BlockBox {.inline.} = new(result) - result.newBlockContext_common2(parent, box) + result.newBlockBox_common2(parent, box) -proc newBlockContext(parent: BlockContext, box: BlockBoxBuilder): BlockContext = - result = newBlockContext_common(parent, box) +proc newFlowRootBox(viewport: Viewport, box: BoxBuilder, parentWidth: int, parentHeight = none(int)): BlockBox {.inline.} = + new(result) + result.viewport = viewport + result.computed = box.computed + result.setPreferredDimensions(parentWidth, parentHeight) + result.shrink = result.computed{"width"}.auto + +proc newBlockBox(parent: BlockBox, box: BlockBoxBuilder): BlockBox = + result = newBlockBox_common(parent, box) result.shrink = result.computed{"width"}.auto and parent.shrink -proc newListItem(parent: BlockContext, builder: ListItemBoxBuilder): ListItem = +proc newListItem(parent: BlockBox, builder: ListItemBoxBuilder): ListItemBox = new(result) - result.newBlockContext_common2(parent, builder.content) + result.newBlockBox_common2(parent, builder.content) result.shrink = result.computed{"width"}.auto and parent.shrink -proc newInlineBlockContext(parent: BlockContext, builder: InlineBlockBoxBuilder): BlockContext = - result = newBlockContext_common(parent, builder.content) - result.shrink = result.computed{"width"}.auto +proc newBlockBox(viewport: Viewport, box: BlockBoxBuilder): BlockBox = + result = newFlowRootBox(viewport, box, viewport.term.width_px) -proc newInlineBlock(parent: BlockContext, builder: InlineBlockBoxBuilder): InlineBlock = +proc newInlineBlock(viewport: Viewport, builder: InlineBlockBoxBuilder, parentWidth: int, parentHeight = none(int)): InlineBlockBox = new(result) - result.bctx = parent.newInlineBlockContext(builder) + result.bctx = newFlowRootBox(viewport, builder.content, parentWidth, parentHeight) -proc newBlockContext(viewport: Viewport, box: BlockBoxBuilder): BlockContext = - new(result) - result.viewport = viewport - result.computed = box.computed - result.setPreferredDimensions(viewport.term.width_px, none(int)) - result.shrink = result.computed{"width"}.auto - -proc newInlineContext(bctx: BlockContext): InlineContext = +proc newInlineContext(bctx: BlockBox): InlineContext = new(result) result.currentLine = LineBox() result.viewport = bctx.viewport result.shrink = bctx.shrink -proc positionInlines(bctx: BlockContext) = +proc positionInlines(bctx: BlockBox) = bctx.width += bctx.padding_left bctx.inline.offset.x += bctx.padding_left @@ -421,27 +420,27 @@ proc positionInlines(bctx: BlockContext) = else: bctx.width = bctx.compwidth -proc buildBlock(box: BlockBoxBuilder, parent: BlockContext): BlockContext -proc buildInlines(bctx: BlockContext, inlines: seq[BoxBuilder]): InlineContext -proc buildBlocks(bctx: BlockContext, blocks: seq[BoxBuilder], node: Node) +proc buildBlock(box: BlockBoxBuilder, parent: BlockBox): BlockBox +proc buildInlines(bctx: BlockBox, inlines: seq[BoxBuilder]): InlineContext +proc buildBlocks(bctx: BlockBox, blocks: seq[BoxBuilder], node: Node) -proc applyInlineDimensions(bctx: BlockContext) = +proc applyInlineDimensions(bctx: BlockBox) = bctx.height += bctx.inline.height if bctx.compheight.issome: bctx.height = bctx.compheight.get bctx.width = max(bctx.width, bctx.inline.maxwidth) # Builder only contains inline boxes. -proc buildInlineLayout(bctx: BlockContext, children: seq[BoxBuilder]) = +proc buildInlineLayout(bctx: BlockBox, children: seq[BoxBuilder]) = bctx.inline = bctx.buildInlines(children) bctx.applyInlineDimensions() bctx.positionInlines() # Builder only contains block boxes. -proc buildBlockLayout(bctx: BlockContext, children: seq[BoxBuilder], node: Node) = +proc buildBlockLayout(bctx: BlockBox, children: seq[BoxBuilder], node: Node) = bctx.buildBlocks(children, node) -func baseline(bctx: BlockContext): int = +func baseline(bctx: BlockBox): int = if bctx.inline != nil: var y = 0 for line in bctx.inline.lines: @@ -453,9 +452,9 @@ func baseline(bctx: BlockContext): int = return bctx.offset.y + bctx.nested[^1].baseline bctx.offset.y -proc buildInlineBlock(builder: InlineBlockBoxBuilder, parent: InlineContext, parentblock: BlockContext): InlineBlock = +proc buildInlineBlock(builder: InlineBlockBoxBuilder, parent: InlineContext, parentWidth: int, parentHeight = none(int)): InlineBlockBox = assert builder.content != nil - result = parentblock.newInlineBlock(builder) + result = newInlineBlock(parent.viewport, builder, parentWidth) let blockbuilder = builder.content if blockbuilder.inlinelayout: @@ -463,7 +462,7 @@ proc buildInlineBlock(builder: InlineBlockBoxBuilder, parent: InlineContext, par else: result.bctx.buildBlockLayout(blockbuilder.children, blockbuilder.node) - let preferred = preferredDimensions(builder.computed, parentblock.viewport, parentblock.compwidth, parentblock.compheight) + let preferred = preferredDimensions(builder.computed, parent.viewport, parentWidth, parentHeight) let pwidth = builder.computed{"width"} if pwidth.auto: # Half-baked shrink-to-fit @@ -486,48 +485,47 @@ proc buildInlineBlock(builder: InlineBlockBoxBuilder, parent: InlineContext, par result.width += result.bctx.margin_left result.width += result.bctx.margin_right -#TODO I don't think we need bctx here anymore. -proc buildInline(bctx: BlockContext, box: InlineBoxBuilder) = +proc buildInline(viewport: Viewport, box: InlineBoxBuilder, parentWidth: int, parentHeight = none(int)) = assert box.ictx != nil if box.newline: - box.ictx.flushLine(box.computed, bctx.compwidth) + box.ictx.flushLine(box.computed, parentWidth) - let margin_left = box.computed{"margin-left"}.px(bctx.viewport, bctx.compwidth) + let margin_left = box.computed{"margin-left"}.px(viewport, parentWidth) box.ictx.currentLine.width += margin_left let paddingformat = ComputedFormat(node: box.node) - let padding_left = box.computed{"padding-left"}.px(bctx.viewport, bctx.compwidth) + let padding_left = box.computed{"padding-left"}.px(viewport, parentWidth) if padding_left > 0: box.ictx.currentLine.addSpacing(padding_left, box.ictx.cellheight, paddingformat) for text in box.text: assert box.children.len == 0 - box.ictx.renderText(text, bctx.compwidth, box.computed, box.node) + box.ictx.renderText(text, parentWidth, box.computed, box.node) for child in box.children: case child.computed{"display"} of DISPLAY_INLINE: let child = InlineBoxBuilder(child) child.ictx = box.ictx - bctx.buildInline(child) + buildInline(viewport, child, parentWidth) of DISPLAY_INLINE_BLOCK: let child = InlineBlockBoxBuilder(child) - let iblock = child.buildInlineBlock(box.ictx, bctx) - box.ictx.addAtom(iblock, bctx.compwidth, child.computed) + let iblock = child.buildInlineBlock(box.ictx, parentWidth, parentHeight) + box.ictx.addAtom(iblock, parentWidth, child.computed) box.ictx.whitespacenum = 0 else: assert false, "child.t is " & $child.computed{"display"} - let padding_right = box.computed{"padding-right"}.px(bctx.viewport, bctx.compwidth) + let padding_right = box.computed{"padding-right"}.px(viewport, parentWidth) if padding_right > 0: # This is a hack. #TODO move this to horizontalAlignLine. box.ictx.currentLine.addSpacing(padding_right, max(box.ictx.currentLine.height, 1), paddingformat) - let margin_right = box.computed{"margin-right"}.px(bctx.viewport, bctx.compwidth) + let margin_right = box.computed{"margin-right"}.px(viewport, parentWidth) box.ictx.currentLine.width += margin_right -proc buildInlines(bctx: BlockContext, inlines: seq[BoxBuilder]): InlineContext = +proc buildInlines(bctx: BlockBox, inlines: seq[BoxBuilder]): InlineContext = let ictx = bctx.newInlineContext() if inlines.len > 0: for child in inlines: @@ -535,10 +533,10 @@ proc buildInlines(bctx: BlockContext, inlines: seq[BoxBuilder]): InlineContext = of DISPLAY_INLINE: let child = InlineBoxBuilder(child) child.ictx = ictx - bctx.buildInline(child) + buildInline(bctx.viewport, child, bctx.compwidth, bctx.compheight) of DISPLAY_INLINE_BLOCK: let child = InlineBlockBoxBuilder(child) - let iblock = child.buildInlineBlock(ictx, bctx) + let iblock = child.buildInlineBlock(ictx, bctx.compwidth) ictx.addAtom(iblock, bctx.compwidth, child.computed) ictx.whitespacenum = 0 else: @@ -547,15 +545,18 @@ proc buildInlines(bctx: BlockContext, inlines: seq[BoxBuilder]): InlineContext = return ictx -proc buildListItem(builder: ListItemBoxBuilder, parent: BlockContext): ListItem = +proc buildListItem(builder: ListItemBoxBuilder, parent: BlockBox): ListItemBox = result = parent.newListItem(builder) - result.marker = result.buildInlines(@[BoxBuilder(builder.marker)]) + + if builder.marker != nil: + result.marker = result.buildInlines(@[BoxBuilder(builder.marker)]) + if builder.content.inlinelayout: result.buildInlineLayout(builder.content.children) else: result.buildBlockLayout(builder.content.children, builder.content.node) -proc positionBlocks(bctx: BlockContext) = +proc positionBlocks(bctx: BlockBox) = var y = 0 var x = 0 var margin_todo: Strut @@ -567,7 +568,7 @@ proc positionBlocks(bctx: BlockContext) = if bctx.computed{"text-align"} == TEXT_ALIGN_MOZ_CENTER: x += bctx.compwidth div 2 - template apply_child(child: BlockContext) = + template apply_child(child: BlockBox) = child.offset.y = y child.offset.x = x + child.margin_left if bctx.computed{"text-align"} == TEXT_ALIGN_MOZ_CENTER: @@ -609,9 +610,9 @@ proc positionBlocks(bctx: BlockContext) = bctx.width += bctx.padding_left bctx.width += bctx.padding_right -proc buildBlocks(bctx: BlockContext, blocks: seq[BoxBuilder], node: Node) = +proc buildBlocks(bctx: BlockBox, blocks: seq[BoxBuilder], node: Node) = for child in blocks: - var cblock: BlockContext + var cblock: BlockBox case child.computed{"display"} of DISPLAY_BLOCK: cblock = buildBlock(BlockBoxBuilder(child), bctx) of DISPLAY_LIST_ITEM: cblock = buildListItem(ListItemBoxBuilder(child), bctx) @@ -620,17 +621,17 @@ proc buildBlocks(bctx: BlockContext, blocks: seq[BoxBuilder], node: Node) = bctx.positionBlocks() # Build a block box inside another block box, based on a builder. -proc buildBlock(box: BlockBoxBuilder, parent: BlockContext): BlockContext = +proc buildBlock(box: BlockBoxBuilder, parent: BlockBox): BlockBox = assert parent != nil - result = parent.newBlockContext(box) + result = parent.newBlockBox(box) if box.inlinelayout: result.buildInlineLayout(box.children) else: result.buildBlockLayout(box.children, box.node) # Establish a new flow-root context and build a block box. -proc buildRootBlock(box: BlockBoxBuilder, viewport: Viewport): BlockContext = - result = viewport.newBlockContext(box) +proc buildRootBlock(box: BlockBoxBuilder, viewport: Viewport): BlockBox = + result = viewport.newBlockBox(box) if box.inlinelayout: result.buildInlineLayout(box.children) else: @@ -672,8 +673,6 @@ proc getMarkerBox(computed: CSSComputedValues, listItemCounter: int): MarkerBoxB result.computed.setDisplay(DISPLAY_INLINE) result.ordinalvalue = listItemCounter - if computed{"list-style-position"} == LIST_STYLE_POSITION_INSIDE: - result.inside = true result.text.add(computed{"list-style-type"}.listMarker(result.ordinalvalue)) proc getListItemBox(computed: CSSComputedValues, listItemCounter: int): ListItemBoxBuilder = @@ -687,7 +686,7 @@ func canGenerateAnonymousInline(blockgroup: seq[BoxBuilder], computed: CSSComput computed{"white-space"} in {WHITESPACE_PRE_LINE, WHITESPACE_PRE, WHITESPACE_PRE_WRAP} or not str.onlyWhitespace() -proc generateBlockBox(styledNode: StyledNode, viewport: Viewport): BlockBoxBuilder +proc generateBlockBox(styledNode: StyledNode, viewport: Viewport, marker = none(MarkerBoxBuilder)): BlockBoxBuilder template flush_block_group(computed: CSSComputedValues) = if blockgroup.len > 0: @@ -721,7 +720,11 @@ proc generateFromElem(box: BlockBoxBuilder, styledNode: StyledNode, blockgroup: of DISPLAY_LIST_ITEM: flush_block_group(styledNode.computed) let childbox = getListItemBox(styledNode.computed, listItemCounter) - childbox.content = styledNode.generateBlockBox(viewport) + if childbox.computed{"list-style-position"} == LIST_STYLE_POSITION_INSIDE: + childbox.content = styledNode.generateBlockBox(viewport, some(childbox.marker)) + childbox.marker = nil + else: + childbox.content = styledNode.generateBlockBox(viewport) box.children.add(childbox) inc listItemCounter of DISPLAY_INLINE: @@ -750,11 +753,15 @@ proc generateInlineBoxes(box: BlockBoxBuilder, styledNode: StyledNode, blockgrou flush_ibox -proc generateBlockBox(styledNode: StyledNode, viewport: Viewport): BlockBoxBuilder = +proc generateBlockBox(styledNode: StyledNode, viewport: Viewport, marker = none(MarkerBoxBuilder)): BlockBoxBuilder = let box = getBlockBox(styledNode.computed) var blockgroup: seq[BoxBuilder] var ibox: InlineBoxBuilder = nil var listItemCounter = 1 # ordinal value of current list + + if marker.issome: + ibox = marker.get + flush_ibox for child in styledNode.children: case child.t diff --git a/src/render/renderdocument.nim b/src/render/renderdocument.nim index d2239416..54628946 100644 --- a/src/render/renderdocument.nim +++ b/src/render/renderdocument.nim @@ -201,7 +201,7 @@ proc paintBackground(lines: var FlexibleGrid, color: CSSColor, startx, starty, e inc y -proc renderBlockContext(grid: var FlexibleGrid, ctx: BlockContext, x, y: int, term: TermAttributes) +proc renderBlockContext(grid: var FlexibleGrid, ctx: BlockBox, x, y: int, term: TermAttributes) proc renderInlineContext(grid: var FlexibleGrid, ctx: InlineContext, x, y: int, term: TermAttributes) = let x = x + ctx.offset.x @@ -215,8 +215,8 @@ proc renderInlineContext(grid: var FlexibleGrid, ctx: InlineContext, x, y: int, grid.addLine() for atom in line.atoms: - if atom of InlineBlock: - let iblock = InlineBlock(atom) + if atom of InlineBlockBox: + let iblock = InlineBlockBox(atom) grid.renderBlockContext(iblock.bctx, x + iblock.offset.x, y + iblock.offset.y, term) elif atom of InlineWord: let word = InlineWord(atom) @@ -225,8 +225,8 @@ proc renderInlineContext(grid: var FlexibleGrid, ctx: InlineContext, x, y: int, let spacing = InlineSpacing(atom) grid.setSpacing(spacing, x, y, term) -proc renderBlockContext(grid: var FlexibleGrid, ctx: BlockContext, x, y: int, term: TermAttributes) = - var stack = newSeqOfCap[(BlockContext, int, int)](100) +proc renderBlockContext(grid: var FlexibleGrid, ctx: BlockBox, x, y: int, term: TermAttributes) = + var stack = newSeqOfCap[(BlockBox, int, int)](100) stack.add((ctx, x, y)) while stack.len > 0: @@ -237,9 +237,10 @@ proc renderBlockContext(grid: var FlexibleGrid, ctx: BlockContext, x, y: int, te if ctx.computed{"background-color"}.rgba.a != 0: #TODO color blending grid.paintBackground(ctx.computed{"background-color"}, x, y, x + ctx.width, y + ctx.height, term) - if ctx of ListItem: - let ctx = ListItem(ctx) - grid.renderInlineContext(ctx.marker, x - ctx.marker.maxwidth, y, term) + if ctx of ListItemBox: + let ctx = ListItemBox(ctx) + if ctx.marker != nil: + grid.renderInlineContext(ctx.marker, x - ctx.marker.maxwidth, y, term) if ctx.inline != nil: assert ctx.nested.len == 0 |