# native.ludic - elements the host draws itself: ui_native("keybind", measure, draw) makes # a tag like any other, laid out and styled by the template, drawn by the host. A native # says what happened with ui_fire(n, "change", value): the element's on-change runs once the frame # is drawn, with `event.value` (and `event.name`) to read. export property UiNative { tag: string = "" measure: fn(UiNode, float) -> void = null # the content's size into n.mw, n.mh; avail is the room draw: fn(UiNode) -> void = null input: fn(UiNode, UiPointer) -> bool = null # the pointer on it (pointer_events.ludic) } export function ui_native(ui_st: mut UiState, tag: string, measure: fn(UiNode, float) -> void, draw: fn(UiNode) -> void) -> void { let nv = new UiNative nv.tag = tag nv.measure = measure nv.draw = draw for i in 0 .. len(ui_st.nt_list) { if ui_st.nt_list[i].tag == tag { nv.input = ui_st.nt_list[i].input ui_st.nt_list[i] = nv return } } push(ui_st.nt_list, nv) } # the pointer on a native: its presses, moves, drags, releases and the wheel, while it is over it or # has it captured; answering false to a pointerdown leaves the pointer free export function ui_native_input(ui_st: mut UiState, tag: string, f: fn(UiNode, UiPointer) -> bool) -> void { let nv = nt_find(ui_st, tag) if nv == null { @alloc_ok("only when a template is wrong: a broken screen, never a working one") ui_err(ui_st, `ui_native_input: there is no native <{tag}> - ui_native it first`) return } nv.input = f } function nt_find(ui_st: UiState, tag: string) -> UiNative { for i in 0 .. len(ui_st.nt_list) { if ui_st.nt_list[i].tag == tag { return ui_st.nt_list[i] } } return null } function nt_fired_clear(ui_st: mut UiState) -> void { List.clear(ui_st.nt_q_nodes) List.clear(ui_st.nt_q_names) List.clear(ui_st.nt_q_vals) } export function ui_fire(ui_st: mut UiState, n: UiNode, name: string, value: Val) -> void { ui_kept(ui_st.nt_q_nodes, n) ui_kept(ui_st.nt_q_names, name) ui_kept(ui_st.nt_q_vals, value) } function nt_fired_run(ui_st: mut UiState) -> void { let nodes = ui_st.nt_q_nodes # run from these while what they fire goes to the spares let names = ui_st.nt_q_names let vals = ui_st.nt_q_vals ui_st.nt_q_nodes = ui_st.nt_sp_nodes ui_st.nt_q_names = ui_st.nt_sp_names ui_st.nt_q_vals = ui_st.nt_sp_vals nt_fired_clear(ui_st) for i in 0 .. len(nodes) { nt_run(ui_st, nodes[i], names[i], vals[i]) } ui_st.nt_sp_nodes = nodes 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-, 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 = nt_ev_next(ui_st) if (lk_in(PE_NAMES, name) or name == "hold") and Value.kind(value) == 6 { ev = value } value_set_str(ev, "name", name) Value.put(ev, "value", value) let e = env_new(ui_st, n.env) env_bind(e, "event", ev) act_run(ui_st, n.on_acts[i], e) } } } # an attribute of an element, as the template gave it (null when it has none) - for a native export function ui_attr(n: UiNode, key: string) -> Val { if n.akeys == null { return n.none } for i in 0 .. len(n.akeys) { if n.akeys[i] == key { return n.vals[i] } } return n.none } # whether an element has the attribute at all: ui_attr's miss is a new null, so a question asked # every frame asks this first export function ui_attr_has(n: UiNode, key: string) -> bool { if n.akeys == null { return false } for i in 0 .. len(n.akeys) { if n.akeys[i] == key { return true } } return false } # an attribute as a number, `none` when it is not there - without ui_attr's new null for a miss export function ui_attr_float(n: UiNode, key: string, none: float) -> float { if not ui_attr_has(n, key) { return none } return Value.as_float(ui_attr(n, key)) } export function ui_attr_text(n: UiNode, key: string) -> string { if n.akeys == null { return "" } for i in 0 .. len(n.akeys) { if n.akeys[i] == key { return vl_text(n.vals[i]) } } return "" } # a boolean attribute: present with no value is true, as HTML's are (