Merge branch 'lang/foundations' into lang/nav
This commit is contained in:
commit
862b048b17
15 changed files with 109 additions and 21 deletions
|
|
@ -38,10 +38,22 @@ export function effects_at(effects_st: EffectsState, i: int) -> Effect {
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
# the indices of the ones that have begun, for a read-out
|
# the indices of the ones that have begun, for a read-out (a frame's read-out fills its own list)
|
||||||
export function effects_live(effects_st: EffectsState) -> []int {
|
export function effects_live(effects_st: EffectsState) -> []int {
|
||||||
let out = new []int
|
let out = new []int
|
||||||
let all = ef_all(effects_st)
|
effects_live_into(effects_st, out)
|
||||||
for i in 0 .. len(all) { if ef_live(all[i]) { push(out, i) } }
|
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function effects_live_into(effects_st: EffectsState, out: []int) -> void {
|
||||||
|
List.clear(out)
|
||||||
|
let all = ef_all(effects_st)
|
||||||
|
for i in 0 .. len(all) { if ef_live(all[i]) { push(out, i) } }
|
||||||
|
}
|
||||||
|
|
||||||
|
export function effects_live_count(effects_st: EffectsState) -> int {
|
||||||
|
var n = 0
|
||||||
|
let all = ef_all(effects_st)
|
||||||
|
for i in 0 .. len(all) { if ef_live(all[i]) { n += 1 } }
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,11 @@ program EffectsTest {
|
||||||
effects_add_after(effects_st, 20.0, LEGS, -5.0, 15.0, false, "Sugar crash")
|
effects_add_after(effects_st, 20.0, LEGS, -5.0, 15.0, false, "Sugar crash")
|
||||||
expect(effects_sum(effects_st, LEGS) == 8.0)
|
expect(effects_sum(effects_st, LEGS) == 8.0)
|
||||||
expect_eq(len(effects_live(effects_st)), 1)
|
expect_eq(len(effects_live(effects_st)), 1)
|
||||||
|
expect_eq(effects_live_count(effects_st), 1)
|
||||||
|
let kept = new []int
|
||||||
|
effects_live_into(effects_st, kept)
|
||||||
|
effects_live_into(effects_st, kept)
|
||||||
|
expect_eq(len(kept), 1)
|
||||||
tick_minutes(25.0)
|
tick_minutes(25.0)
|
||||||
expect(effects_sum(effects_st, LEGS) == -5.0)
|
expect(effects_sum(effects_st, LEGS) == -5.0)
|
||||||
tick_minutes(20.0)
|
tick_minutes(20.0)
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import "ludic.base"
|
||||||
import "defs.ludic"
|
import "defs.ludic"
|
||||||
import "port.ludic"
|
import "port.ludic"
|
||||||
import "rail.ludic"
|
import "rail.ludic"
|
||||||
|
import "rail_into.ludic"
|
||||||
import "card.ludic"
|
import "card.ludic"
|
||||||
import "mute.ludic"
|
import "mute.ludic"
|
||||||
import "system.ludic"
|
import "system.ludic"
|
||||||
|
|
|
||||||
7
packages/ludic.hints/rail_into.ludic
Normal file
7
packages/ludic.hints/rail_into.ludic
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
# rail_into.ludic - the rail into a list the caller keeps: a HUD asks every frame, and a list made
|
||||||
|
# each time is never given back
|
||||||
|
export function hints_rail_into(hints_st: HintsState, out: []int) -> void {
|
||||||
|
List.clear(out)
|
||||||
|
if not HintsWorld.showing() { return }
|
||||||
|
for k in 0 .. hints_st.hn_n { push(out, hints_st.hn_top[k]) }
|
||||||
|
}
|
||||||
|
|
@ -74,6 +74,11 @@ program HintsTest {
|
||||||
hints_test_st.showing = true
|
hints_test_st.showing = true
|
||||||
hints_sort(hints_st)
|
hints_sort(hints_st)
|
||||||
expect_eq(len(hints_rail(hints_st)), 1)
|
expect_eq(len(hints_rail(hints_st)), 1)
|
||||||
|
let kept = new []int
|
||||||
|
hints_rail_into(hints_st, kept)
|
||||||
|
hints_rail_into(hints_st, kept)
|
||||||
|
expect_eq(len(kept), 1)
|
||||||
|
expect_eq(kept[0], hints_rail(hints_st)[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
test "the key opens the picked chip, the arrows walk round, the key puts it down" (hints_st: mut HintsState, hints_test_st: mut HintsTestState) {
|
test "the key opens the picked chip, the arrows walk round, the key puts it down" (hints_st: mut HintsState, hints_test_st: mut HintsTestState) {
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,8 @@ export state UiState {
|
||||||
ev_depth: int = 0 # eval.ludic: a call's arguments, in lists kept one a depth
|
ev_depth: int = 0 # eval.ludic: a call's arguments, in lists kept one a depth
|
||||||
ev_args: []Val = new []Val
|
ev_args: []Val = new []Val
|
||||||
cv_texts: []string = new []string # units_pieces.ludic: a value's var() pieces, kept
|
cv_texts: []string = new []string # units_pieces.ludic: a value's var() pieces, kept
|
||||||
|
tx_pieces: [][]string = new [][]string # eval_text.ludic: a text's pieces, a list a depth
|
||||||
|
tx_depth: int = 0
|
||||||
ca_pool: []CaVal = new []CaVal # calc.ludic: an evaluation's terms, used again by the next
|
ca_pool: []CaVal = new []CaVal # calc.ludic: an evaluation's terms, used again by the next
|
||||||
ca_used: int = 0
|
ca_used: int = 0
|
||||||
ca_args: [][]CaVal = new [][]CaVal
|
ca_args: [][]CaVal = new [][]CaVal
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,11 @@ function bd_class(ui_st: mut UiState, k: UiTpl, e: UiEnv, key: string, out: []Ui
|
||||||
for i in 0 .. len(k.on_keys) { push(ce.on_envs, e) }
|
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)
|
lc_note(ui_st, key, k.on_keys, k.on_acts, e)
|
||||||
let at = len(out)
|
let at = len(out)
|
||||||
let root = new []UiTpl
|
if cls.roots == null or len(cls.roots) != 1 or cls.roots[0] != cls.root {
|
||||||
push(root, cls.root)
|
cls.roots = new []UiTpl # its template as a list of one, made once (again after a reload)
|
||||||
|
push(cls.roots, cls.root)
|
||||||
|
}
|
||||||
|
let root = cls.roots
|
||||||
bd_kids(ui_st, root, ce, key, out)
|
bd_kids(ui_st, root, ce, key, out)
|
||||||
if len(out) > at { bd_pass_down(ui_st, out[at], k, e) }
|
if len(out) > at { bd_pass_down(ui_st, out[at], k, e) }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ export property UiClass {
|
||||||
seen_xml: string = "" # the files as a developer's build last found them on disk
|
seen_xml: string = "" # the files as a developer's build last found them on disk
|
||||||
seen_lss: string = ""
|
seen_lss: string = ""
|
||||||
use: UiTpl = null # screen_class.ludic: the element that shows it as a screen, made once
|
use: UiTpl = null # screen_class.ludic: the element that shows it as a screen, made once
|
||||||
|
roots: []UiTpl = null # build_class.ludic: [root], for bd_kids
|
||||||
none: Val = null # and the empty model it is shown with
|
none: Val = null # and the empty model it is shown with
|
||||||
}
|
}
|
||||||
export function ui_register(ui_st: mut UiState, k: UiClass) -> int {
|
export function ui_register(ui_st: mut UiState, k: UiClass) -> int {
|
||||||
|
|
@ -52,8 +53,14 @@ function uc_reload(ui_st: mut UiState) -> bool {
|
||||||
if k.xml_path != "" and Fs.exists(k.xml_path) {
|
if k.xml_path != "" and Fs.exists(k.xml_path) {
|
||||||
let x = lib_text(ui_st, k.xml_path)
|
let x = lib_text(ui_st, k.xml_path)
|
||||||
var l = ""
|
var l = ""
|
||||||
if k.lss_path != "" and Fs.exists(k.lss_path) { l = lib_text(ui_st, k.lss_path) }
|
let has_l = k.lss_path != "" and Fs.exists(k.lss_path)
|
||||||
if k.seen_xml != "" and (x != k.seen_xml or l != k.seen_lss) {
|
if has_l { l = lib_text(ui_st, k.lss_path) }
|
||||||
|
if k.seen_xml != "" and x == k.seen_xml and l == k.seen_lss {
|
||||||
|
rl_let_go(x) # read only to compare, and the same: nothing keeps it (a dev build
|
||||||
|
if has_l { rl_let_go(l) } # asks every second, and each read was kept for good)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if k.seen_xml != "" {
|
||||||
k.xml = x
|
k.xml = x
|
||||||
k.lss = l
|
k.lss = l
|
||||||
k.root = null
|
k.root = null
|
||||||
|
|
|
||||||
|
|
@ -52,10 +52,11 @@ function el_text(ui_st: mut UiState, n: UiNode, s: string) -> void {
|
||||||
push(n.children, t)
|
push(n.children, t)
|
||||||
}
|
}
|
||||||
# id, class, hidden, disabled and src say what the element is; the rest are applied after the rules
|
# id, class, hidden, disabled and src say what the element is; the rest are applied after the rules
|
||||||
|
# an attribute's text is worked out only where it is read as text (the rest keep their Value; a selector
|
||||||
|
# that asks for one reads it then) - a float's text written per element per frame was a leak
|
||||||
function el_keep(ui_st: mut UiState, n: UiNode, key: string, v: Val) -> void {
|
function el_keep(ui_st: mut UiState, n: UiNode, key: string, v: Val) -> void {
|
||||||
let s = vl_text_u(ui_st, v)
|
if key == "id" { n.id = vl_text_u(ui_st, v) } else if key == "class" {
|
||||||
if key == "id" { n.id = s } else if key == "class" {
|
let parts = mm_split(ui_st, vl_text_u(ui_st, v), 32)
|
||||||
let parts = mm_split(ui_st, s, 32)
|
|
||||||
for i in 0 .. len(parts) {
|
for i in 0 .. len(parts) {
|
||||||
if len(parts[i]) > 0 { push(n.classes, parts[i]) }
|
if len(parts[i]) > 0 { push(n.classes, parts[i]) }
|
||||||
}
|
}
|
||||||
|
|
@ -63,12 +64,12 @@ function el_keep(ui_st: mut UiState, n: UiNode, key: string, v: Val) -> void {
|
||||||
n.popover = true
|
n.popover = true
|
||||||
n.pos = 2
|
n.pos = 2
|
||||||
push(n.akeys, key)
|
push(n.akeys, key)
|
||||||
push(n.avals, s)
|
push(n.avals, "")
|
||||||
push(n.vals, v)
|
push(n.vals, v)
|
||||||
} else if key == "tabindex" { n.focusable = s != "-1" } else if key == "hidden" { n.hidden = el_flag(v) } else if key == "disabled" { n.enabled = not el_flag(v) } else if key == "src" { n.src = s } else {
|
} else if key == "tabindex" { n.focusable = vl_text_u(ui_st, v) != "-1" } else if key == "hidden" { n.hidden = el_flag(v) } else if key == "disabled" { n.enabled = not el_flag(v) } else if key == "src" { n.src = vl_text_u(ui_st, v) } else {
|
||||||
if key == "enabled" { n.enabled = el_flag(v) }
|
if key == "enabled" { n.enabled = el_flag(v) }
|
||||||
push(n.akeys, key)
|
push(n.akeys, key)
|
||||||
push(n.avals, s)
|
push(n.avals, "")
|
||||||
push(n.vals, v)
|
push(n.vals, v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,11 +25,7 @@ function ui_eval(ui_st: mut UiState, x: UiExpr, e: UiEnv) -> Val {
|
||||||
if vl_truthy(ui_eval(ui_st, x.a, e)) { return ui_eval(ui_st, x.b, e) }
|
if vl_truthy(ui_eval(ui_st, x.a, e)) { return ui_eval(ui_st, x.b, e) }
|
||||||
return ui_eval(ui_st, x.c, e)
|
return ui_eval(ui_st, x.c, e)
|
||||||
}
|
}
|
||||||
if x.op == X_TEXT {
|
if x.op == X_TEXT { return ev_text(ui_st, x, e) }
|
||||||
var out = ""
|
|
||||||
for i in 0 .. len(x.args) { out = out + vl_text_u(ui_st, ui_eval(ui_st, x.args[i], e)) }
|
|
||||||
return uv_str(ui_st, out)
|
|
||||||
}
|
|
||||||
return vl_binary(ui_st, x.s, ui_eval(ui_st, x.a, e), ui_eval(ui_st, x.b, e))
|
return vl_binary(ui_st, x.s, ui_eval(ui_st, x.a, e), ui_eval(ui_st, x.b, e))
|
||||||
}
|
}
|
||||||
# a call: len() and range() are the template's own, anything else is the view's
|
# a call: len() and range() are the template's own, anything else is the view's
|
||||||
|
|
|
||||||
38
packages/ludic.ui/eval_text.ludic
Normal file
38
packages/ludic.ui/eval_text.ludic
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
# eval_text.ludic - a text with holes ("{n} left") worked out: its pieces are found in the memo by
|
||||||
|
# comparing in place, so the text it was last frame (and the frame before) is its kept Value, and a
|
||||||
|
# text is built only the first time it reads that way. The memo is bounded; past it a text is built as ever
|
||||||
|
function ev_text(ui_st: mut UiState, x: UiExpr, e: UiEnv) -> Val {
|
||||||
|
let depth = ui_st.tx_depth # a hole may hold a text of its own: a kept list of pieces a depth
|
||||||
|
while len(ui_st.tx_pieces) <= depth { push(ui_st.tx_pieces, new []string) }
|
||||||
|
let ps = ui_st.tx_pieces[depth]
|
||||||
|
ui_st.tx_depth = depth + 1
|
||||||
|
List.clear(ps)
|
||||||
|
for i in 0 .. len(x.args) { push(ps, vl_text_u(ui_st, ui_eval(ui_st, x.args[i], e))) }
|
||||||
|
ui_st.tx_depth = depth
|
||||||
|
let m = mm_of(ui_st)
|
||||||
|
var h = 216613626 ^ (MM_TEXT & 255)
|
||||||
|
for p in 0 .. len(ps) {
|
||||||
|
let t = ps[p]
|
||||||
|
for i in 0 .. len(t) { h = ((h ^ t[i]) * 16777619) & 2147483647 }
|
||||||
|
}
|
||||||
|
var at = h & (MM_CAP - 1)
|
||||||
|
while m.slots[at] != 0 {
|
||||||
|
let i = m.slots[at] - 1
|
||||||
|
if m.tags[i] == MM_TEXT and tx_same(ps, m.keys[i]) { return m.vals[i] }
|
||||||
|
at = (at + 1) & (MM_CAP - 1)
|
||||||
|
}
|
||||||
|
var out = ""
|
||||||
|
for p in 0 .. len(ps) { out = out + ps[p] }
|
||||||
|
let j = mm_add(m, MM_TEXT, out)
|
||||||
|
if j < 0 { return uv_str(ui_st, out) }
|
||||||
|
m.vals[j] = Value.str(out)
|
||||||
|
return m.vals[j]
|
||||||
|
}
|
||||||
|
function tx_same(ps: []string, k: string) -> bool {
|
||||||
|
var at = 0
|
||||||
|
for p in 0 .. len(ps) {
|
||||||
|
if not sat(k, at, ps[p]) { return false }
|
||||||
|
at += len(ps[p])
|
||||||
|
}
|
||||||
|
return at == len(k)
|
||||||
|
}
|
||||||
|
|
@ -12,6 +12,7 @@ import "pool_reset.ludic"
|
||||||
import "pool_reset_more.ludic"
|
import "pool_reset_more.ludic"
|
||||||
import "memo.ludic"
|
import "memo.ludic"
|
||||||
import "memo_join.ludic"
|
import "memo_join.ludic"
|
||||||
|
import "eval_text.ludic"
|
||||||
import "units_pieces.ludic"
|
import "units_pieces.ludic"
|
||||||
import "expr.ludic"
|
import "expr.ludic"
|
||||||
import "expr_parse.ludic"
|
import "expr_parse.ludic"
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
# split into its words, a gradient, a key joined from its parent's. Ludic frees nothing, so parsing
|
# split into its words, a gradient, a key joined from its parent's. Ludic frees nothing, so parsing
|
||||||
# "2px 6px" per element per frame was a leak; the table is bounded, and past it a result is made
|
# "2px 6px" per element per frame was a leak; the table is bounded, and past it a result is made
|
||||||
# (and lost) as before rather than the table growing without end.
|
# (and lost) as before rather than the table growing without end.
|
||||||
const MM_CAP: int = 8192 # slots; at most three quarters are used
|
const MM_CAP: int = 32768 # slots; at most three quarters are used
|
||||||
export property UiMemo {
|
export property UiMemo {
|
||||||
slots: words = null # index + 1 into keys, 0 empty
|
slots: words = null # index + 1 into keys, 0 empty
|
||||||
keys: []string = null # the text a result was made from (or the joined text itself)
|
keys: []string = null # the text a result was made from (or the joined text itself)
|
||||||
|
|
@ -24,6 +24,7 @@ const MM_VARS: int = -6 # cs_vars_in: keys[i] is the text with every
|
||||||
const MM_VTPL: int = -7 # a value's var()s parsed (units_pieces.ludic)
|
const MM_VTPL: int = -7 # a value's var()s parsed (units_pieces.ludic)
|
||||||
const MM_COLOR: int = -8 # ui_color: n1 the colour, n2 its alpha
|
const MM_COLOR: int = -8 # ui_color: n1 the colour, n2 its alpha
|
||||||
const MM_BIMG: int = -9 # border-image: texts the picture, lists what follows its url()
|
const MM_BIMG: int = -9 # border-image: texts the picture, lists what follows its url()
|
||||||
|
const MM_TEXT: int = -10 # a text with holes, keys[i] the text itself (eval_text.ludic)
|
||||||
function mm_new() -> UiMemo {
|
function mm_new() -> UiMemo {
|
||||||
let m = new UiMemo
|
let m = new UiMemo
|
||||||
m.slots = words(MM_CAP)
|
m.slots = words(MM_CAP)
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,11 @@ export function ui_reload(ui_st: mut UiState) -> bool {
|
||||||
var changed = uc_reload(ui_st)
|
var changed = uc_reload(ui_st)
|
||||||
for i in 0 .. len(ui_st.lib_files) {
|
for i in 0 .. len(ui_st.lib_files) {
|
||||||
let f = ui_st.lib_files[i]
|
let f = ui_st.lib_files[i]
|
||||||
if f.disk and lib_text(ui_st, f.path) != f.src { changed = true }
|
if f.disk {
|
||||||
|
let now = lib_text(ui_st, f.path)
|
||||||
|
if now != f.src { changed = true }
|
||||||
|
rl_let_go(now) # read only to compare; a change is read again below
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if not changed { return false }
|
if not changed { return false }
|
||||||
ui_st.ui_errs = new []string
|
ui_st.ui_errs = new []string
|
||||||
|
|
@ -31,3 +35,8 @@ export function ui_reload(ui_st: mut UiState) -> bool {
|
||||||
print(`ui: templates reloaded`)
|
print(`ui: templates reloaded`)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
# a text read from disk only to be compared, given back (never a literal: lib_text's "" is not freed)
|
||||||
|
function rl_let_go(s: string) -> void {
|
||||||
|
if s == null or len(s) == 0 { return }
|
||||||
|
unsafe { free(s) }
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ function sel_has(xs: []string, x: string) -> bool {
|
||||||
function sel_attr_of(n: UiNode, key: string) -> string {
|
function sel_attr_of(n: UiNode, key: string) -> string {
|
||||||
if n.akeys == null { return null }
|
if n.akeys == null { return null }
|
||||||
for i in 0 .. len(n.akeys) {
|
for i in 0 .. len(n.akeys) {
|
||||||
if n.akeys[i] == key { return n.avals[i] }
|
if n.akeys[i] == key { return vl_text(n.vals[i]) }
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue