diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/css/parser.nim | 15 | ||||
-rw-r--r-- | src/css/selector.nim | 3 | ||||
-rw-r--r-- | src/css/style.nim | 56 | ||||
-rw-r--r-- | src/html/dom.nim | 93 | ||||
-rw-r--r-- | src/io/buffer.nim | 24 | ||||
-rw-r--r-- | src/layout/box.nim | 14 | ||||
-rw-r--r-- | src/layout/layout.nim | 340 | ||||
-rw-r--r-- | src/types/enums.nim | 3 |
8 files changed, 231 insertions, 317 deletions
diff --git a/src/css/parser.nim b/src/css/parser.nim index fac1bce8..25a0e21d 100644 --- a/src/css/parser.nim +++ b/src/css/parser.nim @@ -132,7 +132,7 @@ proc reconsume(state: var CSSTokenizerState) = func peek(state: CSSTokenizerState, i: int): Rune = return state.buf[state.at + i] -proc has(state: var CSSTokenizerState, i: int): bool = +proc has(state: var CSSTokenizerState, i: int = 0): bool = if state.at + i >= state.buf.len and not state.stream.atEnd(): state.buf &= state.stream.readLine().toRunes() & Rune('\n') return state.at + i < state.buf.len @@ -143,11 +143,6 @@ func curr(state: CSSTokenizerState): Rune = proc isValidEscape*(state: var CSSTokenizerState): bool = return state.has(1) and state.curr() == Rune('\\') and state.peek(1) != Rune('\n') -proc has(state: var CSSTokenizerState): bool = - if state.at >= state.buf.len and not state.stream.atEnd(): - state.buf &= state.stream.readLine().toRunes() & Rune('\n') - return state.at < state.buf.len - proc startsWithIdentifier*(state: var CSSTokenizerState): bool = if not state.has(): return false @@ -345,16 +340,16 @@ proc consumeIdentLikeToken(state: var CSSTokenizerState): CSSToken = return CSSToken(tokenType: CSS_IDENT_TOKEN, value: s) proc consumeComments(state: var CSSTokenizerState) = - if state.has(2) and state.peek(1) == Rune('/') and state.peek(2) == Rune('*'): + if state.has(1) and state.curr() == Rune('/') and state.peek(1) == Rune('*'): discard state.consume() discard state.consume() - while state.has(2) and not (state.peek(1) == Rune('*') and state.peek(2) == Rune('/')): + while state.has(1) and not (state.curr() == Rune('*') and state.peek(1) == Rune('/')): discard state.consume() - if state.has(2): - discard state.consume() if state.has(1): discard state.consume() + if state.has(): + discard state.consume() proc consumeToken(state: var CSSTokenizerState): CSSToken = state.consumeComments() diff --git a/src/css/selector.nim b/src/css/selector.nim index d381e618..0ec1ac27 100644 --- a/src/css/selector.nim +++ b/src/css/selector.nim @@ -13,6 +13,9 @@ type QUERY_TYPE, QUERY_CLASS, QUERY_ATTR, QUERY_DELIM, QUERY_VALUE, QUERY_PSEUDO, QUERY_PSELEM + PseudoElem* = enum + PSEUDO_NONE, PSEUDO_BEFORE, PSEUDO_AFTER + SelectorParser = object selectors: seq[SelectorList] query: QueryMode diff --git a/src/css/style.nim b/src/css/style.nim index 6d6935d3..5f426e58 100644 --- a/src/css/style.nim +++ b/src/css/style.nim @@ -12,34 +12,6 @@ type unit*: CSSUnit auto*: bool - CSS2Properties* = ref object - rawtext*: string - fmttext*: seq[string] - x*: int - y*: int - ex*: int - ey*: int - width*: int - height*: int - hidden*: bool - before*: CSS2Properties - after*: CSS2Properties - margintop*: CSSLength - marginbottom*: CSSLength - marginleft*: CSSLength - marginright*: CSSLength - centered*: bool - display*: DisplayType - bold*: bool - fontStyle*: CSSFontStyle - underscore*: bool - islink*: bool - selected*: bool - indent*: int - color*: CSSColor - position*: CSSPosition - content*: seq[Rune] - CSSValues* = array[low(CSSRuleType)..high(CSSRuleType), CSSComputedValue] CSSColor* = tuple[r: uint8, g: uint8, b: uint8, a: uint8] @@ -59,6 +31,8 @@ type content*: seq[Rune] of VALUE_NONE: discard + CSSComputedValues* = array[low(CSSRuleType)..high(CSSRuleType), CSSComputedValue] + CSSSpecifiedValue* = object of CSSComputedValue hasGlobalValue: bool globalValue: CSSGlobalValueType @@ -79,7 +53,7 @@ const ValueTypes = { func getValueType*(rule: CSSRuleType): CSSValueType = return ValueTypes[rule] -func cells(l: CSSLength): int = +func cells*(l: CSSLength): int = case l.unit of UNIT_EM: return int(l.num) @@ -214,18 +188,18 @@ func cssLength(d: CSSDeclaration): CSSLength = return CSSLength(num: 0, unit: UNIT_EM) -func hasColor*(style: CSS2Properties): bool = - return style.color.r != 0 or style.color.b != 0 or style.color.g != 0 or style.color.a != 0 - -func termColor*(style: CSS2Properties): ForegroundColor = - if style.color.r > 120: - return fgRed - elif style.color.b > 120: - return fgBlue - elif style.color.g > 120: - return fgGreen - else: - return fgWhite +#func hasColor*(style: CSS2Properties): bool = +# return style.color.r != 0 or style.color.b != 0 or style.color.g != 0 or style.color.a != 0 +# +#func termColor*(style: CSS2Properties): ForegroundColor = +# if style.color.r > 120: +# return fgRed +# elif style.color.b > 120: +# return fgBlue +# elif style.color.g > 120: +# return fgGreen +# else: +# return fgWhite func isToken(d: CSSDeclaration): bool = d.value.len > 0 and d.value[0] of CSSToken func getToken(d: CSSDeclaration): CSSToken = (CSSToken)d.value[0] diff --git a/src/html/dom.nim b/src/html/dom.nim index 82301146..647b1f7c 100644 --- a/src/html/dom.nim +++ b/src/html/dom.nim @@ -7,6 +7,7 @@ import streams import sequtils import sugar import algorithm +import options import css/style import css/parser @@ -84,7 +85,9 @@ type id*: string classList*: seq[string] attributes*: Table[string, Attr] - cssvalues*: array[low(CSSRuleType)..high(CSSRuleType), CSSComputedValue] + cssvalues*: CSSComputedValues + cssvalues_before*: Option[CSSComputedValues] + cssvalues_after*: Option[CSSComputedValues] HTMLElement* = ref HTMLElementObj HTMLElementObj = object of ElementObj @@ -284,13 +287,13 @@ func pseudoSelectorMatches(elem: Element, sel: Selector): bool = of "last-child": return elem.parentNode.lastElementChild == elem else: return false -func pseudoElemSelectorMatches(elem: Element, sel: Selector): bool = +func pseudoElemSelectorMatches(elem: Element, sel: Selector, pseudo: PseudoElem = PSEUDO_NONE): bool = case sel.elem - of "after": return false - of "before": return false + of "after": return pseudo == PSEUDO_AFTER + of "before": return pseudo == PSEUDO_BEFORE else: return false -func selectorMatches(elem: Element, sel: Selector): bool = +func selectorMatches(elem: Element, sel: Selector, pseudo: PseudoElem = PSEUDO_NONE): bool = case sel.t of TYPE_SELECTOR: return elem.tagType == sel.tag @@ -303,15 +306,15 @@ func selectorMatches(elem: Element, sel: Selector): bool = of PSEUDO_SELECTOR: return pseudoSelectorMatches(elem, sel) of PSELEM_SELECTOR: - return pseudoElemSelectorMatches(elem, sel) + return pseudoElemSelectorMatches(elem, sel, pseudo) of UNIVERSAL_SELECTOR: return true of FUNC_SELECTOR: return false -func selectorsMatch(elem: Element, selectors: SelectorList): bool = +func selectorsMatch(elem: Element, selectors: SelectorList, pseudo: PseudoElem = PSEUDO_NONE): bool = for sel in selectors.sels: - if not selectorMatches(elem, sel): + if not selectorMatches(elem, sel, pseudo): return false return true @@ -365,47 +368,69 @@ proc querySelector*(document: Document, q: string): seq[Element] = for sel in selectors: result.add(document.selectElems(sel)) -func calcRules(elem: Element, rules: CSSStylesheet): seq[CSSSimpleBlock] = - var tosort: seq[tuple[s:int,b:CSSSimpleBlock]] - for rule in rules.value: - let selectors = parseSelectors(rule.prelude) #TODO perf: compute this once - for sel in selectors: - if elem.selectorsMatch(sel): - let spec = getSpecificity(sel) - tosort.add((spec,rule.oblock)) - tosort.sort((x, y) => cmp(x.s,y.s)) - return tosort.map((x) => x.b) - -proc applyProperty(elem: Element, decl: CSSDeclaration) = +proc applyProperty(elem: Element, decl: CSSDeclaration, pseudo: PseudoElem = PSEUDO_NONE) = var parentprops: array[low(CSSRuleType)..high(CSSRuleType), CSSComputedValue] if elem.parentElement != nil: parentprops = elem.parentElement.cssvalues else: parentprops = getInitialProperties() let cval = getComputedValue(decl, parentprops) - elem.cssvalues[cval.t] = cval + case pseudo + of PSEUDO_NONE: + elem.cssvalues[cval.t] = cval + of PSEUDO_BEFORE: + if elem.cssvalues_before.isNone: + elem.cssvalues_before = some(getInitialProperties()) + elem.cssvalues_before.get[cval.t] = cval + of PSEUDO_AFTER: + if elem.cssvalues_after.isNone: + elem.cssvalues_after = some(getInitialProperties()) + elem.cssvalues_after.get[cval.t] = cval + +type ParsedRule = tuple[sels: seq[SelectorList], oblock: CSSSimpleBlock] + +func calcRules(elem: Element, rules: seq[ParsedRule]): + array[low(PseudoElem)..high(PseudoElem), seq[CSSSimpleBlock]] = + var tosorts: array[low(PseudoElem)..high(PseudoElem), seq[tuple[s:int,b:CSSSimpleBlock]]] + for rule in rules: + for sel in rule.sels: + #TODO: optimize, like rewrite selector match algorithm output or something + for pseudo in low(PseudoElem)..high(PseudoElem): + if elem.selectorsMatch(sel, pseudo): + let spec = getSpecificity(sel) + tosorts[pseudo].add((spec,rule.oblock)) + + for i in low(PseudoElem)..high(PseudoElem): + tosorts[i].sort((x, y) => cmp(x.s,y.s)) + result[i] = tosorts[i].map((x) => x.b) proc applyRules*(document: Document, rules: CSSStylesheet): seq[tuple[e:Element,d:CSSDeclaration]] = var stack: seq[Element] stack.add(document.root) + let parsed = rules.value.map((x) => (sels: parseSelectors(x.prelude), oblock: x.oblock)) while stack.len > 0: let elem = stack.pop() - for oblock in calcRules(elem, rules): - let decls = parseCSSListOfDeclarations(oblock.value) - for item in decls: - if item of CSSDeclaration: - let decl = CSSDeclaration(item) - if decl.important: - result.add((elem, decl)) - else: - elem.applyProperty(decl) - - for child in elem.children: - stack.add(child) - + #TODO: optimize + #ok this whole idea was stupid, what I should've done is to just check for + #pseudo elem selectors, this is way too slow + let rules_pseudo = calcRules(elem, parsed) + for pseudo in low(PseudoElem)..high(PseudoElem): + let rules = rules_pseudo[pseudo] + for rule in rules: + let decls = parseCSSListOfDeclarations(rule.value) + for item in decls: + if item of CSSDeclaration: + let decl = CSSDeclaration(item) + if decl.important: + result.add((elem, decl)) + else: + elem.applyProperty(decl, pseudo) + + for child in elem.children: + stack.add(child) proc applyDefaultStylesheet*(document: Document) = let important = document.applyRules(stylesheet) diff --git a/src/io/buffer.nim b/src/io/buffer.nim index a64dfb12..a3dde1b7 100644 --- a/src/io/buffer.nim +++ b/src/io/buffer.nim @@ -182,14 +182,23 @@ func width(line: seq[FlexibleCell]): int = for c in line: result += c.rune.width() -func cellWidthOverlap*(buffer: Buffer, x: int, y: int): int = +func cellOrigin(buffer: Buffer, x: int, y: int): int = let row = y * buffer.width var ox = x while buffer.display[row + ox].runes.len == 0 and ox > 0: dec ox + return ox + +func currentCellOrigin(buffer: Buffer): int = + return buffer.cellOrigin(buffer.cursorx - buffer.fromx, buffer.cursory - buffer.fromy) + +func cellWidthOverlap*(buffer: Buffer, x: int, y: int): int = + let ox = buffer.cellOrigin(x, y) + let row = y * buffer.width return buffer.display[row + ox].runes.width() -func currentCellWidth*(buffer: Buffer): int = buffer.cellWidthOverlap(buffer.cursorx - buffer.fromx, buffer.cursory - buffer.fromy) +func currentCellWidth*(buffer: Buffer): int = + return buffer.cellWidthOverlap(buffer.cursorx - buffer.fromx, buffer.cursory - buffer.fromy) func currentLineWidth*(buffer: Buffer): int = if buffer.cursory > buffer.lines.len: @@ -299,21 +308,22 @@ proc cursorUp*(buffer: Buffer) = proc cursorRight*(buffer: Buffer) = let cellwidth = buffer.currentCellWidth() + let cellorigin = buffer.currentCellOrigin() let lw = buffer.currentLineWidth() if buffer.cursorx < lw - 1: - buffer.cursorx = min(lw - 1, buffer.cursorx + cellwidth) + buffer.cursorx = min(lw - 1, cellorigin + cellwidth) buffer.xend = buffer.cursorx if buffer.cursorx - buffer.width >= buffer.fromx: inc buffer.fromx buffer.redraw = true proc cursorLeft*(buffer: Buffer) = - let cellwidth = buffer.currentCellWidth() + let cellorigin = buffer.currentCellOrigin() if buffer.fromx > buffer.cursorx: buffer.fromx = buffer.cursorx buffer.redraw = true elif buffer.cursorx > 0: - buffer.cursorx = max(0, buffer.cursorx - cellwidth) + buffer.cursorx = max(0, cellorigin - 1) if buffer.fromx > buffer.cursorx: buffer.fromx = buffer.cursorx buffer.redraw = true @@ -644,12 +654,12 @@ proc renderDocument*(buffer: Buffer) = if box of CSSInlineBox: let inline = CSSInlineBox(box) var i = 0 - eprint "NEW BOX" + eprint "NEW BOX", inline.context.conty for line in inline.content: eprint line buffer.setRowBox(line) else: - eprint "BLOCK" + eprint "BLOCK h", box.height var i = box.children.len - 1 while i >= 0: diff --git a/src/layout/box.nim b/src/layout/box.nim index a36bb424..bacaa1b1 100644 --- a/src/layout/box.nim +++ b/src/layout/box.nim @@ -2,6 +2,8 @@ import unicode import utils/twtstr import io/cell +import types/enums +import css/style type CSSRect* = object @@ -14,10 +16,20 @@ type CSSBoxObj = object of RootObj x*: int y*: int - fromx*: int width*: int height*: int + bh*: int + bw*: int children*: seq[CSSBox] + context*: FormatContext + + FormatContext* = ref object + context*: FormatContextType + fromx*: int + fromy*: int + conty*: bool + whitespace*: bool + cssvalues*: CSSComputedValues CSSRowBox* = object x*: int diff --git a/src/layout/layout.nim b/src/layout/layout.nim index 96f27c9c..fe96b416 100644 --- a/src/layout/layout.nim +++ b/src/layout/layout.nim @@ -1,4 +1,5 @@ import unicode +import options import layout/box import types/enums @@ -8,252 +9,143 @@ import io/buffer import io/cell import utils/twtstr -#type -# Frame = object -# node: Node -# maxwidth: int -# maxheight: int -# box: CSSBox -# context: FormatContext +func newContext*(box: CSSBox): FormatContext = + new(result) + result.fromx = box.x + result.whitespace = true +func newBlockBox*(parent: CSSBox): CSSBlockBox = + new(result) + result.x = parent.x + if parent.context.conty: + inc parent.height + parent.context.conty = false + result.y = parent.y + parent.height -#proc addText(state: var LayoutState, frame: var Frame, text: Text) = -# let maxwidth = frame.maxwidth -# let fromx = state.fromx -# let x = state.x -# if maxwidth == 0: return -# if not (frame.box of CSSInlineBox): return -# var box = CSSInlineBox(frame.box) -# var r: Rune -# var i = 0 -# var rowbox: CSSRowBox -# if fromx > x: -# rowbox = CSSRowBox(x: state.fromx, y: state.y) -# let m = maxwidth - fromx + x -# var w = 0 -# var lf = false -# while i < text.data.len: -# let pi = i -# fastRuneAt(text.data, i, r) -# let rw = r.width() -# if rw + w > m: -# i = pi -# inc state.y -# lf = true -# break -# else: -# if r.isWhitespace(): -# if not state.whitespace: -# state.whitespace = true -# rowbox.runes.add(Rune(' ')) -# inc rowbox.width -# w += rw -# else: -# if state.whitespace: -# state.whitespace = false -# rowbox.runes.add(r) -# inc rowbox.width -# w += rw -# -# box.content.add(rowbox) -# if lf: -# state.fromx = 0 -# else: -# state.fromx += rowbox.width -# -# if i < text.data.len: -# rowbox = CSSRowBox(x: state.x, y: state.y) -# 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 -# box.content.add(rowbox) -# state.fromx += rowbox.width -# inc state.y -# rowbox = CSSRowBox(x: state.x, y: state.y) -# else: -# rowbox.runes.add(r) -# inc rowbox.width -# w += rw -# -# if rowbox.width > 0: -# box.content.add(rowbox) -# state.fromx += rowbox.width + result.width = parent.width + result.context = newContext(parent) -#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] -# var state: LayoutState -# stack.add(Frame(node: buffer.document.root, box: buffer.rootbox, maxwidth: 80, context: CONTEXT_BLOCK)) -# while stack.len > 0: -# var frame = stack.pop() -# let node = frame.node -# -# 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 -# state.whitespace = false -# frame.box = CSSBlockBox(x: state.x, y: state.y, width: frame.maxwidth) -# parent.children.add(frame.box) -# frame.context = CONTEXT_BLOCK -# of DISPLAY_INLINE: -# let parent = frame.box -# frame.box = CSSInlineBox(x: state.x, y: state.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) -# state.addText(frame, 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 -# -# eprint "?????" -# else: discard -# -# 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, context: frame.context)) -# dec i +func newInlineBox*(parent: CSSBox): CSSInlineBox = + assert parent != nil + new(result) + result.x = parent.x + result.y = parent.y + parent.height -type - LayoutState = object - x: int - y: int - fromx: int - whitespace: bool - context: FormatContext + result.width = parent.width + result.context = parent.context + if result.context == nil: + result.context = newContext(parent) - FormatContext = enum - CONTEXT_BLOCK, CONTEXT_INLINE +proc processInlineBox(parent: CSSBox, str: string): CSSBox = + var ibox: CSSInlineBox + var use_parent = false + if parent of CSSInlineBox: + ibox = CSSInlineBox(parent) + use_parent = true + else: + ibox = newInlineBox(parent) -proc addChild(parent: var CSSBox, box: CSSBox) = + if str.len == 0: + return + + var i = 0 + var rowi = 0 + var fromx = ibox.context.fromx + var rowbox = CSSRowBox(x: fromx, y: ibox.y + rowi) + var r: Rune + while i < str.len: + fastRuneAt(str, i, r) + if rowbox.width + r.width() > ibox.width: + ibox.content.add(rowbox) + inc rowi + fromx = ibox.x + ibox.context.whitespace = true + ibox.context.conty = false + rowbox = CSSRowBox(x: ibox.x, y: ibox.y + rowi) + if r.isWhitespace(): + if ibox.context.whitespace: + continue + else: + ibox.context.whitespace = true + else: + ibox.context.whitespace = false + rowbox.width += r.width() + rowbox.runes.add(r) + if rowbox.runes.len > 0: + ibox.content.add(rowbox) + ibox.context.fromx = fromx + rowbox.width + ibox.context.conty = true + + ibox.height += rowi + if use_parent: + return nil + return ibox + +proc processElemBox(parent: CSSBox, elem: Element): CSSBox = + case elem.cssvalues[RULE_DISPLAY].display + of DISPLAY_BLOCK: + result = newBlockBox(parent) + result.context.cssvalues = elem.cssvalues + of DISPLAY_INLINE: + result = newInlineBox(parent) + result.context.cssvalues = elem.cssvalues + of DISPLAY_NONE: + return nil + else: + return nil + +proc add(parent: var CSSBox, box: CSSBox) = if box == nil: return + if box of CSSBlockBox: + parent.context.fromx = 0 + parent.context.whitespace = true + if box.context.conty: + inc box.height parent.height += box.height parent.children.add(box) +proc processPseudoBox(parent: CSSBox, cssvalues: CSSComputedValues): CSSBox = + case cssvalues[RULE_DISPLAY].display + of DISPLAY_BLOCK: + result = newBlockBox(parent) + result.context.cssvalues = cssvalues + result.add(processInlineBox(parent, $cssvalues[RULE_CONTENT].content)) + of DISPLAY_INLINE: + result = processInlineBox(parent, $cssvalues[RULE_CONTENT].content) + of DISPLAY_NONE: + return nil + else: + return nil + proc processNode(parent: CSSBox, node: Node): CSSBox = case node.nodeType of ELEMENT_NODE: let elem = Element(node) - var box: CSSBox - case elem.cssvalues[RULE_DISPLAY].display - of DISPLAY_BLOCK: - box = CSSBlockBox(x: parent.x, y: parent.y + parent.height, width: parent.width) - of DISPLAY_INLINE: - #TODO split this into its own thing - #TODO also rethink this bc it isn't very great - #TODO like, it doesn't work - var fromx = parent.x - if parent.children.len > 0 and parent.children[^1] of CSSInlineBox: - let sib = CSSInlineBox(parent.children[^1]) - if sib.content.len > 0: - fromx = sib.content[^1].x + sib.content[^1].width - else: - eprint "???" - elif parent of CSSInlineBox: - let sib = CSSInlineBox(parent) - if sib.content.len > 0: - fromx = sib.content[^1].x + sib.content[^1].width - else: - eprint "???" - box = CSSInlineBox(x: parent.x, y: parent.y + parent.height, width: parent.width) - CSSInlineBox(box).content.add(CSSRowBox(x: fromx, y: box.y)) - of DISPLAY_NONE: - return nil - else: - return nil - for child in elem.childNodes: - CSSBox(box).addChild(processNode(box, child)) - return box - of TEXT_NODE: - let text = Text(node) - #TODO not always anonymous - var ibox = CSSInlineBox(x: parent.x, y: parent.y + parent.height, width: parent.width) - var ws = true #TODO doesn't always start with newline - let str = text.data - - if text.data.len == 0: + result = processElemBox(parent, elem) + if result == nil: return - #TODO ok we'll have to rethink this methinks - var fromx = ibox.x - if parent.children.len > 0 and parent.children[^1] of CSSInlineBox: - let sib = CSSInlineBox(parent.children[^1]) - if sib.content.len > 0: - fromx = sib.content[^1].x + sib.content[^1].width - else: - eprint "???" - elif parent of CSSInlineBox: - let sib = CSSInlineBox(parent) - if sib.content.len > 0: - fromx = sib.content[^1].x + sib.content[^1].width - else: - eprint "???" + if elem.cssvalues_before.isSome: + let bbox = processPseudoBox(parent, elem.cssvalues_before.get) + if bbox != nil: + result.add(bbox) - var i = 0 - var w = 0 - var rowi = 0 - var rowbox = CSSRowBox(x: fromx, y: ibox.y + rowi) - var r: Rune - while i < text.data.len: - fastRuneAt(text.data, i, r) - if w + r.width() > ibox.width: - ibox.content.add(rowbox) - inc rowi - rowbox = CSSRowBox(x: ibox.x, y: ibox.y + rowi) - if r.isWhitespace(): - if ws: - continue - else: - ws = true - else: - ws = false - rowbox.width += r.width() - rowbox.runes.add(r) - if rowbox.runes.len > 0: - ibox.content.add(rowbox) - inc rowi + for child in elem.childNodes: + result.add(processNode(result, child)) - ibox.height += rowi - return ibox + if elem.cssvalues_after.isSome: + let abox = processPseudoBox(parent, elem.cssvalues_after.get) + if abox != nil: + result.add(abox) + of TEXT_NODE: + let text = Text(node) + return processInlineBox(parent, text.data) else: discard - return nil proc alignBoxes*(buffer: Buffer) = buffer.rootbox = CSSBlockBox(x: 0, y: 0, width: buffer.width, height: 0) + buffer.rootbox.context = newContext(buffer.rootbox) for child in buffer.document.root.childNodes: - buffer.rootbox.addChild(processNode(buffer.rootbox, child)) + buffer.rootbox.add(processNode(buffer.rootbox, child)) diff --git a/src/types/enums.nim b/src/types/enums.nim index e257e579..3d79091b 100644 --- a/src/types/enums.nim +++ b/src/types/enums.nim @@ -84,6 +84,9 @@ type DrawInstructionType* = enum DRAW_TEXT, DRAW_GOTO, DRAW_FGCOLOR, DRAW_BGCOLOR, DRAW_STYLE, DRAW_RESET + FormatContextType* = enum + CONTEXT_BLOCK, CONTEXT_INLINE + const SelfClosingTagTypes* = { TAG_LI, TAG_P } |