diff options
-rw-r--r-- | res/ua.css | 9 | ||||
-rw-r--r-- | src/buffer/container.nim | 35 | ||||
-rw-r--r-- | src/css/cascade.nim | 2 | ||||
-rw-r--r-- | src/css/values.nim | 38 | ||||
-rw-r--r-- | src/html/htmlparser.nim | 2 | ||||
-rw-r--r-- | src/io/lineedit.nim | 2 | ||||
-rw-r--r-- | src/io/term.nim | 44 | ||||
-rw-r--r-- | src/layout/box.nim | 7 | ||||
-rw-r--r-- | src/layout/engine.nim | 404 | ||||
-rw-r--r-- | src/render/renderdocument.nim | 2 | ||||
-rw-r--r-- | src/types/color.nim | 16 |
11 files changed, 302 insertions, 259 deletions
diff --git a/res/ua.css b/res/ua.css index 3c2b4daa..83e6ec98 100644 --- a/res/ua.css +++ b/res/ua.css @@ -26,6 +26,10 @@ ol, ul, menu, dir { margin-bottom: unset; } +blockquote { + margin: 1em; +} + table { display: table; } @@ -66,6 +70,11 @@ td { vertical-align: inherit; } +caption { + display: table-caption; + text-align: center; +} + input { margin-right: 1ch; white-space: pre; diff --git a/src/buffer/container.nim b/src/buffer/container.nim index b1f73629..20f04f62 100644 --- a/src/buffer/container.nim +++ b/src/buffer/container.nim @@ -511,32 +511,6 @@ proc popCursorPos*(container: Container, nojump = false) = container.sendCursorPosition() container.needslines = true -macro proxy(fun: typed) = - let name = fun[0] # sym - let params = fun[3] # formalparams - let retval = params[0] # sym - var body = newStmtList() - assert params.len >= 2 # return type, container - var x = name.strVal.toScreamingSnakeCase() - if x[^1] == '=': - x = "SET_" & x[0..^2] - let nup = ident(x) - let container = params[1][0] - body.add(quote do: - `container`.ostream.swrite(`nup`)) - for c in params[2..^1]: - let s = c[0] # sym e.g. url - body.add(quote do: - `container`.ostream.swrite(`s`)) - body.add(quote do: - `container`.ostream.flush()) - if retval.kind != nnkEmpty: - body.add(quote do: - `container`.istream.sread(result)) - var params2: seq[NimNode] - for x in params.children: params2.add(x) - result = newProc(name, params2, body) - proc cursorNextLink*(container: Container) {.jsfunc.} = container.writeCommand(FIND_NEXT_LINK, container.cursorx, container.cursory) container.expect(JUMP) @@ -569,8 +543,13 @@ proc gotoAnchor*(container: Container, anchor: string) = container.expect(ANCHOR_FOUND) container.expect(ANCHOR_FAIL) -proc readCanceled*(container: Container) {.proxy.} = discard -proc readSuccess*(container: Container, s: string) {.proxy.} = discard +proc readCanceled*(container: Container) = + container.writeCommand(READ_CANCELED) + +proc readSuccess*(container: Container, s: string) = + container.writeCommand(READ_SUCCESS, s) + container.expect(OPEN) + container.expect(RESHAPE) proc reshape*(container: Container, noreq = false) {.jsfunc.} = container.writeCommand(RENDER) diff --git a/src/css/cascade.nim b/src/css/cascade.nim index bc8db4d7..851e6f83 100644 --- a/src/css/cascade.nim +++ b/src/css/cascade.nim @@ -107,6 +107,8 @@ func calcPresentationalHints(element: Element): CSSComputedValues = map_bgcolor of TAG_COL: map_width + of TAG_BODY: + map_bgcolor else: discard proc applyDeclarations(styledNode: StyledNode, parent: CSSComputedValues, ua, user: DeclarationList, author: seq[DeclarationList]) = diff --git a/src/css/values.nim b/src/css/values.nim index 1e82fe3c..55ee663e 100644 --- a/src/css/values.nim +++ b/src/css/values.nim @@ -81,14 +81,23 @@ type CSSListStylePosition* = enum LIST_STYLE_POSITION_OUTSIDE, LIST_STYLE_POSITION_INSIDE +const RowGroupBox* = {DISPLAY_TABLE_ROW_GROUP, DISPLAY_TABLE_HEADER_GROUP, + DISPLAY_TABLE_FOOTER_GROUP} +const ProperTableChild* = {DISPLAY_TABLE_ROW, DISPLAY_TABLE_COLUMN, + DISPLAY_TABLE_COLUMN_GROUP, DISPLAY_TABLE_CAPTION} + + RowGroupBox +const ProperTableRowParent* = {DISPLAY_TABLE, DISPLAY_INLINE_TABLE} + RowGroupBox +const InternalTableBox* = {DISPLAY_TABLE_CELL, DISPLAY_TABLE_ROW, + DISPLAY_TABLE_COLUMN, DISPLAY_TABLE_COLUMN_GROUP} + + RowGroupBox +const TabularContainer* = {DISPLAY_TABLE_ROW} + ProperTableRowParent + type CSSLength* = object num*: float64 unit*: CSSUnit auto*: bool - CSSColor* = RGBAColor - CSSVerticalAlign* = object length*: CSSLength keyword*: CSSVerticalAlign2 @@ -97,7 +106,7 @@ type t*: CSSPropertyType case v*: CSSValueType of VALUE_COLOR: - color*: CSSColor + color*: RGBAColor of VALUE_LENGTH: length*: CSSLength of VALUE_FONT_STYLE: @@ -290,10 +299,10 @@ func listMarker*(t: CSSListStyleType, i: int): string = of LIST_STYLE_TYPE_LOWER_ROMAN: return romanNumber_lower(i) & ". " of LIST_STYLE_TYPE_JAPANESE_INFORMAL: return japaneseNumber(i) & "、" -const Colors: Table[string, CSSColor] = ((func (): Table[string, CSSColor] = +const Colors: Table[string, RGBAColor] = ((func (): Table[string, RGBAColor] = for name, rgb in ColorsRGB: - result[name] = CSSColor(rgb) - result["transparent"] = CSSColor(rgba(0x00, 0x00, 0x00, 0x00)) + result[name] = rgb + result["transparent"] = rgba(0x00, 0x00, 0x00, 0x00) )()) const Units = { @@ -344,13 +353,13 @@ func parseDimensionValues*(s: string): Option[CSSLength] = if s[i] == '%': return some(CSSLength(num: n, unit: UNIT_PERC)) return some(CSSLength(num: n, unit: UNIT_PX)) -func color(r, g, b: int): CSSColor = - return CSSColor(rgba(r, g, b, 256)) +func color(r, g, b: int): RGBAColor = + return rgba(r, g, b, 256) -func color(r, g, b, a: int): CSSColor = - return CSSColor(rgba(r, g, b, a)) +func color(r, g, b, a: int): RGBAColor = + return rgba(r, g, b, a) -func cssColor(d: CSSDeclaration): CSSColor = +func cssColor(d: CSSDeclaration): RGBAColor = if d.value.len > 0: if d.value[0] of CSSToken: let tok = CSSToken(d.value[0]) @@ -358,7 +367,7 @@ func cssColor(d: CSSDeclaration): CSSColor = of CSS_HASH_TOKEN: let c = parseHexColor(tok.value) if c.isSome: - return CSSColor(c.get) + return c.get else: raise newException(CSSValueError, "Invalid color") of CSS_IDENT_TOKEN: @@ -398,7 +407,7 @@ func cssColor(d: CSSDeclaration): CSSColor = raise newException(CSSValueError, "Invalid color") -func cellColor*(color: CSSColor): CellColor = +func cellColor*(color: RGBAColor): CellColor = return CellColor(rgb: true, rgbcolor: RGBColor(color)) func isToken(d: CSSDeclaration): bool {.inline.} = d.value.len > 0 and d.value[0] of CSSToken @@ -468,6 +477,7 @@ func cssDisplay(d: CSSDeclaration): CSSDisplay = of "table-row-group": return DISPLAY_TABLE_ROW_GROUP of "table-header-group": return DISPLAY_TABLE_HEADER_GROUP of "table-footer-group": return DISPLAY_TABLE_FOOTER_GROUP + of "table-caption": return DISPLAY_TABLE_CAPTION of "none": return DISPLAY_NONE else: return DISPLAY_INLINE raise newException(CSSValueError, "Invalid display") @@ -634,7 +644,7 @@ proc getValueFromDecl(val: CSSComputedValue, d: CSSDeclaration, vtype: CSSValueT of VALUE_LIST_STYLE_POSITION: val.liststyleposition = cssListStylePosition(d) of VALUE_NONE: discard -func getInitialColor(t: CSSPropertyType): CSSColor = +func getInitialColor(t: CSSPropertyType): RGBAColor = case t of PROPERTY_COLOR: return Colors["white"] diff --git a/src/html/htmlparser.nim b/src/html/htmlparser.nim index f28cd300..123d8ac7 100644 --- a/src/html/htmlparser.nim +++ b/src/html/htmlparser.nim @@ -1576,7 +1576,7 @@ proc processInHTMLContent(parser: var HTML5Parser, token: Token, insertionMode = of IN_CAPTION: match token: "</caption>" => (block: - if parser.openElements.hasElementInTableScope(TAG_CAPTION): + if not parser.openElements.hasElementInTableScope(TAG_CAPTION): parse_error else: parser.generateImpliedEndTags() diff --git a/src/io/lineedit.nim b/src/io/lineedit.nim index eb812122..a12604cb 100644 --- a/src/io/lineedit.nim +++ b/src/io/lineedit.nim @@ -94,10 +94,12 @@ proc generateOutput*(edit: LineEdit): FixedGrid = for r in os: result[x].str = "*" x += r.lwidth() + if x > result.width: break else: for r in os: result[x].str &= $r x += r.lwidth() + if x > result.width: break proc getCursorX*(edit: LineEdit): int = return edit.promptw + edit.news.lwidth(edit.shift, edit.cursor) diff --git a/src/io/term.nim b/src/io/term.nim index f892761b..82cb19fd 100644 --- a/src/io/term.nim +++ b/src/io/term.nim @@ -40,7 +40,7 @@ type outfile: File cleared: bool canvas: FixedGrid - prevgrid: FixedGrid + pcanvas: FixedGrid attrs*: WindowAttributes mincontrast: float colormode: ColorMode @@ -138,14 +138,6 @@ proc resetFormat(term: Terminal): string = return SGR() #TODO get rid of this -proc eraseLine*(term: Terminal) = - term.write(term.clearEnd()) - -#TODO ditto -proc resetFormat2*(term: Terminal) = - term.write(term.resetFormat()) - -#TODO ditto proc setCursor*(term: Terminal, x, y: int) = term.write(term.cursorGoto(x, y)) @@ -285,41 +277,35 @@ func generateFullOutput(term: Terminal, grid: FixedGrid): string = let cell = grid[y * grid.width + x] result &= term.processFormat(format, cell.format) result &= cell.str - result &= SGR() result &= term.clearEnd() if y != grid.height - 1: result &= "\r\n" -func generateSwapOutput(term: Terminal, grid: FixedGrid): string = +func generateSwapOutput(term: Terminal, grid: FixedGrid, prev: FixedGrid): string = var format = newFormat() - let curr = grid.cells - let prev = term.prevgrid.cells - var i = 0 var x = 0 - var y = 0 var line = "" var lr = false - while i < curr.len: + for i in 0 ..< grid.cells.len: if x >= grid.width: + format = newFormat() if lr: - result &= SGR() - result &= term.cursorGoto(0, y) + result &= term.cursorGoto(0, i div grid.width - 1) + result &= term.resetFormat() result &= term.clearEnd() result &= line lr = false x = 0 - inc y line = "" - lr = lr or (curr[i] != prev[i]) - line &= term.processFormat(format, curr[i].format) - line &= curr[i].str - inc i + lr = lr or (grid[i] != prev[i]) + line &= term.processFormat(format, grid.cells[i].format) + line &= grid.cells[i].str inc x if lr: - result &= term.cursorGoto(0, y) - result &= EL() + result &= term.cursorGoto(0, grid.height - 1) + result &= term.resetFormat() + result &= term.clearEnd() result &= line - lr = false proc hideCursor*(term: Terminal) = term.outfile.hideCursor() @@ -333,13 +319,13 @@ proc writeGrid*(term: Terminal, grid: FixedGrid, x = 0, y = 0) = term.canvas[ly * term.canvas.width + lx] = grid[(ly - y) * grid.width + (lx - x)] proc outputGrid*(term: Terminal) = - term.outfile.write(SGR()) + term.outfile.write(term.resetFormat()) if not term.cleared: term.outfile.write(term.generateFullOutput(term.canvas)) term.cleared = true else: - term.outfile.write(term.generateSwapOutput(term.canvas)) - term.prevgrid = term.canvas + term.outfile.write(term.generateSwapOutput(term.canvas, term.pcanvas)) + term.pcanvas = term.canvas when defined(posix): import posix diff --git a/src/layout/box.nim b/src/layout/box.nim index ef4f7e8a..8f460a19 100644 --- a/src/layout/box.nim +++ b/src/layout/box.nim @@ -4,6 +4,7 @@ import css/stylednode import css/values import html/dom import io/window +import types/color type #LayoutUnit* = distinct int32 @@ -59,6 +60,8 @@ type rowgroups*: seq[TableRowGroupBoxBuilder] width*: Option[CSSLength] # WIDTH property + TableCaptionBoxBuilder* = ref object of BlockBoxBuilder + InlineAtom* = ref object of RootObj offset*: Offset width*: int @@ -72,7 +75,7 @@ type fontstyle*: CSSFontStyle fontweight*: int textdecoration*: CSSTextDecoration - color*: CSSColor + color*: RGBAColor node*: StyledNode InlineSpacing* = ref object of InlineAtom @@ -141,10 +144,12 @@ type builder*: TableRowBoxBuilder TableContext* = object + caption*: TableCaptionBoxBuilder colwidths*: seq[int] reflow*: seq[bool] colwidths_specified*: seq[int] rows*: seq[RowContext] + maxwidth*: int InlineBlockBox* = ref object of InlineAtom bctx*: BlockBox diff --git a/src/layout/engine.nim b/src/layout/engine.nim index 0fa1c156..d1ce9f27 100644 --- a/src/layout/engine.nim +++ b/src/layout/engine.nim @@ -378,10 +378,13 @@ proc newFlowRootBox(viewport: Viewport, box: BoxBuilder, parentWidth: int, paren result.setPreferredDimensions(parentWidth, parentHeight) result.shrink = result.computed{"width"}.auto -proc newBlockBox(parent: BlockBox, box: BoxBuilder, ignore_parent_shrink = false): BlockBox = +proc newBlockBox(parent: BlockBox, box: BoxBuilder, ignore_parent_shrink = false, maxwidth = none(int)): BlockBox = new(result) result.newBlockBox_common2(parent, box) result.shrink = result.computed{"width"}.auto and (ignore_parent_shrink or parent.shrink) + if maxwidth.isSome: + #TODO TODO TODO this is ugly + result.setPreferredDimensions(maxwidth.get, parent.compheight) proc newTableCellBox(parent: BlockBox, box: TableCellBoxBuilder): BlockBox = return newBlockBox(parent, box, true) @@ -431,7 +434,7 @@ proc positionInlines(bctx: BlockBox) = else: bctx.width = bctx.compwidth -proc buildBlock(box: BlockBoxBuilder, parent: BlockBox): BlockBox +proc buildBlock(box: BlockBoxBuilder, parent: BlockBox, maxwidth = none(int)): BlockBox proc buildInlines(bctx: BlockBox, inlines: seq[BoxBuilder]): InlineContext proc buildBlocks(bctx: BlockBox, blocks: seq[BoxBuilder], node: StyledNode) @@ -678,34 +681,65 @@ proc buildTableRow(pctx: TableContext, ctx: RowContext, parent: BlockBox, builde row.width = x return row -iterator rows(builder: TableBoxBuilder): TableRowBoxBuilder = +iterator rows(builder: TableBoxBuilder): BoxBuilder {.inline.} = + var header: seq[TableRowBoxBuilder] + var body: seq[TableRowBoxBuilder] + var footer: seq[TableRowBoxBuilder] + var caption: TableCaptionBoxBuilder + #TODO this should be done for child in builder.children: - assert child.computed{"display"} in {DISPLAY_TABLE_ROW, DISPLAY_TABLE_ROW_GROUP} + assert child.computed{"display"} in ProperTableChild, $child.computed{"display"} case child.computed{"display"} of DISPLAY_TABLE_ROW: - yield TableRowBoxBuilder(child) + body.add(TableRowBoxBuilder(child)) + of DISPLAY_TABLE_HEADER_GROUP: + for child in child.children: + assert child.computed{"display"} == DISPLAY_TABLE_ROW + header.add(TableRowBoxBuilder(child)) of DISPLAY_TABLE_ROW_GROUP: - for child in TableRowGroupBoxBuilder(child).children: + for child in child.children: + assert child.computed{"display"} == DISPLAY_TABLE_ROW + body.add(TableRowBoxBuilder(child)) + of DISPLAY_TABLE_FOOTER_GROUP: + for child in child.children: assert child.computed{"display"} == DISPLAY_TABLE_ROW - yield TableRowBoxBuilder(child) + footer.add(TableRowBoxBuilder(child)) + of DISPLAY_TABLE_CAPTION: + if caption == nil: + caption = TableCaptionBoxBuilder(child) else: discard + yield caption + for child in header: + yield child + for child in body: + yield child + for child in footer: + yield child proc buildTable(box: TableBoxBuilder, parent: BlockBox): BlockBox = let table = parent.newTableBox(box) var ctx: TableContext - var maxw = 0 for row in box.rows: - let rctx = ctx.preBuildTableRow(row, table) - ctx.rows.add(rctx) - maxw = max(rctx.width, maxw) - if maxw > table.compwidth and false: #TODO - for n in ctx.colwidths_specified: - maxw -= n - ctx.reflow.setLen(ctx.colwidths.len) - for i in 0 ..< ctx.colwidths.len: - if ctx.colwidths[i] != 0: - ctx.colwidths[i] -= (maxw - table.compwidth) div ctx.colwidths[i] - ctx.reflow[i] = true + if unlikely(row.computed{"display"} == DISPLAY_TABLE_CAPTION): + ctx.caption = TableCaptionBoxBuilder(row) + else: + let row = TableRowBoxBuilder(row) + let rctx = ctx.preBuildTableRow(row, table) + ctx.rows.add(rctx) + ctx.maxwidth = max(rctx.width, ctx.maxwidth) + #TODO implement a better table layout + #if ctx.maxwidth > table.compwidth: + # for n in ctx.colwidths_specified: + # ctx.maxwidth -= n + # ctx.reflow.setLen(ctx.colwidths.len) + # for i in 0 ..< ctx.colwidths.len: + # if ctx.colwidths[i] != 0 and (ctx.colwidths_specified.len <= i or ctx.colwidths_specified[i] == 0): + # ctx.colwidths[i] -= (ctx.maxwidth - table.compwidth) div ctx.colwidths[i] + # ctx.reflow[i] = true + if ctx.caption != nil: + let caption = buildBlock(ctx.caption, table, some(ctx.maxwidth)) + table.nested.add(caption) + table.height += caption.height for roww in ctx.rows: let row = ctx.buildTableRow(roww, table, roww.builder) row.offset.y += table.height @@ -726,9 +760,9 @@ proc buildBlocks(bctx: BlockBox, blocks: seq[BoxBuilder], node: StyledNode) = bctx.positionBlocks() # Build a block box inside another block box, based on a builder. -proc buildBlock(box: BlockBoxBuilder, parent: BlockBox): BlockBox = +proc buildBlock(box: BlockBoxBuilder, parent: BlockBox, maxwidth = none(int)): BlockBox = assert parent != nil - result = parent.newBlockBox(box) + result = parent.newBlockBox(box, maxwidth = maxwidth) if box.inlinelayout: result.buildInlineLayout(box.children) else: @@ -803,16 +837,30 @@ proc getTableCellBox(computed: CSSComputedValues): TableCellBoxBuilder = result.computed = computed result.colspan = 1 +proc getTableCaptionBox(computed: CSSComputedValues): TableCaptionBoxBuilder = + new(result) + result.computed = computed + type BlockGroup = object parent: BoxBuilder boxes: seq[BoxBuilder] listItemCounter: int +type InnerBlockContext = object + styledNode: StyledNode + blockgroup: BlockGroup + viewport: Viewport + ibox: InlineBoxBuilder + anonRow: TableRowBoxBuilder + anonTable: TableBoxBuilder + proc add(blockgroup: var BlockGroup, box: BoxBuilder) {.inline.} = + assert box.computed{"display"} in {DISPLAY_INLINE, DISPLAY_INLINE_BLOCK}, $box.computed{"display"} blockgroup.boxes.add(box) proc flush(blockgroup: var BlockGroup) {.inline.} = if blockgroup.boxes.len > 0: + assert blockgroup.parent.computed{"display"} != DISPLAY_INLINE let bbox = getBlockBox(blockgroup.parent.computed.inheritProperties()) bbox.inlinelayout = true bbox.children = blockgroup.boxes @@ -825,13 +873,14 @@ func canGenerateAnonymousInline(blockgroup: BlockGroup, computed: CSSComputedVal computed{"white-space"} in {WHITESPACE_PRE_LINE, WHITESPACE_PRE, WHITESPACE_PRE_WRAP} or not str.onlyWhitespace() -template flush_ibox() = +proc iflush(blockgroup: var BlockGroup, ibox: var InlineBoxBuilder) = if ibox != nil: assert ibox.computed{"display"} in {DISPLAY_INLINE, DISPLAY_INLINE_BLOCK} blockgroup.add(ibox) ibox = nil proc newBlockGroup(parent: BoxBuilder): BlockGroup = + assert parent.computed{"display"} != DISPLAY_INLINE result.parent = parent result.listItemCounter = 1 @@ -839,163 +888,197 @@ proc generateTableBox(styledNode: StyledNode, viewport: Viewport): TableBoxBuild proc generateTableRowGroupBox(styledNode: StyledNode, viewport: Viewport): TableRowGroupBoxBuilder proc generateTableRowBox(styledNode: StyledNode, viewport: Viewport): TableRowBoxBuilder proc generateTableCellBox(styledNode: StyledNode, viewport: Viewport): TableCellBoxBuilder - +proc generateTableCaptionBox(styledNode: StyledNode, viewport: Viewport): TableCaptionBoxBuilder proc generateBlockBox(styledNode: StyledNode, viewport: Viewport, marker = none(MarkerBoxBuilder)): BlockBoxBuilder - -proc generateInlineBoxes(box: BoxBuilder, styledNode: StyledNode, blockgroup: var BlockGroup, viewport: Viewport) +proc generateInlineBoxes(ctx: var InnerBlockContext, styledNode: StyledNode) -proc generateFromElem(styledNode: StyledNode, blockgroup: var BlockGroup, viewport: Viewport, ibox: var InlineBoxBuilder) = - let box = blockgroup.parent +proc flushTableRow(ctx: var InnerBlockContext) = + if ctx.anonRow != nil: + if ctx.blockgroup.parent.computed{"display"} == DISPLAY_TABLE_ROW: + ctx.blockgroup.parent.children.add(ctx.anonRow) + else: + var wrappervals = ctx.styledNode.computed.inheritProperties() + wrappervals.setDisplay(DISPLAY_TABLE) + if ctx.anonTable == nil: + ctx.anonTable = getTableBox(wrappervals) + ctx.anonTable.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) + +proc iflush(ctx: var InnerBlockContext) = + ctx.blockgroup.iflush(ctx.ibox) + +proc bflush(ctx: var InnerBlockContext) = + ctx.iflush() + ctx.blockgroup.flush() + +proc flush(ctx: var InnerBlockContext) = + ctx.blockgroup.flush() + ctx.flushTableRow() + ctx.flushTable() + +proc generateFromElem(ctx: var InnerBlockContext, styledNode: StyledNode) = + let box = ctx.blockgroup.parent if styledNode.node != nil: let elem = Element(styledNode.node) if elem.tagType == TAG_BR: - ibox = box.getTextBox() - ibox.newline = true - flush_ibox + ctx.ibox = box.getTextBox() + ctx.ibox.newline = true + ctx.iflush() case styledNode.computed{"display"} of DISPLAY_BLOCK: - flush_ibox - blockgroup.flush() - let childbox = styledNode.generateBlockBox(viewport) + ctx.flush() + let childbox = styledNode.generateBlockBox(ctx.viewport) box.children.add(childbox) of DISPLAY_LIST_ITEM: - flush_ibox - blockgroup.flush() - let childbox = getListItemBox(styledNode.computed, blockgroup.listItemCounter) + ctx.flush() + let childbox = getListItemBox(styledNode.computed, ctx.blockgroup.listItemCounter) if childbox.computed{"list-style-position"} == LIST_STYLE_POSITION_INSIDE: - childbox.content = styledNode.generateBlockBox(viewport, some(childbox.marker)) + childbox.content = styledNode.generateBlockBox(ctx.viewport, some(childbox.marker)) childbox.marker = nil else: - childbox.content = styledNode.generateBlockBox(viewport) + childbox.content = styledNode.generateBlockBox(ctx.viewport) box.children.add(childbox) - inc blockgroup.listItemCounter + inc ctx.blockgroup.listItemCounter of DISPLAY_INLINE: - flush_ibox - box.generateInlineBoxes(styledNode, blockgroup, viewport) + ctx.iflush() + ctx.generateInlineBoxes(styledNode) of DISPLAY_INLINE_BLOCK: - flush_ibox + ctx.iflush() let childbox = getInlineBlockBox(styledNode.computed) - childbox.content = styledNode.generateBlockBox(viewport) - blockgroup.add(childbox) + childbox.content = styledNode.generateBlockBox(ctx.viewport) + ctx.blockgroup.add(childbox) of DISPLAY_TABLE: - flush_ibox - blockgroup.flush() - let childbox = styledNode.generateTableBox(viewport) + ctx.flush() + let childbox = styledNode.generateTableBox(ctx.viewport) box.children.add(childbox) of DISPLAY_TABLE_ROW: - flush_ibox - blockgroup.flush() - let childbox = styledNode.generateTableRowBox(viewport) - box.children.add(childbox) - of DISPLAY_TABLE_ROW_GROUP: - flush_ibox - blockgroup.flush() - let childbox = styledNode.generateTableRowGroupBox(viewport) - box.children.add(childbox) + ctx.bflush() + ctx.flushTableRow() + let childbox = styledNode.generateTableRowBox(ctx.viewport) + if box.computed{"display"} in ProperTableRowParent: + box.children.add(childbox) + else: + if ctx.anonTable == nil: + var wrappervals = box.computed.inheritProperties() + #TODO make this an inline-table if we're in an inline context + wrappervals.setDisplay(DISPLAY_TABLE) + ctx.anonTable = getTableBox(wrappervals) + ctx.anonTable.children.add(childbox) + of DISPLAY_TABLE_ROW_GROUP, DISPLAY_TABLE_HEADER_GROUP, DISPLAY_TABLE_FOOTER_GROUP: + ctx.bflush() + ctx.flushTableRow() + let childbox = styledNode.generateTableRowGroupBox(ctx.viewport) + if box.computed{"display"} in {DISPLAY_TABLE, DISPLAY_INLINE_TABLE}: + box.children.add(childbox) + else: + if ctx.anonTable == nil: + var wrappervals = box.computed.inheritProperties() + #TODO make this an inline-table if we're in an inline context + wrappervals.setDisplay(DISPLAY_TABLE) + ctx.anonTable = getTableBox(wrappervals) of DISPLAY_TABLE_CELL: - flush_ibox - blockgroup.flush() - let childbox = styledNode.generateTableCellBox(viewport) - box.children.add(childbox) + ctx.bflush() + let childbox = styledNode.generateTableCellBox(ctx.viewport) + if box.computed{"display"} == DISPLAY_TABLE_ROW: + box.children.add(childbox) + else: + if ctx.anonRow == nil: + var wrappervals = box.computed.inheritProperties() + wrappervals.setDisplay(DISPLAY_TABLE_ROW) + ctx.anonRow = getTableRowBox(wrappervals) + ctx.anonRow.children.add(childbox) + of DISPLAY_INLINE_TABLE: + ctx.iflush() + let childbox = styledNode.generateTableBox(ctx.viewport) + ctx.blockgroup.add(childbox) + of DISPLAY_TABLE_CAPTION: + ctx.bflush() + ctx.flushTableRow() + let childbox = styledNode.generateTableCaptionBox(ctx.viewport) + if box.computed{"display"} in {DISPLAY_TABLE, DISPLAY_INLINE_TABLE}: + box.children.add(childbox) + else: + if ctx.anonTable == nil: + var wrappervals = box.computed.inheritProperties() + #TODO make this an inline-table if we're in an inline context + wrappervals.setDisplay(DISPLAY_TABLE) + ctx.anonTable = getTableBox(wrappervals) of DISPLAY_TABLE_COLUMN: discard #TODO of DISPLAY_TABLE_COLUMN_GROUP: discard #TODO of DISPLAY_NONE: discard - else: - discard #TODO - -proc generateInlineBoxes(box: BoxBuilder, styledNode: StyledNode, blockgroup: var BlockGroup, viewport: Viewport) = - var ibox: InlineBoxBuilder = nil +proc generateInlineBoxes(ctx: var InnerBlockContext, styledNode: StyledNode) = for child in styledNode.children: case child.t of STYLED_ELEMENT: - generateFromElem(child, blockgroup, viewport, ibox) + ctx.generateFromElem(child) of STYLED_TEXT: - if ibox == nil: - ibox = getTextBox(styledNode.computed) - ibox.node = styledNode - ibox.text.add(child.text) - - flush_ibox + if ctx.ibox == nil: + ctx.ibox = getTextBox(styledNode.computed) + ctx.ibox.node = styledNode + ctx.ibox.text.add(child.text) + ctx.iflush() + +proc newInnerBlockContext(styledNode: StyledNode, blockgroup: BlockGroup, viewport: Viewport): InnerBlockContext = + return InnerBlockContext( + styledNode: styledNode, + blockgroup: blockgroup, + viewport: viewport + ) + +proc generateInnerBlockBox(ctx: var InnerBlockContext) = + let box = ctx.blockgroup.parent + assert box.computed{"display"} != DISPLAY_INLINE + for child in ctx.styledNode.children: + case child.t + of STYLED_ELEMENT: + ctx.iflush() + ctx.generateFromElem(child) + of STYLED_TEXT: + if canGenerateAnonymousInline(ctx.blockgroup, box.computed, child.text): + if ctx.ibox == nil: + ctx.ibox = getTextBox(ctx.styledNode.computed) + ctx.ibox.node = ctx.styledNode + ctx.ibox.text.add(child.text) + ctx.iflush() proc generateBlockBox(styledNode: StyledNode, viewport: Viewport, marker = none(MarkerBoxBuilder)): BlockBoxBuilder = let box = getBlockBox(styledNode.computed) - var blockgroup = newBlockGroup(box) - var ibox: InlineBoxBuilder = nil + var ctx = newInnerBlockContext(styledNode, newBlockGroup(box), viewport) if marker.issome: - ibox = marker.get - flush_ibox - - for child in styledNode.children: - case child.t - of STYLED_ELEMENT: - flush_ibox - generateFromElem(child, blockgroup, viewport, ibox) - of STYLED_TEXT: - if canGenerateAnonymousInline(blockgroup, box.computed, child.text): - if ibox == nil: - ibox = getTextBox(styledNode.computed) - ibox.node = styledNode - ibox.text.add(child.text) + ctx.ibox = marker.get + ctx.iflush() - flush_ibox - if blockgroup.boxes.len > 0: + ctx.generateInnerBlockBox() + + if ctx.blockgroup.boxes.len > 0: # Avoid unnecessary anonymous block boxes if box.children.len == 0: - box.children = blockgroup.boxes + box.children = ctx.blockgroup.boxes box.inlinelayout = true else: - blockgroup.flush() + ctx.blockgroup.flush() + ctx.flushTableRow() + ctx.flushTable() return box -const RowGroupBox = {DISPLAY_TABLE_ROW_GROUP, DISPLAY_TABLE_HEADER_GROUP, - DISPLAY_TABLE_FOOTER_GROUP} -const ProperTableChild = {DISPLAY_TABLE_ROW, DISPLAY_TABLE_COLUMN, - DISPLAY_TABLE_COLUMN_GROUP} + RowGroupBox -const ProperTableRowParent = {DISPLAY_TABLE, DISPLAY_INLINE_TABLE} + RowGroupBox -const InternalTableBox = {DISPLAY_TABLE_CELL, DISPLAY_TABLE_ROW, DISPLAY_TABLE_COLUMN, DISPLAY_TABLE_COLUMN_GROUP} + RowGroupBox -const TabularContainer = {DISPLAY_TABLE_ROW} + ProperTableRowParent - -# Whether an internal table box is misparented. -func isMisparented(box: BoxBuilder, parent: BoxBuilder): bool = - case box.computed{"display"} - of DISPLAY_TABLE_ROW: - return parent.computed{"display"} notin {DISPLAY_TABLE_COLUMN_GROUP, DISPLAY_TABLE, DISPLAY_INLINE_TABLE} - of DISPLAY_TABLE_COLUMN: - return parent.computed{"display"} notin {DISPLAY_TABLE_COLUMN_GROUP, DISPLAY_TABLE, DISPLAY_INLINE_TABLE} - of RowGroupBox, DISPLAY_TABLE_COLUMN_GROUP, DISPLAY_TABLE_CAPTION: - return parent.computed{"display"} notin {DISPLAY_TABLE, DISPLAY_INLINE_TABLE} - else: assert false - proc generateTableCellBox(styledNode: StyledNode, viewport: Viewport): TableCellBoxBuilder = let box = getTableCellBox(styledNode.computed) if styledNode.node != nil and styledNode.node.nodeType == ELEMENT_NODE: box.colspan = Element(styledNode.node).attri("colspan").get(1) - var blockgroup = newBlockGroup(box) - var ibox: InlineBoxBuilder = nil - for child in styledNode.children: - if child.t == STYLED_ELEMENT: - flush_ibox - generateFromElem(child, blockgroup, viewport, ibox) - else: - if canGenerateAnonymousInline(blockgroup, box.computed, child.text): - if ibox == nil: - ibox = getTextBox(styledNode.computed) - ibox.node = styledNode - ibox.text.add(child.text) - flush_ibox - if blockgroup.boxes.len > 0: - # Avoid unnecessary anonymous block boxes - if box.children.len == 0: - box.children = blockgroup.boxes - box.inlinelayout = true - else: - blockgroup.flush() + var ctx = newInnerBlockContext(styledNode, newBlockGroup(box), viewport) + ctx.generateInnerBlockBox() + ctx.flush() return box proc generateTableRowChildWrappers(box: TableRowBoxBuilder) = @@ -1013,49 +1096,41 @@ proc generateTableRowChildWrappers(box: TableRowBoxBuilder) = proc generateTableRowBox(styledNode: StyledNode, viewport: Viewport): TableRowBoxBuilder = let box = getTableRowBox(styledNode.computed) - var blockgroup = newBlockGroup(box) - var ibox: InlineBoxBuilder = nil - for child in styledNode.children: - if child.t == STYLED_ELEMENT: - generateFromElem(child, blockgroup, viewport, ibox) - else: - if canGenerateAnonymousInline(blockgroup, box.computed, child.text): - if ibox == nil: - ibox = getTextBox(styledNode.computed) - ibox.node = styledNode - ibox.text.add(child.text) + var ctx = newInnerBlockContext(styledNode, newBlockGroup(box), viewport) + ctx.generateInnerBlockBox() + ctx.flush() box.generateTableRowChildWrappers() return box proc generateTableRowGroupChildWrappers(box: TableRowGroupBoxBuilder) = var newchildren = newSeqOfCap[BoxBuilder](box.children.len) var wrappervals = box.computed.inheritProperties() - wrappervals.setDisplay(DISPLAY_TABLE_CELL) + wrappervals.setDisplay(DISPLAY_TABLE_ROW) for child in box.children: if child.computed{"display"} == DISPLAY_TABLE_ROW: newchildren.add(child) else: let wrapper = getTableRowBox(wrappervals) wrapper.children.add(child) + wrapper.generateTableRowChildWrappers() newchildren.add(wrapper) box.children = newchildren proc generateTableRowGroupBox(styledNode: StyledNode, viewport: Viewport): TableRowGroupBoxBuilder = let box = getTableRowGroupBox(styledNode.computed) - var blockgroup = newBlockGroup(box) - var ibox: InlineBoxBuilder = nil - for child in styledNode.children: - if child.t == STYLED_ELEMENT: - generateFromElem(child, blockgroup, viewport, ibox) - else: - if canGenerateAnonymousInline(blockgroup, box.computed, child.text): - if ibox == nil: - ibox = getTextBox(styledNode.computed) - ibox.node = styledNode - ibox.text.add(child.text) + var ctx = newInnerBlockContext(styledNode, newBlockGroup(box), viewport) + ctx.generateInnerBlockBox() + ctx.flush() box.generateTableRowGroupChildWrappers() return box +proc generateTableCaptionBox(styledNode: StyledNode, viewport: Viewport): TableCaptionBoxBuilder = + let box = getTableCaptionBox(styledNode.computed) + var ctx = newInnerBlockContext(styledNode, newBlockGroup(box), viewport) + ctx.generateInnerBlockBox() + ctx.flush() + return box + proc generateTableChildWrappers(box: TableBoxBuilder) = var newchildren = newSeqOfCap[BoxBuilder](box.children.len) var wrappervals = box.computed.inheritProperties() @@ -1072,25 +1147,10 @@ proc generateTableChildWrappers(box: TableBoxBuilder) = proc generateTableBox(styledNode: StyledNode, viewport: Viewport): TableBoxBuilder = let box = getTableBox(styledNode.computed) - var blockgroup = newBlockGroup(box) #TODO this probably shouldn't exist - if styledNode.node != nil and styledNode.node.nodeType == ELEMENT_NODE: - #TODO put this in dom or something - let s = Element(styledNode.node).attr("width") - box.width = parseDimensionValues(s) - var ibox: InlineBoxBuilder = nil - for child in styledNode.children: - if child.t == STYLED_ELEMENT: - generateFromElem(child, blockgroup, viewport, ibox) - else: - if canGenerateAnonymousInline(blockgroup, box.computed, child.text): - if ibox == nil: - ibox = getTextBox(styledNode.computed) - ibox.node = styledNode - ibox.text.add(child.text) - flush_ibox - blockgroup.flush() + var ctx = newInnerBlockContext(styledNode, newBlockGroup(box), viewport) + ctx.generateInnerBlockBox() + ctx.flush() box.generateTableChildWrappers() - #TODO generate missing parents return box proc renderLayout*(viewport: var Viewport, document: Document, root: StyledNode) = diff --git a/src/render/renderdocument.nim b/src/render/renderdocument.nim index a039cc71..9eebe61d 100644 --- a/src/render/renderdocument.nim +++ b/src/render/renderdocument.nim @@ -160,7 +160,7 @@ proc setSpacing(lines: var FlexibleGrid, spacing: InlineSpacing, x, y: int, wind lines.setText(linestr, spacing.format, x, y) -proc paintBackground(lines: var FlexibleGrid, color: CSSColor, startx, starty, endx, endy: int, window: WindowAttributes) = +proc paintBackground(lines: var FlexibleGrid, color: RGBAColor, startx, starty, endx, endy: int, window: WindowAttributes) = let color = color.cellColor() var starty = starty div window.ppl diff --git a/src/types/color.nim b/src/types/color.nim index e85a254e..9273c794 100644 --- a/src/types/color.nim +++ b/src/types/color.nim @@ -258,24 +258,14 @@ func parseLegacyColor*(s: string): Option[RGBColor] = (hexValue(s[1]) * 17 shl 8) or (hexValue(s[2]) * 17) return some(RGBColor(c)) - block sane: - var c: Option[RGBAColor] - for c in s: - if hexValue(c) == -1: - break sane - if s[0] == '#' and s.len == 8: - c = parseHexColor(s[1..^1]) - elif s.len == 8: - c = parseHexColor(s) - else: - break sane - if c.isSome: - return some(RGBColor(c.get)) # Seriously, what the hell. var s2 = if s[0] == '#': s.substr(1) else: s + for i in 0 ..< s2.len: + if hexValue(s2[i]) == -1: + s2[i] = '0' while s2.len == 0 or s2.len mod 3 != 0: s2 &= '0' var l = s2.len div 3 |