Skip to main content

Module widgets

Module widgets 

Source
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§

ListPlan
What a ListSlice comes to: the rows to build, the two spacers’ heights, and the scroll correction the frame needs (see list).
ListReading
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. list takes 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.
ListSlice
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; see list for the loop that drives it, which every binding’s port repeats.
RowHeights
The heights a list slices 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_with draws.

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’s control_text; the widget itself reads ui.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_KEY is 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 Metrics carries the same number as titlebar_h, and the titlebar draws from that, so an app that set its own metrics lays out against ui.metrics().titlebar_h rather than this constant — and where the OS keeps controls of its own over the strip, against titlebar_height.

Functions§

button
A push button showing text, keyed by it; a click posts payload as a UiEvent on the button’s key.
button_indexed
button_with keyed 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> and button { 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
button with its spec in the caller’s hands: spec is button_spec plus what the caller declared on it — the on_click, and the rows the stock button admits in every binding (schema::BUTTON_ROWS_JSX): a label when the text is not the name, a description, disabled, and the hover tracking and description a tooltip sets, whose float is hint — drawn under the button while it is hovered, as every binding’s tooltip prop floats one. Keyed by key, 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 from checked; a press — pointer, Space, Enter or assistive technology — posts payload, 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; fit is 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 compact and scaled move 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. Feed core.stats (and core.env) from your frame driver (the built-in runner does this automatically).
latency_hud
Floating latency HUD: latency_graph in a translucent panel pinned to a viewport corner, above all content and out of layout flow. Call anywhere in the view; pick the corner with latency_hud_at.
latency_hud_at
list
A vertically scrolling column of rows of different heights that builds only the visible ones — uniform_list where no single stride describes the list.
menu_bar
The application menu: bar is 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 under label, 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 a Ui in one hand cannot lend it to this and to menu_panel in the same expression; let t = ui.theme(); first is the idiom.
radio
A radio labelled text, keyed by it; see checkbox. Radios belong in a radio_group_with, whose arrows move the choice.
radio_group
A radio group over named options: current is the one in force, and a choice posts payload(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 label opens with: spec with the group’s role and name, and the stock gap where it has none. What radio_group_with opens, and what C’s kui_radio_group_open does, whose radios are declared between it and kui_close.
radio_group_spec
The stock radio group’s spec: a column of radios. What radio_group_with is handed by radio_group.
radio_group_with
A radio group named label: one Tab stop whose arrows, Home and End move the choice among the radios f declares 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 whatever spec said; a row spec lays the radios out across, and its arrows run across with it. A spec with no gap takes radio_group_spec’s, so a binding that built the spec from its rows — where dir="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_list labelled label so row i shows, 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 a row_h that is not positive.
rows_in_view
How many whole rows of row_h the uniform_list labelled label shows as of the last layout — a PageDown’s stride. 0 before it has laid out, and for a row_h that 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. options are the labels, current the index in force (or none). Keyed by label, which is the accessible name too.
select_items
select over items the caller built: their labels are the rows, their ids what a choice posts, and the currentth 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_spec is the stock button’s. What select_with is handed by select_items; a caller with a spec of its own starts here and adds to it.
select_with
select_items with its spec and text style in the caller’s hands — a compact field in a dense panel — the way button_with takes the button’s. The border, the click, the role and the disclosure are added here whatever spec said.
slider
A slider named label over min..=max, at value, moving by step. Its changes arrive as {kind: "change", value, phase, tag} with tag — from the pointer, the arrows, the Page keys, Home / End and assistive technology alike — and the view stores value and 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_spec plus the value rows (value_now, value_min, value_max, value_step, value_text), on_change, description, disabled, a width, and hint, the tooltip drawn while it is hovered. Keyed by label, which is its accessible name unless the spec carries a label of 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: thickness px 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, and tag as its on_drag. dir is the parent’s: in a Dir::Row the panes sit side by side and the bar stands between them; in a Dir::Column it 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; see checkbox.
text_input
Single-line text input with chrome (background, focus ring). Read the value with ui.edit_text(key); “changed”/“submit” events arrive in on_event with this key. The label is the key and the accessible name both ("search", "name"), so a screen reader has something to announce; use ui.text_edit with NodeSpec::label when 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.window and 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 and Metrics::titlebar_h is 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_spec is the button’s. A caller with a spec of its own starts here and adds the state (checked, mixed), the on_click and the access rows to it.
toggle_with
A toggle with its spec in the caller’s hands, the way button_with takes the button’s: spec is toggle_spec plus the state and the rows the element admits — checked, mixed (a checkbox’s third state), on_click, label, description, disabled, and hint, the tooltip drawn while it is hovered. The role is kind’s whatever the spec said. Keyed by key; an empty text draws the indicator alone, which then wants a label. 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
tooltip chrome around arbitrary content (legends, shortcut hints, …).
uniform_list
A vertically scrolling column of rows uniform rows that builds only the visible ones. row(ui, i) declares row i; it must come out exactly row_h logical px tall, since that is the arithmetic placing every row above and below it.
uniform_list_with
uniform_list with each row’s own node spelled by row_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 to row_h, the stride the arithmetic assumes, and a width the spec leaves fit grows across the list.
visible_rows
The half-open range of rows a container of rows rows, each row_h logical px tall, has any reason to build — those crossing the visible band, plus overscan on each side — given the geometry of the frame before. Pure arithmetic, exposed for views that build their own container instead of using uniform_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.