about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2021-08-10 15:19:32 +0200
committerbptato <nincsnevem662@gmail.com>2021-08-10 15:19:32 +0200
commita5f7da0428fdb74d9691cd6d589047478f422898 (patch)
tree0bbea58202a8f509c83378a4b57bbe419131c7d5
parent3d24875924b088e5d771e4b901f692659c5281c0 (diff)
downloadchawan-a5f7da0428fdb74d9691cd6d589047478f422898.tar.gz
Some refactoring
-rw-r--r--res/config24
-rw-r--r--res/default.css19
-rw-r--r--src/css/box.nim35
-rw-r--r--src/css/cssparser.nim5
-rw-r--r--src/html/dom.nim36
-rw-r--r--src/html/renderer.nim82
-rw-r--r--src/io/buffer.nim55
-rw-r--r--src/io/cell.nim34
-rw-r--r--src/main.nim5
9 files changed, 178 insertions, 117 deletions
diff --git a/res/config b/res/config
index 9fce379b..8e7eb7f1 100644
--- a/res/config
+++ b/res/config
@@ -79,6 +79,18 @@ comp i' í
 comp o' ó
 comp u' ú
 
+COMP A: Ä
+COMP O: Ö
+COMP U: Ü
+COMP U" Ű
+COMP O" Ő
+
+COMP A' Á
+COMP E' É
+COMP I' Í
+COMP O' Ó
+COMP U' Ú
+
 comp sS ß
 comp n~ ñ
 
@@ -442,6 +454,18 @@ comp TTI ッティ
 comp DI ディ
 comp DDI ッディ
 
+comp KKYA ッキャ
+comp KKYU ッキュ
+comp KKYO ッキョ
+
+comp SSHA ッシャ
+comp SSHU ッシュ
+comp SSHO ッショ
+
+comp CCHA ッチャ
+comp CCHU ッチュ
+comp CCHO ッチョ
+
 comp SHE シェ
 comp SSHE ッシェ
 
diff --git a/res/default.css b/res/default.css
index 02f0683a..6a176492 100644
--- a/res/default.css
+++ b/res/default.css
@@ -14,15 +14,26 @@ br {
 }
 
 a, abbr, b, bdo, button, cite, code, del, dfn, em, font, i, img, ins,
-input, iframe, kbd, label, map, object, q, samp, select, small, span, strong,
-sub, sup, textarea, tt, var, font, iframe, u, s, strike, frame, img, input {
-	display: inline;
+iframe, kbd, label, map, object, q, samp, select, small, span, strong
+{
+	display: inline
 }
 
-i {
+sub, sup, textarea, tt, var, font, iframe, u, s, strike, frame, input, img {
+	display: inline-block;
+}
+
+i, em {
 	font-style: italic;
 }
 
+b, strong {
+	font-weight: bold;
+}
+
+u, ins {
+	text-decoration: underline;
+
 col {
 	display: table-column;
 }
diff --git a/src/css/box.nim b/src/css/box.nim
index 3b74380a..0994dca0 100644
--- a/src/css/box.nim
+++ b/src/css/box.nim
@@ -1,6 +1,7 @@
 import unicode
 
 import utils/twtstr
+import io/cell
 
 type
   CSSRect* = object
@@ -20,7 +21,7 @@ type
 
   CSSInlineBox* = ref CSSInlineBoxObj
   CSSInlineBoxObj = object of CSSBox
-    nextpart: CSSInlineBox
+    nextpart*: CSSInlineBox
 
   CSSBlockBox* = ref CSSBlockBoxObj
   CSSBlockBoxObj = object of CSSBox
@@ -36,35 +37,3 @@ 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 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
-  var i = 0
-  while i < text.len and sy < height:
-    let cw = text[i].width()
-    if w + cw > width:
-      result[^1].innerEdge.x1 = sx
-      result[^1].innerEdge.x2 = sx + w
-      result[^1].innerEdge.y1 = sy
-      result[^1].innerEdge.y2 = sy + 1
-      sx = lx
-      inc sy
-      w = 0
-
-      result[^2].nextpart = result[^1]
-      result.add(CSSInlineBox())
-
-    result[^1].content &= text[i]
-    w += cw
-    inc i
-
-  if result.len > 1:
-    result[^2].nextpart = result[^1]
-  if w > 0:
-    result[^1].innerEdge.x1 = sx
-    result[^1].innerEdge.x2 = sx + w
-    result[^1].innerEdge.y1 = sy
-    result[^1].innerEdge.y2 = sy + 1
diff --git a/src/css/cssparser.nim b/src/css/cssparser.nim
index afbdf413..fac1bce8 100644
--- a/src/css/cssparser.nim
+++ b/src/css/cssparser.nim
@@ -1,6 +1,5 @@
-# CSS tokenizer and parser. The tokenizer is a mess, and may or may not work
-# correctly. The parser should work, though the outputted object model is
-# questionable at best.
+# CSS tokenizer and parser. It kinda works, though certain less specific
+# details of the specification might have been implemented incorrectly.
 
 import unicode
 import streams
diff --git a/src/html/dom.nim b/src/html/dom.nim
index c7d3a650..dde6525f 100644
--- a/src/html/dom.nim
+++ b/src/html/dom.nim
@@ -11,7 +11,6 @@ import algorithm
 import css/style
 import css/cssparser
 import css/selector
-import css/box
 import types/enums
 import utils/twtstr
 
@@ -89,7 +88,6 @@ type
     attributes*: Table[string, Attr]
     style*: CSS2Properties
     cssvalues*: seq[CSSComputedValue]
-    box*: CSSBox
 
   HTMLElement* = ref HTMLElementObj
   HTMLElementObj = object of ElementObj
@@ -478,39 +476,6 @@ proc applyRules*(document: Document, rules: CSSStylesheet): seq[tuple[e:Element,
     for child in elem.children:
       stack.add(child)
 
-proc generateBox*(elem: Element, x: int, y: int, w: int, h: int) =
-  let display = elem.cssvalues.getValue(RULE_DISPLAY)
-  if display.t == RULE_DISPLAY:
-    if display.display == DISPLAY_NONE:
-      return
-  var box = new(CSSBlockBox)
-  box.innerEdge.x1 = x
-  box.innerEdge.y1 = y
-
-  var lx = x
-  var rx = x
-  var ry = y
-  for child in elem.childNodes:
-    if child.nodeType == ELEMENT_NODE:
-      let elem = Element(child)
-      elem.generateBox(rx, ry, w, h)
-      if elem.box != nil:
-        box.innerEdge.x2 += elem.box.size().w
-        box.innerEdge.y2 += elem.box.size().h
-        rx = x
-        lx = x
-        ry += elem.box.size().h
-        box.children.add(elem.box)
-    elif child.nodeType == TEXT_NODE:
-      let text = Text(child)
-      let runes = text.data.toRunes()
-      let boxes = boxesForText(runes, w, h, lx, rx, ry)
-      for child in boxes:
-        box.children.add(child)
-        box.innerEdge.y2 += child.size().h
-        ry += child.size().h
-      lx = boxes[^1].innerEdge.x2
-  elem.box = box
 
 proc applyDefaultStylesheet*(document: Document) =
   let important = document.applyRules(stylesheet)
@@ -518,4 +483,3 @@ proc applyDefaultStylesheet*(document: Document) =
     rule.e.style.applyProperty(rule.d)
     rule.e.cssvalues.add(getComputedValue(rule.d))
 
-  document.root.generateBox(0, 0, 80, 24)
diff --git a/src/html/renderer.nim b/src/html/renderer.nim
new file mode 100644
index 00000000..6b63cf29
--- /dev/null
+++ b/src/html/renderer.nim
@@ -0,0 +1,82 @@
+import unicode
+
+import types/enums
+import html/dom
+import css/box
+import css/style
+import io/buffer
+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
+  var i = 0
+  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
+
+      result[^2].nextpart = result[^1]
+      result.add(CSSInlineBox())
+      result[^1].innerEdge.x1 = sx
+      result[^1].innerEdge.y1 = sy
+
+    result[^1].content &= text[i]
+    w += cw
+    inc i
+
+    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, x: int, y: int, w: int, h: int, fromx: int = x): CSSBox =
+  let display = elem.cssvalues.getValue(RULE_DISPLAY)
+  if display.t == RULE_DISPLAY:
+    if display.display == DISPLAY_NONE:
+      return nil
+  new(result)
+  result.innerEdge.x1 = x
+  result.innerEdge.y1 = y
+
+  var anonBoxes = false
+  for child in elem.children:
+    let rule = child.cssvalues.getValue(RULE_DISPLAY)
+    if rule.t == RULE_DISPLAY and rule.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()
+      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
+
+proc alignBoxes*(buffer: Buffer) =
+  buffer.rootbox = buffer.document.root.generateBox(0, 0, buffer.width, buffer.height)
diff --git a/src/io/buffer.nim b/src/io/buffer.nim
index 6288bf88..637fc87b 100644
--- a/src/io/buffer.nim
+++ b/src/io/buffer.nim
@@ -13,25 +13,9 @@ import css/box
 import config/config
 import io/term
 import io/lineedit
+import io/cell
 
 type
-  Cell = object of RootObj
-    fgcolor*: CellColor
-    bgcolor*: CellColor
-    italic: bool
-    bold: bool
-    underline: bool
-
-  BufferCell = object of Cell
-    rune*: Rune
-
-  BufferRow = seq[BufferCell]
-
-  DisplayCell = object of Cell
-    runes*: seq[Rune]
-
-  DisplayRow = seq[DisplayCell]
-
   DrawInstruction = object
     case t: DrawInstructionType
     of DRAW_TEXT:
@@ -51,10 +35,10 @@ type
   Buffer* = ref BufferObj
   BufferObj = object
     title*: string
-    lines*: seq[BufferRow]
-    display*: DisplayRow
-    prevdisplay*: DisplayRow
-    statusmsg*: DisplayRow
+    lines*: FlexibleGrid
+    display*: FixedGrid
+    prevdisplay*: FixedGrid
+    statusmsg*: FixedGrid
     hovertext*: string
     width*: int
     height*: int
@@ -75,6 +59,7 @@ type
     location*: Uri
     source*: string #TODO
     showsource*: bool
+    rootbox*: CSSBox
 
 func newBuffer*(attrs: TermAttributes): Buffer =
   new(result)
@@ -82,9 +67,9 @@ func newBuffer*(attrs: TermAttributes): Buffer =
   result.height = attrs.termHeight - 1
   result.attrs = attrs
 
-  result.display = newSeq[DisplayCell](result.width * result.height)
-  result.prevdisplay = newSeq[DisplayCell](result.width * result.height)
-  result.statusmsg = newSeq[DisplayCell](result.width)
+  result.display = newSeq[FixedCell](result.width * result.height)
+  result.prevdisplay = newSeq[FixedCell](result.width * result.height)
+  result.statusmsg = newSeq[FixedCell](result.width)
 
 func generateFullOutput*(buffer: Buffer): seq[string] =
   var x = 0
@@ -192,7 +177,7 @@ func numLines*(buffer: Buffer): int = buffer.lines.len
 
 func lastVisibleLine*(buffer: Buffer): int = min(buffer.fromy + buffer.height, buffer.numLines - 1)
 
-func width(line: seq[BufferCell]): int =
+func width(line: seq[FlexibleCell]): int =
   for c in line:
     result += c.rune.width()
 
@@ -553,21 +538,11 @@ proc refreshTermAttrs*(buffer: Buffer): bool =
     return true
   return false
 
-proc setText*(buffer: Buffer, x: int, y: int, text: seq[Rune]) =
-  while buffer.lines.len <= y:
-    buffer.lines.add(newSeq[BufferCell]())
-
-  while buffer.lines[y].len < x + text.len:
-    buffer.lines[y].add(BufferCell())
-  
-  var i = 0
-  while i < text.len:
-    buffer.lines[y][i].rune = text[i]
-    inc i
+proc setText*(buffer: Buffer, x: int, y: int, text: seq[Rune]) = buffer.lines.setText(x, y, text)
 
 proc reshape*(buffer: Buffer) =
-  buffer.display = newSeq[DisplayCell](buffer.width * buffer.height)
-  buffer.statusmsg = newSeq[DisplayCell](buffer.width)
+  buffer.display = newSeq[FixedCell](buffer.width * buffer.height)
+  buffer.statusmsg = newSeq[FixedCell](buffer.width)
 
 proc clearDisplay*(buffer: Buffer) =
   var i = 0
@@ -620,8 +595,10 @@ proc renderPlainText*(buffer: Buffer, text: string) =
 
 proc renderDocument*(buffer: Buffer) =
   buffer.clearText()
+  if buffer.rootbox == nil:
+    return
   var stack: seq[CSSBox]
-  stack.add(buffer.document.root.box)
+  stack.add(buffer.rootbox)
   while stack.len > 0:
     let box = stack.pop()
     buffer.setText(box.innerEdge.x1, box.innerEdge.y1, box.content)
diff --git a/src/io/cell.nim b/src/io/cell.nim
new file mode 100644
index 00000000..d4a55b14
--- /dev/null
+++ b/src/io/cell.nim
@@ -0,0 +1,34 @@
+import unicode
+
+import types/color
+
+type
+  Cell* = object of RootObj
+    fgcolor*: CellColor
+    bgcolor*: CellColor
+    italic*: bool
+    bold*: bool
+    underline*: bool
+
+  FlexibleCell* = object of Cell
+    rune*: Rune
+
+  FlexibleGrid* = seq[seq[FlexibleCell]]
+
+  FixedCell* = object of Cell
+    runes*: seq[Rune]
+
+  FixedGrid* = seq[FixedCell]
+
+proc setText*(grid: var FlexibleGrid, x: int, y: int, text: seq[Rune]) =
+  while grid.len <= y:
+    grid.add(newSeq[FlexibleCell]())
+
+  while grid[y].len < x + text.len:
+    grid[y].add(FlexibleCell())
+  
+  var i = 0
+  while i < text.len:
+    grid[y][i].rune = text[i]
+    inc i
+
diff --git a/src/main.nim b/src/main.nim
index 4ce11f79..b1345ce2 100644
--- a/src/main.nim
+++ b/src/main.nim
@@ -5,6 +5,7 @@ import streams
 
 import html/parser
 import html/dom
+import html/renderer
 import io/buffer
 import io/term
 import config/config
@@ -32,7 +33,6 @@ proc getPageUri(uri: Uri): Stream =
 
 var buffers: seq[Buffer]
 
-
 proc main*() =
   if paramCount() != 1:
     eprint "Invalid parameters. Usage:\ntwt <url>"
@@ -42,10 +42,11 @@ proc main*() =
   let buffer = newBuffer(attrs)
   let uri = parseUri(paramStr(1))
   buffers.add(buffer)
-  buffer.source = getPageUri(uri).readAll() #TODO buffer.renderPlainText(getPageUri(uri).readAll())
+  buffer.source = getPageUri(uri).readAll() #TODO get rid of this
   buffer.document = parseHtml(newStringStream(buffer.source))
   buffer.setLocation(uri)
   buffer.document.applyDefaultStylesheet()
+  buffer.alignBoxes()
   buffer.renderDocument()
   var lastUri = uri
   while displayPage(attrs, buffer):