Skip to main content

Crate kui_lua

Crate kui_lua 

Source
Expand description

Lua extensions for kui. A script defines view(env, slot) returning a plain table tree (built with the injected row/column/text/button prelude) and optionally on_event(ev), whose return value is its replies to the host; slot says which slot the host is filling (slot.name, slot.namespace, slot.params, slot.key; ADR 0014), and a slots global lists the names it fills. env carries host facts (refresh rate, focus, viewport), queries (env.edit_text(key), env.is_focused(key), env.is_hovered(key), env.is_pressed(key), env.measure_text(s, opts, max_w)), focus verbs (env.set_focus(key), env.blur(), env.focus_next(), env.focus_prev()), env.announce(text, politeness) and scroll calls (env.reveal(key), env.scroll_offset(key), env.set_scroll(key, x, y), env.shift_scroll(key, drawn, target), env.scroll_geometry(key)), file dialogs (env.request_files(opts), env.awaiting_files()), text queries (env.text_hit(key, x, y), env.caret_rect(key, byte)) and window requests (env.set_window_size(window, w, h), env.focus_window(window)); the root table may set window_title, always_on_top, secure_input and option_as_alt. Because the IR is data all the way down, the binding is just table-to-node conversion — no closures cross the boundary. One global is native rather than the prelude’s: row_heights(rows, estimate), the core’s RowHeights as userdata the script keeps, which the prelude’s list slices a variable-height list by (backlog C46).

§The two focus names

env.focused and env.focus are one letter apart and are not the same fact, so neither is going away:

  • env.focused (bool) is the window’s keyboard focus — whether this window has the keyboard at all. It is Env::focused, and every binding carries it; Node reads it off its own env object.
  • env.focus (integer, nil for none) is the focused node’s key. Node spells this ctx.focused() and C kui_focused.

Lua puts host facts and runtime queries on one table where Node has two objects (env and ctx), so the name focused was already spent on the window fact and the node reading could not have it. That also settles the verbs: env.focus is a value, so moving focus is env.set_focus(key) rather than the focus(key) of the other bindings. env.blur(), env.focus_next() and env.focus_prev() need no such dodge and keep the Node and C spellings.

env.set_focus, env.is_focused and env.reveal take the node either way it can be spelled: the integer key an event carried, or the string its key field declared — env.set_focus("note") — resolved through the frame being built so far and then the last finished one (Ui::key_of), so a node no event has come from can be named — among the script’s own nodes: a script is a guest in the host’s frame, and a label the host or another plugin declared is not one it can see. Two nodes of its own on one label under different parents resolve to the first in tree order with an ambiguous-key warning; a label no node declared is an error naming both spellings.

set_focus and blur take effect at once; focus_next / focus_prev resolve when the frame finishes, because the Tab ring is made of a finished tree and view is still declaring one (Ui::focus_next). Either way env.focus is a value the host wrote before view ran, so it still reads the focus the frame opened with — env.is_focused(key) is the query that answers about now.

Props come from the shared schema (kui_core::schema): every row is reachable from Lua under its snake_case name (min_width, on_click, line_height, …), so Lua and the Node binding accept the same surface by construction. Only the composites keep Lua-flavored shapes: pad = 8 | {all,x,y,l,r,t,b}, border = {w, color}, scroll/ scroll_x/scroll_y/clip booleans, float = "below" | {anchor, at, self, dx, dy, fit} (self_at still answers to self), sizing {pct = 50} | {grow = 2}, and tooltip = "hint" on a container (hover-gated). Those are shapes, not rules: what a name falls back to, what a preset attaches to and what a hint implies are decided in kui_core::spec, which this module hands its extracted scalars to.

§A script may host a plugin of its own

env.add_extension(namespace, path) loads a C extension — a .so / .dylib / .dll exporting the kui_ext_* entry points crates/kui-ffi/include/kui.h describes, the same plugin a Rust, C or Node host loads — and fill { name = "ns/slot", params = ... } is the position it draws in, among the script’s own children. The script is a host to it exactly as its host is a host to the script: it declares where, it passes params every frame, and the plugin’s replies come back to on_event with from naming the namespace (kui_core::slot). It is C libraries and only that; a script does not load another script, because a host that wants two scripts loads two.

Events arrive as their payload table plus node_key (the emitting node’s key as an integer), which is what env.edit_text takes.

Structs§

LuaExtension

Constants§

MAX_VALUE_DEPTH
How deep a Lua value may nest before lua_to_value refuses it: a table holding itself, or one nested past any payload a view means, would otherwise recurse off the Rust stack and abort the process before anything read the value (backlog RG95).
MAX_VIEW_DEPTH
How deep a Lua view tree may nest: twice deep_nesting_64_levels, and inside a debug build’s 2 MB test thread whatever the nodes on the way (a chain of tooltips, radio groups and fragments overflowed it between 128 and 256). Past it, or on a node that is its own ancestor (t[1] = t), [build_node] refuses the view rather than recurse off the stack (backlog RG95).

Functions§

lua_to_value
luals_meta
The meta file’s text.
parse_props
Table → props. Constructor-order specials first (dir from the node type, size before any style prop), then the Lua-shaped composites, then every schema row by its snake_case name. Unknown keys (type, value, label, children) fall through. refs is what a $name resolves through ([with_refs]).
parse_tokens
{ colors = { name = colour | { light =, dark = } | { from =, ops = } }, lengths = { name = px } } as a kui_core::Tokens (ADR 0027). Names are sorted, since a Lua table’s iteration order is not one a declaration can promise and the index is only what env.tokens lists them in. A colour with from is a derived token (ADR 0028), its ops a list of { verb, … } tuples; the values are declared first and the derived ones after, each once every source it names is in — position cannot carry that order here, so the name does. One whose source never arrives goes in last as written, for the core to drop with unknown-token; a cycle drops the same way, each naming the other.
value_to_lua

Type Aliases§

Refs
What a $name in a prop resolves through while a table is parsed (ADR 0027): the core’s lookup for the running origin — its own table over the host’s, the roles in front — and the names that did not resolve, raised as unknown-token once the borrow is handed back ([with_refs]). A slot whose name resolves to nothing is left out, so it keeps its default — the theme’s foreground for a text’s color, a fit width, the default text size — the way Node’s encoder drops the prop; not an explicit transparent or zero, which would hide the text a typo was on. The core’s kui_core::NameRefs since AR14: the one miss policy, written once, that a keyframe stop and an entrance resolve through in every binding.