about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2021-08-28 23:15:46 +0200
committerbptato <nincsnevem662@gmail.com>2021-08-28 23:15:46 +0200
commit9996586865f931b6da19779520eb7671eddc6c4d (patch)
tree626f98684ffc9b8aba51f863cec72ad4962c0d01
parent672ceca5730f6ff1b17a715f88214ff2a8e895c3 (diff)
downloadchawan-9996586865f931b6da19779520eb7671eddc6c4d.tar.gz
Rewrite renderer (still non-functional)
-rw-r--r--src/css/box.nim10
-rw-r--r--src/html/renderer.nim158
-rw-r--r--src/io/buffer.nim39
-rw-r--r--src/io/cell.nim11
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()