Expand description
The windowed kui runner: a winit window and a wgpu renderer around
kui_core, with the App trait an application implements.
kui is a Rust UI library whose view is a plain data tree rebuilt every
frame. kui_core holds the model, the layout and the widgets, kui_wgpu
draws them, and this crate puts a window around both; it is the crate
most apps depend on. It re-exports all of kui-core, so kui_native::
reaches everything, and adds the App trait, the app launcher
builder, multiple windows, the clipboard, file dialogs, audio, AccessKit
and a headless testing driver. kui-derive supplies
#[derive(Message)]; kui-lua, kui-ffi and kui-node are bindings over the
same core.
§Quick start
use kui_native::{App, TextStyle, Ui};
struct Hello;
impl App for Hello {
// Called once per frame. Everything on screen is declared here,
// from scratch, every time.
fn view(&mut self, ui: &mut Ui<'_>) {
let theme = ui.theme();
ui.text("Hello, kui", TextStyle::new(24.0).color(theme.fg));
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
kui_native::app("Hello").size(360.0, 200.0).run(Hello)
}§State and events
A button carries a message. After the frame, each event it produced
reaches App::on_event, and UiEvent::message reads the message
back as the app’s own enum.
use kui_native::widgets;
use kui_native::{App, Message, NodeSpec, TextStyle, Ui, UiEvent};
// `derive(Message)` turns each variant into plain data on the way out
// and back into `Msg` on the way in.
#[derive(Message, Clone, Debug, PartialEq)]
enum Msg {
Inc,
Dec,
}
#[derive(Default)]
struct Counter {
count: i64,
}
impl App for Counter {
fn view(&mut self, ui: &mut Ui<'_>) {
let t = ui.theme();
ui.with(NodeSpec::column().fill().center().gap(16.0).bg(t.bg), |ui| {
ui.text(&self.count.to_string(), TextStyle::new(56.0).color(t.fg));
ui.with(NodeSpec::row().gap(8.0), |ui| {
widgets::button(ui, "-1", Msg::Dec);
widgets::button(ui, "+1", Msg::Inc);
});
});
}
fn on_event(&mut self, ev: UiEvent) {
match ev.message::<Msg>() {
Some(Msg::Inc) => self.count += 1,
Some(Msg::Dec) => self.count -= 1,
// Not ours: a resize, a focus change, a window event.
None => {}
}
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
kui_native::app("Counter").size(360.0, 240.0).run(Counter::default())
}The book walks from here through controls, lists, the keyboard, floating windows, motion, effects and tests, one step per chapter.
§Where to look
App: the trait an app implements;view,on_event,setup,teardown.appandLauncher: title, size, chrome, icon, extensions, thenLauncher::run;Launcher::opengives a host that owns the loop aPumpRunnerinstead.Ui: whatviewis given;Ui::with,Ui::text,Ui::window.NodeSpecandTextStyle: a box’s layout, look and handlers; a text’s size, font and colour.widgets: buttons, checkboxes, text inputs, selects, sliders, lists, tables and menus.UiEvent: whaton_eventreceives, with the readers on it.testing: a headless driver for anApp, forcargo test.- From kui-core:
theme,anim(transitions and springs),window(several windows),messageandValue(the payload behind a message),Core(one window’s runtime, for hosts and tests).
§Windows
One event loop, any number of windows. The launcher opens the main
window; a frame that declares another with Ui::window opens it on the
same session and GPU device, and a frame that stops declaring it closes
it. view runs once per open window per frame, Ui::window_name says
which ("main" for the launcher’s), and every event carries its window’s
WindowId.
§Features
audio(default): plays sounds through kira and cpal. Off, audio commands are dropped and no audio library is linked (ALSA on Linux).accesskit(default): exposes the access tree to screen readers through AccessKit. Off, the tree is still built and nothing reaches the OS.derive(default):#[derive(Message)]. Off, messages are built and matched asValues by hand.dialogs(default): the platform’s file dialogs through rfd (the XDG portal on Linux). Off, every dialog answers at once as cancelled.smoke: honoursKUI_SMOKE_FRAMES=n, which closes the window afternframes, in a release build. A debug build honours it regardless.
§Linux
Building needs pkg-config and ALSA’s headers (libasound2-dev on
Debian and Ubuntu). A window loads the rest at run time: libxkbcommon
(and libxkbcommon-x11 on X11); libX11, libXcursor, libXrandr and
libXi on X11 or libwayland-client on Wayland; libvulkan with a
driver, or libEGL. A missing one fails when the window opens, naming
the library. macOS and Windows need only a Rust toolchain.
The book: https://kui-book.qxuken.dev. Repository and design records
(under docs/adr): https://github.com/qxuken/kui.
Re-exports§
pub use wgpu;
Modules§
- access
- Accessibility as data: the semantic tree of a frame, derived from what
nodes do and from the
roleandlabelprops a view declares. - anim
- Transitions: retained tweens keyed by node identity.
- atlas
- CPU-side glyph atlas: a single RGBA page with shelf packing. Renderers
mirror it to a texture;
dirty/epochtell them when to re-upload. - audio
- The audio device behind the core’s
AudioCommands, used by the runner and by hosts that drive aCorethemselves. - calc
- Size expressions: CSS’s
min(),max()andclamp()over lengths and percentages, resolved by layout against the parent’s content box (the same box aPercentsizing takes its cut of). - cells
- A cell grid: a terminal’s screen as one node,
rows × colscells each with a character, a foreground, a background and attribute bits. - color
Color: straight-alpha sRGB withf32channels, plus the small amount of colour arithmetic a palette needs (mixing, luminance, WCAG contrast).- cursor
- Pointer shape as declared data. A view says what the pointer is over
a node with the
cursorprop — a button is a hand because it declared one, a handle a grab because it declared one — and the core resolves which declaration is under the pointer per frame (crate::runtime::Core::cursor_shape), which the frame driver hands to the real window. Nothing is inferred from what a node does: anon_clicknode with nocursoris the plain arrow, as a native button is, and so is anon_dragnode. The one shape the core implies is the I-beam over an editor or a selection scope, the way every desktop marks text that can be taken. The stock button declaresPointerfor itself, so<button>is a hand in every binding without the app saying so. - deco
- Decoration lines that are not a rect: the wavy and dotted
underlines a text, a span or a cell asks for. A solid line is one
Solidquad, as it always was; these are runs ofQuadKind::Segment— the capsule the backend already draws for alinenode — so no backend, header or protocol learns a kind. A wave is a zigzag of short pieces whose round caps soften the corners; a dotted line is a row of zero-length pieces, which the capsule SDF draws as dots. Both are sized from the stroke the face recommends, so they scale with the text and the display. - depart
- Exit transitions: a subtree the view stopped declaring, kept as a picture and played out.
- diag
- Warnings as data: misconfigurations the core notices, drained through
crate::Core::take_warnings. - dialog
- File dialogs as an ask: the app describes an Open, Save or folder
dialog, the host shows the platform’s own, and the answer comes back as
one
{kind:"files", paths, tag}event (pathsempty when cancelled). - display
- The renderer boundary: a flat list of quads in physical pixels.
- edit
- Editable text: the retained state of every
text_editnode, keyed by nodeKey, built on cosmic-text’s editor. - enter
- Entrance transitions: where a node’s animatable slots start on the first frame it is seen.
- env
Env: the host facts a frame driver pushes into the core, which a view reads back withui.env().- event
- Typed readings of the core’s own event payloads: drags, buttons, scrolls, hovers, layouts, key presses and text input.
- fragment
- A fragment: WGSL an app registers, validated here so a frame never sees a source that cannot compile.
- geom
- Plain geometry in logical pixels:
Vec2,Size,RectandEdges. - gradient
- Gradients: what a box’s
gradientrow paints (docs/adr/0042-a-gradient-is-an-image-the-core-paints.md). - input
- Input in, events out.
- key
Key: stable node identity across frames.- keyframes
- Keyframes: CSS
@keyframesfor a node’s animatable slots. - layout
- The flex solver: clay-style layout over the flat tree, run by
computeonce per frame. - line
- Strokes: what a
linenode draws. - menu
- Menus as data: the rows of a context menu or the application menu bar, what the window has open, and what a host still has to do after a row is chosen.
- message
- Typed messages over the plain-data payload.
- metrics
Metrics: the sizes the stock widgets are built from, one struct beside the palette.- path
- Paths: what a
pathnode draws (docs/adr/0040-a-path-is-a-mask-in-the-atlas.md). - resources
- Long-lived, host-registered resources: fonts, images, sounds and fragment shaders, behind typed handles.
- runtime
Core: one window’s runtime, the state machine a runner drives frame by frame.- schema
- The prop schema: the one table of node props, elements, events and readings that every kui binding is generated from or checked against.
- scroll
- Retained scroll state: the offset of every scroll container, keyed by
node
Key, and the geometry the last layout resolved for it. - select
- The window’s text selection outside an editor: what a
selectablenode scopes, what a press-drag across it produces, and what a copy reads. - session
Session: what a set of windows shares.- slider
- A slider’s arithmetic: what value a pointer position, an arrow, a Page
key or Home / End means on a node whose role is
Sliderand that declaredon_change. - slot
- Slots: the places a host declares in its own view for an extension to fill, with parameters in and replies out.
- slots
- The animatable slots an entrance or a keyframe stop may name (width, height, bg, radius, opacity) as one value.
- spec
NodeSpecandTextStyle: everything a node and a text declare, as plain data with a builder.- stats
- Frame timing samples for the latency graph. The core only stores data —
whoever drives the frame loop (the runner, an FFI host) measures and
pushes;
widgets::latency_graphrenders it with ordinary primitives. - testing
- A headless driver for an
App: clicks, keys, typed text and drags through the realviewandon_event, with no window and no GPU. - text
- Core-owned text stack. Shaping and line layout run through cosmic-text and are cached across frames keyed by (content, style, scale) — the layout pass measures through this cache, so shaping survives resizes and static text costs a hash lookup per frame. Rasterization feeds the shared glyph atlas; renderers only ever see positioned atlas quads.
- theme
Theme: the named colours a view and the stock widgets paint with, derived from the OS’s appearance and accent.- tokens
Tokens: named colours and lengths an app declares beside the theme and references by name ($peach,$sidebar) in any colour or length prop.- tree
Tree: the per-frame flat tree the core builds, lays out and emits.- ui
Ui: the frame builder a Rust view declares its tree through.- value
Value: the plain-data payload type for everything that crosses an event or binding boundary.- widgets
- Stock widgets built from the primitives: buttons, toggles, text input, select, slider, splitter, tooltips, menus, a titlebar and virtual lists.
- window
- Windows as data: what a frame declares about the OS windows it wants, and the commands a frame driver applies to the real ones.
Structs§
- Accel
- A keyboard shortcut, parsed out of the string an item declares.
- Access
Node - One semantic node of a frame.
- Access
Request - A request from assistive technology, delivered as
crate::InputEvent::Access. - Access
Run - One visual line (or a piece of one) of an editor’s text, with what a
screen reader needs to read it by character and word and to place a
caret: every character’s byte length, x position and width. A line
that continues into another ends with its
"\n", counted as a character of zero width. Runs longer thanRUN_CHARScharacters are split, so indices fit the platform’s byte-sized ones. - Access
Tree - The semantic nodes of a finished frame, in tree order (a parent always precedes its descendants; the root comes first).
- Announcement
- One thing to say once, with no node behind it: “Saved”, “3 results”.
Queued by
Core::announceand drained byCore::take_announcements, the way window commands, audio commands and warnings are: an announcement is an event on a timeline, and the frame’s tree has no place to keep one. - Audio
Env - What the driver’s audio output is doing, for views to read. A fact, not a verb: nothing here lets a view close the device, which stays the driver’s decision (it closes an idle one itself, after a while).
- Audio
Spec - What an
audionode declares each frame (Core::audio_node). - Audio
Store - Playback bookkeeping on the session: the command queue, the tagged
playbacks awaiting their
endedevent, and theaudionodes’ retained playbacks. The queue and the ids are the session’s, because the process has one device; the mounts are keyed by window as well as by node, because each is reconciled against one window’s frame (finish_framehands in the window it finished, and only that window’s slice is diffed). - BarMenu
- One menu of the bar: what the bar reads, and what drops out of it.
- Bounce
- How far a spring overshoots, 0 (glides in, no overshoot) to
MAX_BOUNCE(rings a while): the one number that shapes a spring besides its duration. A bouncebis a damping ratio of1 - b(SwiftUI’sSpring(duration:bounce:)). - Button
Event - A
{kind:"button"}event: a non-primary button anonButtonnode claimed, captured by it from press to release. - Buttons
- Which of the non-primary buttons a node’s
on_buttonclaims:Buttons::SECONDARY,Buttons::MIDDLEandButtons::OTHER(every button past the named three), or-ed together. A node declaringon_buttonclaimsButtons::ALLunless it says otherwise. The primary button is never in it: that one presses, drags and clicks for every node. - Calc
- An expression that depends on the room, by its place in the table.
Copy, so aSizingholding one still is. - Cell
- One cell: a character, its colours as
0xRRGGBBAA(a background of 0 is none, an underline colour of 0 the foreground’s), and attribute bits. Sixteen bytes, so a 200×50 pane is a 160 KB slice a frame. - CellEnd
- One end of a selection in a cell grid: an absolute line (the grid’s
origin_lineplus the row) and a column. Absolute because a grid is one screenful of an app’s own history, so a row number means a different line after every scroll. - Cell
Grid - A grid to draw: the cells in row-major order (
rows × colsof them; fewer draw as blank), the style the glyphs are shaped in (size,line_heightas the cell height,family/font), and the cursor. - Cell
Selection - A selection inside one
cellsgrid. - Cell
Store - The frame’s grids and the glyph tables they draw from. The frame
before it is kept too, the way the text store keeps its places: a host
that reads the selection from inside its own
viewis asking about a frame that has not been built yet. - CellsId
- Index into the frame’s grid list.
- Clip
- The clip a node inherits: a rect, and the radii to round its corners by.
- Clipboard
Marks - What the pasteboard said about the text a paste brought back: the markers
password managers set on a copied secret, after the
convention at nspasteboard.org that 1Password, Bitwarden, KeePassXC and
the macOS clipboard managers follow. Read by the driver, which owns the
clipboard, and handed over with the text as
InputEvent::Paste. - Color
- A straight-alpha sRGB colour with
f32channels in0.0..=1.0. - Core
- One window’s runtime: the state a runner drives frame by frame.
- Dash
- A stroke’s dash pattern (backlog V2): a mark, a gap, a second mark and a second gap, repeated along the stroke’s length from its first point.
- Depart
Store - The departing subtrees, bounded (see
MAX_NODES). - Display
List - Drag
- A
{kind:"drag"}event: anonDragnode’s pointer capture. - Edges
- Per-side lengths: padding, borders.
- Edit
Options - How a
text_editnode behaves: its text style, whether it is a single-line field or a multiline document, and how it takes focus and paints its selection. Build one with struct update syntax fromDefault. - Endpoint
- One end of a selection: the node whose text it lands in, and a byte offset into that node’s content (not into the scope’s).
- Enter
- Where a node’s slots start on first sight. Every field is optional: a
slot
enterdoesn’t name simply snaps as it always did. Derefs to itsSlots, soenter.bgreads the slot. - Env
- What the host knows about the display/window. Defaults are safe for headless drivers (tests, benches) that never set anything.
- Extensions
- The runner’s extensions, each under the namespace the host gave it.
Origins are positions here: the host is
OriginId::HOSTand the extension at indexiisOriginId(i + 1), which is what an event’s origin indexes back into. - File
Dialog - An Open, Save or folder dialog, as an app asks for one.
- File
Filter - One entry of a dialog’s file-type menu:
Imagesoverpng,jpg. Extensions are without the dot. - Float
Config - Takes a node out of flex flow: it does not consume space in its parent,
sizes Grow/Percent against its anchor, is positioned by attach points,
and escapes ancestor clips unless
FloatConfig::clipkeeps it in its parent’s. It paints as a layer of its own, above the in-flow tree and every float that opened before it and under every one that opened after, and takes input in the same order. - Font
Features - The OpenType features a style asks the shaper for: up to
FontFeatures::MAXfour-letter tags with a value each.liga0 keeps a coding font from joining->,tnum1 gives tabular figures,ss011 turns on a stylistic set. Plain data andCopy, since aTextStyleis; the spelling every binding shares isFontFeatures::parse’s. - FontId
- A registered font (
Core::add_font_data/add_system_font), used throughTextStyle::font. - Fragment
Draw - What a
QuadKind::Fragmentquad points at: which registered WGSL paints it, and the sixteen numbers that frame passes it. - Fragment
Draw Id - Where a node’s
Drawlives in the frame’sFragmentList. - Fragment
Id - A registered WGSL fragment function (
Core::add_fragment), drawn by afragmentnode. - Fragment
List - The frame’s fragment draws, one per
fragmentnode, indexed by theFragmentDrawIdthe node’sNodeContentcarries. - Fragment
Ref - What a
fragmentnode names: the function, and the image it reads throughkui_sampleif it declared one. Every fragment door takesimpl Into<FragmentRef>, so a bareFragmentIdis the no-image form andid.with_image(img)the other; nothing else about the node changes. - Frame
Cause - Why a frame was drawn: every reason that reached the window between
the start of the last frame and the start of this one.
Read from a view as
Core::frame_cause. A set, because a frame answers everything that asked since the last one — a keystroke and the caret’s blink, a wheel and the transition it started. - Frame
Holder - One thing that holds an owed frame, named for a person: see
OwedBy. - Frame
Request - Where a frame was asked for: a
Core::request_framecall, or one the core makes for itself. - Frame
Sample - One frame’s cost in milliseconds, split by phase.
- Frame
Stats - Fixed-size ring of recent frame samples, and the count of every frame ever pushed through it.
- Gradient
- A box’s gradient: see the module’s notes. Built by
Gradient::to,Gradient::angleorGradient::radial, or read from plain data byparse_with. - Gradient
Stop - One stop: a colour, and where along the gradient it sits, 0 to 1. A stop with no position is spaced evenly between its neighbours that have one, the first defaulting to 0 and the last to 1, as CSS spaces them.
- Handles
- How a binding spells the handles inside a readback (a node key, a
resource id) when a shape crosses as a
Value. A JS number cannot hold a 64-bit handle and a Lua integer can, so Node writes sixteen hex digits (Handles::HEX) and Lua the integer itself (Handles::INT). - Hover
- A
{kind:"hover"}event: anonHovernode entered or left. - ImageId
- Image
Opts - How an
imagenode meets the pixels it shows: its two per-node rows. Carried on the node’s content rather than onNodeSpec, so a box pays nothing for a row only an image reads. - Key
- A node’s identity: the hash of its path from the root.
Key::ROOTis the tree’s root;Key::strandKey::indexderive children. - KeyLocks
- What the lock keys hold at a press: Caps Lock and Num Lock on or off.
Not a modifier held —
KeyModsis only what is down, which accelerators and chords compare exactly — but state a press was made under, which a terminal speaking kitty’s keyboard protocol reports and a keypad reading needs (its1is an End with Num Lock off). - KeyMods
- Physical modifier state. Unlike
Mods— which abstracts platform conventions for the input widget (word,doc) — nothing here is normalized: an app bindingCtrl-wneeds to know it was Control and not Command. - KeyPress
- One key press, delivered to whatever holds key focus. Carries both the
binding view (
code+mods) and the typing view (text), so an app can serve a modal keymap and an insert mode from the same event. - Keyframe
- One stop. Every field is optional:
atresolves by position, and a slot a stop doesn’t name is left to its neighbours. Derefs to itsSlots, sostop.bgreads the slot. - Launcher
- The windowed runner’s builder, made by
app. - Layout
- A
{kind:"layout"}event: anonLayoutnode’s placed rect. - LineId
- Index into the frame’s line list.
- Line
Store - The frame’s strokes, and the previous frame’s while an
exitneeds it. - Locale
- A language tag as the host reports it —
"en","en-US","zh-Hans-CN". Carried inline rather than as aStringsoEnvstaysCopy: a view readsui.env()every frame, and a tag that allocated would allocate on every one of them. - Menu
- The menu a window has open: its items, where it opened, and what it is about. One per window — opening a second closes the first, the way one selection closes the last.
- MenuBar
- The application menu: what a frame declares, in order
(
Core::declare_menu_bar). - Menu
Item - One row of a menu.
- Metrics
- The sizes the stock widgets are built from. Plain data and
Copy: a view reads it offui.metrics()and may keep or change its own copy, andCore::set_metricsmakes one the frame’s. - Min
- A lower clamp on one axis: a number of logical px, or the node’s own
fit size on that axis (
Min::FIT,minWidth: "fit"in the bindings).FITis what lets aGrowchild keep a content floor, like CSS’sflex: 1 0 auto: tabs split a bar evenly while they fit and sit at their label’s width, scrolling, once they do not. Layout resolves it to a number in the fit pass of its axis, so every later clamp reads one; until then it clamps like no floor at all. - Mods
- Modifier state for editing keys.
wordis Alt/Option (word-wise motion),docis the platform primary modifier (line/document-wise motion). - Name
Refs - A lookup plus the names it could not answer — the shape every
by-name lowering wants (Lua’s props, a keyframe stop or an entrance
in any binding, since those cross as plain data with the name still
in them), so the one miss policy is written once: a
$namethat resolves to nothing, or to the other kind, isNone— the slot is left at the row’s default, the way the prop would be if it were not declared — and the miss is remembered for the caller to raise asunknown-tokenonce the lookup’s borrow of the core is handed back (Core::warn_unknown_token). - Node
Info - One node of the last finished frame.
- Node
Spec - Everything one node declares. This, not any Rust trait, is the contract
every frontend (Rust builders, Lua, JSX, C) lowers into. Start from
NodeSpec::row,NodeSpec::columnorNodeSpec::tableand chain builders; see the module docs for an example. - OpRange
- Where a derived token’s steps are in
Tokens’ chain: the table owns the ops, so the token staysCopy. - Origin
Id - Which frontend produced a node: 0 is the host app, extensions get 1+.
- Owed
- What a frame left owed, by kind:
Core::owed.any()is whatCore::animatinganswers;beyond_cycles()is the same with a keyframe cycle — which never ends — left out. - OwedBy
- Who holds the frame the last one left owed:
Core::owed, named. Each list is empty when its kind incrate::Owedis false, and names what made it true when it is. - PadShorthand
- The
padshorthand family as declared — any subset of the seven names, eachNonewhen the frontend did not see it.PadShorthand::resolvedecides what a missing edge falls back to; a binding only reports what it found. - Path
- A path as an app builds one: the ops, the fill rule, and an optional
stroke. The fill colour is the node’s
bg; the stroke’s width and colour are thecrate::line::Stroke’s (curveis ignored). - Path
Error - Where a
dstring went wrong: the byte offset and what was expected. - PathId
- Index into the frame’s path list.
- Path
Store - The frame’s paths, and the previous frame’s while an
exitneeds it. - Play
Options - How to start a playback (
Core::play). - Playback
Id - One playback instance. Allocated by the core when the play command is queued, so callers get it synchronously without a driver round trip; 0 is never issued.
- Pump
Runner - A windowed runner driven from outside, from
Launcher::open: the same runner asLauncher::run(input mapping, IME, clipboard, chrome, caret blink), but the host callspumporpump_untilon its own cadence instead of parking in the event loop. - Quad
- Range
End - One end of the range an app is asked to fill in
(
Core::selection_range): the data index of the row it is in, and the byte inside that row’s own text. An end outside every virtualised row has no index — it is text the core built and can answer for itself. - Rect
- Renderer
- One window’s renderer: its surface, the pipelines and a GPU copy of the core’s glyph atlas.
- Resources
- One session’s registry. The maps are secondary to the process-wide the process-wide mint (see the module doc), so a lookup that misses is a handle this session never registered or has since removed — never somebody else’s entry.
- Scroll
- A
{kind:"scroll"}event: the wheel over anonScrollnode. - Scroll
Geometry - What the last layout resolved for a scroll container: where the container
landed, how big its content came out, and the offset it clamped. All
logical px, in the same viewport coordinates
on_layoutreports. - Scroll
State - A scroll view’s offsets and range (logical px).
- Scrollbar
- A scrolling node’s bars, per node. Every field’s default is the stock
bar — the theme’s
scrollbar/scrollbar_activecolours, 4 px at rest and 6 px under the pointer, always drawn while the content overflows — so a binding that sets none of the four rows gets exactly what it always had. The bars are overlays and take no layout space whatever their width; the grabbable track is at least as wide as the active thumb plus its inset. - Selection
- The window’s selection: a scope and two ends of it.
anchoris where the press landed andfocusis where the pointer is now, so the pair is directed — dragging back past the anchor selects the other way without the two swapping, which is what keeps a drag from feeling like it jumps when it crosses its own start. - Session
- The font database, registries and audio store a set of windows share.
Cloning one clones the handle, not the contents: that is how a second
Corejoins (Core::new_in(&session)). - Session
Id - Which
Sessiona registry — and so every handle it minted — belongs to. Process-wide unique, from a counter; never serialized, so the number means nothing across runs and is only ever compared or printed. - Shadow
- One outer drop shadow: the node’s rounded rect, moved by
dx/dy, grown byspreadand its edge blurred overblur, painted incolorbehind the node. CSS’sbox-shadowwithout the inset and multi-shadow forms. - Shared
Audio - A window’s handle to the session’s audio store —
Core‘saudiofield. The process has one audio device, so the playbacks and the command queue are the session’s and not this window’s: whichever window’s driver drains the queue applies every window’s sounds. Theaudionodes’ mounts are per window inside it (see the module doc), which is why the readers here take one. - Shared
Resources - A window’s handle to the session’s resource registry —
Core’sresourcesfield. A font, image or sound registered through one is registered for every window in the session, and draws in all of them. - Size
- Slot
- Which slot an extension is filling, handed to
Extension::view. - SoundId
- A registered sound (
Core::add_sound): encoded file bytes the driver’s audio backend decodes. Played throughCore::play, anaudionode, orNodeSpec::click_sound/hover_sound. - Span
- One styled run inside a rich-text paragraph. Spans are shaped and wrapped together as a single flow; plain data, so every frontend can build them.
- Stroke
- How a
lineis drawn: a width, a colour, and whether the points are the corners of a polyline or the knots of a curve through them. - System
Env - The user’s OS settings, as the host reports them. Not window facts and
not display facts: things the person chose once, in a settings app, that
a view is expected to honour. The core acts on two of them in one way:
appearanceandaccentderive the theme, so the stock widgets and a<text>with no colour follow the OS — and nothing else moves. Reduced motion does not shorten an animation and a dark appearance repaints none of the app’s own colours: the view decides, because only it knows which of its colours is the background and which of its animations carries meaning. - System
Font - One family of the font database — installed or loaded — as its faces
describe it, from what the database read off each face’s tables when
it was scanned: nothing is loaded or shaped to answer.
What
Core::system_fontslists, one per family. - TextHit
- Where a point landed in the text a keyed node drew: a byte offset and
the visual row it is on.
byteis a caret position: between two characters, past the last one at the end, and cosmic-text’s rule for which side of a glyph the point fell on. For a node holding several text runs the offset runs across them in tree order, the way the access tree reads aline.lineis the visual row within the node, 0-based, counted across every run the key covers by where the rows sit: alinerow of three inline runs is one row, a run that wrapped is as many as it wrapped to, and two runs stacked are two — not the wrapped line within one run’s buffer, and not the ordinalrole="line"node a pointer event’slinenames (that one counts rows of the editor, this one rows of the text asked about). - Text
Input - A
{kind:"text"}event: what a focused key sink or editor was given to insert. - Text
Metrics - What a piece of text measures, in logical px at the current scale — the same numbers layout uses for a text node with that content and style, so a view can size a column to its widest label or pick a tier that fits without hand-tuned magic numbers.
- TextPos
- A position in an editor’s text: a run and a character index into it
(
character == char countis the end of the run). - Text
Style - How a run of text is shaped and painted: size, line height, colour, family, wrapping, line limits, OpenType features and decorations.
- Theme
- Every colour the stock widgets and the core’s own chrome paint with,
as roles rather than values. Plain data and
Copy: a view reads it offui.theme()and may keep, mutate or replace its own copy. - Token
Lookup - A frame’s view of the tokens a lowering can reference: the running
origin’s table over the host’s, and the roles in front of both.
Borrowed from the core for the length of a lowering
(
crate::Core::token_lookup). - Tokens
- One origin’s declared tokens, in declaration order. Built with the
chaining constructors and handed to
crate::Core::set_tokenswhole; each call replaces the caller’s table. - Transition
- How a node’s animatable values move when the view changes them: a
duration, an easing (a timed curve or a spring), and for keyframes a
repeat direction and a delay. Built with
Transition::msand the builders, or through theNodeSpecshorthands (transition,easing,bounce,repeat,delay). - Turn
- A path’s turn (
docs/adr/0041-a-mask-turns-about-its-centre.md): how far it is turned, in turns — clockwise with y down, asPath::sectorcounts them — and the point it turns about, in the path’s own coordinates;Noneis the centre of the outline’s box. A path with a turn is boxed by the square the turn sweeps, so its mask is one mask at every angle and the quad that draws it carries the angle. - Ui
- UiEvent
- An event produced by the UI, ready for routing.
- Unresolved
- Why a derived token was dropped at declaration: the name it was given and the source or operand that resolved to no colour token declared before it and no theme role.
- Vec2
- A point or offset in logical pixels.
- Vec2
Offset - Plain offset pair (kept separate from geometry to stay
Copy+ FFI-flat). - Waker
- A handle into the event loop that any thread may hold;
Waker::wakeasks for a frame from wherever the app’s data arrived. Cheap to clone, and harmless after the loop has ended (a wake nobody hears is dropped). An app gets one inApp::setup, a host fromPumpRunner::waker. - Warning
- A silent misconfiguration the core noticed while finishing a frame.
- Window
Config - What a frame says about a window it declares (
Ui::window,Core::declare_window). Plain data, no title and no callbacks, so aWindowCommandstaysCopyand two declarations of one name compare by value. - Window
Env - What the host knows about its window, pushed into
Envby the frame driver. Views (e.g.widgets::titlebar) read this to adapt: reserve space for native controls, pick the maximize/restore glyph, or render nothing at all under native decorations. - Window
Id - Which OS window something belongs to: an opaque integer the core
assigns when it opens a window, not a handle an app builds.
WindowId::MAINis 0, the window the launcher opens, which is always live. Apps name windows with a stable string (Core::declare_window); the id is how the driver andUiEvent::windowrefer to the surface that string opened.
Enums§
- Access
Action - What assistive technology can ask of a node. Each node advertises the
subset it supports (
AccessNode::actions), and a request for one arrives ascrate::InputEvent::Access. - Align
- Where children sit along an axis, and where a float attaches.
- Appearance
- The OS light/dark setting.
Unknownis a real answer — a host with no way to ask says it, and a view that has one palette per appearance picks its own default for it rather than being handed a guess. - Assistive
- Whether assistive technology is listening: the difference between an
alert that blinks and one that announces.
Listeningis “an accessibility client has asked this window for its tree”, which is the one signal the platform adapters give and the moment the runner starts deriving trees.Noneis “the bridge is up and nobody has asked”;Unknownis “there is no bridge” — a headless core, a driver built without theaccesskitfeature, a C host that never called the setter. - Audio
Command - An audio intent for the frame driver. Durations are ms; volumes are linear amplitude. Drivers ignore playbacks they no longer hold.
- Audio
Device - The output device’s state.
Closedis the default and what a headless driver reports;Openingis the ~90 ms the open takes on its own thread;Failedis a device that refused to open, after which commands are dropped. - Bound
- What a
min_width/max_width/min_height/max_heightdeclares: px, the node’s own fit size (a min only), or a size expression that layout resolves against the parent’s content box when it sizes the node. Until then a calc bound clamps like none, as a percentage clamp does in CSS’s intrinsic sizing. - Button
Phase - Which part of a held non-primary button an event reports.
- Cell
Cursor - How the grid’s cursor is drawn, in the colour given with it.
- Chrome
- Who draws the window chrome.
- ColorOp
- One step of a derived token’s recipe, as declared: a verb and its
operands, a colour named by its token or role. Each is a method the
core already paints with —
liftanddarkenareColor::mixtoward white and black,raiseisTheme::raise(toward the front of whichever base is in effect),alphaisColor::with_alpha,mixisColor::mixtoward another token, andreadableisColor::toward_contrasttoward black or white — whichever reads on the named colour — until it clears the ratio on it. - Color
Token - A colour token: one value per base, or a recipe over an earlier token
or a theme role.
ColorToken::sameis the unthemed case, and what a declaration with one colour builds; a derived one is built byTokens::derive, since its source is an index into the table that holds it. - Content
- What a node opened through
Core::open_fromholds: a box (left open for its children), a fragment (likewise), acellsgrid or a stroke (leaves, closed by the door). - Copy
Request - What asking for a copy answered (
Core::request_copy). - Cursor
Shape - The shape the pointer takes. Spelled the way CSS and the platform
toolkits do, so a driver maps it one-to-one (winit’s
CursorIcon, the Web’scursor, GTK’s names) instead of interpreting it. - Devtools
Dock - Where the panel sits. The header has a button per placement, and
Ctrl+Shift+Dwalks them inDock::ALL’s order. - Dir
- Which way a container stacks its children.
- Dismiss
Reason - Why a window was asked to go away (
Core::dismiss_window): the same two reasons amodalnode has, one level up. - Drag
Phase - Which part of a drag an event reports.
- Easing
- Easing curve for a
Transition. - EditKey
- Editing keys, decoupled from any windowing library’s key codes.
- File
Dialog Mode - What a dialog picks.
- Fill
Rule - How a path’s inside is decided: SVG’s default, or the polygon’s rule.
- Float
Anchor - What a floating node is positioned against.
- Font
Family - Which face a text shapes with: one of the three stock families, or a font registered with the core.
- Fragment
Image - Where a fragment’s
imagerow lands for one frame: nowhere, in the glyph atlas the fragment pipeline already has bound, or in a texture of the image’s own that the backend binds in the atlas’s place for that one quad — the same swap aQuadKind::Texturequad asks for. The core decides between the last two on the image’s backing, so a backend meets the same two cases it already draws. - Grain
- What a drag-select moves by. A press sets it from the click count the driver counted, the way every text UI does: one click drags by characters, two by words, three by whole runs.
- HoverBy
- What moved to change the hover.
- Hover
Phase - Which half of a hover an event reports.
- Image
Backing - Where a registered image’s pixels are kept for drawing. The core decides on the two facts that matter — whether the image fits an atlas page, and whether its pixels were ever replaced — and the app never chooses.
- Image
Fit - The
fitrow: how the pixels meet the node’s box. The box itself — its layout, its hit region, its access rect — is the same in every mode; only what is painted inside it moves. - Input
Event - KeyCode
- A physical key press: the full keyboard, decoupled from any windowing
library.
EditKeyis the input widget’s closed navigation vocabulary; this is what apps that own their own text model bind against — an editor with modal keymaps, a game, a scripted panel. - KeyLocation
- Where on the keyboard a key sits, for the keys that have twins: the
left or right Shift, Ctrl, Alt or Super, and the keypad’s digits,
operators, Enter and (with Num Lock off) arrows beside the main
block’s. Everything else is
Standard.codestays what the key is — the keypad’s1isChar('1'), its Enter isEnter— so a keymap that does not care reads nothing new, and one that does (a terminal speaking kitty’s keyboard protocol, a game) reads this. - KeyPhase
- Which half of a key’s life an event reports. Both halves arrive as one
{kind="key"}payload — the way a drag’s three phases and a hover’s two do — so an app binds one handler and matchesphase. - Layout
Script - Which alphabet the layout a press was typed on writes, as the platform answers it: what decides whose ASCII a keymap matches.
- Live
- How urgently a reader should read a change it was not asked to read:
ARIA’s
aria-live, AccessKit’sLive. Declared on the node holding the text (liveprop) and, for a one-off with no node behind it, the politeness of anAnnouncement. - Menu
Action - What choosing an item leaves for the host to do, drained with
crate::runtime::Core::take_menu_actionsthe way window and audio commands are. - Menu
Role - What an item means, as far as anything outside the app is concerned.
- Message
Error - Why a payload is not the message it was read as.
- Motion
Pref - The OS reduce-motion setting:
Reducedis “the user asked for less animation”,Fullis “the user did not”,Unknownis “nobody asked the OS”. Spelled as what the user wants rather than as areduce_motionboolean because the third reading has no place in a boolean, and a missing answer is not the same as a “no”. - Mouse
Button - Which button a press came from — driver-facing rather than shaped after any one windowing library, so every driver maps its own vocabulary onto this one.
- Node
Kind - What kind of node a snapshot row is.
- Option
AsAlt - Which Option keys act as Alt on macOS — what a frame
declares with
crate::Ui::option_as_alt. On a Mac, Option composes: ⌥m types “µ”, and ⌥u, ⌥e, ⌥i, ⌥n and ⌥` are dead keys that start an accent and wait for the next key, so the press never arrives as a key at all and a keymap that binds<A-u>never hears it. An Option key named here is Alt instead: it composes nothing, types nothing, and every key under it arrives as a chord of the key the layout prints unmodified — what a terminal’s “Option as Meta” and an editor’s Alt bindings want.None, the default, is the Mac’s own behaviour; one side leaves the other composing, so a user keepsüon the right Option while the left one is Alt. Other platforms have no such composition on Alt and read nothing here. - Orientation
- How a container arranges its items, for the platform to announce
(
AXOrientation, UIA’sOrientation). Derived from the container’sdirand never declared: the layout is what arranges the items, so a row that says it is a column would be a fact with two owners. It is an announcement and not a gate — the arrows move both ways whatever this says — so a container whose visual arrangement does not match itsdircosts a less precise announcement rather than a dead keyboard. - Overscroll
- What a scroll gesture that starts over a scroller already at its limit
does: CSS’s
overscroll-behavior, spelled by theoverscrollrow. - PathOp
- One drawing command, every coordinate absolute, in the space the path was declared in.
- Quad
Kind - Render
Error - A frame that produced no image, and what to do about it.
- Repeat
- How keyframes cycle: CSS’s
animation-direction, always infinite. - Role
- What a node is to assistive technology. Most of these a view declares
(
roleprop;schema::ROLESis that list, and the wire order); the ones the core derives from a node’s content and behaviour instead areschema::DERIVED_ONLY, which says what derives each. Every variant is on one list or the other —schema’severy_role_is_declarable_or_derivedfails when a new one is on neither. - Sampling
- The
samplingrow: how a backend reads texels between pixel centres. - Scroll
Axes - Which axes an
on_scrollnode takes, spelled by thescrollAxesrow. - Scroll
Axis - Scrollbar
Mode - When a scrolling node’s bars are drawn. Spelled by the
scrollbarrow (crate::schema::SCROLLBARS, in this order). - Side
- Where a linear gradient runs to: a side or a corner of the box.
- Size
Expr - A parsed expression: lengths in logical px, percentages as fractions.
- Sizing
- How a node sizes one axis. A plain number converts to
Fixed, so.width(120.0)and.width(Sizing::Fixed(120.0))are the same. - TextAa
- How outline glyphs are antialiased.
- Text
Wrap - How a text node breaks lines at its width.
- Theme
Source - Where a
Core’s theme comes from, re-read at the start of every frame.Derivedis the default. - Token
Error - Why a name did not resolve.
- Token
Kind - What kind of value a token holds, and which prop slots it fits.
- Token
Ref - Where a name resolved to: a role, or an app token by index.
- Underline
Style - The shape of an underline: the face’s line, a wave under a diagnostic,
dots. Where it goes and how thick it is are the face’s
recommendation either way; a wave is three strokes tall around the
line’s centre with a six-stroke period, dots two strokes across and
four apart (
crate::deco). - Value
- A dynamically typed value: null, bool, int, float, string, list or an ordered string-keyed map. See the module docs for an example.
- Why
- Why a one-shot playback was cut off — what
diag::TRUNCATED_PLAYBACKreports once the driver confirms the sound was still running. - Window
Button - Window
Command - A window-level intent for the frame driver, drained with
Core::take_window_commandsafter each input dispatch and each frame. Three things produce one: input on a chrome node, the diff of the declared window set, and an app asking directly (Core::set_window_size,Core::focus_window,Core::push_window_command). A headless driver never drains. - Window
Kind - What kind of OS surface a declared window is.
- Window
Role - Role a node plays in window chrome (set via
NodeSpec::window_drag/NodeSpec::window_button). Chrome nodes never emitUiEvents — their interactions becomeWindowCommands for the driver instead.
Constants§
- ANY_
SLOT - The one entry in
Extension::slotsthat means “every name the host declares under my namespace”: for an extension that learns its slots after it loads.fillmatches any declared name against it andfinishhas nothing to warn about for it. - DEFAULT_
TEXT_ CACHE_ BYTES - The default byte budget for the shaped-text cache. Sized
so a screenful of code never meets it — two panes of 55 highlighted
lines are ~900 short entries, a few megabytes — and a pane streaming
new text meets it within seconds, which is when the clock alone let the
cache reach gigabytes.
Core::set_text_cache_budgetchanges it. - FLOAT_
PRESETS - The float preset names, in wire order: the index of a name here is what
the binary protocol writes for it and what
KUI_FLOAT_*counts from. - LONG_
LINE_ BYTES - A non-wrapping text at least this long is shaped in chunks: a minified bundle, a log line with a blob in it, a base64 field. Shorter text takes the path it always took, so nothing below the line moves. Bytes, not characters, for the same reason a step line carries integers: every binding can count them.
- MAX_
BOUNCE - The most bounce a spring takes: at 1 it would never settle, and past 0.9 it rings for seconds.
- MAX_
UNDECLARED_ EDITS - How many undeclared editors the store keeps before the longest
undeclared one is dropped. A declared editor is never evicted, however
many there are: retention across absence is what
text_editpromises, so this is a ceiling, not a prune. An editor holding a short line is a few KB and one holding a long line about 22 KB, so a full budget is a few MB. - MAX_
UNDECLARED_ SCROLLS - How many undeclared scroll entries the store keeps before the longest undeclared one is dropped. An entry a layout resolved in the frame that just ended is never evicted, however many there are. An entry is about 64 bytes, so a full budget is about 64 KB.
- NAMESPACE_
SEPARATOR - What separates a namespace from a slot name in a full name. A
namespace may contain it (the host chooses namespaces, and
"left/fs"is a fine one); a slot name an extension lists may not, so a full name splits at its last one. - NO_CLIP
- A clip that clips nothing.
- NO_
CLIP_ ID - The entry every frame’s clip table starts with:
Clip::NONEin physical pixels. Emission seeds it before any quad is made, so a quad that is clipped by nothing — most quads of most frames — names this without interning anything. - OVERFLOW_
CLIP overflowas bits: the C struct’s field, the binary wire’s payload and what theclip/scrollX/scrollYbooleans OR together. One set of values so a binding cannot invent its own numbering.- OVERFLOW_
SCROLL_ X - OVERFLOW_
SCROLL_ Y - ROOT_
SLOT - The reserved slot name an extension listing none fills.
Traits§
- App
- What a kui application implements: a
viewthat declares each frame and anon_eventthat answers what the frame produced. - Extension
- A frontend that draws into the shared tree each frame — the trait the runner uses to host Lua (or any other) extensions without knowing what they are. Origins are assigned by the runner.
- Fill
- What fills slots: the runner’s extension list, or a test’s stand-in.
A frame begun with
Core::frame_withcarries one;Ui::slotcallsFill::fillat the position the host declared, andUi::finishcallsFill::finishonce the host’s view is done. - Message
Field - A type a message field can hold: to a
Valueand back. Implemented for the numbers,bool,String,Valueitself,Option(absent or null isNone) andVec;#[derive(Message)]implements it for the type it derives, so one message can carry another.
Functions§
- app
- Starts a
Launcherfor a window titledtitle. - full_
name namespace/name, ornamealone when the namespace is empty.- run
app(title).extensions(extensions).run(application)in one call.- split_
name - Splits a full slot name at its last separator into (namespace, name); a name with none has the empty namespace.
Type Aliases§
- ClipId
- An index into
DisplayList::clips. Every quad has one; there is no “no clip” value, because a frame that clips nothing still names an entry —Clip::NONEscaled — and a backend that reads it needs no special case.
Derive Macros§
- Message
#[derive(Message)], with thederivefeature (kui-nativeturns it on). The generated code reaches::kui_nativeunless told otherwise, so a crate that depends on kui-core alone adds#[message(crate = "kui_core")]to the enum. DerivesFrom<Self> for Value,TryFrom<&Value>,TryFrom<Value>andMessageFieldfor an enum or a struct; see the crate docs.