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 /src/layout | |
parent | 872db4726aaf53f307139dd42e79809ce9796c0e (diff) | |
download | chawan-cac5382d4e9f9f7536a12448dcbe3657f19dbbf4.tar.gz |
Another failed attempt at the layout engine
Diffstat (limited to 'src/layout')
-rw-r--r-- | src/layout/box.nim | 42 | ||||
-rw-r--r-- | src/layout/layout.nim | 271 |
2 files changed, 249 insertions, 64 deletions
diff --git a/src/layout/box.nim b/src/layout/box.nim new file mode 100644 index 00000000..277bbef4 --- /dev/null +++ b/src/layout/box.nim @@ -0,0 +1,42 @@ +import unicode + +import utils/twtstr +import io/cell + +type + CSSRect* = object + x1*: int + y1*: int + x2*: int + y2*: int + + CSSBox* = ref CSSBoxObj + CSSBoxObj = object of RootObj + 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 + content*: seq[CSSRowBox] + + 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 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 |