wip: stateful components

This commit is contained in:
Araq 2017-04-16 09:51:10 +02:00
commit 0255f58964
5 changed files with 99 additions and 130 deletions

View file

@ -30,6 +30,53 @@ proc addTags() {.compileTime.} =
static:
addTags()
template toState(x): untyped = "state" & x
proc accessState(sv: string): NimNode {.compileTime.} =
newTree(nnkBracketExpr, newIdentNode(sv), newIdentNode("key"))
proc stateDecl(n: NimNode; names: TableRef[string, bool]; decl, init: NimNode) =
case n.kind
of nnkVarSection, nnkLetSection:
for c in n:
expectKind c, nnkIdentDefs
let typ = c[^2]
let val = c[^1]
let usedType = if typ.kind != nnkEmpty: typ else: val
if usedType.kind == nnkEmpty:
error(c, "cannot determine the variable's type")
for i in 0 .. c.len-3:
let v = $c[i]
let sv = toState v
decl.add quote do:
var `sv` = newJDict[VKey, `usedType`]()
if val.kind != nnkEmpty:
init.add newTree(nnkAsgn, accessState(sv), val)
names[v] = true
of nnkStmtList, nnkStmtListExpr:
for x in n: stateDecl(x, names, decl, init)
of nnkDo:
stateDecl(n.body, names, decl, init)
of nnkCommentStmt: discard
else:
error(n, "invalid 'state' declaration")
proc doState(n: NimNode; names: TableRef[string, bool]; decl, init: NimNode): NimNode =
result = n
case n.kind
of nnkCallKinds:
# handle 'state' declaration and remove it from the AST:
if n.len == 2 and repr(n[0]) == "state":
stateDecl(n[1], names, decl, init)
result = newTree(nnkEmpty)
of nnkSym, nnkIdent:
let v = $n
if v in names:
let sv = toState v
result = accessState(sv)
else:
for i in 0..<n.len:
result[i] = doState(n[i], names, decl, init)
proc unpack(symbolicType: NimNode; index: int): NimNode {.compileTime.} =
#let t = symbolicType.getTypeImpl
let t = repr(symbolicType)

View file

@ -75,10 +75,12 @@ proc vnodeToDom(n: VNode): Node =
elif n.kind == VNodeKind.vthunk:
let x = callThunk(vcomponents[n.text], n)
result = vnodeToDom(x)
n.key = result.key
attach n
return result
elif n.kind == VNodeKind.dthunk:
result = callThunk(dcomponents[n.text], n)
n.key = result.key
attach n
return result
else:
@ -114,6 +116,8 @@ proc same(n: VNode, e: Node): bool =
var
dorender: proc (): VNode {.closure.}
currentTree: VNode
dirty = newJDict[cstring, bool]()
dirtyCount: int
proc replaceById(id: cstring; newTree: Node) =
let x = document.getElementById(id)
@ -145,7 +149,27 @@ proc equalsTree(a, b: VNode): bool =
b.calcHash()
return a.hash == b.hash
else:
return eq(a, b)
result = eq(a, b)
proc markDirty*(key: VKey) =
dirty[&key] = true
inc dirtyCount
proc updateDirtyElements(parent, current: Node, newNode: VNode) =
if newNode.key >= 0 and dirty.contains(&newNode.key):
dirty.del(&newNode.key)
dec dirtyCount
let n = vnodeToDom(newNode)
if parent == nil:
replaceById("ROOT", n)
else:
parent.replaceChild(n, current)
elif newNode.kind != VNodeKind.text and newNode.kind != VNodeKind.vthunk and
newNode.kind != VNodeKind.dthunk:
for i in 0..newNode.len-1:
updateDirtyElements(current, current[i], newNode[i])
# leave early if we know there cannot be anything left to do:
#if dirtyCount <= 0: return
proc updateElement(parent, current: Node, newNode, oldNode: VNode) =
if not equalsShallow(newNode, oldNode):
@ -217,6 +241,9 @@ proc dodraw() =
let olddom = document.getElementById("ROOT")
updateElement(nil, olddom, newtree, currentTree)
#assert same(newtree, document.getElementById("ROOT"))
if dirtyCount > 0:
updateDirtyElements(nil, olddom, newtree)
dirtyCount = 0
currentTree = newtree
# now that it's part of the DOM, give it the focus:
if toFocus != nil: