about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2021-11-07 09:44:09 +0100
committerbptato <nincsnevem662@gmail.com>2021-11-07 09:44:09 +0100
commitcac5382d4e9f9f7536a12448dcbe3657f19dbbf4 (patch)
tree53118177c7219c4dc16f16215afbb0be96bb7455
parent872db4726aaf53f307139dd42e79809ce9796c0e (diff)
downloadchawan-cac5382d4e9f9f7536a12448dcbe3657f19dbbf4.tar.gz
Another failed attempt at the layout engine
-rw-r--r--.gitignore2
-rw-r--r--makefile2
-rw-r--r--src/io/buffer.nim18
-rw-r--r--src/layout/box.nim (renamed from src/css/box.nim)20
-rw-r--r--src/layout/layout.nim271
5 files changed, 223 insertions, 90 deletions
diff --git a/.gitignore b/.gitignore
index 8c73d90c..59b4d77a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,3 @@
 a
 twt
-wtst.html
+test/
diff --git a/makefile b/makefile
index 5d6e99c6..8ee91b18 100644
--- a/makefile
+++ b/makefile
@@ -6,5 +6,7 @@ debug:
 	$(NIMC) $(FLAGS) $(FILES)
 release:
 	$(NIMC) $(FLAGS) -d:release -d:strip -d:lto $(FILES)
+install:
+	cp twt /usr/local/bin/
 clean:
 	rm ./twt
diff --git a/src/io/buffer.nim b/src/io/buffer.nim
index 66ed6aaf..b8efd730 100644
--- a/src/io/buffer.nim
+++ b/src/io/buffer.nim
@@ -9,7 +9,7 @@ import types/color
 import types/enums
 import utils/twtstr
 import html/dom
-import css/box
+import layout/box
 import config/config
 import io/term
 import io/lineedit
@@ -57,7 +57,7 @@ type
     displaycontrols*: bool
     redraw*: bool
     location*: Uri
-    source*: string #TODO
+    source*: string
     showsource*: bool
     rootbox*: CSSBox
 
@@ -608,6 +608,7 @@ proc renderPlainText*(buffer: Buffer, text: string) =
 
 proc renderDocument*(buffer: Buffer) =
   buffer.clearText()
+  #TODO
   if buffer.rootbox == nil:
     return
   var stack: seq[CSSBox]
@@ -617,18 +618,13 @@ proc renderDocument*(buffer: Buffer) =
     if box of CSSInlineBox:
       let inline = CSSInlineBox(box)
       var i = 0
+      eprint "NEW BOX"
       for line in inline.content:
-        var x = inline.innerEdge.x1
-        if i == 0:
-          x = inline.fromx
-        var y = box.innerEdge.y1 + i
-
-        buffer.setRowBox(x, y, line)
+        eprint line
+        buffer.setRowBox(inline.x + line.x, inline.y + line.y, line)
 
     for child in box.children:
       stack.add(child)
-  
-  eprint "lines", buffer.lines.len
 
 proc cursorBufferPos(buffer: Buffer) =
   let x = max(buffer.cursorx - buffer.fromx, 0)
@@ -709,7 +705,6 @@ proc displayBuffer(buffer: Buffer) =
   let full = buffer.generateFullOutput()
   for line in full:
     print(line)
-    #TODO
     print("\e[K")
     print('\n')
 
@@ -773,6 +768,7 @@ proc inputLoop(attrs: TermAttributes, buffer: Buffer): bool =
       var url = $buffer.location
 
       termGoto(0, buffer.height)
+      print("\e[K")
       let status = readLine("URL: ", url, buffer.width)
       if status:
         buffer.setLocation(parseUri(url))
diff --git a/src/css/box.nim b/src/layout/box.nim
index 457cccb0..277bbef4 100644
--- a/src/css/box.nim
+++ b/src/layout/box.nim
@@ -12,20 +12,21 @@ type
 
   CSSBox* = ref CSSBoxObj
   CSSBoxObj = object of RootObj
-    innerEdge*: CSSRect
-    paddingEdge*: CSSRect
-    borderEdge*: CSSRect
-    marginEdge*: CSSRect
+    x*: int
+    y*: int
+    width*: int
+    height*: int
     children*: seq[CSSBox]
 
   CSSRowBox* = object
+    x*: int
+    y*: int
     width*: int
     height*: int
     runes*: seq[Rune]
 
   CSSInlineBox* = ref CSSInlineBoxObj
   CSSInlineBoxObj = object of CSSBox
-    fromx*: int
     content*: seq[CSSRowBox]
 
   CSSBlockBox* = ref CSSBlockBoxObj
@@ -39,12 +40,3 @@ func `+`(a: CSSRect, b: CSSRect): CSSRect =
 
 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 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/layout/layout.nim b/src/layout/layout.nim
index 043c0e08..57220df1 100644
--- a/src/layout/layout.nim
+++ b/src/layout/layout.nim
@@ -1,16 +1,150 @@
 import unicode
 
+import layout/box
 import types/enums
 import html/dom
-import css/box
 import css/style
 import io/buffer
 import io/cell
 import utils/twtstr
 
-proc generateGrids(text: Text, maxwidth: int, maxheight: int, x: int, y: int, fromx: int = x): seq[CSSRowBox] =
+#proc generateGrids(text: Text, maxwidth: int, maxheight: int, x: int, y: int, fromx: int = x): seq[CSSRowBox] =
+#  var r: Rune
+#  var rowbox: CSSRowBox
+#  var i = 0
+#  var whitespace = false
+#  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:
+#        if r.isWhitespace():
+#          if not whitespace:
+#            whitespace = true
+#            rowbox.runes.add(Rune(' '))
+#            inc rowbox.width
+#            w += rw
+#        else:
+#          if whitespace:
+#            whitespace = false
+#          rowbox.runes.add(r)
+#          inc rowbox.width
+#          w += rw
+#
+#    result.add(rowbox)
+#
+#  if i < text.data.len:
+#    rowbox = CSSRowBox()
+#    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(rowbox)
+#        rowbox = CSSRowBox()
+#      else:
+#        rowbox.runes.add(r)
+#        inc rowbox.width
+#        w += rw
+#
+#  if rowbox.width > 0:
+#    result.add(rowbox)
+#
+#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.x = x
+#  result.y = y
+#  var height = 0
+#  var width = 0
+#  for grid in result.content:
+#    inc height
+#    width = max(width, grid.width)
+#  
+#  height = min(height, maxheight)
+#  width = min(width, maxwidth)
+#  result.width = width
+#  result.height = height
+#
+#proc generateBox(elem: Element, maxwidth: int, maxheight: int, x: int = 0, y: int = 0, fromx: int = x): CSSBox
+#
+#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.height
+#        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, maxwidth: int, maxheight: int, x: int = 0, y: int = 0, fromx: int = x): CSSBox =
+#  if elem.cssvalues[RULE_DISPLAY].display == DISPLAY_NONE:
+#    return nil
+#
+#  result = CSSBlockBox()
+#  result.x = x
+#  result.y = y
+#
+#  var width = 0
+#  var height = 0
+#  for box in elem.generateChildBoxes(maxwidth, maxheight, x, y, fromx):
+#    result.children.add(box)
+#    height += box.height
+#    height = min(height, maxheight)
+#    width = max(width, box.width)
+#    width = min(width, maxwidth)
+#
+#  result.width = width
+#  result.height = height
+#
+#proc genBox(elem: Element, w: int, h: int, x: int = 0, y: int = 0): CSSBlockBox =
+#  if elem.cssvalues[RULE_DISPLAY].display == DISPLAY_NONE:
+#    return nil
+#  result = CSSBlockBox()
+#
+type
+  Frame = object
+    node: Node
+    maxwidth: int
+    maxheight: int
+    x: int
+    y: int
+    fromx: int
+    box: CSSBox
+    context: FormatContext
+
+  FormatContext = enum
+    CONTEXT_BLOCK, CONTEXT_INLINE
+
+proc addText(frame: var Frame, text: Text) =
+  let maxwidth = frame.maxwidth
+  let fromx = frame.fromx
+  let x = frame.x
+  if maxwidth == 0: return
+  if not (frame.box of CSSInlineBox): return
+  var box = CSSInlineBox(frame.box)
   var r: Rune
-  var rowbox: CSSRowBox
+  var rowbox = CSSRowBox(x: frame.x, y: frame.y)
   var i = 0
   var whitespace = false
   if fromx > x:
@@ -37,10 +171,11 @@ proc generateGrids(text: Text, maxwidth: int, maxheight: int, x: int, y: int, fr
           inc rowbox.width
           w += rw
 
-    result.add(rowbox)
+    box.content.add(rowbox)
+    frame.x += rowbox.width
 
   if i < text.data.len:
-    rowbox = CSSRowBox()
+    rowbox = CSSRowBox(x: frame.x, y: frame.y)
     var w = 0
     while i < text.data.len:
       let pi = i
@@ -49,73 +184,81 @@ proc generateGrids(text: Text, maxwidth: int, maxheight: int, x: int, y: int, fr
       if rw + w > maxwidth:
         i = pi
         w = 0
-        result.add(rowbox)
-        rowbox = CSSRowBox()
+        box.content.add(rowbox)
+        frame.x += rowbox.width
+        rowbox = CSSRowBox(x: frame.x, y: frame.y)
       else:
         rowbox.runes.add(r)
         inc rowbox.width
         w += rw
 
   if rowbox.width > 0:
-    result.add(rowbox)
-
-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.width)
-  
-  height = min(height, maxheight)
-  width = min(width, maxwidth)
-  result.innerEdge.x2 = x + width
-  result.innerEdge.y2 = y + height
-
-proc generateBox(elem: Element, maxwidth: int, maxheight: int, x: int = 0, y: int = 0, fromx: int = x): CSSBox
+    box.content.add(rowbox)
+    frame.x += rowbox.width
 
-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, maxwidth: int, maxheight: int, x: int = 0, y: int = 0, fromx: int = x): CSSBox =
-  if elem.cssvalues[RULE_DISPLAY].display == DISPLAY_NONE:
-    return nil
+proc alignBoxes*(buffer: Buffer) =
+  #buffer.rootbox = buffer.document.root.genBox(buffer.width, buffer.height)
+  buffer.rootbox = CSSBlockBox(x: 0, y: 0, width: buffer.width, height: buffer.height)
+  buffer.rootbox.children.add(CSSInlineBox(x: 0, y: 0, width: buffer.width, height: buffer.height))
+  var x = 0
+  var stack: seq[Frame]
+  stack.add(Frame(node: buffer.document.root, box: buffer.rootbox, x: 0, y: 0, fromx: 0, maxwidth: 80, context: CONTEXT_BLOCK))
+  while stack.len > 0:
+    var frame = stack.pop()
+    let node = frame.node
 
-  result = CSSBlockBox()
-  result.innerEdge.x1 = x
-  result.innerEdge.y1 = y
+    case frame.context 
+    of CONTEXT_BLOCK:
+      case node.nodeType
+      of TEXT_NODE: #anonymous
+        discard
+      of ELEMENT_NODE: #new formatting context
+        let elem = Element(node)
+        case elem.cssvalues[RULE_DISPLAY].display
+        of DISPLAY_BLOCK:
+          let parent = frame.box
+          frame.box = CSSBlockBox(x: frame.x, y: frame.y, width: frame.maxwidth)
+          parent.children.add(frame.box)
+          frame.context = CONTEXT_BLOCK
+        of DISPLAY_INLINE:
+          let parent = frame.box
+          frame.box = CSSInlineBox(x: frame.x, y: frame.y, width: frame.maxwidth)
+          parent.children.add(frame.box)
+          frame.context = CONTEXT_INLINE
+        of DISPLAY_NONE: continue
+        else: discard #TODO
+      else: discard
+    of CONTEXT_INLINE:
+      case node.nodeType
+      of TEXT_NODE: #just add to existing inline box no problem
+        let text = Text(node)
+        frame.addText(text)
+      of ELEMENT_NODE:
+        let elem = Element(node)
+        case elem.cssvalues[RULE_DISPLAY].display
+        of DISPLAY_NONE: continue
+        else:
+          #ok this is the difficult part (TODO)
+          #NOTE we're assuming the parent isn't inline, if it is we're screwed
+          #basically what we have to do is:
+          #* create a new anonymous BLOCK box
+          #* for every previous INLINE box in parent (BLOCK) box, do:
+          #*  copy INLINE box into new anonymous BLOCK box
+          #*  delete INLINE box
+          #* create a new BLOCK box (this one)
+          #* NOTE after our BLOCK there's a continuation of the last INLINE box
 
-  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)
+          eprint "?????"
+      else: discard
 
-  result.innerEdge.x2 = x + width
-  result.innerEdge.y2 = y + height
+    # look ahead to figure out if inline box will have to be split
+    #var i = node.childNodes.len - 1
+    #while i >= 0:
+    #  let child = node.childNodes[i]
+    #  stack.add(Frame(node: child, box: frame.box, maxwidth: frame.maxwidth, x: frame.x, y: frame.y, fromx: frame.fromx, context: frame.context))
 
-proc alignBoxes*(buffer: Buffer) =
-  buffer.rootbox = buffer.document.root.generateBox(buffer.width, buffer.height)
+    var i = node.childNodes.len - 1
+    while i >= 0:
+      let child = node.childNodes[i]
+      stack.add(Frame(node: child, box: frame.box, maxwidth: frame.maxwidth, x: frame.x, y: frame.y, fromx: frame.fromx, context: frame.context))
+      dec i