diff --git a/examples/library/ui_pointer.ludic b/examples/library/ui_pointer.ludic
index 9433e22e..3a6e615d 100644
--- a/examples/library/ui_pointer.ludic
+++ b/examples/library/ui_pointer.ludic
@@ -53,10 +53,10 @@ program UiPointer {
let active = down.children[2].bg
let mid = said(down)
frame(ui_st, hold.x + 5.0, hold.y + 90.0, false, 0.0, false)
- let off = frame(ui_st, -1.0, -1.0, false, 0.0, false)
+ let off = said(frame(ui_st, -1.0, -1.0, false, 0.0, false))
frame(ui_st, -1.0, -1.0, false, 0.0, true)
let keyed = said(frame(ui_st, -1.0, -1.0, false, 0.0, true))
let up = frame(ui_st, -1.0, -1.0, false, 0.0, false)
- print(`{ui_pointer_st.log}dragged {int(ui_pointer_st.dragged)} | {mid} active {active} | {said(off)} | {keyed} | {said(up)} {up.children[2].bg}`)
+ print(`{ui_pointer_st.log}dragged {int(ui_pointer_st.dragged)} | {mid} active {active} | {off} | {keyed} | {said(up)} {up.children[2].bg}`)
}
}
diff --git a/packages/ludic.ui/backend.ludic b/packages/ludic.ui/backend.ludic
index 3b6d3558..dbbba037 100644
--- a/packages/ludic.ui/backend.ludic
+++ b/packages/ludic.ui/backend.ludic
@@ -32,6 +32,8 @@ function ui_default_backend() -> UiBackend {
return UiRenderers[0].make()
}
export state UiState {
+ pl_pools: []UiPool = new []UiPool # pool.ludic: each screen's nodes and envs, used again
+ pl_cur: UiPool = null
an_frames: []UiKeyframes = new []UiKeyframes
an_keys: []string = new []string
an_start: []float = new []float
diff --git a/packages/ludic.ui/build.ludic b/packages/ludic.ui/build.ludic
index 74b12d07..0e76d38b 100644
--- a/packages/ludic.ui/build.ludic
+++ b/packages/ludic.ui/build.ludic
@@ -47,7 +47,7 @@ function bd_each(ui_st: mut UiState, k: UiTpl, e: UiEnv, key: string, out: []UiN
let at_name = bd_name(k, "index", "index")
let by = tpl_attr(k, "key")
for j in 0 .. Value.count(list) {
- let le = env_new(e)
+ let le = env_new(ui_st, e)
env_bind(le, as_name, Value.at(list, j))
env_bind(le, at_name, Value.int(j))
var id = string(j)
@@ -77,7 +77,7 @@ function bd_comp(ui_st: mut UiState, k: UiTpl, e: UiEnv, key: string, out: []UiN
}
if k.comp == null { return false }
let c = k.comp
- let ce = env_new(null)
+ let ce = env_new(ui_st, null)
ce.model = e.model
ce.ctx = e.ctx
for i in 0 .. len(c.keys) {
@@ -86,7 +86,6 @@ function bd_comp(ui_st: mut UiState, k: UiTpl, e: UiEnv, key: string, out: []UiN
for i in 0 .. len(k.keys) { env_bind(ce, k.keys[i], ui_eval(ui_st, k.vals[i], e)) }
ce.on_names = k.on_keys
ce.on_acts = k.on_acts
- ce.on_envs = new []UiEnv
for i in 0 .. len(k.on_keys) { push(ce.on_envs, e) }
ce.slot = k
ce.slot_env = e
diff --git a/packages/ludic.ui/build_class.ludic b/packages/ludic.ui/build_class.ludic
index 62e11c71..d2b2ed76 100644
--- a/packages/ludic.ui/build_class.ludic
+++ b/packages/ludic.ui/build_class.ludic
@@ -11,7 +11,7 @@ function bd_class(ui_st: mut UiState, k: UiTpl, e: UiEnv, key: string, out: []Ui
if a != "class" and a != "style" and a != "id" { Value.put(props, a, ui_eval(ui_st, k.vals[i], e)) }
}
cls.props(it.ptr, props)
- let ce = env_new(null)
+ let ce = env_new(ui_st, null)
ce.model = cls.model(it.ptr)
ce.inst = it
ce.ctx = e.ctx
@@ -19,7 +19,6 @@ function bd_class(ui_st: mut UiState, k: UiTpl, e: UiEnv, key: string, out: []Ui
ce.slot_env = e
ce.on_names = k.on_keys
ce.on_acts = k.on_acts
- ce.on_envs = new []UiEnv
for i in 0 .. len(k.on_keys) { push(ce.on_envs, e) }
lc_note(ui_st, key, k.on_keys, k.on_acts, e)
let at = len(out)
diff --git a/packages/ludic.ui/build_more.ludic b/packages/ludic.ui/build_more.ludic
index 0e58a44c..e0a2386b 100644
--- a/packages/ludic.ui/build_more.ludic
+++ b/packages/ludic.ui/build_more.ludic
@@ -1,14 +1,14 @@
# build_more.ludic - names a value for the siblings after it, and
# gives it to everything inside, components included (React's context)
function bd_let(ui_st: mut UiState, k: UiTpl, e: UiEnv) -> UiEnv {
- let le = env_new(e)
+ let le = env_new(ui_st, e)
for i in 0 .. len(k.keys) { env_bind(le, k.keys[i], ui_eval(ui_st, k.vals[i], e)) }
return le
}
function bd_provide(ui_st: mut UiState, k: UiTpl, e: UiEnv, key: string, out: []UiNode) -> void {
- let cx = env_new(e.ctx)
+ let cx = env_new(ui_st, e.ctx)
for i in 0 .. len(k.keys) { env_bind(cx, k.keys[i], ui_eval(ui_st, k.vals[i], e)) }
- let pe = env_new(e)
+ let pe = env_new(ui_st, e)
pe.ctx = cx
bd_kids(ui_st, k.kids, pe, key, out)
}
@@ -18,12 +18,11 @@ function bd_group(ui_st: mut UiState, k: UiTpl, e: UiEnv, key: string, out: []Ui
bd_kids(ui_st, k.kids, e, key, out)
return
}
- let t = new UiNode
+ let t = nd_take(ui_st)
t.kind = UI_TEXT
t.key = key
t.text = ui_tr(ui_st, ui_eval_text(ui_st, k.text, e))
t.env = e
- t.children = new []UiNode
push(out, t)
}
# text inside text - Hi there
, - reads as one line of it
@@ -37,5 +36,5 @@ function el_inline(n: UiNode) -> void {
s = s + c.text
}
n.text = s
- n.children = new []UiNode
+ List.clear(n.children)
}
diff --git a/packages/ludic.ui/cascade.ludic b/packages/ludic.ui/cascade.ludic
index 4cbc3530..6aad6834 100644
--- a/packages/ludic.ui/cascade.ludic
+++ b/packages/ludic.ui/cascade.ludic
@@ -9,11 +9,14 @@ function cs_tree(ui_st: mut UiState, n: UiNode) -> void {
}
if n.tag != "" { cs_node(ui_st, n) }
for i in 0 .. len(n.children) { cs_tree(ui_st, n.children[i]) }
- var shown = new []UiNode
+ var kept = 0
for i in 0 .. len(n.children) {
- if not n.children[i].hidden { push(shown, n.children[i]) }
+ if not n.children[i].hidden {
+ n.children[kept] = n.children[i]
+ kept += 1
+ }
}
- n.children = shown
+ while len(n.children) > kept { List.pop(n.children) }
cs_order(n)
}
# twice: the font size (and zoom) first, from every rule, so an em anywhere reads the size the
diff --git a/packages/ludic.ui/controls_build.ludic b/packages/ludic.ui/controls_build.ludic
index 450d8350..631a61d0 100644
--- a/packages/ludic.ui/controls_build.ludic
+++ b/packages/ludic.ui/controls_build.ludic
@@ -4,18 +4,13 @@
# input[type=text|number|key] .ui-label .ui-field > .ui-value (+ .ui-caret while focused)
# select .ui-label .ui-prev .ui-value .ui-next (its s hidden)
# with a note="...", the label is .ui-labels > .ui-label + .ui-note
-function ct_part(n: UiNode, cls: string, text: string) -> UiNode {
- let p = new UiNode
+function ct_part(ui_st: mut UiState, n: UiNode, cls: string, text: string) -> UiNode {
+ let p = nd_take(ui_st)
p.key = `{n.key}/{cls}`
p.tag = "span"
p.env = n.env
p.sheet = n.sheet
- p.children = new []UiNode
- p.classes = new []string
push(p.classes, cls)
- p.akeys = new []string
- p.avals = new []string
- p.vals = new []Val
if text != "" {
p.kind = UI_TEXT
p.text = text
@@ -23,18 +18,18 @@ function ct_part(n: UiNode, cls: string, text: string) -> UiNode {
return p
}
# and : a track with a fill as far as the value
-function ct_build_bar(n: UiNode) -> void {
+function ct_build_bar(ui_st: mut UiState, n: UiNode) -> void {
n.dir = UI_ROW
let lo = Value.as_float(ui_attr(n, "min"))
var hi = Value.as_float(ui_attr(n, "max"))
if hi <= lo { hi = lo + 1.0 }
- let fill = ct_part(n, "ui-fill", "")
+ let fill = ct_part(ui_st, n, "ui-fill", "")
fill.w_pct = Math.clamp((Value.as_float(ui_attr(n, "value")) - lo) / (hi - lo), 0.0, 1.0) * 100.0
push(n.children, fill)
}
-function ct_build(ui_st: UiState, n: UiNode) -> void {
+function ct_build(ui_st: mut UiState, n: UiNode) -> void {
if n.tag == "progress" or n.tag == "meter" {
- ct_build_bar(n)
+ ct_build_bar(ui_st, n)
return
}
n.dir = UI_ROW
@@ -46,41 +41,41 @@ function ct_build(ui_st: UiState, n: UiNode) -> void {
}
ct_label(ui_st, n)
if t == "checkbox" or t == "radio" {
- let track = ct_part(n, "ui-track", "")
- push(track.children, ct_part(n, "ui-knob", ""))
+ let track = ct_part(ui_st, n, "ui-track", "")
+ push(track.children, ct_part(ui_st, n, "ui-knob", ""))
push(n.children, track)
} else if t == "range" {
- ct_build_range(n)
+ ct_build_range(ui_st, n)
} else {
- let field = ct_part(n, "ui-field", "")
- push(field.children, ct_part(n, "ui-value", ct_field_text(ui_st, n)))
- if ui_st.fc_key == n.key and t != "key" { push(field.children, ct_part(n, "ui-caret", "")) }
+ let field = ct_part(ui_st, n, "ui-field", "")
+ push(field.children, ct_part(ui_st, n, "ui-value", ct_field_text(ui_st, n)))
+ if ui_st.fc_key == n.key and t != "key" { push(field.children, ct_part(ui_st, n, "ui-caret", "")) }
push(n.children, field)
}
}
-function ct_build_range(n: UiNode) -> void {
+function ct_build_range(ui_st: mut UiState, n: UiNode) -> void {
let lo = Value.as_float(ui_attr(n, "min"))
var hi = Value.as_float(ui_attr(n, "max"))
if hi <= lo { hi = lo + 1.0 }
let v = Value.as_float(ui_attr(n, "value"))
- let track = ct_part(n, "ui-track", "")
- let fill = ct_part(n, "ui-fill", "")
+ let track = ct_part(ui_st, n, "ui-track", "")
+ let fill = ct_part(ui_st, n, "ui-fill", "")
fill.w_pct = Math.clamp((v - lo) / (hi - lo), 0.0, 1.0) * 100.0
push(track.children, fill)
- push(track.children, ct_part(n, "ui-thumb", ""))
+ push(track.children, ct_part(ui_st, n, "ui-thumb", ""))
push(n.children, track)
- push(n.children, ct_part(n, "ui-value", ct_value_text(n)))
+ push(n.children, ct_part(ui_st, n, "ui-value", ct_value_text(n)))
}
-function ct_build_select(ui_st: UiState, n: UiNode) -> void {
+function ct_build_select(ui_st: mut UiState, n: UiNode) -> void {
let opts = n.children
n.children = new []UiNode
ct_label(ui_st, n)
var shown = ""
let at = ct_option_at(n, opts)
if at >= 0 { shown = opts[at].text }
- push(n.children, ct_part(n, "ui-prev", "<"))
- push(n.children, ct_part(n, "ui-value", shown))
- push(n.children, ct_part(n, "ui-next", ">"))
+ push(n.children, ct_part(ui_st, n, "ui-prev", "<"))
+ push(n.children, ct_part(ui_st, n, "ui-value", shown))
+ push(n.children, ct_part(ui_st, n, "ui-next", ">"))
n.opts = opts
}
# which option the select's value names: an option's value="...", else its place
diff --git a/packages/ludic.ui/controls_parts.ludic b/packages/ludic.ui/controls_parts.ludic
index e9bd0d41..362f180b 100644
--- a/packages/ludic.ui/controls_parts.ludic
+++ b/packages/ludic.ui/controls_parts.ludic
@@ -1,16 +1,16 @@
# controls_parts.ludic - what a control shows besides its own parts: its label, with a `note` (a line
# of help under it) in .ui-labels > .ui-label + .ui-note, and a range's or a number's value as text:
# `decimals="1"` places, `format="percent"` its place between min and max, and a `unit` after it
-function ct_label(ui_st: UiState, n: UiNode) -> void {
+function ct_label(ui_st: mut UiState, n: UiNode) -> void {
let label = ui_attr_text(n, "label")
let note = ui_attr_text(n, "note")
if note == "" {
- if label != "" { push(n.children, ct_part(n, "ui-label", ui_tr(ui_st, label))) }
+ if label != "" { push(n.children, ct_part(ui_st, n, "ui-label", ui_tr(ui_st, label))) }
return
}
- let both = ct_part(n, "ui-labels", "")
- if label != "" { push(both.children, ct_part(n, "ui-label", ui_tr(ui_st, label))) }
- push(both.children, ct_part(n, "ui-note", ui_tr(ui_st, note)))
+ let both = ct_part(ui_st, n, "ui-labels", "")
+ if label != "" { push(both.children, ct_part(ui_st, n, "ui-label", ui_tr(ui_st, label))) }
+ push(both.children, ct_part(ui_st, n, "ui-note", ui_tr(ui_st, note)))
push(n.children, both)
}
function ct_value_text(n: UiNode) -> string {
diff --git a/packages/ludic.ui/elem.ludic b/packages/ludic.ui/elem.ludic
index 1a7d4e76..e8bb9d3c 100644
--- a/packages/ludic.ui/elem.ludic
+++ b/packages/ludic.ui/elem.ludic
@@ -4,17 +4,12 @@ const EL_BOXES: string = " col div progress meter section header footer nav main
const EL_TEXTS: string = " text span p label h1 h2 h3 h4 h5 h6 strong em small b i a option "
function bd_elem(ui_st: mut UiState, k: UiTpl, e: UiEnv, key: string) -> UiNode {
ui_st.ui_at = k.where
- let n = new UiNode
+ let n = nd_take(ui_st)
n.key = key
n.tag = k.tag
n.env = e
n.sheet = k.sheet
n.style = k.style
- n.children = new []UiNode
- n.classes = new []string
- n.akeys = new []string
- n.avals = new []string
- n.vals = new []Val
el_kind(ui_st, n, k.tag)
for i in 0 .. len(k.keys) { el_keep(n, k.keys[i], ui_eval(ui_st, k.vals[i], e)) }
n.hovered = fr_was_hovered(ui_st, key)
@@ -28,7 +23,7 @@ function bd_elem(ui_st: mut UiState, k: UiTpl, e: UiEnv, key: string) -> UiNode
let say = ui_attr_text(n, "translate")
if say == "no" { ui_st.tr_no = true } else if say == "yes" { ui_st.tr_no = false }
bd_kids(ui_st, k.kids, e, key, n.children)
- if k.text != null { el_text(n, ui_tr(ui_st, ui_eval_text(ui_st, k.text, e))) }
+ if k.text != null { el_text(ui_st, n, ui_tr(ui_st, ui_eval_text(ui_st, k.text, e))) }
el_inline(n)
if k.tag == "input" or k.tag == "select" or k.tag == "progress" or k.tag == "meter" { ct_build(ui_st, n) }
ui_st.tr_no = was_no
@@ -45,16 +40,15 @@ function el_kind(ui_st: mut UiState, n: UiNode, tag: string) -> void {
} else if tag == "input" or tag == "select" { n.focusable = true } else if tag == "spacer" { n.kind = UI_SPACER } else if tag == "scroll" { n.kind = UI_SCROLL } else if tag == "img" { n.kind = UI_IMAGE } else if tag == "hr" { n.kind = UI_RULE } else if not lk_in(EL_BOXES, tag) { ui_err(ui_st, `<{tag}>: there is no such element`) }
}
# a box with words in it holds them as a text of its own, as words
does
-function el_text(n: UiNode, s: string) -> void {
+function el_text(ui_st: mut UiState, n: UiNode, s: string) -> void {
if n.kind != UI_BOX and n.kind != UI_SCROLL {
n.text = s
return
}
- let t = new UiNode
+ let t = nd_take(ui_st)
t.kind = UI_TEXT
t.text = s
t.env = n.env
- t.children = new []UiNode
push(n.children, t)
}
# id, class, hidden, disabled and src say what the element is; the rest are applied after the rules
diff --git a/packages/ludic.ui/env.ludic b/packages/ludic.ui/env.ludic
index 45f0603f..ac751606 100644
--- a/packages/ludic.ui/env.ludic
+++ b/packages/ludic.ui/env.ludic
@@ -16,10 +16,8 @@ export property UiEnv {
inst: UiInst = null # the Ludic component whose template this is
}
-function env_new(parent: UiEnv) -> UiEnv {
- let e = new UiEnv
- e.names = new []string
- e.vals = new []Val
+function env_new(ui_st: mut UiState, parent: UiEnv) -> UiEnv {
+ let e = env_take(ui_st)
e.parent = parent
if parent != null {
e.model = parent.model
diff --git a/packages/ludic.ui/frame.ludic b/packages/ludic.ui/frame.ludic
index c9745e05..4ff3e98b 100644
--- a/packages/ludic.ui/frame.ludic
+++ b/packages/ludic.ui/frame.ludic
@@ -39,20 +39,16 @@ export function ui_nodes(ui_st: mut UiState, screen: string, view: UiView) -> Ui
return null
}
let t = ui_st.lib_screens[si]
+ pl_begin(ui_st, screen)
lc_begin(ui_st)
ui_st.ui_view = view
- let e = env_new(null)
+ let e = env_new(ui_st, null)
if view != null and view.model != null { e.model = view.model() } else { e.model = Value.object() }
e.state = st_for(ui_st, screen, t, e)
- let root = new UiNode
+ let root = nd_take(ui_st)
root.key = screen
- root.children = new []UiNode
root.env = e
root.sheet = t.sheet
- root.classes = new []string
- root.akeys = new []string
- root.avals = new []string
- root.vals = new []Val
root.tag = "screen"
for i in 0 .. len(t.keys) {
if t.keys[i] != "name" { el_keep(root, t.keys[i], ui_eval(ui_st, t.vals[i], e)) }
diff --git a/packages/ludic.ui/index.ludic b/packages/ludic.ui/index.ludic
index b2722b0e..342e6f03 100644
--- a/packages/ludic.ui/index.ludic
+++ b/packages/ludic.ui/index.ludic
@@ -7,6 +7,9 @@ import "node.ludic"
import "node_more.ludic"
import "view.ludic"
import "backend.ludic"
+import "pool.ludic"
+import "pool_reset.ludic"
+import "pool_reset_more.ludic"
import "expr.ludic"
import "expr_parse.ludic"
import "expr_prim.ludic"
diff --git a/packages/ludic.ui/native.ludic b/packages/ludic.ui/native.ludic
index 6d083559..6c22b246 100644
--- a/packages/ludic.ui/native.ludic
+++ b/packages/ludic.ui/native.ludic
@@ -64,7 +64,7 @@ function nt_run(ui_st: mut UiState, n: UiNode, name: string, value: Val) -> void
if (lk_in(PE_NAMES, name) or name == "hold") and Value.kind(value) == 6 { ev = value }
Value.put(ev, "name", Value.str(name))
Value.put(ev, "value", value)
- let e = env_new(n.env)
+ let e = env_new(ui_st, n.env)
env_bind(e, "event", ev)
act_run(ui_st, n.on_acts[i], e)
}
diff --git a/packages/ludic.ui/pool.ludic b/packages/ludic.ui/pool.ludic
new file mode 100644
index 00000000..f6670f86
--- /dev/null
+++ b/packages/ludic.ui/pool.ludic
@@ -0,0 +1,84 @@
+# pool.ludic - what a frame is built of is kept and used again: ludic.ui builds a screen's tree anew
+# every frame, and Ludic frees nothing, so a node or an env made per element per frame was a leak
+# (the valley's HUD: ~100 MB/s). Each screen keeps two generations and alternates, so last frame's
+# tree - which an unmount's env, a hover or ui_box still reads - stays whole while this one is built.
+# So a host may read a tree until the same screen has been built twice more, and never keep it longer.
+export property UiPool {
+ key: string = ""
+ gen: int = 0
+ nodes0: []UiNode = null
+ nodes1: []UiNode = null
+ envs0: []UiEnv = null
+ envs1: []UiEnv = null
+ un: int = 0
+ ue: int = 0
+}
+# a build of `screen` begins: its other generation is handed out again from the start
+function pl_begin(ui_st: mut UiState, screen: string) -> void {
+ var p: UiPool = null
+ for i in 0 .. len(ui_st.pl_pools) {
+ if ui_st.pl_pools[i].key == screen { p = ui_st.pl_pools[i] }
+ }
+ if p == null {
+ p = new UiPool
+ p.key = screen
+ p.nodes0 = new []UiNode
+ p.nodes1 = new []UiNode
+ p.envs0 = new []UiEnv
+ p.envs1 = new []UiEnv
+ push(ui_st.pl_pools, p)
+ }
+ p.gen = 1 - p.gen
+ p.un = 0
+ p.ue = 0
+ ui_st.pl_cur = p
+}
+# a node, blank, with its own lists (empty) - from the pool when a build is under way
+function nd_take(ui_st: mut UiState) -> UiNode {
+ let p = ui_st.pl_cur
+ if p == null { return nd_fresh() }
+ var list = p.nodes0
+ if p.gen == 1 { list = p.nodes1 }
+ if p.un < len(list) {
+ let n = list[p.un]
+ p.un += 1
+ nd_reset(n)
+ return n
+ }
+ let n = nd_fresh()
+ push(list, n)
+ p.un += 1
+ return n
+}
+function nd_fresh() -> UiNode {
+ let n = new UiNode
+ n.children = new []UiNode
+ n.classes = new []string
+ n.akeys = new []string
+ n.avals = new []string
+ n.vals = new []Val
+ return n
+}
+function env_take(ui_st: mut UiState) -> UiEnv {
+ let p = ui_st.pl_cur
+ if p == null { return env_fresh() }
+ var list = p.envs0
+ if p.gen == 1 { list = p.envs1 }
+ if p.ue < len(list) {
+ let e = list[p.ue]
+ p.ue += 1
+ env_reset(e)
+ return e
+ }
+ let e = env_fresh()
+ push(list, e)
+ p.ue += 1
+ return e
+}
+function env_fresh() -> UiEnv {
+ let e = new UiEnv
+ e.names = new []string
+ e.vals = new []Val
+ e.on_envs = new []UiEnv
+ return e
+}
diff --git a/packages/ludic.ui/pool_reset.ludic b/packages/ludic.ui/pool_reset.ludic
new file mode 100644
index 00000000..73797538
--- /dev/null
+++ b/packages/ludic.ui/pool_reset.ludic
@@ -0,0 +1,61 @@
+# pool_reset.ludic - a pooled node put back to its defaults: every field, and the lists it owns
+# emptied and kept (generated from node.ludic - a field added there is added here)
+function nd_reset(n: UiNode) -> void {
+ nd_reset_a(n)
+ nd_reset_b(n)
+}
+function nd_reset_a(n: UiNode) -> void {
+ n.kind = 0
+ n.dir = 0
+ n.text = ""
+ n.key = ""
+ n.tag = ""
+ n.id = ""
+ if n.classes != null { List.clear(n.classes) }
+ if n.akeys != null { List.clear(n.akeys) }
+ if n.avals != null { List.clear(n.avals) }
+ if n.vals != null { List.clear(n.vals) }
+ n.style = null
+ n.sheet = null
+ if n.ups != null { List.clear(n.ups) }
+ n.parent = null
+ n.index = 0
+ n.src = ""
+ n.size = 0.0
+ n.w = -1000000.0
+ n.h = -1000000.0
+ n.w_pct = 0.0
+ n.h_pct = 0.0
+ n.pt = 0.0
+ n.pr = 0.0
+ n.pb = 0.0
+ n.pl = 0.0
+ n.mt = 0.0
+ n.mr = 0.0
+ n.mb = 0.0
+ n.ml = 0.0
+ n.border = 0.0
+ n.bt = 0.0
+ n.br = 0.0
+ n.bb = 0.0
+ n.bl = 0.0
+ n.border_c = -2
+ n.gap = 0.0
+ n.align = 0
+ n.justify = 0
+ n.talign = 0
+ n.self = -1
+ n.grow = 0.0
+ n.wrap = false
+ n.min_w = 0.0
+ n.max_w = 0.0
+ n.min_h = 0.0
+ n.max_h = 0.0
+ n.hidden = false
+ n.pos = 0
+ n.top = -1000000.0
+ n.right = -1000000.0
+ n.bottom = -1000000.0
+ n.left = -1000000.0
+ n.z = 0
+}
diff --git a/packages/ludic.ui/pool_reset_more.ludic b/packages/ludic.ui/pool_reset_more.ludic
new file mode 100644
index 00000000..b68aadb7
--- /dev/null
+++ b/packages/ludic.ui/pool_reset_more.ludic
@@ -0,0 +1,96 @@
+# pool_reset_more.ludic - the rest of a pooled node's fields, its extras and an env, to their defaults
+function nd_reset_b(n: UiNode) -> void {
+ n.nowrap = false
+ n.ellipsis = false
+ if n.lines != null { List.clear(n.lines) }
+ if n.var_names != null { List.clear(n.var_names) }
+ if n.var_vals != null { List.clear(n.var_vals) }
+ n.fg = -1
+ n.fg_a = 1.0
+ n.bg = -2
+ n.bg_a = 1.0
+ n.border_a = 1.0
+ n.rfg_a = 1.0
+ n.alpha = 1.0
+ n.enabled = true
+ n.act = null
+ n.on_names = null
+ n.on_acts = null
+ n.native = null
+ n.env = null
+ if n.children != null { List.clear(n.children) }
+ n.mw = 0.0
+ n.mh = 0.0
+ n.content_h = 0.0
+ n.x = 0.0
+ n.y = 0.0
+ n.cw = 0.0
+ n.ch = 0.0
+ n.rsize = 0.0
+ n.rfg = 0
+ n.hovered = false
+ n.focusable = false
+ n.radius = 0.0
+ n.outline_w = 0.0
+ n.outline_c = -1
+ n.outline_a = 1.0
+ n.outline_off = 0.0
+ n.scroll_y = 0.0
+ n.bar_c = -1
+ n.bar_track = -2
+ n.opts = null
+ n.bg_img = ""
+ n.bi_src = ""
+ n.bi_slice = 0.0
+ n.bi_width = 0.0
+ n.sh_x = 0.0
+ n.sh_y = 0.0
+ n.sh_c = -2
+ n.sh_a = 1.0
+ n.anim = ""
+ n.anim_dur = 0.0
+ n.anim_loop = false
+ n.anim_alt = false
+ n.tr_dur = 0.0
+ n.popover = false
+ if n.ex != null { nx_reset(n.ex) }
+}
+function nx_reset(x: UiNodeX) -> void {
+ x.shrink = -1.0
+ x.order = 0
+ x.w_off = 0.0
+ x.h_off = 0.0
+ x.mh_min = 0.0
+ x.has_min = false
+ x.tsh_set = false
+ x.tsh_x = 0.0
+ x.tsh_y = 0.0
+ x.tsh_c = -2
+ x.tsh_a = 1.0
+ x.no_pointer = false
+ x.fit_min = 0.0
+ x.lh_kind = 0
+ x.lh_v = 0.0
+ x.ip_t = 0.0
+ x.ip_r = 0.0
+ x.ip_b = 0.0
+ x.ip_l = 0.0
+ x.grad = null
+ x.ratio = 0.0
+ x.fit_obj = 0
+ x.zoom = 1.0
+}
+function env_reset(e: UiEnv) -> void {
+ if e.names != null { List.clear(e.names) }
+ if e.vals != null { List.clear(e.vals) }
+ e.state = null
+ e.parent = null
+ e.model = null
+ e.slot = null
+ e.slot_env = null
+ e.on_names = null
+ e.on_acts = null
+ if e.on_envs != null { List.clear(e.on_envs) }
+ e.ctx = null
+ e.inst = null
+}
diff --git a/packages/ludic.ui/screen_class.ludic b/packages/ludic.ui/screen_class.ludic
index 9b605b2d..c3070bf0 100644
--- a/packages/ludic.ui/screen_class.ludic
+++ b/packages/ludic.ui/screen_class.ludic
@@ -1,11 +1,12 @@
# screen_class.ludic - a Ludic component shown as a whole screen
# a Ludic component shown as a screen: an instance at the top, keyed by its name
function fr_class_nodes(ui_st: mut UiState, cls: UiClass) -> UiNode {
+ pl_begin(ui_st, cls.name)
lc_begin(ui_st)
ui_st.in_build += 1
ui_st.ui_view = null
uc_load(ui_st, cls)
- let root = fr_root(cls.name, cls.sheet)
+ let root = fr_root(ui_st, cls.name, cls.sheet)
let use = new UiTpl
use.tag = cls.name
use.cls = cls
@@ -14,7 +15,7 @@ function fr_class_nodes(ui_st: mut UiState, cls: UiClass) -> UiNode {
use.on_keys = new []string
use.on_acts = new []UiAct
use.kids = new []UiTpl
- let e = env_new(null)
+ let e = env_new(ui_st, null)
e.model = Value.object()
root.env = e
bd_class(ui_st, use, e, cls.name, root.children)
@@ -22,15 +23,10 @@ function fr_class_nodes(ui_st: mut UiState, cls: UiClass) -> UiNode {
cs_tree(ui_st, root)
return root
}
-function fr_root(key: string, sheet: UiSheet) -> UiNode {
- let root = new UiNode
+function fr_root(ui_st: mut UiState, key: string, sheet: UiSheet) -> UiNode {
+ let root = nd_take(ui_st)
root.key = key
root.tag = "screen"
root.sheet = sheet
- root.children = new []UiNode
- root.classes = new []string
- root.akeys = new []string
- root.avals = new []string
- root.vals = new []Val
return root
}
diff --git a/packages/ludic.ui/scroll_bar.ludic b/packages/ludic.ui/scroll_bar.ludic
index 1e8ab558..b18236b7 100644
--- a/packages/ludic.ui/scroll_bar.ludic
+++ b/packages/ludic.ui/scroll_bar.ludic
@@ -3,10 +3,10 @@
# it where it was taken; a press on the track jumps the thumb's middle to the pointer and holds it there.
# the bar's parts, styled as descendants of the box, and where they are at this offset
function sb_geo(ui_st: mut UiState, n: UiNode, off: float, room: float, most: float) -> void {
- let bar = ct_part(n, "ui-scrollbar", "")
+ let bar = ct_part(ui_st, n, "ui-scrollbar", "")
bar.tag = "div"
bar.parent = n
- push(bar.children, ct_part(n, "ui-thumb", ""))
+ push(bar.children, ct_part(ui_st, n, "ui-thumb", ""))
cs_tree(ui_st, bar)
ui_st.sb_bar = bar
ui_st.sb_w = ui_px(ui_st, 6.0)
diff --git a/packages/ludic.ui/tooltip.ludic b/packages/ludic.ui/tooltip.ludic
index 74d6f0a9..494a9951 100644
--- a/packages/ludic.ui/tooltip.ludic
+++ b/packages/ludic.ui/tooltip.ludic
@@ -39,12 +39,11 @@ function tt_under(n: UiNode, x: float, y: float) -> UiNode {
}
function tt_draw(ui_st: mut UiState, root: UiNode) -> void {
if ui_st.tt_key == "" or ui_st.tt_text == "" or an_now(ui_st) - ui_st.tt_since < 0.5 { return }
- let n = ct_part(root, "ui-tooltip", "")
+ let n = ct_part(ui_st, root, "ui-tooltip", "")
n.tag = "div"
n.parent = root
- n.children = new []UiNode
let lines = tt_lines(ui_st.tt_text)
- for i in 0 .. len(lines) { push(n.children, ct_part(root, "ui-tooltip-text", lines[i])) }
+ for i in 0 .. len(lines) { push(n.children, ct_part(ui_st, root, "ui-tooltip-text", lines[i])) }
cs_tree(ui_st, n)
ly_measure(ui_st, n, ui_st.ly_vp_w * 0.4, ui_px(ui_st, 16.0), root.rfg)
let x = Math.min(ui_st.tt_x + ui_px(ui_st, 16.0), ui_st.ly_vp_x + ui_st.ly_vp_w - n.mw)