diff options
author | bptato <nincsnevem662@gmail.com> | 2021-11-07 09:44:09 +0100 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2021-11-07 09:44:09 +0100 |
commit | cac5382d4e9f9f7536a12448dcbe3657f19dbbf4 (patch) | |
tree | 53118177c7219c4dc16f16215afbb0be96bb7455 | |
parent | 872db4726aaf53f307139dd42e79809ce9796c0e (diff) | |
download | chawan-cac5382d4e9f9f7536a12448dcbe3657f19dbbf4.tar.gz |
Another failed attempt at the layout engine
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | makefile | 2 | ||||
-rw-r--r-- | src/io/buffer.nim | 18 | ||||
-rw-r--r-- | src/layout/box.nim (renamed from src/css/box.nim) | 20 | ||||
-rw-r--r-- | src/layout/layout.nim | 271 |
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 |