diff --git a/src/karax.nim b/src/karax.nim index ed956d8..85a126a 100644 --- a/src/karax.nim +++ b/src/karax.nim @@ -165,6 +165,7 @@ proc updateDirtyElements(parent, current: Node, newNode: VNode) = #if dirtyCount <= 0: return proc updateElement(parent, current: Node, newNode, oldNode: VNode) = + newNode.dom = oldNode.dom if not equalsShallow(newNode, oldNode): detach(oldNode) let n = vnodeToDom(newNode) @@ -175,7 +176,6 @@ proc updateElement(parent, current: Node, newNode, oldNode: VNode) = elif newNode.kind != VNodeKind.text: let newLength = newNode.len var oldLength = oldNode.len - let minLength = min(newLength, oldLength) assert oldNode.kind == newNode.kind when defined(simpleDiff): for i in 0..min(newLength, oldLength)-1: @@ -188,36 +188,46 @@ proc updateElement(parent, current: Node, newNode, oldNode: VNode) = detach(oldNode[i]) current.removeChild(current.lastChild) else: - var commonPrefix = 0 - while commonPrefix < minLength and - equalsTree(newNode[commonPrefix], oldNode[commonPrefix]): - inc commonPrefix + var left = 0 + var rightNew = newLength - 1 + var rightOld = oldLength - 1 + + while left <= min(rightNew, rightOld): + var minRight = min(rightOld, rightNew) + + while left <= minRight and + equalsTree(newNode[left], oldNode[left]): + newNode[left] = oldNode[left] + inc left - var oldPos = oldLength - 1 - var newPos = newLength - 1 - while oldPos >= commonPrefix and newPos >= commonPrefix and - equalsTree(newNode[newPos], oldNode[oldPos]): - dec oldPos - dec newPos + while rightOld > left and rightNew > left and + equalsTree(newNode[rightNew], oldNode[rightOld]): + newNode[rightNew] = oldNode[rightOld] + dec rightOld + dec rightNew + + minRight = min(rightOld, rightNew) + if left <= minRight: + updateElement(current, oldNode[left].dom, newNode[left], oldNode[left]) + inc left - var pos = min(oldPos, newPos) + 1 - for i in commonPrefix..pos-1: - updateElement(current, current.childNodes[i], newNode[i], oldNode[i]) + if left < minRight: + updateElement(current, oldNode[rightOld].dom, newNode[rightNew], oldNode[rightOld]) + dec rightNew + dec rightOld + + var posNextElem = rightOld + 1 + var isPushBack = posNextElem == oldLength + while left <= rightNew: + if isPushBack: + current.appendChild(vnodeToDom(newNode[left])) + else: + current.insertBefore(vnodeToDom(newNode[left]), oldNode[posNextElem].dom) + inc left - var nextChildPos = oldPos + 1 - while pos <= newPos: - if nextChildPos == oldLength: - current.appendChild(vnodeToDom(newNode[pos])) - else: - current.insertBefore(vnodeToDom(newNode[pos]), current.childNodes[nextChildPos]) - # added new Node, so old state of VDOM have one more Node - inc oldLength - inc pos - inc nextChildPos - - for i in pos..oldPos: - detach(oldNode[i]) - current.removeChild(current.childNodes[pos]) + for i in left..rightOld: + current.removeChild(oldNode[i].dom) + detach(oldNode[i]) when false: var drawTimeout: Timeout @@ -256,6 +266,8 @@ proc redraw*() = else: dodraw() +proc redrawForce*() = dodraw() + proc init(ev: Event) = reqFrame(dodraw) @@ -263,6 +275,12 @@ proc setRenderer*(renderer: proc (): VNode) = dorender = renderer window.onload = init +proc setRendererOnly*(renderer: proc (): VNode) = + dorender = renderer + +proc setOnloadOnly*() = + window.onload = init + proc addEventHandler*(n: VNode; k: EventKind; action: EventHandler) = ## Implements the foundation of Karax's event management. ## Karax DSL transforms ``tag(onEvent = handler)`` to diff --git a/src/karaxdsl.nim b/src/karaxdsl.nim index 5ba525a..5553d8f 100644 --- a/src/karaxdsl.nim +++ b/src/karaxdsl.nim @@ -4,7 +4,7 @@ from strutils import startsWith, toLowerAscii const StmtContext = ["kout", "inc", "echo", "dec", "!"] - SpecialAttrs = ["id", "class", "value", "key"] + SpecialAttrs = ["id", "class", "value", "key", "track"] proc getName(n: NimNode): string = case n.kind diff --git a/src/vdom.nim b/src/vdom.nim index 25f6c12..a80fc10 100644 --- a/src/vdom.nim +++ b/src/vdom.nim @@ -102,12 +102,14 @@ type kind*: VNodeKind key*: VKey id*, class*, text*: cstring + track*: int kids: seq[VNode] # even index: key, odd index: value; done this way for memory efficiency: attrs: seq[cstring] events*: seq[(EventKind, EventHandler)] - hash*: Hash - validHash*: bool + when false: + hash*: Hash + validHash*: bool dom*: Node ## the attached real DOM node. Can be 'nil' if the virtual node ## is not part of the virtual DOM anymore. @@ -146,7 +148,11 @@ proc eq*(a, b: VNode): bool = proc setAttr*(n: VNode; key: cstring; val: cstring = "") = if n.attrs.isNil: - n.attrs = @[key, val] + when true: + let x = @[key, val] + shallowCopy n.attrs, x + else: + n.attrs = @[key, val] else: for i in countup(0, n.attrs.len-2, 2): if n.attrs[i] == key: @@ -161,6 +167,7 @@ proc getAttr*(n: VNode; key: cstring): cstring = proc len*(x: VNode): int = x.kids.len proc `[]`*(x: VNode; idx: int): VNode = x.kids[idx] +proc `[]=`*(x: VNode; idx: int; y: VNode) = x.kids[idx] = y proc add*(parent, kid: VNode) = parent.kids.add kid proc newVNode*(kind: VNodeKind): VNode = VNode(kind: kind, key: -1) @@ -216,32 +223,33 @@ proc toString*(n: VNode; result: var string; indent: int) = for i in 1..indent: result.add ' ' result.add "\L" & $n.kind & ">" -proc calcHash*(n: VNode) = - if n.validHash: return - n.validHash = true - var h: Hash = ord n.kind - if n.id != nil: - h &= "id" - h &= n.id - if n.class != nil: - h &= "class" - h &= n.class - if n.key >= 0: - h &= "k" - h &= n.key - for k, v in attrs(n): - h &= " " - h &= k - h &= "=" - h &= v - if n.kind == VNodeKind.text or n.text != nil: - h &= "t" - h &= n.text - else: - for child in items(n): - calcHash(child) - h &= child.hash - n.hash = h +when false: + proc calcHash*(n: VNode) = + if n.validHash: return + n.validHash = true + var h: Hash = ord n.kind + if n.id != nil: + h &= "id" + h &= n.id + if n.class != nil: + h &= "class" + h &= n.class + if n.key >= 0: + h &= "k" + h &= n.key + for k, v in attrs(n): + h &= " " + h &= k + h &= "=" + h &= v + if n.kind == VNodeKind.text or n.text != nil: + h &= "t" + h &= n.text + else: + for child in items(n): + calcHash(child) + h &= child.hash + n.hash = h proc `$`*(n: VNode): cstring = var res = "" diff --git a/uibench/index.html b/uibench/index.html new file mode 100644 index 0000000..5b2cb4c --- /dev/null +++ b/uibench/index.html @@ -0,0 +1,16 @@ + + +
+ + +