diff options
author | bptato <nincsnevem662@gmail.com> | 2021-08-06 23:24:40 +0200 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2021-08-06 23:24:40 +0200 |
commit | ab963e4efe0871a6bdedf2f56fcfb9ed15636d12 (patch) | |
tree | 78397ac1ca492e4be08af07d02a0d609896e8ecc | |
parent | 559d2ca622e15c5781303940bc0c87e9aed1e5d1 (diff) | |
download | chawan-ab963e4efe0871a6bdedf2f56fcfb9ed15636d12.tar.gz |
HTML display with highly broken box model
-rw-r--r-- | src/css/box.nim | 72 | ||||
-rw-r--r-- | src/css/style.nim | 113 | ||||
-rw-r--r-- | src/html/dom.nim | 41 | ||||
-rw-r--r-- | src/html/htmlparser.nim | 2 | ||||
-rw-r--r-- | src/io/buffer.nim | 15 | ||||
-rw-r--r-- | src/main.nim | 10 |
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...") |