Expand description
The headless model behind kui: a per-frame flat tree, flex layout, text shaping, events as data and a quad display list.
A Core owns one window’s worth of state. Each frame
the app rebuilds a flat tree of NodeSpecs through a
Ui, the core runs a clay-style flex layout over it, shapes
the text, and emits a DisplayList of quads for
a renderer to draw. Input arrives as InputEvents
and comes back out as UiEvents whose payloads are
plain-data Values. There is no window, no GPU and no
clock in this crate: a driver supplies the viewport, the input, the
time and the renderer.
Most Rust apps do not depend on kui-core directly. They use
kui-native, the windowed runner,
which re-exports all of this crate and pairs it with
kui-wgpu, the renderer. Reach for
kui-core on its own when you are writing a custom runner, a binding to
another language (kui-lua, kui-ffi and kui-node are built on it),
or headless tests that build frames and feed input without a window.
§Quick start
A frame built and clicked with no window at all:
use kui_core::{Core, InputEvent, NodeSpec, Size, TextStyle, widgets};
let mut core = Core::new();
core.set_inspect(true); // keep a readable snapshot of each finished frame
// One frame: begin, declare the tree, finish (layout and emission).
let mut ui = core.frame(Size::new(320.0, 200.0), 1.0);
ui.configure_root(NodeSpec::column().fill().pad(16.0).gap(8.0));
ui.text("Hello from kui-core", TextStyle::new(16.0));
widgets::button(&mut ui, "Save", "save");
ui.finish();
// What a renderer draws: a flat list of quads.
let (list, _atlas) = core.output();
assert!(!list.quads.is_empty());
// Input goes in as `InputEvent`s and comes out as `UiEvent`s carrying
// the payload the view declared.
let button = core
.nodes()
.into_iter()
.find(|n| n.label.as_deref() == Some("Save"))
.expect("the button was laid out");
core.handle_input(InputEvent::CursorMoved(button.rect.center()));
core.handle_input(InputEvent::mouse_down(1));
let events = core.handle_input(InputEvent::mouse_up());
let click = events
.iter()
.find(|e| e.payload.as_str() == Some("save"))
.expect("the click reached the button");
assert_eq!(click.key, button.key);A real driver repeats the frame whenever input arrives or the core asks
for one, hands the display list to a renderer, and calls
Core::set_time before each frame so
transitions can run.
§Where to look
ui::Ui: the frame builder.open/close,with,leaf,text, and the readbacks a view needs (is_hovered,focused,theme).runtime::Core: the per-window state.frame,handle_input,output,set_time, fonts and images, focus and scrolling.spec::NodeSpecandspec::TextStyle: everything a node and a text declare, as plain data with a builder.layout: the flex solver, and what each sizing means.text: shaping, wrapping, measuring and the glyph atlas.inputandinput::UiEvent: what goes in and what comes out;eventhas typed readings of the core’s own event payloads.value::Value: the payload type, andmessagefor typed messages over it.display::DisplayList: the renderer boundary.widgets: buttons, toggles, inputs, menus and virtual lists built from the primitives.theme,metricsandtokens: the colours, sizes and named values a view paints with.session: fonts, images and sounds shared between windows.
§Features
devtools(default): the inspector panel the core can draw into any app’s frame (Core::set_devtools, orKUI_DEVTOOLS=1).derive: re-exports#[derive(Message)]fromkui-derive, a typed Rust enum to and from the payload map.conformance: the scene corpus and the headless test driver (testing). Test infrastructure, off in every shipped binary.
§Links
- The book: https://kui-book.qxuken.dev
- The repository: https://github.com/qxuken/kui (design records live
under
docs/adrthere)
Re-exports§
pub use access::AccessAction;pub use access::AccessNode;pub use access::AccessRequest;pub use access::AccessRun;pub use access::AccessTree;pub use access::Announcement;pub use access::Live;pub use access::Orientation;pub use access::Role;pub use access::ScrollState;pub use access::TextPos;pub use anim::Bounce;pub use anim::Easing;pub use anim::MAX_BOUNCE;pub use anim::Repeat;pub use anim::Transition;pub use audio::AudioCommand;pub use audio::AudioSpec;pub use audio::AudioStore;pub use audio::PlayOptions;pub use audio::PlaybackId;pub use audio::Why;pub use calc::Calc;pub use calc::Expr as SizeExpr;pub use cells::Cell;pub use cells::CellGrid;pub use cells::CellStore;pub use cells::CellsId;pub use cells::CursorShape as CellCursor;pub use color::Color;pub use cursor::CursorShape;pub use depart::DepartStore;pub use diag::Warning;pub use dialog::FileDialog;pub use dialog::FileDialogMode;pub use dialog::FileFilter;pub use display::Clip;pub use display::ClipId;pub use display::DisplayList;pub use display::FragmentDraw;pub use display::FragmentImage;pub use display::NO_CLIP;pub use display::NO_CLIP_ID;pub use display::Quad;pub use display::QuadKind;pub use edit::EditOptions;pub use edit::MAX_UNDECLARED_EDITS;pub use enter::Enter;pub use env::Appearance;pub use env::Assistive;pub use env::AudioDevice;pub use env::AudioEnv;pub use env::Env;pub use env::Locale;pub use env::MotionPref;pub use env::SystemEnv;pub use event::ButtonEvent;pub use event::ButtonPhase;pub use event::Drag;pub use event::DragPhase;pub use event::Hover;pub use event::HoverBy;pub use event::HoverPhase;pub use event::Layout;pub use event::Scroll;pub use event::TextInput;pub use fragment::FragmentDrawId;pub use fragment::FragmentList;pub use fragment::FragmentRef;pub use geom::Edges;pub use geom::Rect;pub use geom::Size;pub use geom::Vec2;pub use gradient::Gradient;pub use gradient::Side;pub use gradient::Stop as GradientStop;pub use input::ScrollAxis;pub use input::Buttons;pub use input::ClipboardMarks;pub use input::EditKey;pub use input::InputEvent;pub use input::KeyCode;pub use input::KeyLocation;pub use input::KeyLocks;pub use input::KeyMods;pub use input::KeyPhase;pub use input::KeyPress;pub use input::LayoutScript;pub use input::Mods;pub use input::MouseButton;pub use input::OptionAsAlt;pub use input::UiEvent;pub use key::Key;pub use keyframes::Keyframe;pub use line::Dash;pub use line::LineId;pub use line::LineStore;pub use line::Stroke;pub use menu::Accel;pub use menu::BarMenu;pub use menu::Menu;pub use menu::MenuAction;pub use menu::MenuBar;pub use menu::MenuItem;pub use menu::MenuRole;pub use message::MessageError;pub use message::MessageField;pub use metrics::Metrics;pub use path::FillRule;pub use path::Path;pub use path::PathError;pub use path::PathId;pub use path::PathOp;pub use path::PathStore;pub use path::Turn;pub use resources::FontId;pub use resources::FragmentId;pub use resources::ImageBacking;pub use resources::ImageFit;pub use resources::ImageId;pub use resources::ImageOpts;pub use resources::Resources;pub use resources::Sampling;pub use resources::SessionId;pub use resources::SoundId;pub use resources::SystemFont;pub use runtime::cause::FrameCause;pub use runtime::cause::FrameHolder;pub use runtime::cause::FrameRequest;pub use runtime::cause::OwedBy;pub use runtime::devtools;pub use runtime::devtools::Dock as DevtoolsDock;pub use runtime::inspect::NodeInfo;pub use runtime::inspect::NodeKind;pub use runtime::Content;pub use runtime::Core;pub use runtime::Extension;pub use runtime::Owed;pub use scroll::MAX_UNDECLARED_SCROLLS;pub use scroll::ScrollGeometry;pub use select::CellEnd;pub use select::CellSelection;pub use select::CopyRequest;pub use select::Endpoint;pub use select::Grain;pub use select::RangeEnd;pub use select::Selection;pub use session::Session;pub use slot::ANY_SLOT;pub use slot::Extensions;pub use slot::Fill;pub use slot::NAMESPACE_SEPARATOR;pub use slot::ROOT_SLOT;pub use slot::Slot;pub use slot::full_name;pub use slot::split_name;pub use spec::Align;pub use spec::Bound;pub use spec::Dir;pub use spec::FLOAT_PRESETS;pub use spec::FloatAnchor;pub use spec::FloatConfig;pub use spec::FontFamily;pub use spec::FontFeatures;pub use spec::Min;pub use spec::NodeSpec;pub use spec::OVERFLOW_CLIP;pub use spec::OVERFLOW_SCROLL_X;pub use spec::OVERFLOW_SCROLL_Y;pub use spec::Overscroll;pub use spec::PadShorthand;pub use spec::ScrollAxes;pub use spec::Scrollbar;pub use spec::ScrollbarMode;pub use spec::Shadow;pub use spec::Sizing;pub use spec::TextStyle;pub use spec::TextWrap;pub use spec::UnderlineStyle;pub use spec::Vec2Offset;pub use spec::corner;pub use stats::FrameSample;pub use stats::FrameStats;pub use text::DEFAULT_TEXT_CACHE_BYTES;pub use text::LONG_LINE_BYTES;pub use text::Span;pub use text::TextHit;pub use text::TextMetrics;pub use theme::Theme;pub use theme::ThemeSource;pub use tokens::ColorOp;pub use tokens::ColorToken;pub use tokens::NameRefs;pub use tokens::OpRange;pub use tokens::TokenError;pub use tokens::TokenKind;pub use tokens::TokenLookup;pub use tokens::TokenRef;pub use tokens::Tokens;pub use tokens::Unresolved;pub use tree::OriginId;pub use ui::Ui;pub use value::Handles;pub use value::Value;pub use window::Backdrop;pub use window::DismissReason;pub use window::WindowButton;pub use window::WindowCommand;pub use window::WindowConfig;pub use window::WindowEnv;pub use window::WindowId;pub use window::WindowKind;pub use window::WindowRole;
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
- Audio as data: sounds are session resources, and playing one is a command the frame driver drains and applies to the device.
- 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. - 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.