Expand description
Stock widgets built from the primitives: buttons, toggles, text input, select, slider, splitter, tooltips, menus, a titlebar and virtual lists.
Every widget here is a plain function over a Ui that opens ordinary
nodes with ordinary NodeSpecs; there is no widget trait and no
retained object. State lives in the core by key (focus, hover, an edit
buffer, a scroll offset), and the app’s model is the only other state.
A custom widget follows the same pattern, and the *_spec functions
(button_spec, toggle_spec, slider_spec, menu_panel_spec)
are the starting points for one that should look like the stock set.
use kui_core::{Core, NodeSpec, Size, Value, widgets};
let mut core = Core::new();
let mut ui = core.frame(Size::new(400.0, 300.0), 1.0);
ui.configure_root(NodeSpec::column().fill().pad(12.0).gap(8.0));
widgets::label(&mut ui, "Settings");
let name = widgets::text_input(&mut ui, "name", "Ada");
widgets::checkbox(&mut ui, "Dark mode", true, "toggle-dark");
widgets::slider(&mut ui, "volume", 40.0, 0.0, 100.0, 1.0, "volume");
widgets::button(&mut ui, "Save", Value::str("save"));
assert_eq!(ui.edit_text(name).as_deref(), Some("Ada"));
ui.finish();Each control posts the payload it was given as a
UiEvent when it is used, and the view redraws
from its model; a checkbox does not flip itself.
Structs§
- List
Plan - What a
ListSlicecomes to: the rows to build, the two spacers’ heights, and the scroll correction the frame needs (seelist). - List
Reading - What a variable-height list reads before it slices: the container’s
last layout, where its scroll is going, the window, and the padding
its rows sit inside.
listtakes it from the frame (Self::of); a binding builds it from the same readings its view already has (scrollGeometry,scrollOffset, the viewport), so the arithmetic after it is this module’s in every language. - List
Slice - One frame’s slicing of a variable-height list, between the reading and
the rows: which rows to measure, and — once they are — where the window
lands and what to build. Made by
RowHeights::slice; seelistfor the loop that drives it, which every binding’s port repeats. - RowHeights
- The heights a
listslices by: a measured number per row where one is known, an estimate everywhere else, and the prefix sums over both.
Enums§
- Toggle
- Which toggle a
toggle_withdraws.
Constants§
- BUTTON_
DISABLED_ OPACITY - What a disabled stock button’s opacity is multiplied by. The core makes it inert and drops its hover and pressed backgrounds, and nothing else would show a sighted user the state a reader is told.
- BUTTON_
TEXT - The stock button’s text size —
Metrics::default’scontrol_text; the widget itself readsui.metrics(). - MENU_
ACCEL_ GAP - The least room between a row’s label and its accelerator, logical px, beside the row’s own gap on either side: about what AppKit leaves before a key equivalent.
- MENU_
BAR_ H - The bar’s height, logical px — a little under a titlebar’s, which is what every platform that draws one in the window does.
- MENU_
BAR_ KEY - The reserved label the drawn menu bar is keyed under, the way
MENU_KEYis the open menu’s. - MENU_
CHEVRON - The chevron a submenu’s row draws where an accelerator would be.
- MENU_
KEY - The reserved label the stock menu is keyed under. A menu the core opened is found by key, not by guessing at payloads, so an app is free to post whatever it likes from its own items.
- MENU_
TEXT - MENU_
WIDTH - Menu chrome, in one place so a native renderer’s absence still looks deliberate rather than improvised.
- TITLEBAR_
H - Default titlebar height, logical px, where the strip is the app’s
alone. Follows platform conventions (as measured by gpui): 32 on
Windows (the native caption height), 34 elsewhere. The stock
Metricscarries the same number astitlebar_h, and the titlebar draws from that, so an app that set its own metrics lays out againstui.metrics().titlebar_hrather than this constant — and where the OS keeps controls of its own over the strip, againsttitlebar_height.
Functions§
- button
- A push button showing
text, keyed by it; a click postspayloadas aUiEventon the button’s key. - button_
indexed button_withkeyed by a data index rather than a label — a row of a virtual list (Ui::open_indexed), so the button keeps its focus, its hover and its tweens as the built range slides and the same text on two rows is two nodes. What<button index>andbutton { index = }lower to.- button_
palette - A button’s three backgrounds from one base colour: the base, a hover a step toward white, a pressed a step toward black. The steps are the distances the stock button’s own trio sits at, so an accent-painted button reads as the same control in a different colour.
- button_
spec - The standard button’s spec: hover and pressed backgrounds are declared on the node and resolved by the core, so every binding’s button is this same data. Add the label as a child.
- button_
with buttonwith its spec in the caller’s hands:specisbutton_specplus what the caller declared on it — theon_click, and the rows the stock button admits in every binding (schema::BUTTON_ROWS_JSX): alabelwhen the text is not the name, adescription,disabled, and the hover tracking and description atooltipsets, whose float ishint— drawn under the button while it is hovered, as every binding’stooltipprop floats one. Keyed bykey, so a label that changes need not re-key the node. A disabled button is dimmed (BUTTON_DISABLED_OPACITY) as well as inert.- checkbox
- A checkbox labelled
text, keyed by it, drawn fromchecked; a press — pointer, Space, Enter or assistive technology — postspayload, and the view flips its model and draws it again. - context_
menu - Draws a context menu at
at(logical viewport px) and returns the key of its root. A float anchored to the viewport rather than to a parent, because a context menu belongs at the pointer and not under whatever node happens to enclose it;fitis what keeps it in the window, which for a menu near the bottom edge means flipping above the point. - control_
box - The side of a stock control’s box — a checkbox, a radio’s circle, a
switch’s height, a slider’s thumb — from the metrics’ control text, so
compactandscaledmove it with the stock button: 16 px at the comfortable density, 14 at the compact one. - label
- A line of text in the default style:
ui.text(text, TextStyle::default()). - latency_
graph - Frame-latency graph: the last ~120 frames as stacked per-phase bars
(input / view / layout / render, bottom to top) against the display’s
frame budget (
env.refresh_hz, 120 Hz fallback) — a bar that blows the budget turns red. Feedcore.stats(andcore.env) from your frame driver (the built-in runner does this automatically). - latency_
hud - Floating latency HUD:
latency_graphin a translucent panel pinned to a viewport corner, above all content and out of layout flow. Call anywhere in the view; pick the corner withlatency_hud_at. - latency_
hud_ at - list
- A vertically scrolling column of rows of different heights that builds
only the visible ones —
uniform_listwhere no single stride describes the list. - menu_
bar - The application menu:
baris what the app’s menu is, and calling this is where its titles go when they have to be drawn in the window. - menu_
panel - Builds the rows of one menu into
spec, keyed underlabel, and reports the keys they took. The one place a menu’s rows are drawn: both menus kui has are this function with a different container. - menu_
panel_ spec - The panel every menu is: a fixed-width column of rows, in the palette
the stock menu paints. What the caller adds is where it goes and what
scope it belongs to — a context menu floats at the pointer and declares
its own
modal; the menu bar’s drops out of its title and lives inside the bar’s. Takes the palette and the metrics rather than reading them, because a caller that has aUiin one hand cannot lend it to this and tomenu_panelin the same expression;let t = ui.theme();first is the idiom. - radio
- A radio labelled
text, keyed by it; seecheckbox. Radios belong in aradio_group_with, whose arrows move the choice. - radio_
group - A radio group over named options:
currentis the one in force, and a choice postspayload(i). Each radio is keyed by its index, so two options with one label are two radios. - radio_
group_ open_ spec - The spec a radio group named
labelopens with:specwith the group’s role and name, and the stock gap where it has none. Whatradio_group_withopens, and what C’skui_radio_group_opendoes, whose radios are declared between it andkui_close. - radio_
group_ spec - The stock radio group’s spec: a column of radios. What
radio_group_withis handed byradio_group. - radio_
group_ with - A radio group named
label: one Tab stop whose arrows, Home and End move the choice among the radiosfdeclares and press the one they land on, so a group of radios whose payloads each set the choice answers the keyboard with no more code. The role and the name are the group’s whateverspecsaid; arowspec lays the radios out across, and its arrows run across with it. A spec with no gap takesradio_group_spec’s, so a binding that built the spec from its rows — wheredir="row"starts one from nothing — gets the stock spacing without restating it. - readable_
on - Black or white, whichever a reader can see on
bg. - reveal_
row - Scrolls the
uniform_listlabelledlabelso rowishows, when it does not already: to the middle of the list, so a jump lands with rows on both sides of it. Call it before the list is declared, in the same parent — the frame that scrolls then slices its rows by the offset it scrolls to, instead of a frame late. Returns whether it scrolled. The first frame, before the list has laid out, has no geometry and scrolls nothing; the row arithmetic assumes the list’s rows start at its content top and fill its box, as they do without padding. A row past the list’s content — an index past its end — scrolls nothing and answers false, as does arow_hthat is not positive. - rows_
in_ view - How many whole rows of
row_htheuniform_listlabelledlabelshows as of the last layout — a PageDown’s stride. 0 before it has laid out, and for arow_hthat is not positive. - select
- A choice among a few named options: a field that shows the one in
force and, clicked, drops a menu of them all with the current one
checked.
optionsare the labels,currentthe index in force (or none). Keyed bylabel, which is the accessible name too. - select_
items selectover items the caller built: their labels are the rows, theirids what a choice posts, and thecurrentth is drawn checked whatever the item said. A separator is a separator here too.- select_
spec - The stock select field’s spec: a sunken field with the stock radius
and padding, as
button_specis the stock button’s. Whatselect_withis handed byselect_items; a caller with a spec of its own starts here and adds to it. - select_
with select_itemswith its spec and text style in the caller’s hands — a compact field in a dense panel — the waybutton_withtakes the button’s. The border, the click, the role and the disclosure are added here whateverspecsaid.- slider
- A slider named
labelovermin..=max, atvalue, moving bystep. Its changes arrive as{kind: "change", value, phase, tag}withtag— from the pointer, the arrows, the Page keys, Home / End and assistive technology alike — and the view storesvalueand draws the slider again at it. - slider_
spec - The stock slider’s spec: a row as wide as a menu and as tall as its thumb, padded by half the thumb on either side so the thumb’s centre is under the pointer at both ends — the content box is the track the core reads a press along. A caller sizing its own slider changes the width and keeps the padding.
- slider_
with - A slider with its spec in the caller’s hands:
slider_specplus the value rows (value_now,value_min,value_max,value_step,value_text),on_change,description,disabled, a width, andhint, the tooltip drawn while it is hovered. Keyed bylabel, which is its accessible name unless the spec carries alabelof its own. The role is the slider’s whatever the spec said. What<slider>and its Lua and C doors lower to. - splitter
- A divider between two panes that the pointer drags:
thicknesspx across, growing along the rest of its parent, in the theme’s border colour and its accent while hovered or held, with the resize arrows, andtagas itson_drag.diris the parent’s: in aDir::Rowthe panes sit side by side and the bar stands between them; in aDir::Columnit lies across. A press on it leaves the keyboard where it was (keep_focus), as a divider beside an editor should. - switch
- A switch labelled
text, keyed by it; seecheckbox. - text_
input - Single-line text input with chrome (background, focus ring).
Read the value with
ui.edit_text(key); “changed”/“submit” events arrive inon_eventwith this key. Thelabelis the key and the accessible name both ("search","name"), so a screen reader has something to announce; useui.text_editwithNodeSpec::labelwhen they differ. - titlebar
- A cross-platform titlebar: a full-width drag strip with the window title
left-aligned next to the window controls. Reads
env.windowand adapts by itself — under macOS custom chrome it insets past the native traffic lights and draws no buttons; under custom chrome elsewhere it appends minimize/maximize/close; under native decorations it is just a drag strip (no duplicate buttons). - titlebar_
height - The height the titlebar strip draws at — what an app laying out its
own strip, or something under it, should read instead of
ui.metrics().titlebar_h. Where the OS keeps controls of its own over the strip (env.window.native_controls: the macOS traffic lights under custom chrome) the strip is the OS’s own titlebar, as tall as the keep-out rect says that titlebar is, so the strip’s content centres on the buttons the OS centred in it. Everywhere else the strip is the app’s alone andMetrics::titlebar_his its height. A keep-out with no height (a host that reported a width only) falls back to the metric. - titlebar_
with - Titlebar with custom content (tabs, a search box, …) between the platform inset and the window buttons. The whole strip is a drag handle; interactive children declared inside it sit on top and win hit-testing, so buttons in a titlebar just work.
- toggle_
spec - A stock toggle’s spec — the row its indicator and label sit in — as
button_specis the button’s. A caller with a spec of its own starts here and adds the state (checked,mixed), theon_clickand the access rows to it. - toggle_
with - A toggle with its spec in the caller’s hands, the way
button_withtakes the button’s:specistoggle_specplus the state and the rows the element admits —checked,mixed(a checkbox’s third state),on_click,label,description,disabled, andhint, the tooltip drawn while it is hovered. The role iskind’s whatever the spec said. Keyed bykey; an emptytextdraws the indicator alone, which then wants alabel. A disabled toggle is dimmed as well as inert. This is what<checkbox>,<radio>,<switch>and their Lua and C doors lower to. - tooltip
- Small floating label hanging below the node it’s declared inside. The
placement is dynamic (
FloatConfig::fit): it flips above when the viewport bottom is too close and slides sideways off window edges. Typical use:if ui.is_hovered(key) { widgets::tooltip(ui, "..."); } - tooltip_
with tooltipchrome around arbitrary content (legends, shortcut hints, …).- uniform_
list - A vertically scrolling column of
rowsuniform rows that builds only the visible ones.row(ui, i)declares rowi; it must come out exactlyrow_hlogical px tall, since that is the arithmetic placing every row above and below it. - uniform_
list_ with uniform_listwith each row’s own node spelled byrow_spec(i)— the click, the zebra stripe, the hover background, the role a row carries — where the plain form’s rows are bare and the callback nests a second node inside each to carry them. The height is forced torow_h, the stride the arithmetic assumes, and a width the spec leavesfitgrows across the list.- visible_
rows - The half-open range of rows a container of
rowsrows, eachrow_hlogical px tall, has any reason to build — those crossing the visible band, plusoverscanon each side — given the geometry of the frame before. Pure arithmetic, exposed for views that build their own container instead of usinguniform_list. - window_
buttons - The minimize/maximize/close cluster. Renders nothing when the OS already
provides controls (native decorations, or macOS traffic lights), so it
is always safe to call. It grows to the height it is given — the
strip’s, in
titlebar_with— and is a titlebar tall where nothing gives it one, since a grow child adds nothing to a fit parent’s height.