This commit is contained in:
Andreas Rumpf 2017-06-05 17:06:58 +02:00
commit 8a639f3121
2 changed files with 51 additions and 47 deletions

View file

@ -127,33 +127,35 @@ type
EqResult = enum EqResult = enum
different, similar, identical different, similar, identical
proc equalsShallow(a, b: VNode): EqResult = proc eq(a, b: VNode; shallow: bool): EqResult =
if a.kind != b.kind: return different if a.kind != b.kind: return different
if a.id != b.id: return different if a.id != b.id: return different
if a.key != b.key: return different result = identical
if a.key != b.key:
kout cstring"different keys", a.key, b.key
if b.key == 0:
kout cstring"key for ", cstring($b.kind)
return different
if a.kind == VNodeKind.text: if a.kind == VNodeKind.text:
if a.text != b.text: return different if a.text != b.text: return different
elif a.kind == VNodeKind.vthunk or a.kind == VNodeKind.dthunk: elif a.kind == VNodeKind.vthunk or a.kind == VNodeKind.dthunk:
if a.text != b.text: return different if a.text != b.text: return different
if a.len != b.len: return different if a.len != b.len: return different
for i in 0..<a.len: for i in 0..<a.len:
if equalsShallow(a[i], b[i]) == different: return different if eq(a[i], b[i], shallow) == different: return different
elif not shallow:
if a.len != b.len: return different
for i in 0..<a.len:
let res = eq(a[i], b[i], shallow)
if res == different: return different
elif res == similar:
# but continue, maybe something makes it 'different'!
result = similar
if not sameAttrs(a, b): return different if not sameAttrs(a, b): return different
if a.class != b.class: return different if a.class != b.class: return different
if a.style != b.style: return similar if a.style != b.style: return similar
# Do not test event listeners here! # Do not test event listeners here!
return identical return result
proc equalsTree(a, b: VNode): bool =
when false:
# hashing is too fragile now with component support:
if not a.validHash:
a.calcHash()
if not b.validHash:
b.calcHash()
return a.hash == b.hash
else:
result = eq(a, b)
proc updateDirtyElements(parent, current: Node, newNode: VNode) = proc updateDirtyElements(parent, current: Node, newNode: VNode) =
if newNode.key >= 0 and isDirty(newNode.key): if newNode.key >= 0 and isDirty(newNode.key):
@ -170,8 +172,21 @@ proc updateDirtyElements(parent, current: Node, newNode: VNode) =
# leave early if we know there cannot be anything left to do: # leave early if we know there cannot be anything left to do:
#if dirtyCount <= 0: return #if dirtyCount <= 0: return
proc updateStyles(newNode, oldNode: VNode; shallow: bool) =
# we keep the oldNode, but take over the style from the new node:
if oldNode.dom != nil:
kout cstring"updateStyle deep updated dom"
if newNode.style != nil: applyStyle(oldNode.dom, newNode.style)
else: oldNode.dom.style = Style()
oldNode.style = newNode.style
if not shallow:
assert newNode.len == oldNode.len
for i in 0 ..< newNode.len:
updateStyles(newNode[i], oldNode[i], shallow)
proc updateElement(parent, current: Node, newNode, oldNode: VNode) = proc updateElement(parent, current: Node, newNode, oldNode: VNode) =
let res = equalsShallow(newNode, oldNode) let res = eq(newNode, oldNode, shallow=true)
kout cstring($res), cstring"shallow"
if res == different: if res == different:
detach(oldNode) detach(oldNode)
let n = vnodeToDom(newNode) let n = vnodeToDom(newNode)
@ -180,12 +195,7 @@ proc updateElement(parent, current: Node, newNode, oldNode: VNode) =
else: else:
parent.replaceChild(n, current) parent.replaceChild(n, current)
else: else:
if res == similar: if res == similar: updateStyles(newNode, oldNode, true)
# we keep the oldNode, but take over the style from the new node:
if oldNode.dom != nil:
if newNode.style != nil: applyStyle(oldNode.dom, newNode.style)
else: oldNode.dom.style = Style()
oldNode.style = newNode.style
if newNode.kind != VNodeKind.text: if newNode.kind != VNodeKind.text:
let newLength = newNode.len let newLength = newNode.len
@ -204,16 +214,27 @@ proc updateElement(parent, current: Node, newNode, oldNode: VNode) =
current.removeChild(current.lastChild) current.removeChild(current.lastChild)
else: else:
var commonPrefix = 0 var commonPrefix = 0
while commonPrefix < minLength and
equalsTree(newNode[commonPrefix], oldNode[commonPrefix]): template eqAndUpdate(a, b: VNode; action: untyped) =
inc commonPrefix let r = eq(a, b, false)
kout cstring($r), cstring"eqAndUpdate"
case r
of identical: action
of different: break
of similar:
updateStyles(a, b, false)
action
while commonPrefix < minLength:
eqAndUpdate(newNode[commonPrefix], oldNode[commonPrefix]):
inc commonPrefix
var oldPos = oldLength - 1 var oldPos = oldLength - 1
var newPos = newLength - 1 var newPos = newLength - 1
while oldPos >= commonPrefix and newPos >= commonPrefix and while oldPos >= commonPrefix and newPos >= commonPrefix:
equalsTree(newNode[newPos], oldNode[oldPos]): eqAndUpdate(newNode[newPos], oldNode[oldPos]):
dec oldPos dec oldPos
dec newPos dec newPos
var pos = min(oldPos, newPos) + 1 var pos = min(oldPos, newPos) + 1
for i in commonPrefix..pos-1: for i in commonPrefix..pos-1:
@ -241,6 +262,7 @@ proc dodraw() =
if dorender.isNil: return if dorender.isNil: return
let newtree = dorender() let newtree = dorender()
newtree.id = "ROOT" newtree.id = "ROOT"
kout cstring"do draw!", cstring getStackTrace()
toFocus = nil toFocus = nil
if currentTree == nil: if currentTree == nil:
currentTree = newtree currentTree = newtree

View file

@ -133,24 +133,6 @@ proc vthunk*(name: cstring; args: varargs[VNode, vn]): VNode =
proc dthunk*(name: cstring; args: varargs[VNode, vn]): VNode = proc dthunk*(name: cstring; args: varargs[VNode, vn]): VNode =
VNode(kind: VNodeKind.dthunk, text: name, key: -1, kids: @args) VNode(kind: VNodeKind.dthunk, text: name, key: -1, kids: @args)
proc eq*(a, b: VNode): bool =
if a.kind != b.kind: return false
if a.id != b.id: return false
if a.class != b.class: return false
if a.key != b.key: return false
if a.kind != VNodeKind.text:
if a.kids.len != b.kids.len: return false
for i in 0..<a.kids.len:
if not eq(a.kids[i], b.kids[i]): return false
if a.text != b.text: return false
if a.attrs.len != b.attrs.len: return false
for i in 0..<a.attrs.len:
if a.attrs[i] != b.attrs[i]: return false
# Do not compare styles here. The idea is that nodes only
# differing in styling can be made equal easily enough:
#if a.style != b.style: return false
result = true
proc setAttr*(n: VNode; key: cstring; val: cstring = "") = proc setAttr*(n: VNode; key: cstring; val: cstring = "") =
if n.attrs.isNil: if n.attrs.isNil:
n.attrs = @[key, val] n.attrs = @[key, val]