about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2021-08-06 23:24:40 +0200
committerbptato <nincsnevem662@gmail.com>2021-08-06 23:24:40 +0200
commitab963e4efe0871a6bdedf2f56fcfb9ed15636d12 (patch)
tree78397ac1ca492e4be08af07d02a0d609896e8ecc
parent559d2ca622e15c5781303940bc0c87e9aed1e5d1 (diff)
downloadchawan-ab963e4efe0871a6bdedf2f56fcfb9ed15636d12.tar.gz
HTML display with highly broken box model
-rw-r--r--src/css/box.nim72
-rw-r--r--src/css/style.nim113
-rw-r--r--src/html/dom.nim41
-rw-r--r--src/html/htmlparser.nim2
-rw-r--r--src/io/buffer.nim15
-rw-r--r--src/main.nim10
6 files changed, 138 insertions, 115 deletions
diff --git a/src/css/box.nim b/src/css/box.nim
new file mode 100644
index 00000000..c3f0280d
--- /dev/null
+++ b/src/css/box.nim
@@ -0,0 +1,72 @@
+import unicode
+
+import ../types/enums
+
+import ../utils/twtstr
+
+type
+  CSSRect* = object
+    x1*: int
+    y1*: int
+    x2*: int
+    y2*: int
+
+  CSSBox* = ref CSSBoxObj
+  CSSBoxObj = object of RootObj
+    content*: seq[Rune]
+    innerEdge*: CSSRect
+    paddingEdge*: CSSRect
+    borderEdge*: CSSRect
+    marginEdge*: CSSRect
+    children*: seq[CSSBox]
+
+  CSSInlineBox* = ref CSSInlineBoxObj
+  CSSInlineBoxObj = object of CSSBox
+    nextpart: CSSInlineBox
+
+  CSSBlockBox* = ref CSSBlockBoxObj
+  CSSBlockBoxObj = object of CSSBox
+
+func `+`(a: CSSRect, b: CSSRect): CSSRect =
+  result.x1 = a.x1 + b.x1
+  result.y1 = a.y1 + b.y1
+  result.x2 = a.x2 + b.x2
+  result.y2 = a.y2 + b.y2
+
+proc `+=`(a: var CSSRect, b: CSSRect) =
+  a = a + b
+
+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/style.nim b/src/css/style.nim
index 6df59f22..3b70263d 100644
--- a/src/css/style.nim
+++ b/src/css/style.nim
@@ -44,61 +44,28 @@ type
     position*: CSSPosition
     content*: seq[Rune]
 
-  CSSCanvas* = object
-    rootBox*: CSSBox
-    width*: int
-    height*: int
-
-  CSSRect* = object
-    x1*: int
-    y1*: int
-    x2*: int
-    y2*: int
-
-  CSSBox* = ref object
-    display*: DisplayType
-    x*: int
-    y*: int
-    innerEdge*: CSSRect
-    paddingEdge*: CSSRect
-    borderEdge*: CSSRect
-    marginEdge*: CSSRect
-    props*: CSS2Properties
-    content*: seq[Rune]
-    dispcontent*: string
-    children*: seq[CSSBox]
-
   CSSColor* = tuple[r: uint8, g: uint8, b: uint8, a: uint8]
   
   CSSComputedValue* = object of RootObj
-    case t: CSSRuleType
+    case t*: CSSRuleType
     of RULE_ALL: discard
     of RULE_COLOR:
-      color: CSSColor
+      color*: CSSColor
     of RULE_MARGIN, RULE_MARGIN_TOP, RULE_MARGIN_LEFT, RULE_MARGIN_RIGHT,
        RULE_MARGIN_BOTTOM:
-      length: CSSLength
+      length*: CSSLength
     of RULE_FONT_STYLE:
-      fontStyle: CSSFontStyle
+      fontStyle*: CSSFontStyle
     of RULE_DISPLAY:
-      display: DisplayType
+      display*: DisplayType
     of RULE_CONTENT:
-      content: seq[Rune]
+      content*: seq[Rune]
 
   CSSSpecifiedValue* = object of CSSComputedValue
     hasGlobalValue: bool
     globalValue: CSSGlobalValueType
 
 
-func `+`(a: CSSRect, b: CSSRect): CSSRect =
-  result.x1 = a.x1 + b.x1
-  result.y1 = a.y1 + b.y1
-  result.x2 = a.x2 + b.x2
-  result.y2 = a.y2 + b.y2
-
-proc `+=`(a: var CSSRect, b: CSSRect) =
-  a = a + b
-
 func cells(l: CSSLength): int =
   case l.unit
   of UNIT_EM:
@@ -309,10 +276,16 @@ func getComputedValue*(rule: CSSSpecifiedValue): CSSComputedValue =
   let initial = rule.hasGlobalValue and (rule.globalValue == VALUE_INHERIT)
   let unset = rule.hasGlobalValue and (rule.globalValue == VALUE_INHERIT)
   let revert = rule.hasGlobalValue and (rule.globalValue == VALUE_INHERIT)
+  return rule
   #case rule.t
   #of RULE_COLOR:
   #  return CSSComputedValue(t: rule.t, 
 
+func getValue*(vals: seq[CSSComputedValue], rule: CSSRuleType): CSSComputedValue =
+  for val in vals:
+    if val.t == rule:
+      result = val
+
 func getComputedValue*(d: CSSDeclaration): CSSComputedValue =
   return getComputedValue(getSpecifiedValue(d))
 
@@ -342,65 +315,3 @@ proc applyProperty*(props: CSS2Properties, d: CSSDeclaration) =
     props.content = cssString(d)
   else:
     printc(d) #TODO
-
-func getLength(s: seq[Rune], start: int, wlimit: int): tuple[wrap: bool, len: int, width: int] =
-  var len = 0
-  var width = 0
-  var i = start
-  while i < s.len:
-    let r = s[i]
-    let cw = r.width()
-    if width + cw > wlimit:
-      return (wrap: true, len: len, width: width)
-    width += cw
-    len += 1
-  
-  return (wrap: false, len: len, width: width)
-
-proc arrangeBoxes*(canvas: CSSCanvas) =
-  var stack: seq[CSSBox]
-  stack.add(canvas.rootBox)
-  var x = 0
-  var y = 0
-
-  while stack.len > 0:
-    let box = stack.pop()
-
-    #arrange box
-    box.marginEdge.x1 = x
-    box.marginEdge.y1 = y
-    x += box.props.marginleft.cells()
-    y += box.props.margintop.cells()
-
-    if box.display == DISPLAY_BLOCK:
-      x = 0
-      inc y
-
-    if x > canvas.width:
-      x = 0
-      inc y
-
-    box.x = x
-    box.y = y
-
-    var l = 0
-    while l < box.content.len:
-      let (wrap, wraplen, wrapwidth) = box.content.getLength(l, canvas.width - x)
-      var wrapbox = new(CSSBox)
-      wrapbox.content = box.content.substr(l, l + wraplen)
-      box.children.add(wrapbox)
-      l += wraplen
-      x += wrapwidth
-      if wrap:
-        inc y
-        x = 0
-
-    x += box.props.marginright.cells()
-    y += box.props.marginbottom.cells()
-    box.marginEdge.x2 = x
-    box.marginEdge.y2 = y
-
-    var i = box.children.len - 1
-    while i >= 0:
-      stack.add(box.children[i])
-      i -= 1
diff --git a/src/html/dom.nim b/src/html/dom.nim
index e5aee036..96bbd8a1 100644
--- a/src/html/dom.nim
+++ b/src/html/dom.nim
@@ -11,10 +11,12 @@ import algorithm
 import ../css/style
 import ../css/cssparser
 import ../css/selector
+import ../css/box
 
 import ../types/enums
 
 import ../utils/twtstr
+import ../utils/eprint
 
 const css = staticRead"../../res/default.css"
 let stylesheet = parseCSS(newStringStream(css))
@@ -44,7 +46,6 @@ type
     width*: int
     height*: int
     hidden*: bool
-    box*: CSSBox
 
   Attr* = ref AttrObj
   AttrObj = object of NodeObj
@@ -91,6 +92,7 @@ type
     attributes*: Table[string, Attr]
     style*: CSS2Properties
     cssvalues*: seq[CSSComputedValue]
+    box*: CSSBox
 
   HTMLElement* = ref HTMLElementObj
   HTMLElementObj = object of ElementObj
@@ -477,13 +479,38 @@ proc applyRules*(document: Document, rules: CSSStylesheet): seq[tuple[e:Element,
     for child in elem.children:
       stack.add(child)
 
-proc generateBox*(elem: Element) =
-  if elem.style.display == DISPLAY_INLINE:
-    discard
-
+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:
-      Element(child).generateBox()
+      let elem = Element(child)
+      elem.generateBox(rx, ry, w, h)
+      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)
@@ -491,4 +518,4 @@ proc applyDefaultStylesheet*(document: Document) =
     rule.e.style.applyProperty(rule.d)
     rule.e.cssvalues.add(getComputedValue(rule.d))
 
-  document.root.generateBox()
+  document.root.generateBox(0, 0, 80, 24)
diff --git a/src/html/htmlparser.nim b/src/html/htmlparser.nim
index 2a652304..c060d666 100644
--- a/src/html/htmlparser.nim
+++ b/src/html/htmlparser.nim
@@ -378,7 +378,7 @@ proc processDocumentPart(state: var HTMLParseState, buf: string) =
             #TODO for doctype
             while p < max and buf[p] != '>':
               inc p
-            at = p
+            at = p + 1
             continue
 
         if not state.in_comment:
diff --git a/src/io/buffer.nim b/src/io/buffer.nim
index de600dde..20d7c497 100644
--- a/src/io/buffer.nim
+++ b/src/io/buffer.nim
@@ -13,6 +13,8 @@ import ../utils/eprint
 
 import ../html/dom
 
+import ../css/box
+
 import ../config
 
 import ./term
@@ -322,10 +324,10 @@ proc cursorTo*(buffer: Buffer, x: int, y: int) =
     buffer.redraw = true
 
 proc cursorDown*(buffer: Buffer) =
-  if buffer.cursory < buffer.numLines:
+  if buffer.cursory < buffer.numLines - 1:
     inc buffer.cursory
     buffer.restoreCursorX()
-    if buffer.cursory >= buffer.lastVisibleLine and buffer.lastVisibleLine != buffer.numLines - 1:
+    if buffer.cursory - buffer.height >= buffer.fromy:
       inc buffer.fromy
       buffer.redraw = true
 
@@ -620,6 +622,15 @@ proc renderPlainText*(buffer: Buffer, text: string) =
   if line.len > 0:
     buffer.setText(0, y, line.toRunes())
 
+proc renderDocument*(buffer: Buffer) =
+  var stack: seq[CSSBox]
+  stack.add(buffer.document.root.box)
+  while stack.len > 0:
+    let box = stack.pop()
+    buffer.setText(box.innerEdge.x1, box.innerEdge.y1, box.content)
+
+    for child in box.children:
+      stack.add(child)
 
 proc cursorBufferPos(buffer: Buffer) =
   let x = max(buffer.cursorx - buffer.fromx, 0)
diff --git a/src/main.nim b/src/main.nim
index eaed73ed..fe7eeb99 100644
--- a/src/main.nim
+++ b/src/main.nim
@@ -6,6 +6,7 @@ import streams
 import utils/eprint
 
 import html/htmlparser
+import html/dom
 
 import io/buffer
 import io/term
@@ -45,10 +46,11 @@ proc main*() =
   let buffer = newBuffer(attrs)
   let uri = parseUri(paramStr(1))
   buffers.add(buffer)
-  #buffer.document = parseHtml(getPageUri(uri))
-  #buffer.setLocation(uri)
-  #buffer.document.applyDefaultStylesheet()
-  buffer.renderPlainText(getPageUri(uri).readAll())
+  buffer.document = parseHtml(getPageUri(uri))
+  buffer.setLocation(uri)
+  buffer.document.applyDefaultStylesheet()
+  #buffer.renderPlainText(getPageUri(uri).readAll())
+  buffer.renderDocument()
   var lastUri = uri
   while displayPage(attrs, buffer):
     buffer.setStatusMessage("Loading...")