diff options
author | bptato <nincsnevem662@gmail.com> | 2022-01-22 23:33:36 +0100 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2022-01-22 23:33:36 +0100 |
commit | 6ff61c5ad2ad2af36195b83582ed98be57b93f18 (patch) | |
tree | 899799dfea6cc07433aaf77985f2aefc9964b74a /src | |
parent | c73367b5e940c184247908a22cd4a495de4a0fde (diff) | |
download | chawan-6ff61c5ad2ad2af36195b83582ed98be57b93f18.tar.gz |
Avoid unnecessary rendering on hover change etc
Diffstat (limited to 'src')
-rw-r--r-- | src/css/cascade.nim | 28 | ||||
-rw-r--r-- | src/css/values.nim | 41 | ||||
-rw-r--r-- | src/layout/box.nim | 2 | ||||
-rw-r--r-- | src/layout/engine.nim | 56 | ||||
-rw-r--r-- | src/types/color.nim | 3 |
5 files changed, 98 insertions, 32 deletions
diff --git a/src/css/cascade.nim b/src/css/cascade.nim index 8ce999e0..223a3ec5 100644 --- a/src/css/cascade.nim +++ b/src/css/cascade.nim @@ -27,14 +27,13 @@ proc applyProperty(elem: Element, d: CSSDeclaration, pseudo: PseudoElem) = case pseudo of PSEUDO_NONE: - elem.rendered = not elem.css.applyValue(parent, d) + elem.css.applyValue(parent, d) of PSEUDO_BEFORE, PSEUDO_AFTER: if elem.pseudo[pseudo] == nil: elem.pseudo[pseudo] = elem.css.inheritProperties() - elem.rendered = not elem.pseudo[pseudo].applyValue(parent, d) + elem.pseudo[pseudo].applyValue(parent, d) elem.cssapplied = true - elem.rendered = false func applies(mq: MediaQuery): bool = case mq.t @@ -108,6 +107,23 @@ proc applyImportant(ares: var ApplyResult, decls: seq[CSSDeclaration]) = if decl.important: ares.important.add(decl) +proc checkRendered(element: Element, prev: CSSSpecifiedValues, ppseudo: array[PSEUDO_BEFORE..PSEUDO_AFTER, CSSSpecifiedValues]) = + if element.rendered: + for p in PSEUDO_BEFORE..PSEUDO_AFTER: + if ppseudo[p] != element.pseudo[p] and ppseudo[p] == nil: + element.rendered = false + return + for t in CSSPropertyType: + if not element.css[t].equals(prev[t]): + element.rendered = false + return + for p in PSEUDO_BEFORE..PSEUDO_AFTER: + if ppseudo[p] != nil: + for t in CSSPropertyType: + if not element.pseudo[p][t].equals(ppseudo[p][t]): + element.rendered = false + return + proc applyRules(element: Element, ua, user, author: RuleList, pseudo: PseudoElem) = var ares: ApplyResult @@ -175,11 +191,13 @@ proc applyRules*(document: Document, ua, user: CSSStylesheet) = embedded_rules.add(rules_local) if not elem.cssapplied: + let prev = elem.css + let ppseudo = elem.pseudo if elem.parentElement != nil: elem.css = elem.parentElement.css.inheritProperties() else: elem.css = rootProperties() - for pseudo in [PSEUDO_BEFORE, PSEUDO_AFTER]: + for pseudo in PSEUDO_BEFORE..PSEUDO_AFTER: elem.pseudo[pseudo] = nil let uarules = calcRules(elem, ua) @@ -190,6 +208,8 @@ proc applyRules*(document: Document, ua, user: CSSStylesheet) = for pseudo in PseudoElem: elem.applyRules(uarules, userrules, authorrules, pseudo) + elem.checkRendered(prev, ppseudo) + for i in countdown(elem.children.high, 0): stack.add(elem.children[i]) diff --git a/src/css/values.nim b/src/css/values.nim index 7da9a248..638dc2be 100644 --- a/src/css/values.nim +++ b/src/css/values.nim @@ -691,7 +691,26 @@ func getSpecifiedValue(d: CSSDeclaration, parent: CSSSpecifiedValues): tuple[a:C return (val, cssGlobal(d)) -proc applyValue(vals, parent: CSSSpecifiedValues, t: CSSPropertyType, val: CSSSpecifiedValue, global: CSSGlobalValueType): bool = +func equals*(a, b: CSSSpecifiedValue): bool = + if a == b: + return true + if a == nil or b == nil: + return false + case valueType(a.t) + of VALUE_COLOR: return a.color == b.color + of VALUE_LENGTH: return a.length == b.length + of VALUE_FONT_STYLE: return a.fontstyle == b.fontstyle + of VALUE_DISPLAY: return a.display == b.display + of VALUE_CONTENT: return a.content == b.content + of VALUE_WHITESPACE: return a.whitespace == b.whitespace + of VALUE_INTEGER: return a.integer == a.integer + of VALUE_TEXT_DECORATION: return a.textdecoration == b.textdecoration + of VALUE_WORD_BREAK: return a.wordbreak == b.wordbreak + of VALUE_LIST_STYLE_TYPE: return a.liststyletype == b.liststyletype + of VALUE_NONE: return true + return false + +proc applyValue(vals, parent: CSSSpecifiedValues, t: CSSPropertyType, val: CSSSpecifiedValue, global: CSSGlobalValueType) = let oval = vals[t] case global of VALUE_INHERIT, VALUE_UNSET: @@ -705,16 +724,8 @@ proc applyValue(vals, parent: CSSSpecifiedValues, t: CSSPropertyType, val: CSSSp vals[t] = getDefault(t) #TODO of VALUE_NOGLOBAL: vals[t] = val - return oval != vals[t] -proc applyShorthand(vals, parent: CSSSpecifiedValues, left, right, top, bottom: CSSSpecifiedValue, global: CSSGlobalValueType): bool = - result = result or vals.applyValue(parent, left.t, left, global) - result = result or vals.applyValue(parent, right.t, right, global) - result = result or vals.applyValue(parent, top.t, top, global) - result = result or vals.applyValue(parent, bottom.t, bottom, global) - -# Returns true if anything has changed. (TODO: not always...) -proc applyValue*(vals, parent: CSSSpecifiedValues, d: CSSDeclaration): bool = +proc applyValue*(vals, parent: CSSSpecifiedValues, d: CSSDeclaration) = let vv = getSpecifiedValue(d, parent) let val = vv.a let oval = vals[val.t] @@ -723,21 +734,23 @@ proc applyValue*(vals, parent: CSSSpecifiedValues, d: CSSDeclaration): bool = let global = cssGlobal(d) if global != VALUE_NOGLOBAL: for t in CSSPropertyType: - result = result or vals.applyValue(parent, t, nil, global) + vals.applyValue(parent, t, nil, global) of PROPERTY_MARGIN: let left = CSSSpecifiedValue(t: PROPERTY_MARGIN_LEFT, v: VALUE_LENGTH, length: val.length) let right = CSSSpecifiedValue(t: PROPERTY_MARGIN_RIGHT, v: VALUE_LENGTH, length: val.length) let top = CSSSpecifiedValue(t: PROPERTY_MARGIN_TOP, v: VALUE_LENGTH, length: val.length) let bottom = CSSSpecifiedValue(t: PROPERTY_MARGIN_BOTTOM, v: VALUE_LENGTH, length: val.length) - return vals.applyShorthand(parent, left, right, top, bottom, vv.b) + for val in [left, right, top, bottom]: + vals.applyValue(parent, val.t, val, vv.b) of PROPERTY_PADDING: let left = CSSSpecifiedValue(t: PROPERTY_PADDING_LEFT, v: VALUE_LENGTH, length: val.length) let right = CSSSpecifiedValue(t: PROPERTY_PADDING_RIGHT, v: VALUE_LENGTH, length: val.length) let top = CSSSpecifiedValue(t: PROPERTY_PADDING_TOP, v: VALUE_LENGTH, length: val.length) let bottom = CSSSpecifiedValue(t: PROPERTY_PADDING_BOTTOM, v: VALUE_LENGTH, length: val.length) - return vals.applyShorthand(parent, left, right, top, bottom, vv.b) + for val in [left, right, top, bottom]: + vals.applyValue(parent, val.t, val, vv.b) else: - return vals.applyValue(parent, val.t, vv.a, vv.b) + vals.applyValue(parent, val.t, vv.a, vv.b) func inheritProperties*(parent: CSSSpecifiedValues): CSSSpecifiedValues = new(result) diff --git a/src/layout/box.nim b/src/layout/box.nim index 67cb9be9..a49bde7f 100644 --- a/src/layout/box.nim +++ b/src/layout/box.nim @@ -17,6 +17,7 @@ type inlinelayout*: bool specified*: CSSSpecifiedValues node*: Node + element*: Element InlineAtom* = ref object of RootObj relx*: int @@ -59,6 +60,7 @@ type compwidth*: int compheight*: Option[int] + done*: bool RowBox* = object x*: int diff --git a/src/layout/engine.nim b/src/layout/engine.nim index 0063ac05..a0046f36 100644 --- a/src/layout/engine.nim +++ b/src/layout/engine.nim @@ -165,7 +165,6 @@ proc newBlockContext_common(parent: BlockContext, box: CSSBox): BlockContext {.i proc newBlockContext(parent: BlockContext, box: BlockBox): BlockContext = result = newBlockContext_common(parent, box) - parent.nested.add(result) proc newInlineBlockContext(parent: BlockContext, box: InlineBlockBox): BlockContext = newBlockContext_common(parent, box) @@ -176,7 +175,6 @@ proc newBlockContext(parent: BlockContext): BlockContext = result.specified = parent.specified.inheritProperties() result.viewport = parent.viewport result.computedDimensions(parent.compwidth, parent.compheight) - parent.nested.add(result) # Anonymous block box (root). proc newBlockContext(viewport: Viewport): BlockContext = @@ -306,13 +304,14 @@ proc alignBlocks(bctx: BlockContext, blocks: seq[CSSBox]) = let gctx = newBlockContext(bctx) gctx.alignInlines(blockgroup) blockgroup.setLen(0) + bctx.nested.add(gctx) for child in blocks: case child.t of DISPLAY_BLOCK, DISPLAY_LIST_ITEM: let child = BlockBox(child) flush_group() - child.bctx = newBlockContext(bctx, child) + bctx.nested.add(child.bctx) alignBlock(child) of DISPLAY_INLINE: if child.inlinelayout: @@ -327,6 +326,9 @@ proc alignBlocks(bctx: BlockContext, blocks: seq[CSSBox]) = flush_group() proc alignBlock(box: BlockBox) = + if box.bctx.done: + return + box.bctx.done = true if box.node != nil: box.bctx.viewport.nodes.add(box.node) if box.inlinelayout: @@ -378,11 +380,28 @@ proc getPseudoBox(specified: CSSSpecifiedValues): CSSBox = box.children.add(content) return box -proc generateBox(elem: Element, viewport: Viewport, first = false): CSSBox = +proc generateBox(elem: Element, viewport: Viewport, bctx: BlockContext = nil): CSSBox = + elem.rendered = true if viewport.map[elem.uid] != nil: + let box = viewport.map[elem.uid] + var bctx = bctx + if box.specified{"display"} in {DISPLAY_BLOCK, DISPLAY_LIST_ITEM}: + let box = BlockBox(box) + if bctx == nil: + box.bctx = viewport.newBlockContext() + else: + box.bctx = bctx.newBlockContext(box) + bctx = box.bctx + + var i = 0 + while i < box.children.len: + let child = box.children[i] + if child.element != nil: + box.children[i] = generateBox(child.element, viewport, bctx) + inc i return viewport.map[elem.uid] - let box = if not first: + let box = if bctx != nil: getBox(elem.css) else: getBlockBox(elem.css) @@ -391,6 +410,16 @@ proc generateBox(elem: Element, viewport: Viewport, first = false): CSSBox = return nil box.node = elem + box.element = elem + + var bctx = bctx + if box.specified{"display"} in {DISPLAY_BLOCK, DISPLAY_LIST_ITEM}: + let box = BlockBox(box) + if bctx == nil: + box.bctx = viewport.newBlockContext() + else: + box.bctx = bctx.newBlockContext(box) + bctx = box.bctx var ibox: InlineBox template add_ibox() = @@ -429,13 +458,13 @@ proc generateBox(elem: Element, viewport: Viewport, first = false): CSSBox = ibox = box.getTextBox() ibox.newline = true - let cbox = elem.generateBox(viewport) + let cbox = elem.generateBox(viewport, bctx) if cbox != nil: add_ibox() add_box(cbox) of TEXT_NODE: let text = Text(child) - # Don't generate empty anonymous inline blocks. + # Don't generate empty anonymous inline blocks between block boxes if box.specified{"display"} == DISPLAY_INLINE or box.children.len > 0 and box.children[^1].specified{"display"} == DISPLAY_INLINE or box.specified{"white-space"} in {WHITESPACE_PRE_LINE, WHITESPACE_PRE, WHITESPACE_PRE_WRAP} or @@ -459,12 +488,11 @@ proc generateBox(elem: Element, viewport: Viewport, first = false): CSSBox = proc renderLayout*(viewport: var Viewport, document: Document) = if viewport.root == nil or document.all_elements.len != viewport.map.len: viewport.map = newSeq[CSSBox](document.all_elements.len) - viewport.root = BlockBox(document.root.generateBox(viewport, true)) else: - for uid in 0..viewport.map.high: - if not document.all_elements[uid].rendered: - viewport.map[uid] = nil - viewport.root = BlockBox(document.root.generateBox(viewport)) - - viewport.root.bctx = viewport.newBlockContext() + var i = 0 + while i < viewport.map.len: + if not document.all_elements[i].rendered: + viewport.map[i] = nil + inc i + viewport.root = BlockBox(document.root.generateBox(viewport)) alignBlock(viewport.root) diff --git a/src/types/color.nim b/src/types/color.nim index 2690d35d..588e59d3 100644 --- a/src/types/color.nim +++ b/src/types/color.nim @@ -34,6 +34,9 @@ func a*(c: RGBAColor): int = func rgb*(r, g, b: int): RGBColor = return RGBColor((r shl 16) or (g shl 8) or b) +func `==`*(a, b: RGBAColor): bool = + return int(a) == int(b) + func r*(c: RGBColor): int = return int(c) shr 16 and 0xff |