From 5f083a7d01c9f0f0e24ffdd0be8b976ee59b3de1 Mon Sep 17 00:00:00 2001 From: Araq Date: Sun, 16 Apr 2017 01:36:42 +0200 Subject: [PATCH 1/4] bugfix: virtual thunks need to have a key of -1 --- src/vdom.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vdom.nim b/src/vdom.nim index d138c7a..25f6c12 100644 --- a/src/vdom.nim +++ b/src/vdom.nim @@ -124,10 +124,10 @@ template callThunk*(fn: typed; n: VNode): untyped = fn(n.kids) proc vthunk*(name: cstring; args: varargs[VNode, vn]): VNode = - VNode(kind: VNodeKind.vthunk, text: name, kids: @args) + VNode(kind: VNodeKind.vthunk, text: name, key: -1, kids: @args) proc dthunk*(name: cstring; args: varargs[VNode, vn]): VNode = - VNode(kind: VNodeKind.dthunk, text: name, 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 From 0255f58964a7c2ccd8761991d1f938481d0b7e22 Mon Sep 17 00:00:00 2001 From: Araq Date: Sun, 16 Apr 2017 09:51:10 +0200 Subject: [PATCH 2/4] wip: stateful components --- experiments/example.html | 4 -- experiments/example.nim | 148 ++++++--------------------------------- experiments/example.nims | 1 + src/components.nim | 47 +++++++++++++ src/karax.nim | 29 +++++++- 5 files changed, 99 insertions(+), 130 deletions(-) create mode 100644 experiments/example.nims diff --git a/experiments/example.html b/experiments/example.html index 35b9837..2303d95 100644 --- a/experiments/example.html +++ b/experiments/example.html @@ -3,7 +3,6 @@ Todo app - @@ -11,9 +10,6 @@
- diff --git a/experiments/example.nim b/experiments/example.nim index 0ea805e..78dcaae 100644 --- a/experiments/example.nim +++ b/experiments/example.nim @@ -1,135 +1,33 @@ -import vdom, karax, karaxdsl, jdict, jstrutils - -type - Filter = enum - all, active, completed +import vdom, components, karax, karaxdsl, jdict, jstrutils var - entries: seq[(cstring, bool)] - selectedEntry = -1 - filter: Filter + images: seq[cstring] = @[cstring"a", "b", "c", "d"] -proc onTodoEnter(ev: Event; n: VNode) = - entries.add((n.value, false)) - n.value = "" +proc carousel*(key: VKey): VNode {.component.} = + state: + var counter = 0 -proc removeHandler(ev: Event; n: VNode) = - let id = suffixAsInt(n.id, "remove:") - entries[id] = (cstring(nil), false) + proc next(ev: Event; n: VNode) = + counter = (counter + 1) mod images.len + markDirty key -proc editHandler(ev: Event; n: VNode) = - let id = suffixAsInt(n.id, "edit:") - selectedEntry = id - -when defined(usecache): - var entryCache = newJDict[int, VNode]() - -proc focusLost(ev: Event; n: VNode) = selectedEntry = -1 - -proc editEntry(ev: Event; n: VNode) = - let id = suffixAsInt(n.id, "todo-edit:") - entries[id][0] = n.value - selectedEntry = -1 - -proc toggleEntry(ev: Event; n: VNode) = - let id = suffixAsInt(n.id, "toggle:") - entries[id][1] = not entries[id][1] - -proc onAllDone(ev: Event; n: VNode) = - entries = @[] - selectedEntry = -1 - -proc clearCompleted(ev: Event, n: VNode) = - for i in 0..= 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: From f0e2d41fdee293a14b37b92fe1995ece556e6c32 Mon Sep 17 00:00:00 2001 From: Araq Date: Mon, 17 Apr 2017 09:39:13 +0200 Subject: [PATCH 3/4] next step to get stateful components --- experiments/example.nim | 1 - experiments/oldwidgets.nim | 48 ++++++++++++++++ src/components.nim | 111 ++++++++++++++++++++++++------------- src/karax.nim | 15 ++--- 4 files changed, 126 insertions(+), 49 deletions(-) diff --git a/experiments/example.nim b/experiments/example.nim index 78dcaae..61235f8 100644 --- a/experiments/example.nim +++ b/experiments/example.nim @@ -10,7 +10,6 @@ proc carousel*(key: VKey): VNode {.component.} = proc next(ev: Event; n: VNode) = counter = (counter + 1) mod images.len - markDirty key result = buildHtml(tdiv(key=key)): text images[counter] diff --git a/experiments/oldwidgets.nim b/experiments/oldwidgets.nim index 023dc98..c947d7f 100644 --- a/experiments/oldwidgets.nim +++ b/experiments/oldwidgets.nim @@ -201,3 +201,51 @@ proc visibleKeys(e: Node; a, b: var VKey; h, count: var int) = else: for i in 0..= 0 and dirty.contains(&newNode.key): - dirty.del(&newNode.key) - dec dirtyCount + if newNode.key >= 0 and isDirty(newNode.key): + unmarkDirty(newNode.key) let n = vnodeToDom(newNode) if parent == nil: replaceById("ROOT", n) @@ -241,9 +234,9 @@ proc dodraw() = let olddom = document.getElementById("ROOT") updateElement(nil, olddom, newtree, currentTree) #assert same(newtree, document.getElementById("ROOT")) - if dirtyCount > 0: + if someDirty: updateDirtyElements(nil, olddom, newtree) - dirtyCount = 0 + someDirty = false currentTree = newtree # now that it's part of the DOM, give it the focus: if toFocus != nil: From be4159fc117c0ac79d26570162665b3d9953bb96 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 19 Apr 2017 11:17:00 +0200 Subject: [PATCH 4/4] stateful components now work --- experiments/example.nim | 2 +- src/components.nim | 93 ++++++++++++++++++++++++----------------- 2 files changed, 55 insertions(+), 40 deletions(-) diff --git a/experiments/example.nim b/experiments/example.nim index 61235f8..3fe4000 100644 --- a/experiments/example.nim +++ b/experiments/example.nim @@ -6,7 +6,7 @@ var proc carousel*(key: VKey): VNode {.component.} = state: - var counter = 0 + var counter: int = 0 proc next(ev: Event; n: VNode) = counter = (counter + 1) mod images.len diff --git a/src/components.nim b/src/components.nim index f86cd14..b93742d 100644 --- a/src/components.nim +++ b/src/components.nim @@ -4,8 +4,9 @@ import macros, jdict, dom, vdom, tables, strutils type StateDict*[V] = ref object + defaultValue*: V -proc `[]`*[V](d: StateDict[V], k: VKey): V {.importcpp: "#[#]".} +proc get[V](d: StateDict[V], k: VKey): V {.importcpp: "#[#]".} proc put[V](d: StateDict[V], k: VKey, v: V) {.importcpp: "#[#] = #".} proc contains*[V](d: StateDict[V], k: VKey): bool {.importcpp: "#.hasOwnProperty(#)".} @@ -24,6 +25,10 @@ proc markDirty*(key: VKey) = proc unmarkDirty*(key: VKey) = dirty.del key proc isDirty*(key: VKey): bool = dirty.contains(key) +proc `[]`*[V](d: StateDict[V], k: VKey): V = + if d.contains(k): result = d.get(k) + else: result = d.defaultValue + proc `[]=`*[V](d: StateDict[V], k: VKey, v: V) = d.put(k, v) markDirty(k) @@ -56,50 +61,62 @@ proc addTags() {.compileTime.} = static: addTags() -proc stateDecl(n: NimNode; names: TableRef[string, bool]) = + +template toState(x): untyped = newIdentNode("state" & x) +proc accessState(sv: NimNode): NimNode {.compileTime.} = + newTree(nnkBracketExpr, sv, newIdentNode("key")) + +proc stateDecl(n: NimNode; names: TableRef[string, bool]; decl: 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: newCall("type", val) + if usedType.kind == nnkEmpty: + error("cannot determine the variable's type", c) for i in 0 .. c.len-3: let v = $c[i] + let sv = toState v + decl.add quote do: + var `sv` = newStateDict[`usedType`]() + if val.kind != nnkEmpty: + decl.add newTree(nnkAsgn, newDotExpr(sv, newIdentNode"defaultValue"), + val) + else: + error("component state must have a primitive initializer") names[v] = true of nnkStmtList, nnkStmtListExpr: - for x in n: stateDecl(x, names) + for x in n: stateDecl(x, names, decl) of nnkDo: - stateDecl(n.body, names) - else: discard - -proc accessesState(n: NimNode; names: TableRef[string, bool]): bool = - case n.kind - of nnkSym, nnkIdent: - result = $n in names - of nnkBracketExpr, nnkDotExpr: - result = accessesState(n[0], names) + stateDecl(n.body, names, decl) + of nnkCommentStmt: discard else: - for i in 0..