ludic.ui: a fired event, a press, a hold and a native's events make nothing - a ring of 16 event objects and one hold object made with the state with their keys, and every ui_fire with no value carries the kept null

The last kept bytes of the leak gate's pack, shop and journal screens (memory_final, count mode).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-28 19:20:01 +03:00
parent 1e714623b7
commit 4df15eacfb
7 changed files with 37 additions and 10 deletions

View file

@ -45,6 +45,9 @@ export state UiState {
ev_args: []Val = new []Val
@max(64) ev_ring: []Val = ev_ring_new() # eval.ludic: calls' answers while an action runs, EV_RING of them, made with the state
ev_ring_at: int = 0
nt_evs: []Val = nt_evs_new() # native.ludic: fired events' objects, NT_EVS of them, made with the state
nt_ev_at: int = 0
hd_ev: Val = hd_ev_new() # hold.ludic: the held control's event, filled in place
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

View file

@ -25,7 +25,7 @@ function ct_activate(ui_st: mut UiState, n: UiNode) -> void {
let t = ct_type(n)
if t == "checkbox" { ct_change(ui_st, n, vl_bool(ui_st, not ui_attr_on(n, "checked"))) } else if t == "radio" { ct_change(ui_st, n, ui_attr(n, "value")) } else if t == "select" { ct_step(ui_st, n, 1) } else if t == "key" { ct_key_start(ui_st, n) } else if t == "number" { ct_num_commit(ui_st, n) } else if t == "text" { ct_submit(ui_st, n) } else if t == "" {
ui_sound(ui_st, ct_sound(n))
ui_fire(ui_st, n, "press", Value.null())
ui_fire(ui_st, n, "press", ui_st.fc_none)
}
}
function ct_step(ui_st: mut UiState, n: UiNode, d: int) -> void {

View file

@ -27,7 +27,7 @@ function fc_keys(ui_st: mut UiState, root: UiNode) -> void {
let top = pp_top(root)
if top != null { fc_collect(ui_st, top) } else { fc_collect(ui_st, root) }
if ct_key_capture(ui_st, ui_st.fc_list) { return }
if top != null and ui_st.in_now.escape { ui_fire(ui_st, top, "close", Value.null()) }
if top != null and ui_st.in_now.escape { ui_fire(ui_st, top, "close", ui_st.fc_none) }
let i = ui_st.in_now
if fc_find(ui_st) < 0 { fc_autofocus(ui_st) }
let at = fc_find(ui_st)

View file

@ -1,6 +1,12 @@
# hold.ludic - on-hold: every frame a control is held down - by the pointer (wherever it goes until it
# is let go), or by Enter or the pad's A on the focus - with event.dt, the seconds since the last
# frame, and event.t, how long it has been held, on the ui clock
function hd_ev_new() -> Val {
let v = Value.object()
value_set_float(v, "dt", 0.0) # its keys made with the state, so a first hold grows nothing
value_set_float(v, "t", 0.0)
return v
}
function hd_tick(ui_st: mut UiState, root: UiNode) -> void {
var key = ui_st.fr_key_press
if ui_st.fr_press_key != "" and ui_st.in_now.down { key = ui_st.fr_press_key }
@ -16,9 +22,9 @@ function hd_tick(ui_st: mut UiState, root: UiNode) -> void {
}
let n = fr_node(root, key)
if n != null {
let v = Value.object()
Value.put(v, "dt", Value.float(now - ui_st.hd_last))
Value.put(v, "t", Value.float(now - ui_st.hd_t0))
let v = ui_st.hd_ev # one object kept with the state, filled in place each held frame
value_set_float(v, "dt", now - ui_st.hd_last)
value_set_float(v, "t", now - ui_st.hd_t0)
ui_fire(ui_st, n, "hold", v)
}
ui_st.hd_last = now

View file

@ -62,14 +62,32 @@ function nt_fired_run(ui_st: mut UiState) -> void {
ui_st.nt_sp_names = names
ui_st.nt_sp_vals = vals
}
# an event's object, from NT_EVS kept with the state: an event is read by its handler long before
# NT_EVS more come round to its object
const NT_EVS: int = 16
function nt_evs_new() -> []Val {
let l = new []Val
for i in 0 .. NT_EVS {
let o = Value.object()
value_set_str(o, "name", "") # its keys made now, so a first event grows nothing
Value.put(o, "value", Value.null())
push(l, o)
}
return l
}
function nt_ev_next(ui_st: mut UiState) -> Val {
let at = ui_st.nt_ev_at % NT_EVS
ui_st.nt_ev_at = at + 1
return ui_st.nt_evs[at]
}
# the element's on-<name>, with `event` bound to what happened
function nt_run(ui_st: mut UiState, n: UiNode, name: string, value: Val) -> void {
if n.on_names == null or not n.enabled { return }
for i in 0 .. len(n.on_names) {
if n.on_names[i] == name {
var ev = Value.object()
var ev = nt_ev_next(ui_st)
if (lk_in(PE_NAMES, name) or name == "hold") and Value.kind(value) == 6 { ev = value }
Value.put(ev, "name", Value.str(name))
value_set_str(ev, "name", name)
Value.put(ev, "value", value)
let e = env_new(ui_st, n.env)
env_bind(e, "event", ev)

View file

@ -21,7 +21,7 @@ function fr_pointer(ui_st: mut UiState, root: UiNode) -> void {
if i.down and ui_st.fr_press_key != "" { ct_drag(ui_st, root, ui_st.fr_press_key, i.x, i.y) }
if not i.down and ui_st.in_was_down {
let was = fr_node(root, ui_st.fr_press_key)
if was != null { ui_fire(ui_st, was, "up", Value.null()) }
if was != null { ui_fire(ui_st, was, "up", ui_st.fc_none) }
if hit != null and hit.key == ui_st.fr_press_key and hit.enabled { ct_activate(ui_st, hit) }
ui_st.fr_press_key = ""
}
@ -32,7 +32,7 @@ function fr_press(ui_st: mut UiState, hit: UiNode, x: float, y: float) -> void {
if hit == null { return }
ui_st.fr_press_key = hit.key
if hit.focusable { ui_focus(ui_st, hit.key) }
if hit.enabled { ui_fire(ui_st, hit, "down", Value.null()) }
if hit.enabled { ui_fire(ui_st, hit, "down", ui_st.fc_none) }
ct_press(ui_st, hit, x, y)
}
# :active - the press began on it, or inside it, and is still held (by the pointer, Enter or A)

View file

@ -25,7 +25,7 @@ function pp_pointer(ui_st: mut UiState, top: UiNode) -> bool {
if not (i.down and not ui_st.in_was_down) { return false }
if i.x >= top.x and i.y >= top.y and i.x < top.x + top.cw and i.y < top.y + top.ch { return false }
ui_st.fr_press_key = ""
ui_fire(ui_st, top, "close", Value.null())
ui_fire(ui_st, top, "close", ui_st.fc_none)
return true
}
function pp_draw_top(ui_st: mut UiState) -> void {