kui_core/lib.rs
1//! The headless model behind kui: a per-frame flat tree, flex layout, text shaping, events as data and a quad display list.
2//!
3//! A [`Core`] owns one window's worth of state. Each frame
4//! the app rebuilds a flat tree of [`NodeSpec`]s through a
5//! [`Ui`], the core runs a clay-style flex layout over it, shapes
6//! the text, and emits a [`DisplayList`] of quads for
7//! a renderer to draw. Input arrives as [`InputEvent`]s
8//! and comes back out as [`UiEvent`]s whose payloads are
9//! plain-data [`Value`]s. There is no window, no GPU and no
10//! clock in this crate: a driver supplies the viewport, the input, the
11//! time and the renderer.
12//!
13//! Most Rust apps do not depend on `kui-core` directly. They use
14//! [`kui-native`](https://crates.io/crates/kui-native), the windowed runner,
15//! which re-exports all of this crate and pairs it with
16//! [`kui-wgpu`](https://crates.io/crates/kui-wgpu), the renderer. Reach for
17//! `kui-core` on its own when you are writing a custom runner, a binding to
18//! another language (`kui-lua`, `kui-ffi` and `kui-node` are built on it),
19//! or headless tests that build frames and feed input without a window.
20//!
21//! # Quick start
22//!
23//! A frame built and clicked with no window at all:
24//!
25//! ```rust
26//! use kui_core::{Core, InputEvent, NodeSpec, Size, TextStyle, widgets};
27//!
28//! let mut core = Core::new();
29//! core.set_inspect(true); // keep a readable snapshot of each finished frame
30//!
31//! // One frame: begin, declare the tree, finish (layout and emission).
32//! let mut ui = core.frame(Size::new(320.0, 200.0), 1.0);
33//! ui.configure_root(NodeSpec::column().fill().pad(16.0).gap(8.0));
34//! ui.text("Hello from kui-core", TextStyle::new(16.0));
35//! widgets::button(&mut ui, "Save", "save");
36//! ui.finish();
37//!
38//! // What a renderer draws: a flat list of quads.
39//! let (list, _atlas) = core.output();
40//! assert!(!list.quads.is_empty());
41//!
42//! // Input goes in as `InputEvent`s and comes out as `UiEvent`s carrying
43//! // the payload the view declared.
44//! let button = core
45//! .nodes()
46//! .into_iter()
47//! .find(|n| n.label.as_deref() == Some("Save"))
48//! .expect("the button was laid out");
49//! core.handle_input(InputEvent::CursorMoved(button.rect.center()));
50//! core.handle_input(InputEvent::mouse_down(1));
51//! let events = core.handle_input(InputEvent::mouse_up());
52//! let click = events
53//! .iter()
54//! .find(|e| e.payload.as_str() == Some("save"))
55//! .expect("the click reached the button");
56//! assert_eq!(click.key, button.key);
57//! ```
58//!
59//! A real driver repeats the frame whenever input arrives or the core asks
60//! for one, hands the display list to a renderer, and calls
61//! [`Core::set_time`](runtime::Core::set_time) before each frame so
62//! transitions can run.
63//!
64//! # Where to look
65//!
66//! - [`ui::Ui`]: the frame builder. `open`/`close`, `with`, `leaf`, `text`,
67//! and the readbacks a view needs (`is_hovered`, `focused`, `theme`).
68//! - [`runtime::Core`]: the per-window state. `frame`, `handle_input`,
69//! `output`, `set_time`, fonts and images, focus and scrolling.
70//! - [`spec::NodeSpec`] and [`spec::TextStyle`]: everything a node and a
71//! text declare, as plain data with a builder.
72//! - [`layout`]: the flex solver, and what each sizing means.
73//! - [`text`]: shaping, wrapping, measuring and the glyph atlas.
74//! - [`input`] and [`input::UiEvent`]: what goes in and what comes out;
75//! [`event`] has typed readings of the core's own event payloads.
76//! - [`value::Value`]: the payload type, and [`message`] for typed messages
77//! over it.
78//! - [`display::DisplayList`]: the renderer boundary.
79//! - [`widgets`]: buttons, toggles, inputs, menus and virtual lists built
80//! from the primitives.
81//! - [`theme`], [`metrics`] and [`tokens`]: the colours, sizes and named
82//! values a view paints with.
83//! - [`session`]: fonts, images and sounds shared between windows.
84//!
85//! # Features
86//!
87//! - `devtools` (default): the inspector panel the core can draw into any
88//! app's frame (`Core::set_devtools`, or `KUI_DEVTOOLS=1`).
89//! - `derive`: re-exports `#[derive(Message)]` from `kui-derive`, a typed
90//! Rust enum to and from the payload map.
91//! - `conformance`: the scene corpus and the headless test driver
92//! (`testing`). Test infrastructure, off in every shipped binary.
93//!
94//! # Links
95//!
96//! - The book: <https://kui-book.qxuken.dev>
97//! - The repository: <https://github.com/qxuken/kui> (design records live
98//! under `docs/adr` there)
99
100pub mod access;
101pub mod anim;
102pub mod atlas;
103pub mod audio;
104pub mod calc;
105pub mod cells;
106pub mod color;
107pub(crate) mod composite;
108/// The scene corpus every binding is checked against. Test infrastructure,
109/// behind the `conformance` feature so no shipped binary carries it.
110#[cfg(feature = "conformance")]
111pub mod conformance;
112pub mod cursor;
113pub mod deco;
114pub mod depart;
115pub mod diag;
116pub mod dialog;
117pub mod display;
118pub mod edit;
119pub mod enter;
120pub mod env;
121pub mod event;
122pub mod fragment;
123pub mod geom;
124pub mod gradient;
125pub mod input;
126pub(crate) mod join;
127pub mod key;
128pub mod keyframes;
129pub mod layout;
130pub mod line;
131pub mod menu;
132pub mod message;
133pub mod metrics;
134pub mod path;
135pub mod resources;
136pub(crate) mod retain;
137pub mod runtime;
138pub mod schema;
139pub mod scroll;
140pub mod select;
141pub mod session;
142pub mod slider;
143pub mod slot;
144pub mod slots;
145pub mod spec;
146pub mod stats;
147/// The headless driver the crate's own tests use. Test infrastructure,
148/// behind the `conformance` feature like the corpus.
149#[cfg(feature = "conformance")]
150pub mod testing;
151pub mod text;
152pub mod theme;
153pub mod tokens;
154pub mod tree;
155pub mod ui;
156pub mod value;
157pub(crate) mod weights;
158pub mod widgets;
159pub mod window;
160
161pub use access::{
162 AccessAction, AccessNode, AccessRequest, AccessRun, AccessTree, Announcement, Live,
163 Orientation, Role, ScrollState, TextPos,
164};
165pub use anim::{Bounce, Easing, MAX_BOUNCE, Repeat, Transition};
166pub use audio::{AudioCommand, AudioSpec, AudioStore, PlayOptions, PlaybackId, Why};
167pub use calc::{Calc, Expr as SizeExpr};
168pub use cells::{Cell, CellGrid, CellStore, CellsId, CursorShape as CellCursor};
169pub use color::Color;
170pub use cursor::CursorShape;
171pub use depart::DepartStore;
172pub use diag::Warning;
173pub use dialog::{FileDialog, FileDialogMode, FileFilter};
174pub use display::{
175 Clip, ClipId, DisplayList, FragmentDraw, FragmentImage, NO_CLIP, NO_CLIP_ID, Quad, QuadKind,
176};
177pub use edit::{EditOptions, MAX_UNDECLARED_EDITS};
178pub use enter::Enter;
179pub use env::{Appearance, Assistive, AudioDevice, AudioEnv, Env, Locale, MotionPref, SystemEnv};
180pub use event::{
181 ButtonEvent, ButtonPhase, Drag, DragPhase, Hover, HoverBy, HoverPhase, Layout, Scroll,
182 TextInput,
183};
184pub use fragment::{FragmentDrawId, FragmentList, FragmentRef};
185pub use geom::{Edges, Rect, Size, Vec2};
186pub use gradient::{Gradient, Side, Stop as GradientStop};
187pub use input::ScrollAxis;
188pub use input::{
189 Buttons, ClipboardMarks, EditKey, InputEvent, KeyCode, KeyLocation, KeyLocks, KeyMods,
190 KeyPhase, KeyPress, LayoutScript, Mods, MouseButton, OptionAsAlt, UiEvent,
191};
192pub use key::Key;
193pub use keyframes::Keyframe;
194/// `#[derive(Message)]`, with the `derive` feature (`kui-native` turns it
195/// on). The generated code reaches `::kui_native` unless told otherwise,
196/// so a crate that depends on kui-core alone adds
197/// `#[message(crate = "kui_core")]` to the enum.
198#[cfg(feature = "derive")]
199pub use kui_derive::Message;
200pub use line::{Dash, LineId, LineStore, Stroke};
201pub use menu::{Accel, BarMenu, Menu, MenuAction, MenuBar, MenuItem, MenuRole};
202pub use message::{MessageError, MessageField};
203pub use metrics::Metrics;
204pub use path::{FillRule, Path, PathError, PathId, PathOp, PathStore, Turn};
205pub use resources::{
206 FontId, FragmentId, ImageBacking, ImageFit, ImageId, ImageOpts, Resources, Sampling, SessionId,
207 SoundId, SystemFont,
208};
209pub use runtime::cause::{FrameCause, FrameHolder, FrameRequest, OwedBy};
210pub use runtime::devtools;
211pub use runtime::devtools::Dock as DevtoolsDock;
212pub use runtime::inspect::{NodeInfo, NodeKind};
213pub use runtime::{Content, Core, Extension, Owed};
214pub use scroll::{MAX_UNDECLARED_SCROLLS, ScrollGeometry};
215pub use select::{CellEnd, CellSelection, CopyRequest, Endpoint, Grain, RangeEnd, Selection};
216pub use session::{Session, SharedAudio, SharedResources};
217pub use slot::{
218 ANY_SLOT, Extensions, Fill, NAMESPACE_SEPARATOR, ROOT_SLOT, Slot, full_name, split_name,
219};
220pub use spec::{
221 Align, Bound, Dir, FLOAT_PRESETS, FloatAnchor, FloatConfig, FontFamily, FontFeatures, Min,
222 NodeSpec, OVERFLOW_CLIP, OVERFLOW_SCROLL_X, OVERFLOW_SCROLL_Y, Overscroll, PadShorthand,
223 ScrollAxes, Scrollbar, ScrollbarMode, Shadow, Sizing, TextStyle, TextWrap, UnderlineStyle,
224 Vec2Offset, corner,
225};
226pub use stats::{FrameSample, FrameStats};
227pub use text::{DEFAULT_TEXT_CACHE_BYTES, LONG_LINE_BYTES, Span, TextHit, TextMetrics};
228pub use theme::{Theme, ThemeSource};
229pub use tokens::{
230 ColorOp, ColorToken, NameRefs, OpRange, TokenError, TokenKind, TokenLookup, TokenRef, Tokens,
231 Unresolved,
232};
233pub use tree::OriginId;
234pub use ui::Ui;
235pub use value::{Handles, Value};
236pub use window::{
237 Backdrop, DismissReason, WindowButton, WindowCommand, WindowConfig, WindowEnv, WindowId,
238 WindowKind, WindowRole,
239};