diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/layout/box.nim | 6 | ||||
-rw-r--r-- | src/layout/engine.nim | 91 | ||||
-rw-r--r-- | src/render/renderdocument.nim | 18 |
3 files changed, 75 insertions, 40 deletions
diff --git a/src/layout/box.nim b/src/layout/box.nim index 8a5c33da..d1e9c464 100644 --- a/src/layout/box.nim +++ b/src/layout/box.nim @@ -35,6 +35,8 @@ type InlineBoxBuilder* = ref object of BoxBuilder text*: seq[string] newline*: bool + splitstart*: bool + splitend*: bool BlockBoxBuilder* = ref object of BoxBuilder @@ -70,6 +72,10 @@ type textdecoration*: set[CSSTextDecoration] color*: RGBAColor node*: StyledNode + #TODO: background color should not be stored in inline words. Instead, + # inline box fragments should be passed on to the renderer, which could + # then properly blend them. + bgcolor*: RGBAColor InlineSpacing* = ref object of InlineAtom format*: ComputedFormat diff --git a/src/layout/engine.nim b/src/layout/engine.nim index 908f6671..fdbfa81c 100644 --- a/src/layout/engine.nim +++ b/src/layout/engine.nim @@ -8,6 +8,7 @@ import css/stylednode import css/values import io/window import layout/box +import types/color import utils/twtstr # Build phase @@ -60,18 +61,21 @@ proc applyLineHeight(viewport: Viewport, line: LineBox, computed: CSSComputedVal computed{"line-height"}.px(viewport, viewport.cellheight) line.lineheight = max(lineheight, line.lineheight) +func getComputedFormat(computed: CSSComputedValues, node: StyledNode): ComputedFormat = + return ComputedFormat( + color: computed{"color"}, + fontstyle: computed{"font-style"}, + fontweight: computed{"font-weight"}, + textdecoration: computed{"text-decoration"}, + bgcolor: computed{"background-color"}, + node: node + ) + proc newWord(state: var InlineState) = let word = InlineWord() - let format = ComputedFormat() - let computed = state.computed - format.color = computed{"color"} - format.fontstyle = computed{"font-style"} - format.fontweight = computed{"font-weight"} - format.textdecoration = computed{"text-decoration"} - format.node = state.node - word.format = format - word.vertalign = computed{"vertical-align"} - state.ictx.format = format + word.format = getComputedFormat(state.computed, state.node) + word.vertalign = state.computed{"vertical-align"} + state.ictx.format = word.format state.word = word state.wrappos = -1 state.hasshy = false @@ -342,7 +346,7 @@ proc processWhitespace(state: var InlineState, c: char) = else: inc state.ictx.whitespacenum -proc renderText*(ictx: InlineContext, str: string, computed: CSSComputedValues, node: StyledNode) = +proc layoutText(ictx: InlineContext, str: string, computed: CSSComputedValues, node: StyledNode) = var state: InlineState state.computed = computed state.ictx = ictx @@ -681,17 +685,18 @@ proc buildInline(ictx: InlineContext, box: InlineBoxBuilder) = if box.newline: ictx.flushLine(box.computed) - let margin_left = box.computed{"margin-left"}.px(ictx.viewport, ictx.contentWidth) - ictx.currentLine.width += margin_left + let paddingformat = getComputedFormat(box.computed, box.node) + if box.splitstart: + let margin_left = box.computed{"margin-left"}.px(ictx.viewport, ictx.contentWidth) + ictx.currentLine.width += margin_left - let paddingformat = ComputedFormat(node: box.node) - let padding_left = box.computed{"padding-left"}.px(ictx.viewport, ictx.contentWidth) - if padding_left > 0: - ictx.currentLine.addSpacing(padding_left, ictx.cellheight, paddingformat) + let padding_left = box.computed{"padding-left"}.px(ictx.viewport, ictx.contentWidth) + if padding_left > 0: + ictx.currentLine.addSpacing(padding_left, ictx.cellheight, paddingformat) assert not (box.children.len > 0 and box.text.len > 0) for text in box.text: - ictx.renderText(text, box.computed, box.node) + ictx.layoutText(text, box.computed, box.node) for child in box.children: case child.computed{"display"} @@ -706,13 +711,12 @@ proc buildInline(ictx: InlineContext, box: InlineBoxBuilder) = else: assert false, "child.t is " & $child.computed{"display"} - let padding_right = box.computed{"padding-right"}.px(ictx.viewport, ictx.contentWidth) - if padding_right > 0: - # I don't like this, but it works... - ictx.currentLine.addSpacing(padding_right, max(ictx.currentLine.height, 1), paddingformat) - - let margin_right = box.computed{"margin-right"}.px(ictx.viewport, ictx.contentWidth) - ictx.currentLine.width += margin_right + if box.splitend: + let padding_right = box.computed{"padding-right"}.px(ictx.viewport, ictx.contentWidth) + if padding_right > 0: + ictx.currentLine.addSpacing(padding_right, max(ictx.currentLine.height, 1), paddingformat) + let margin_right = box.computed{"margin-right"}.px(ictx.viewport, ictx.contentWidth) + ictx.currentLine.width += margin_right proc buildInlines(parent: BlockBox, inlines: seq[BoxBuilder]): InlineContext = let ictx = parent.newInlineContext() @@ -1218,7 +1222,7 @@ proc getBlockBox(computed: CSSComputedValues): BlockBoxBuilder = proc getTextBox(computed: CSSComputedValues): InlineBoxBuilder = new(result) result.inlinelayout = true - result.computed = computed.inheritProperties() + result.computed = computed proc getMarkerBox(computed: CSSComputedValues, listItemCounter: int): MarkerBoxBuilder = new(result) @@ -1371,7 +1375,6 @@ proc generateFromElem(ctx: var InnerBlockContext, styledNode: StyledNode) = childbox.content.computed{"display"} = DISPLAY_BLOCK box.children.add(childbox) of DISPLAY_INLINE: - ctx.iflush() ctx.generateInlineBoxes(styledNode) of DISPLAY_INLINE_BLOCK: ctx.iflush() @@ -1439,9 +1442,9 @@ proc generateFromElem(ctx: var InnerBlockContext, styledNode: StyledNode) = discard #TODO of DISPLAY_NONE: discard -proc generateInlineText(ctx: var InnerBlockContext, text: string, styledNode: StyledNode) = +proc generateAnonymousInlineText(ctx: var InnerBlockContext, text: string, styledNode: StyledNode) = if ctx.ibox == nil: - ctx.ibox = getTextBox(styledNode.computed) + ctx.ibox = getTextBox(styledNode.computed.inheritProperties()) ctx.ibox.node = styledNode ctx.ibox.text.add(text) @@ -1455,7 +1458,7 @@ proc generateReplacement(ctx: var InnerBlockContext, child, parent: StyledNode) elif quotes.auto: text = quoteStart(ctx.quoteLevel) else: return - ctx.generateInlineText(text, parent) + ctx.generateAnonymousInlineText(text, parent) inc ctx.quoteLevel of CONTENT_CLOSE_QUOTE: if ctx.quoteLevel > 0: dec ctx.quoteLevel @@ -1466,32 +1469,48 @@ proc generateReplacement(ctx: var InnerBlockContext, child, parent: StyledNode) elif quotes.auto: text = quoteEnd(ctx.quoteLevel) else: return - ctx.generateInlineText(text, parent) + ctx.generateAnonymousInlineText(text, parent) of CONTENT_NO_OPEN_QUOTE: inc ctx.quoteLevel of CONTENT_NO_CLOSE_QUOTE: if ctx.quoteLevel > 0: dec ctx.quoteLevel of CONTENT_STRING: #TODO canGenerateAnonymousInline? - ctx.generateInlineText(child.content.s, parent) + ctx.generateAnonymousInlineText(child.content.s, parent) of CONTENT_IMAGE: #TODO idk - ctx.generateInlineText(child.content.s, parent) + ctx.generateAnonymousInlineText(child.content.s, parent) of CONTENT_NEWLINE: ctx.iflush() - ctx.ibox = parent.computed.getTextBox() + ctx.ibox = getTextBox(parent.computed.inheritProperties()) ctx.ibox.newline = true ctx.iflush() proc generateInlineBoxes(ctx: var InnerBlockContext, styledNode: StyledNode) = + ctx.iflush() + var lbox = getTextBox(styledNode.computed) + lbox.node = styledNode + lbox.splitstart = true + ctx.ibox = lbox for child in styledNode.children: case child.t of STYLED_ELEMENT: ctx.generateFromElem(child) of STYLED_TEXT: - ctx.generateInlineText(child.text, styledNode) + if ctx.ibox != lbox: + ctx.iflush() + lbox = getTextBox(styledNode.computed) + lbox.node = styledNode + ctx.ibox = lbox + lbox.text.add(child.text) of STYLED_REPLACEMENT: ctx.generateReplacement(child, styledNode) + if ctx.ibox != lbox: + ctx.iflush() + lbox = getTextBox(styledNode.computed) + lbox.node = styledNode + ctx.ibox = lbox + lbox.splitend = true ctx.iflush() proc newInnerBlockContext(styledNode: StyledNode, box: BoxBuilder, viewport: Viewport, parent: ptr InnerBlockContext): InnerBlockContext = @@ -1519,7 +1538,7 @@ proc generateInnerBlockBox(ctx: var InnerBlockContext) = ctx.generateFromElem(child) of STYLED_TEXT: if canGenerateAnonymousInline(ctx.blockgroup, box.computed, child.text): - ctx.generateInlineText(child.text, ctx.styledNode) + ctx.generateAnonymousInlineText(child.text, ctx.styledNode) of STYLED_REPLACEMENT: ctx.generateReplacement(child, ctx.styledNode) ctx.iflush() diff --git a/src/render/renderdocument.nim b/src/render/renderdocument.nim index 5c9991a3..e2aa1b0e 100644 --- a/src/render/renderdocument.nim +++ b/src/render/renderdocument.nim @@ -15,6 +15,8 @@ import utils/twtstr func formatFromWord(computed: ComputedFormat): Format = result.fgcolor = computed.color.cellColor() + if computed.bgcolor.a != 0: + result.bgcolor = computed.bgcolor.cellColor() if computed.fontstyle in { FONT_STYLE_ITALIC, FONT_STYLE_OBLIQUE }: result.italic = true if computed.fontweight > 500: @@ -117,20 +119,28 @@ proc setText(lines: var FlexibleGrid, linestr: string, cformat: ComputedFormat, lnode = lines[y].formats[fi].node if lines[y].formats[fi].pos == x: # Replace. - format.bgcolor = lines[y].formats[fi].format.bgcolor + if cformat.bgcolor.a == 0: #TODO alpha blending + # We must check if the old string's last x position is greater than + # the new string's first x position. If not, we cannot inherit + # its bgcolor (which is supposed to end before the new string started.) + if nx > cx: + format.bgcolor = lines[y].formats[fi].format.bgcolor lines[y].formats.delete(fi) lines[y].insertFormat(x, fi, format, cformat.node) else: # First format's pos < x => split it up. assert lines[y].formats[fi].pos < x - format.bgcolor = lines[y].formats[fi].format.bgcolor + if cformat.bgcolor.a == 0: #TODO alpha blending + if nx > cx: # see above + format.bgcolor = lines[y].formats[fi].format.bgcolor inc fi # insert after first format lines[y].insertFormat(x, fi, format, cformat.node) inc fi # skip last format while fi < lines[y].formats.len and lines[y].formats[fi].pos < nx: # Other formats must be > x => replace them - format.bgcolor = lines[y].formats[fi].format.bgcolor + if cformat.bgcolor.a == 0: #TODO alpha blending + format.bgcolor = lines[y].formats[fi].format.bgcolor let px = lines[y].formats[fi].pos lformat = lines[y].formats[fi].format # save for later use lnode = lines[y].formats[fi].node @@ -308,7 +318,7 @@ proc renderBlockContext(grid: var FlexibleGrid, ctx: BlockBox, x, y: int, window if ctx.computed{"background-color"}.a != 0: #TODO color blending grid.paintBackground(ctx.computed{"background-color"}, x, y, x + ctx.width, y + ctx.height, ctx.node, window) if ctx.computed{"background-image"}.t == CONTENT_IMAGE: - # ugly hack for background-image display... TODO actually implement images + # ugly hack for background-image display... TODO actually display images let s = ctx.computed{"background-image"}.s # [img] let w = s.len * window.ppc var x = x |