This commit is contained in:
Andreas Rumpf 2017-06-22 21:21:54 +02:00
commit 9aa3ba545d
5 changed files with 118 additions and 13 deletions

View file

@ -106,7 +106,10 @@ proc vnodeToDom(n: VNode; kxi: KaraxInstance): Node =
let x = VComponent(n) let x = VComponent(n)
if x.onAttachImpl != nil: x.onAttachImpl(x) if x.onAttachImpl != nil: x.onAttachImpl(x)
assert x.renderImpl != nil assert x.renderImpl != nil
result = vnodeToDom(x.renderImpl(x), kxi) if x.expanded == nil:
x.expanded = x.renderImpl(x)
x.updatedImpl(x)
result = vnodeToDom(x.expanded, kxi)
attach n attach n
return result return result
else: else:
@ -133,7 +136,9 @@ proc vnodeToDom(n: VNode; kxi: KaraxInstance): Node =
if not n.style.isNil: applyStyle(result, n.style) if not n.style.isNil: applyStyle(result, n.style)
proc same(n: VNode, e: Node): bool = proc same(n: VNode, e: Node): bool =
if toTag[n.kind] == e.nodename: if n.kind == VNodeKind.component:
result = same(VComponent(n).expanded, e)
elif toTag[n.kind] == e.nodename:
result = true result = true
if n.kind != VNodeKind.text: if n.kind != VNodeKind.text:
if e.len != n.len: return false if e.len != n.len: return false
@ -177,7 +182,9 @@ proc eq(a, b: VNode; deep: bool): EqResult =
result = similar 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 not eq(a.style, b.style):
kout cstring"yes, styles differ"
return similar
# Do not test event listeners here! # Do not test event listeners here!
return result return result
@ -218,11 +225,22 @@ proc updateDom(newNode, oldNode: VNode) =
for i in 0 ..< newNode.len: for i in 0 ..< newNode.len:
updateDom(newNode[i], oldNode[i]) updateDom(newNode[i], oldNode[i])
proc printV(n: VNode; depth: cstring = "") =
kout depth, cstring($n.kind), n.myid, cstring"key ", n.key
#for k, v in pairs(n.style):
# kout depth, "style: ", k, v
if n.kind == VNodeKind.component:
let nn = VComponent(n)
if nn.expanded != nil: printV(nn.expanded, ">>" & depth)
for i in 0 ..< n.len:
printV(n[i], depth & " ")
proc updateElement(parent, current: Node, newNode, oldNode: VNode; proc updateElement(parent, current: Node, newNode, oldNode: VNode;
kxi: KaraxInstance): EqResult = kxi: KaraxInstance): EqResult =
result = eq(newNode, oldNode, deep=false) result = eq(newNode, oldNode, deep=false)
if result <= different: if result <= different:
var n: Node var n: Node
var state = 0
if result == changed: if result == changed:
assert oldNode.kind == VNodeKind.component assert oldNode.kind == VNodeKind.component
let x = VComponent(oldNode) let x = VComponent(oldNode)
@ -231,7 +249,11 @@ proc updateElement(parent, current: Node, newNode, oldNode: VNode;
x.updatedImpl(x) x.updatedImpl(x)
if oldExpanded == nil: if oldExpanded == nil:
n = vnodeToDom(x.expanded, kxi) n = vnodeToDom(x.expanded, kxi)
state = 1
else: else:
kout cstring"now comparing components"
printV(oldExpanded)
printV(x.expanded)
if updateElement(parent, current, x.expanded, oldExpanded, kxi) >= similar: if updateElement(parent, current, x.expanded, oldExpanded, kxi) >= similar:
x.expanded = oldExpanded x.expanded = oldExpanded
n = oldExpanded.dom n = oldExpanded.dom
@ -239,12 +261,16 @@ proc updateElement(parent, current: Node, newNode, oldNode: VNode;
else: else:
n = x.expanded.dom n = x.expanded.dom
doAssert n != nil, "expanded.dom is nil" doAssert n != nil, "expanded.dom is nil"
state = 2
return
else: else:
detach(oldNode) detach(oldNode)
n = vnodeToDom(newNode, kxi) n = vnodeToDom(newNode, kxi)
state = 3
if parent == nil: if parent == nil:
replaceById(kxi.rootId, n) replaceById(kxi.rootId, n)
else: else:
kout cstring"state ", state, parent, current
parent.replaceChild(n, current) parent.replaceChild(n, current)
elif result == similar: elif result == similar:
updateStyles(newNode, oldNode, false) updateStyles(newNode, oldNode, false)
@ -295,8 +321,9 @@ proc updateElement(parent, current: Node, newNode, oldNode: VNode;
var pos = min(oldPos, newPos) + 1 var pos = min(oldPos, newPos) + 1
for i in commonPrefix..pos-1: for i in commonPrefix..pos-1:
if updateElement(current, current.childNodes[i], let res = updateElement(current, current.childNodes[i],
newNode[i], oldNode[i], kxi) >= similar: newNode[i], oldNode[i], kxi)
if res != different:
newNode[i] = oldNode[i] newNode[i] = oldNode[i]
var nextChildPos = oldPos + 1 var nextChildPos = oldPos + 1
@ -323,6 +350,9 @@ proc dodraw(kxi: KaraxInstance) =
let newtree = kxi.renderer() let newtree = kxi.renderer()
newtree.id = kxi.rootId newtree.id = kxi.rootId
kxi.toFocus = nil kxi.toFocus = nil
#if kxi.currentTree != nil:
# kout cstring"same? ", same(kxi.currentTree, document.getElementById(kxi.rootId))
kout cstring"dodraw -----------------------------"
if kxi.currentTree == nil: if kxi.currentTree == nil:
kxi.currentTree = newtree kxi.currentTree = newtree
let asdom = vnodeToDom(kxi.currentTree, kxi) let asdom = vnodeToDom(kxi.currentTree, kxi)
@ -330,8 +360,8 @@ proc dodraw(kxi: KaraxInstance) =
else: else:
let olddom = document.getElementById(kxi.rootId) let olddom = document.getElementById(kxi.rootId)
discard updateElement(nil, olddom, newtree, kxi.currentTree, kxi) discard updateElement(nil, olddom, newtree, kxi.currentTree, kxi)
#assert same(newtree, document.getElementById("ROOT"))
kxi.currentTree = newtree kxi.currentTree = newtree
#kout cstring"same? ", same(kxi.currentTree, document.getElementById(kxi.rootId))
if not kxi.postRenderCallback.isNil: if not kxi.postRenderCallback.isNil:
kxi.postRenderCallback() kxi.postRenderCallback()
@ -348,10 +378,10 @@ proc redraw*(kxi: KaraxInstance = kxi) =
if drawTimeout != nil: if drawTimeout != nil:
clearTimeout(drawTimeout) clearTimeout(drawTimeout)
drawTimeout = setTimeout(dodraw, 30) drawTimeout = setTimeout(dodraw, 30)
elif true: elif false:
reqFrame(proc () = kxi.dodraw) reqFrame(proc () = kxi.dodraw)
else: else:
dodraw() dodraw(kxi)
proc init(ev: Event) = proc init(ev: Event) =
reqFrame(proc () = kxi.dodraw) reqFrame(proc () = kxi.dodraw)

View file

@ -119,6 +119,7 @@ type
style*: VStyle ## the style that should be applied to the virtual node. style*: VStyle ## the style that should be applied to the virtual node.
dom*: Node ## the attached real DOM node. Can be 'nil' if the virtual node dom*: Node ## the attached real DOM node. Can be 'nil' if the virtual node
## is not part of the virtual DOM anymore. ## is not part of the virtual DOM anymore.
myid*: int
VComponent* = ref object of VNode ## The abstract class for every karax component. VComponent* = ref object of VNode ## The abstract class for every karax component.
renderImpl*: proc(self: VComponent): VNode renderImpl*: proc(self: VComponent): VNode

View file

@ -213,7 +213,9 @@ type
attrs: array[StyleAttr, cstring] attrs: array[StyleAttr, cstring]
mask: set[StyleAttr] mask: set[StyleAttr]
proc `==`*(a, b: VStyle): bool = proc kout[T](x: T) {.importc: "console.log", varargs.}
proc eq*(a, b: VStyle): bool =
if a.isNil: if a.isNil:
if b.isNil: return true if b.isNil: return true
else: return false else: return false
@ -247,9 +249,10 @@ proc setStyle(d: Style; key, val: cstring) {.importcpp: "#[#] = #".}
proc merge*(a, b: VStyle): VStyle = proc merge*(a, b: VStyle): VStyle =
## merges two styles. ``b`` takes precedence over ``a``. ## merges two styles. ``b`` takes precedence over ``a``.
result = VStyle(mask: a.mask + b.mask) result = VStyle(mask: {})
for i in low(StyleAttr)..high(StyleAttr): for i in low(StyleAttr)..high(StyleAttr):
result.attrs[i] = if b.attrs[i].isNil: a.attrs[i] else: b.attrs[i] result.attrs[i] = if b.attrs[i].isNil: a.attrs[i] else: b.attrs[i]
if result.attrs[i] != nil: incl(result.mask, i)
proc applyStyle*(n: Node; s: VStyle) = proc applyStyle*(n: Node; s: VStyle) =
## apply the style to the real DOM node ``n``. ## apply the style to the real DOM node ``n``.
@ -257,6 +260,11 @@ proc applyStyle*(n: Node; s: VStyle) =
for x in s.mask: for x in s.mask:
n.style.setStyle(toStyleAttrName[x], s.attrs[x]) n.style.setStyle(toStyleAttrName[x], s.attrs[x])
iterator pairs*(v: VStyle): (cstring, cstring) =
if v != nil:
for x in v.mask:
yield (toStyleAttrName[x], v.attrs[x])
import jstrutils import jstrutils
proc rgb*(r, g, b: range[0..255]): cstring = proc rgb*(r, g, b: range[0..255]): cstring =

58
tests/blur.nim Normal file
View file

@ -0,0 +1,58 @@
import vdom, kdom, vstyles, karax, karaxdsl, jdict, jstrutils
type TextInput* = ref object of VComponent
value: cstring
isActive: bool
var renderId: int
proc render(x: VComponent): VNode =
let self = TextInput(x)
let style = style(
(StyleAttr.position, cstring"relative"),
(StyleAttr.paddingLeft, cstring"10px"),
(StyleAttr.paddingRight, cstring"5px"),
(StyleAttr.height, cstring"30px"),
(StyleAttr.lineHeight, cstring"30px"),
(StyleAttr.border, cstring"solid 1px " & (if self.isActive: cstring"red" else: cstring"black")),
(StyleAttr.fontSize, cstring"12px"),
(StyleAttr.fontWeight, cstring"600")
).merge(self.style)
let inputStyle = style.merge(style(
(StyleAttr.color, cstring"inherit"),
(StyleAttr.fontSize, cstring"inherit"),
(StyleAttr.fontWeight, cstring"inherit"),
(StyleAttr.fontFamily, cstring"inherit"),
(StyleAttr.position, cstring"absolute"),
(StyleAttr.top, cstring"0"),
(StyleAttr.left, cstring"0"),
(StyleAttr.height, cstring"100%"),
(StyleAttr.width, cstring"100%"),
(StyleAttr.border, cstring"none"),
(StyleAttr.backgroundColor, cstring"transparent"),
))
proc flip(ev: Event; n: VNode) =
self.isActive = not self.isActive
markDirty(self)
kout cstring"rendering ", self.myid
inc renderId
result = buildHtml(tdiv(style=style, key=renderId)):
input(style=inputStyle, value=self.value, onblur=flip, onfocus=flip,
key=renderId)
var gid = 0
proc newTextInput*(style: VStyle = VStyle(); value: cstring = cstring""): TextInput =
result = newComponent(TextInput, render)
result.style = style
result.value = value
inc gid
result.myid = gid
proc createDom(): VNode =
result = buildHtml(tdiv):
newTextInput(value=cstring"test")
setRenderer createDom

View file

@ -21,12 +21,20 @@ proc exec(cmd: string) =
proc main = proc main =
var op = initOptParser() var op = initOptParser()
let rest = op.cmdLineRest var rest = op.cmdLineRest
var file = "" var file = ""
var run = false
while true: while true:
op.next() op.next()
case op.kind case op.kind
of cmdLongOption, cmdShortOption: discard of cmdLongOption:
if op.key == "run":
run = true
rest = rest.replace("--run ")
of cmdShortOption:
if op.key == "r":
run = true
rest = rest.replace("-r ")
of cmdArgument: file = op.key of cmdArgument: file = op.key
of cmdEnd: break of cmdEnd: break
@ -36,6 +44,6 @@ proc main =
exec("nim js --out:nimcache/" & name & ".js " & rest) exec("nim js --out:nimcache/" & name & ".js " & rest)
let dest = "nimcache" / name & ".html" let dest = "nimcache" / name & ".html"
writeFile(dest, html % name) writeFile(dest, html % name)
openDefaultBrowser(dest) if run: openDefaultBrowser(dest)
main() main()