diff options
author | bptato <nincsnevem662@gmail.com> | 2021-08-28 23:15:46 +0200 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2021-08-28 23:15:46 +0200 |
commit | 9996586865f931b6da19779520eb7671eddc6c4d (patch) | |
tree | 626f98684ffc9b8aba51f863cec72ad4962c0d01 | |
parent | 672ceca5730f6ff1b17a715f88214ff2a8e895c3 (diff) | |
download | chawan-9996586865f931b6da19779520eb7671eddc6c4d.tar.gz |
Rewrite renderer (still non-functional)
-rw-r--r-- | src/css/box.nim | 10 | ||||
-rw-r--r-- | src/html/renderer.nim | 158 | ||||
-rw-r--r-- | src/io/buffer.nim | 39 | ||||
-rw-r--r-- | src/io/cell.nim | 11 |
4 files changed, 140 insertions, 78 deletions
diff --git a/src/css/box.nim b/src/css/box.nim index 0994dca0..e0777878 100644 --- a/src/css/box.nim +++ b/src/css/box.nim @@ -12,7 +12,6 @@ type CSSBox* = ref CSSBoxObj CSSBoxObj = object of RootObj - content*: seq[Rune] innerEdge*: CSSRect paddingEdge*: CSSRect borderEdge*: CSSRect @@ -21,7 +20,8 @@ type CSSInlineBox* = ref CSSInlineBoxObj CSSInlineBoxObj = object of CSSBox - nextpart*: CSSInlineBox + fromx*: int + content*: FlexibleGrid CSSBlockBox* = ref CSSBlockBoxObj CSSBlockBoxObj = object of CSSBox @@ -37,3 +37,9 @@ proc `+=`(a: var CSSRect, b: CSSRect) = func size*(box: CSSBox): tuple[w: int, h: int] = return (box.innerEdge.x2 - box.innerEdge.x1, box.innerEdge.y2 - box.innerEdge.x1) + +func w*(box: CSSBox): int = + return box.innerEdge.x2 - box.innerEdge.x1 + +func h*(box: CSSBox): int = + return box.innerEdge.y2 - box.innerEdge.y1 diff --git a/src/html/renderer.nim b/src/html/renderer.nim index 50eac169..9e947534 100644 --- a/src/html/renderer.nim +++ b/src/html/renderer.nim @@ -5,87 +5,107 @@ import html/dom import css/box import css/style import io/buffer +import io/cell import utils/twtstr -func boxesForText*(text: seq[Rune], width: int, height: int, lx: int, x: int, y: int): seq[CSSInlineBox] = - result.add(CSSInlineBox()) - var w = x - var sx = x - var sy = y +# basically these are the "line boxes". though honestly this is an awful +# way to model them... but it's fine for now, I guess. TODO +# no it's actually not fine number one priority is making it work TODO TODO TODO +proc generateGrids(text: Text, maxwidth: int, maxheight: int, x: int, y: int, fromx: int = x): FlexibleGrid = + var r: Rune + var rowgrid: FlexibleLine var i = 0 - var whitespace = false - while i < text.len and sy < height: - let cw = text[i].width() - if w + cw > width: - result[^1].innerEdge.x2 = sx + w - result[^1].innerEdge.y2 = sy + 1 - sx = lx - inc sy - w = 0 + if fromx > x: + let m = fromx - x + maxwidth + var w = 0 + while i < text.data.len: + let pi = i + fastRuneAt(text.data, i, r) + let rw = r.width() + if rw + w > m: + i = pi + break + else: + rowgrid.add(FlexibleCell(rune: r)) + w += rw + result.add(rowgrid) + + if i < text.data.len: + rowgrid.setLen(0) + var w = 0 + while i < text.data.len: + let pi = i + fastRuneAt(text.data, i, r) + let rw = r.width() + if rw + w > maxwidth: + i = pi + w = 0 + result.add(rowgrid) + rowgrid.setLen(0) + else: + rowgrid.add(FlexibleCell(rune: r)) + w += rw - result[^2].nextpart = result[^1] - result.add(CSSInlineBox()) - result[^1].innerEdge.x1 = sx - result[^1].innerEdge.y1 = sy + if rowgrid.len > 0: + result.add(rowgrid) - if text[i].isWhitespace(): - if not whitespace: - whitespace = true - result[^1].content &= text[i] - else: - whitespace = false - result[^1].content &= text[i] - w += cw - inc i +proc generateBox(text: Text, maxwidth: int, maxheight: int, x: int, y: int, fromx: int): CSSInlineBox = + new(result) + result.content = text.generateGrids(maxwidth, maxheight, x, y, fromx) + result.fromx = fromx + result.innerEdge.x1 = x + result.innerEdge.y1 = y + var height = 0 + var width = 0 + for grid in result.content: + inc height + width = max(width, grid.len) + + height = min(height, maxheight) + width = min(width, maxwidth) + result.innerEdge.x2 = x + width + result.innerEdge.y2 = y + height - result[^1].innerEdge.y1 = sy - if result.len > 1: - result[^2].nextpart = result[^1] - if w > 0: - result[^1].innerEdge.x1 = sx - result[^1].innerEdge.y1 = sy - result[^1].innerEdge.x2 = sx + w - result[^1].innerEdge.y2 = sy + 1 +proc generateBox(elem: Element, maxwidth: int, maxheight: int, x: int = 0, y: int = 0, fromx: int = x): CSSBox -#func insertText(text: seq[Rune], +proc generateChildBoxes(elem: Element, maxwidth: int, maxheight: int, x: int, y: int, fromx: int = 0): seq[CSSBox] = + var cx = fromx + var cy = y + for child in elem.childNodes: + case child.nodeType + of TEXT_NODE: + let box = Text(child).generateBox(maxwidth, maxheight, x, cy, cx) + if box != nil: + result.add(box) + cy += box.h + if box.content.len > 0: + cx = box.content[^1].width() + of ELEMENT_NODE: + let box = Element(child).generateBox(maxwidth, maxheight, x, cy, cx) + if box != nil: + result.add(box) + else: + discard -proc generateBox(elem: Element, x: int, y: int, w: int, h: int, fromx: int = x): CSSBox = +proc generateBox(elem: Element, maxwidth: int, maxheight: int, x: int = 0, y: int = 0, fromx: int = x): CSSBox = if elem.cssvalues[RULE_DISPLAY].display == DISPLAY_NONE: return nil - new(result) + + result = CSSBlockBox() result.innerEdge.x1 = x result.innerEdge.y1 = y - var anonBoxes = false - for child in elem.children: - if child.cssvalues[RULE_DISPLAY].display == DISPLAY_BLOCK: - anonBoxes = true - break - var lx = fromx - var rx = x - var ry = y - for child in elem.childNodes: - if child.nodeType == ELEMENT_NODE: - let elem = Element(child) - let nbox = elem.generateBox(rx, ry, w, h) - if nbox != nil: - result.innerEdge.x2 = min(max(nbox.size().w, result.innerEdge.x2), w) - result.innerEdge.y2 = min(result.innerEdge.y2 + nbox.size().h, w) - rx = x - ry += nbox.size().h - result.children.add(nbox) - elif child.nodeType == TEXT_NODE: - let text = Text(child) - let runes = text.data.toRunes() - if anonBoxes: - let boxes = boxesForText(runes, w, h, lx, rx, ry) - for child in boxes: - result.children.add(child) - result.innerEdge.y2 += child.size().h - ry += child.size().h - lx = boxes[^1].innerEdge.x2 - else: - discard #TODO TODO TODO + var width = 0 + var height = 0 + for box in elem.generateChildBoxes(maxwidth, maxheight, x, y, fromx): + result.children.add(box) + height += box.h + height = min(height, maxheight) + width = max(width, box.w) + width = min(width, maxwidth) + + result.innerEdge.x2 = x + width + result.innerEdge.y2 = y + height proc alignBoxes*(buffer: Buffer) = - buffer.rootbox = buffer.document.root.generateBox(0, 0, buffer.width, buffer.height) + buffer.rootbox = buffer.document.root.generateBox(buffer.width, buffer.height) diff --git a/src/io/buffer.nim b/src/io/buffer.nim index c299e79c..cfea36e3 100644 --- a/src/io/buffer.nim +++ b/src/io/buffer.nim @@ -67,9 +67,9 @@ func newBuffer*(attrs: TermAttributes): Buffer = result.height = attrs.termHeight - 1 result.attrs = attrs - result.display = newSeq[FixedCell](result.width * result.height) - result.prevdisplay = newSeq[FixedCell](result.width * result.height) - result.statusmsg = newSeq[FixedCell](result.width) + result.display = newFixedGrid(result.width, result.height) + result.prevdisplay = newFixedGrid(result.width, result.height) + result.statusmsg = newFixedGrid(result.width) func generateFullOutput*(buffer: Buffer): seq[string] = var x = 0 @@ -510,9 +510,25 @@ proc refreshTermAttrs*(buffer: Buffer): bool = proc setText*(buffer: Buffer, x: int, y: int, text: seq[Rune]) = buffer.lines.setText(x, y, text) +proc setLine*(buffer: Buffer, x: int, y: int, line: FlexibleLine) = + while buffer.lines.len <= y: + buffer.lines.add(newSeq[FlexibleCell]()) + + var i = 0 + var cx = 0 + while cx < x and i < buffer.lines[y].len: + cx += buffer.lines[y][i].rune.width() + inc i + + buffer.lines[y].setLen(i) + i = 0 + while i < line.len: + buffer.lines[y].add(line[i]) + inc i + proc reshape*(buffer: Buffer) = - buffer.display = newSeq[FixedCell](buffer.width * buffer.height) - buffer.statusmsg = newSeq[FixedCell](buffer.width) + buffer.display = newFixedGrid(buffer.width, buffer.height) + buffer.statusmsg = newFixedGrid(buffer.width) proc clearDisplay*(buffer: Buffer) = var i = 0 @@ -524,6 +540,9 @@ proc refreshDisplay*(buffer: Buffer) = var y = 0 buffer.prevdisplay = buffer.display buffer.clearDisplay() + if buffer.fromy > buffer.lastVisibleLine - 1: + buffer.fromy = 0 + buffer.cursory = buffer.lastVisibleLine - 1 for line in buffer.lines[buffer.fromy..buffer.lastVisibleLine - 1]: var w = 0 var i = 0 @@ -572,7 +591,15 @@ proc renderDocument*(buffer: Buffer) = stack.add(buffer.rootbox) while stack.len > 0: let box = stack.pop() - buffer.setText(box.innerEdge.x1, box.innerEdge.y1, box.content) + if box of CSSInlineBox: + let inline = CSSInlineBox(box) + var i = 0 + for line in inline.content: + var x = inline.innerEdge.x1 + if i == 0: + x = inline.fromx + var y = box.innerEdge.y1 + i + buffer.setLine(x, y, line) for child in box.children: stack.add(child) diff --git a/src/io/cell.nim b/src/io/cell.nim index d4a55b14..6ea887e3 100644 --- a/src/io/cell.nim +++ b/src/io/cell.nim @@ -1,6 +1,7 @@ import unicode import types/color +import utils/twtstr type Cell* = object of RootObj @@ -13,7 +14,9 @@ type FlexibleCell* = object of Cell rune*: Rune - FlexibleGrid* = seq[seq[FlexibleCell]] + FlexibleLine* = seq[FlexibleCell] + + FlexibleGrid* = seq[FlexibleLine] FixedCell* = object of Cell runes*: seq[Rune] @@ -32,3 +35,9 @@ proc setText*(grid: var FlexibleGrid, x: int, y: int, text: seq[Rune]) = grid[y][i].rune = text[i] inc i +func newFixedGrid*(w: int, h: int = 1): FixedGrid = + return newSeq[FixedCell](w * h) + +func width*(line: FlexibleLine): int = + for c in line: + result += c.rune.width() |