# ltk widget catalogue A flat reference of every widget and layout exported at the crate root. Each entry has the same four sections: **what it is**, **when to use it**, **minimal example**, **see also**. The full builder API and per-method docs live on `cargo doc`; this file is the at-a-glance index. For mental model and architecture, read [`docs/onboarding.md`](./onboarding.md) and [`docs/architecture.md`](./architecture.md) first. For copy-pasteable patterns built from these widgets, see [`docs/cookbook.md`](./cookbook.md). ## Table of contents - [Buttons and activations](#buttons-and-activations) - [`button`](#button) · [`icon_button`](#icon_button) · [`pressable`](#pressable) · [`window_button`](#window_button) · [`list_item`](#list_item) - [Stateful binary controls](#stateful-binary-controls) - [`toggle`](#toggle) · [`checkbox`](#checkbox) · [`radio`](#radio) - [Continuous controls](#continuous-controls) - [`slider`](#slider) · [`vslider`](#vslider) · [`progress_bar`](#progress_bar) - [Text input and display](#text-input-and-display) - [`text`](#text) · [`text_edit`](#text_edit) · [`rich_text`](#rich_text) - [Decoration and chrome](#decoration-and-chrome) - [`container`](#container) · [`separator`](#separator) · [`img_widget`](#img_widget) · [`external`](#external) - [Clipping wrappers](#clipping-wrappers) - [`scroll`](#scroll) · [`viewport`](#viewport) · [`flex`](#flex) · [`carousel`](#carousel) - [Overlays and feedback](#overlays-and-feedback) - [`spinner`](#spinner) · [`toast`](#toast) · [`tooltip`](#tooltip) · [`combo`](#combo) · [`tabs`](#tabs) · [`notebook`](#notebook) · [`dialog`](#dialog) - [Pickers](#pickers) - [`date_picker`](#date_picker) · [`time_picker`](#time_picker) · [`color_picker`](#color_picker) - [Layouts](#layouts) - [`column`](#column) · [`row`](#row) · [`stack`](#stack) · [`grid`](#grid) · [`spacer`](#spacer) --- ## Buttons and activations ### `button` A standard text button. Activates on tap, Enter, or Space when focused. `font_size`, `height` and `width` all take an `impl Into`, so the box can scale with the surface (e.g. a full-width form button) instead of sizing to its label. **When**: any place a normal app would have a "Save" / "Cancel" / "Send" control. ```rust,no_run # use ltk::{ button, Element, Length }; # #[ derive( Clone ) ] enum Msg { Save } # fn _ex() -> Element { button( "Save" ) .height( Length::vmin( 9.0 ).clamp( 44.0, 72.0 ) ) .width( Length::orient( 95.0, 25.0 ) ) .on_press( Msg::Save ) .into() # } ``` **See also**: [`pressable`](#pressable) for activation on a richer custom-shaped surface, [`icon_button`](#icon_button) for image-only buttons. ### `icon_button` A button whose visual is an RGBA bitmap instead of text. Same dispatch shape as [`button`](#button). **When**: toolbar icons, system tray glyphs, anywhere the visual is icon-first. ```rust,no_run # use std::sync::Arc; # use ltk::{ icon_button, Element }; # #[ derive( Clone ) ] enum Msg { OpenSearch } # fn _ex( rgba_bytes: Arc>, w: u32, h: u32 ) -> Element { icon_button( rgba_bytes, w, h ).on_press( Msg::OpenSearch ) .into() # } ``` **See also**: [`button`](#button), [`window_button`](#window_button) (specialised icon button for window decorations). ### `pressable` Wraps any [`Element`](../src/widget/mod.rs) so it dispatches a press message. Invisible to drawing — the wrapped child paints itself. **When**: a card, a custom-styled list row, or any non-trivial visual that should behave like a button. Inner widgets that are themselves interactive (a button nested inside) keep priority. ```rust,no_run # use ltk::{ column, container, pressable, row, Element, Pressable }; # #[ derive( Clone ) ] enum Msg { OpenWifiPicker } # fn _ex( # icon: Element, # title: Element, # subtitle: Element, # ) -> Pressable { pressable( container( row() .push( icon ) .push( column().push( title ).push( subtitle ) ) ) .surface( "surface-card" ) ) .on_press( Msg::OpenWifiPicker ) # } ``` **See also**: [`button`](#button) when a plain text button is enough, [`list_item`](#list_item) for the standard "label + subtitle + trailing" pattern. ### `window_button` A title-bar control button (minimize / maximize / restore / close). Comes with a special hover-tint for `Close`. **When**: building custom server-side window decorations. ```rust,no_run # use ltk::{ window_button, window_controls, Element, WindowButton, WindowButtonKind }; # #[ derive( Clone ) ] enum Msg { CloseWindow, Minimize, Maximize, Close } # fn _ex() -> ( WindowButton, Element ) { let close = window_button( WindowButtonKind::Close ).on_press( Msg::CloseWindow ); // Or the full standard set: let bar = window_controls( Some( Msg::Minimize ), WindowButtonKind::Maximize, Some( Msg::Maximize ), Some( Msg::Close ), ); # ( close, bar.into() ) # } ``` `focusable( true )` opts the button into the Tab cycle (off by default to match desktop convention). **See also**: `theme_window_controls` for the colour tokens each mode supplies. ### `list_item` A row with a primary label, optional subtitle, optional leading icon, optional right-aligned trailing text and/or trailing icon, and a tappable surface. **When**: settings menus, navigation lists, contact rows. Grows taller when a subtitle is set. ```rust,no_run # use ltk::{ list_item, ListItem }; # #[ derive( Clone ) ] enum Msg { OpenWifi } # fn _ex() -> ListItem { let mut it = list_item( "Wi-Fi" ) .subtitle( "Eduroam" ) .on_press( Msg::OpenWifi ); // Disclosure arrow from the active theme, tinted to match the // trailing-text colour. if let Some( ( rgba, w, h ) ) = ltk::theme_icon_rgba( "general/right", 21 ) { let tinted = std::sync::Arc::new( ltk::tint_symbolic( &rgba, ltk::theme_palette().text_secondary ) ); it = it.trailing_icon( tinted, w, h ); } it # } ``` `trailing( text )` right-aligns a text value (current setting, badge count); `trailing_icon( rgba, w, h )` draws an icon at the right edge. When both are set the text sits to the left of the icon. `pad_h( px )` overrides the horizontal inset between the row edge and its content (theme default 16 px) — lower it when the enclosing view's own padding already provides the margin. **See also**: [`pressable`](#pressable) for free-form tappable rows, [`scroll`](#scroll) to wrap a list of items in a scrollable container. --- ## Stateful binary controls ### `toggle` A two-state on / off switch. Renders as a horizontal pill with a sliding thumb. **When**: prominent settings toggles ("Wi-Fi", "Do not disturb"). ```rust,no_run # use ltk::{ toggle, Toggle }; # #[ derive( Clone ) ] enum Msg { ToggleWifi } # struct App { wifi_enabled: bool } # impl App { fn _ex( &self ) -> Toggle { toggle( self.wifi_enabled ) .label( "Wi-Fi" ) .on_toggle( Msg::ToggleWifi ) # }} ``` **See also**: [`checkbox`](#checkbox) for less prominent opt-ins, [`radio`](#radio) for mutually-exclusive groups. ### `checkbox` A two-state opt-in with a square box and a check glyph. **When**: form fields, terms acceptance, multi-select lists. ```rust,no_run # use ltk::{ checkbox, Checkbox }; # #[ derive( Clone ) ] enum Msg { ToggleTerms } # struct App { accept_terms: bool } # impl App { fn _ex( &self ) -> Checkbox { checkbox( self.accept_terms ) .label( "I accept the terms" ) .on_toggle( Msg::ToggleTerms ) # }} ``` **See also**: [`toggle`](#toggle), [`radio`](#radio). ### `radio` A single option inside a mutually-exclusive group. Build one per variant; the application owns "which is selected". **When**: priority pickers, layout choices, anywhere exactly one of N is selected. ```rust,no_run # use ltk::{ column, radio, Element }; # #[ derive( Clone, PartialEq ) ] enum Priority { Low, Medium, High } # #[ derive( Clone ) ] enum Msg { SetPriority( Priority ) } # struct App { priority: Priority } # impl App { fn _ex( &self ) -> Element { column() .push( radio( self.priority == Priority::Low ).label( "Low" ).on_select( Msg::SetPriority( Priority::Low ) ) ) .push( radio( self.priority == Priority::Medium ).label( "Medium" ).on_select( Msg::SetPriority( Priority::Medium ) ) ) .push( radio( self.priority == Priority::High ).label( "High" ).on_select( Msg::SetPriority( Priority::High ) ) ) .into() # }} ``` **See also**: [`checkbox`](#checkbox), [`toggle`](#toggle). --- ## Continuous controls ### `slider` A horizontal slider for selecting a value in `[0.0, 1.0]`. **When**: brightness / volume / scrub bars. Drag the thumb or tap a position; the change message fires continuously during drag. ```rust,no_run # use ltk::{ slider, Slider }; # #[ derive( Clone ) ] enum Msg { SetBrightness( f32 ) } # struct App { brightness: f32 } # impl App { fn _ex( &self ) -> Slider { slider( self.brightness ) .on_change( |v| Msg::SetBrightness( v ) ) # }} ``` `accent_thumb( true )` swaps the default thumb for the two-circle brand-coloured variant. `track_surface( id )` and `fill_surface( id )` override the default theme slots. **See also**: [`vslider`](#vslider) for the vertical axis, [`progress_bar`](#progress_bar) for read-only progress display. ### `vslider` A vertical slider. Same value model as [`slider`](#slider) — `0.0` at the bottom, `1.0` at the top. **When**: column-shaped equalisers, compact volume / brightness picks inside narrow side panels. ```rust,no_run # use ltk::{ vslider, VSlider }; # #[ derive( Clone ) ] enum Msg { SetVolume( f32 ) } # struct App { volume: f32 } # impl App { fn _ex( &self ) -> VSlider { vslider( self.volume ).on_change( |v| Msg::SetVolume( v ) ) # }} ``` **See also**: [`slider`](#slider). ### `progress_bar` A read-only linear progress indicator. **When**: determinate operations with a known fraction (downloads, file copies, install steps). ```rust,no_run # use ltk::{ progress_bar, ProgressBar }; # struct App { download_fraction: f32 } # impl App { fn _ex( &self ) -> ProgressBar { progress_bar( self.download_fraction ) # }} ``` **See also**: [`spinner`](#spinner) for indeterminate operations without a known fraction. --- ## Text input and display ### `text` A text label. By default it stays on one line and truncates with an ellipsis when wider than its rect; `wrap( true )` instead word-wraps to the layout width, and `line_height( mult )` scales the gap between the wrapped lines (`1.0` = the font's natural leading). **When**: titles, captions, multi-line hints, anything non-interactive. ```rust,no_run # use ltk::{ text, Color, Element }; # #[ derive( Clone ) ] enum Msg {} # fn _ex() -> ( Element, Element, Element ) { let title = text( "Title" ).size( 24.0 ).color( Color::WHITE ); let centred = text( "Centred" ).align_center(); let hint = text( "Swipe up or press Enter to unlock" ) .align_center() .wrap( true ) .line_height( 1.4 ); # ( title.into(), centred.into(), hint.into() ) # } ``` **See also**: [`text_edit`](#text_edit) for editable text. ### `text_edit` A text input with cursor, Backspace, Enter / Submit, clipboard support, a `secure( true )` password mode that masks the visible characters and zeroizes the buffer on drop, and a `password_toggle( visible, on_toggle )` builder that pins a show / hide-password eye icon to the right edge of the field. Single-line by default; `multiline( true )` switches to a multi-row editor whose visible height follows `rows( n )`. **When**: login fields, chat inputs, search boxes. ```rust,no_run # use ltk::{ text_edit, Element }; # #[ derive( Clone ) ] enum Msg { # UsernameChanged( String ), PasswordChanged( String ), # TogglePassword, Submit, # } # struct App { username: String, password: String, show_password: bool } # impl App { fn _ex( &self ) -> ( Element, Element ) { let user = text_edit( "Username", &self.username ) .on_change( |s| Msg::UsernameChanged( s ) ) .on_submit( Msg::Submit ); // Password field with the built-in show / hide eye. The widget // owns the icon hit-testing — taps inside the eye zone fire // `TogglePassword` instead of moving the caret. Flip the bool in // your `update`; the widget reads it back on the next render. let pw = text_edit( "Password", &self.password ) .on_change( |s| Msg::PasswordChanged( s ) ) .password_toggle( self.show_password, Msg::TogglePassword ); # ( user.into(), pw.into() ) # }} ``` `password_toggle` keeps the wipe-on-drop and IME-bypass guarantees of `secure( true )` regardless of the current visibility, so flipping the eye does not weaken the field's threat model at runtime — only what the user sees on screen changes. **See also**: the password recipe in [`docs/cookbook.md`](./cookbook.md#password-field-with-pam-submit). ### `rich_text` A wrapped paragraph that carries a `Msg` per clickable link range — the ltk counterpart of an Android `Spanned` with `URLSpan` / `ClickableSpan`. Unlike `text`, the layout pass emits one hit rect per link *line*, so a link that wraps across lines is hit-tested on each of its lines and taps land on the link rather than on the whole paragraph. **When**: body copy with inline links — a terms-and-conditions blurb, a chat message with a URL, an "about" screen crediting a project. ```rust,no_run # use ltk::{ rich_text, Element }; # #[ derive( Clone ) ] enum Msg { OpenTerms, OpenPrivacy } # fn _ex() -> Element { let body = "By continuing you accept the Terms and the Privacy Policy."; rich_text( body ) .size( 14.0 ) .link( 30, 35, Msg::OpenTerms ) // byte range of "Terms" .link( 44, 58, Msg::OpenPrivacy ) // byte range of "Privacy Policy" .into() # } ``` Link ranges are **byte** offsets into the content `[start, end)`, drawn underlined in `link_color`. `color` sets the non-link text colour, `size` the font size (any `Length`), and `font( family, weight, style )` the typeface resolved through the active theme on draw. --- ## Decoration and chrome ### `container` A wrapper that adds padding, a background colour or themed surface, a border radius, and (via theme slots) shadows / inset shadows / backdrop blur. **When**: cards, panels, callouts, anywhere a child needs a backdrop or elevation. ```rust,no_run # use ltk::{ column, container, Element }; # #[ derive( Clone ) ] enum Msg {} # fn _ex( title: Element, subtitle: Element ) -> Element { container( column().push( title ).push( subtitle ) ) .surface( "surface-card" ) .padding( 16.0 ) .radius( 24.0 ) .into() # } ``` Per-edge padding is available with `padding_top` / `padding_right` / `padding_bottom` / `padding_left`. Per-corner radius takes a `Corners` struct or a tuple. `max_width(px)` caps the container's outer width — when the parent offers a wider rect, the container reports `min( offered, px )` as its preferred width instead of stretching to fill. Note the semantics differ from [`column`](#column)'s `max_width`, which caps the *content* width while the column still claims the full available rect ([`row`](#row) has no such flag); the container cap shrinks the widget itself, so a single decorated child gets a real width cap without extra wrapping. **See also**: [`pressable`](#pressable) wrapping a `container` makes a card interactive. ### `separator` A horizontal divider line with a theme-default colour and a mode-scaled thickness / vertical padding. `thickness` and `pad_v` take an `impl Into`; both default to the process widget-scaling mode, and passing `pad_v( 0.0 )` gives a flush, padding-less divider (distinct from the mode default). **When**: visual breaks between settings groups, list categories, content blocks. ```rust,no_run # use ltk::{ column, separator, text, Element }; # #[ derive( Clone ) ] enum Msg {} # fn _ex() -> Element { column() .push( text( "General" ) ) .push( separator() ) .push( text( "Network" ) ) .into() # } ``` ### `img_widget` A static image rendered from RGBA pixel data shared via `Arc`. **When**: wallpapers, icons, illustrations. The shared `Arc` makes re-using the same buffer across frames a pointer copy. ```rust,no_run # use std::sync::Arc; # use ltk::{ img_widget, Element }; # #[ derive( Clone ) ] enum Msg {} # fn _ex( rgba_bytes: Arc>, width: u32, height: u32 ) -> Element { img_widget( rgba_bytes, width, height ) .opacity( 0.8 ) .into() # } ``` `cover()` scales to fill the rect preserving aspect; `size( w, h )` sets explicit display dimensions. **See also**: [`Image::from_path`](../src/widget/image.rs) helper for disk-loaded files (PNG, JPEG via the `image` crate). ### `external` An escape hatch that reserves layout space and defers its pixels to a caller-provided producer, composited in-line with the rest of the tree. Two sources: - `External::cpu( w, h, |canvas, rect| … )` — an immediate-mode CPU drawing closure invoked once per frame with the `Canvas` and the widget's laid-out `rect` (physical pixels). Works on **both** backends and is the way to host a custom `onDraw`-style routine — paths, clips, text — straight onto the canvas with no GL round-trip. - `External::new( w, h, ExternalSource::Texture( … ) )` — samples a caller-owned GL texture each frame (a web engine, a video decoder). **GLES only**; the producer keeps the texture and ltk only composites. **When**: a `VectorDrawable` / Lottie frame, a custom-painted gauge, or embedding another renderer's output. ```rust,no_run # use ltk::{ External, Element }; # #[ derive( Clone ) ] enum Msg {} # fn _ex() -> Element { External::cpu( 120.0, 120.0, |canvas, rect| { canvas.fill_rect( rect, ltk::Color::rgb( 0.1, 0.1, 0.12 ), 8.0 ); // any Canvas primitive: fill_path, set_clip_path, draw_text, … } ).into() # } ``` **See also**: the CPU-drawing and path-clip recipe in [`docs/cookbook.md`](./cookbook.md#custom-cpu-drawing-and-path-clipping). --- ## Clipping wrappers ### `scroll` A scrollable viewport — vertical by default, with `.horizontal()` and `.both()` builders switching the axis (`ScrollAxis::{ Vertical, Horizontal, Both }`). Drag the content to scroll; clipping is automatic. **When**: lists or grids that may overflow the available height. ```rust,no_run # use ltk::{ column, list_item, scroll, Element }; # #[ derive( Clone ) ] enum Msg {} # fn _ex() -> Element { scroll( column() .push( list_item::( "Item 1" ) ) .push( list_item( "Item 2" ) ) .push( list_item( "Item 3" ) ) ) .into() # } ``` The scroll widget owns its own gesture handling — drags inside it do not trigger the app-level `on_swipe_*` callbacks. **See also**: [`viewport`](#viewport) for passive clipping without gestures, [`grid`](#grid) inside a `scroll` for app drawers. ### `viewport` A passive clipping wrapper. Clips its child to the assigned rect; no scrolling, no gesture handling. **When**: panels that animate in via a parent translation, fade-in content, anywhere you want a hard clip without scrolling. The `fade_bottom( px )` builder feathers the bottom edge to transparent during slide-in animations (GLES backend; software backend renders a hard edge). ```rust,no_run # use ltk::{ viewport, Element }; # #[ derive( Clone ) ] enum Msg {} # fn _ex( panel_view: Element, panel_height: f32 ) -> Element { viewport( panel_view ) .height( panel_height ) .fade_bottom( 16.0 ) .into() # } ``` **See also**: the slide-in panel recipe in [`docs/cookbook.md`](./cookbook.md#slide-in-panel). ### `flex` A filler wrapper for both flow layouts. Treats its non-spacer child like a [`spacer`](#spacer) for leftover-space distribution but draws the child inside the allocated rect: leftover width inside a [`row`](#row), leftover height inside a [`column`](#column). Weights split the leftover proportionally between flex / spacer siblings. **When**: a row where one non-trivial child should fill the remaining width (a card next to a fixed-size icon), or a column where a child should absorb the remaining height (a log pane under fixed toolbars). ```rust,no_run # use ltk::{ column, flex, row, Element }; # #[ derive( Clone ) ] enum Msg {} # fn _ex( # icon: Element, # title: Element, # subtitle: Element, # ) -> Element { row() .push( icon ) .push( flex( column().push( title ).push( subtitle ) ) ) .into() # } ``` `weight( n )` sets the relative share when there are several flex / spacer siblings. **See also**: [`spacer`](#spacer) for invisible fillers, [`column`](#column) and [`row`](#row). ### `carousel` Horizontal carousel: the focused child sits centred in the viewport at `focused_width_frac` of the viewport width and its neighbours peek out on the left / right at `gap` separation. The widget is a pure layout primitive — the `offset` (positive shifts content right) is owned by the caller, so drag / inertia / snap-ease live in the host (compositor, gesture recogniser, etc.). **When**: mobile-style app switchers, image / story strips, any single-focus horizontal navigation where neighbours hint at the next / previous tile. ```rust,no_run # use ltk::{ button, carousel, container, Element, Color, Corners }; # #[ derive( Clone ) ] enum Msg { Open( usize ) } # fn _ex( offset: f32 ) -> Element { carousel() .focused_width_frac( 0.8 ) .gap( 16.0 ) .offset( offset ) .push( container( button::( "Tile 1" ).on_press( Msg::Open( 0 ) ) ) .background( Color::rgb( 0.95, 0.4, 0.4 ) ) .radius( Corners::all( 12.0 ) ) ) .push( container( button::( "Tile 2" ).on_press( Msg::Open( 1 ) ) ) .background( Color::rgb( 0.95, 0.85, 0.3 ) ) .radius( Corners::all( 12.0 ) ) ) .into() # } ``` Helpers on the widget translate between offsets and indices: `snap_offset( viewport_w, idx )` returns the offset that centres tile `idx`; `focused_index( viewport_w )` rounds the current offset to the nearest tile. The runnable demo at [`examples/carousel.rs`](../examples/carousel.rs) shows Prev / Next buttons and arrow-key navigation against an external `offset` state. **See also**: [`scroll`](#scroll) for free vertical / horizontal panning of arbitrary content; [`tabs`](#tabs) for a non-touch alternative. --- ## Overlays and feedback ### `spinner` Indeterminate progress indicator. The widget is stateless: the application owns the rotation phase and advances it each frame — any monotonically increasing value works, only the fractional part is used. Pair with `App::is_animating` so the run loop keeps requesting redraws while the spinner is on screen. **When**: long-running operations with no known fraction (network calls, indexing, "waiting for compositor"). ```rust,no_run # use ltk::{ spinner, Spinner }; # struct App { spinner_phase: f32 } # impl App { fn _ex( &self ) -> Spinner { spinner().phase( self.spinner_phase ).size( 24.0 ) # }} ``` **See also**: [`progress_bar`](#progress_bar) for determinate progress. ### `toast` Transient notification pill anchored near the bottom of the surface. Not an `Element` — build it in `App::overlays` and return its `.overlay()` while a toast is pending. Auto-dismissal is the application's responsibility: `duration( secs )` only stores a value read back via `duration_value()`, so the app schedules its own "toast expired" timer and clears the state when it fires. **When**: confirmation snackbars ("Saved"), non-blocking errors, status flashes. ```rust,no_run # use ltk::{ toast, OverlaySpec }; # #[ derive( Clone ) ] enum Msg {} # struct App { toast_message: Option } # impl App { fn overlays( &self ) -> Vec> { match &self.toast_message { Some( m ) => vec![ toast( m ).duration( 3.0 ).overlay() ], None => vec![], } } # } ``` **See also**: [`tooltip`](#tooltip) for hover-anchored hints. ### `tooltip` Anchored hint rendered below a target widget. Like [`toast`](#toast) it is not an `Element` — return its `.overlay()` from `App::overlays` while the hint should be visible. Hover detection and the show / hide delay are the application's responsibility; the automatic variant is `Button::tooltip( text )`, which shows the hint by itself after a pointer dwell on the button. **When**: discoverable affordances on icon-only controls, keyboard shortcut reminders, helper text that should not occupy permanent layout space. ```rust,no_run # use ltk::{ tooltip, Tooltip, WidgetId }; # #[ derive( Clone ) ] enum Msg {} # fn _ex() -> Tooltip { tooltip( "Ctrl+S", WidgetId( "btn/save" ) ).max_width( 240 ) # } ``` The overlay anchors below the widget built with the matching `.id( WidgetId )`. See [`docs/cookbook.md`](./cookbook.md) for the full overlay wiring. **See also**: [`toast`](#toast) for transient self-dismissing notifications. ### `combo` Select / dropdown with a popup list — single- or multi-select (`multi_select( true )` adds selection chips), with optional type-to-filter (`searchable( true )`). The widget is a stateless projection over an app-owned `ComboState`, and the app places **two** pieces: the trigger (`Combo::trigger()`) goes in the view tree like any widget, and the open popup is either layered into the same surface via `Combo::popup()` inside a [`stack`](#stack), or returned as a real xdg-popup from `App::overlays` via `Combo::overlay()`. **When**: pick-one (or pick-several) fields with too many options for a row of [`radio`](#radio) buttons (autocomplete, country list, theme picker). ```rust,no_run # use ltk::{ column, combo, Combo, ComboState, Element, OverlaySpec }; # #[ derive( Clone ) ] enum Msg { Pick( usize ), ToggleOpen, Dismiss } # struct App { fruits: ComboState } # impl App { # fn build_combo( &self ) -> Combo { # let items = vec![ "One".to_string(), "Two".to_string(), "Three".to_string() ]; combo( self.fruits.clone(), items ) .on_toggle_open( Msg::ToggleOpen ) .on_select_idx( Msg::Pick ) .on_dismiss( Msg::Dismiss ) # } fn view( &self ) -> Element { column().push( self.build_combo().trigger() ).into() } fn overlays( &self ) -> Vec> { self.build_combo().overlay().into_iter().collect() } # } ``` `update()` then flips `is_open` on `ToggleOpen` / `Dismiss` and writes the selection into the `ComboState` on `Pick`. **See also**: [`radio`](#radio) for small mutually-exclusive sets, [`notebook`](#notebook) for tabbed mode switches. ### `tabs` A bare tab bar — visual selection without owning the page content. Use this when you want full control over what each tab shows. **When**: paged settings screens where the panes are large and you want to keep them as siblings in the tree, not as [`notebook`](#notebook) children. ```rust,no_run # use ltk::{ tabs, TabBar }; # #[ derive( Clone ) ] enum Msg { TabChanged( usize ) } # fn _ex( active: usize ) -> TabBar { tabs::( [ "Profile", "Network", "Privacy" ] ) .selected( active ) .on_select( Msg::TabChanged ) # } ``` **See also**: [`notebook`](#notebook) for tabs that own their pages. ### `notebook` Tabbed container. Owns the pages and shows only the active one. **When**: settings dialogs, multi-step forms, anywhere a flat [`tabs`](#tabs) bar over hand-managed pages would be more code than benefit. ```rust,no_run # use ltk::{ notebook, text, Notebook }; # #[ derive( Clone ) ] enum Msg { TabChanged( usize ) } # fn _ex( active: usize ) -> Notebook { notebook::() .page( "General", text( "general body" ) ) .page( "Advanced", text( "advanced body" ) ) .selected( active ) .on_select( Msg::TabChanged ) # } ``` **See also**: [`tabs`](#tabs) for a bare tab bar without page ownership. ### `dialog` Modal or non-modal centered confirmation card with a built-in scrim, optional title, subtitle, custom body, and a right-aligned action row. Pressing `Esc` fires the configured `cancel` message. **When**: destructive confirmations ("Delete file?"), guided pickers that block other interaction until resolved, "are you sure" gates before a long-running operation. ```rust,no_run # use ltk::{ button, dialog, ButtonVariant, Element }; # #[ derive( Clone ) ] enum Msg { Cancel, Confirm } # fn _ex() -> Element { dialog() .title( "Delete partition?" ) .subtitle( "This will erase every file on /dev/sda2." ) .cancel( Msg::Cancel ) .action( button::( "Cancel" ).variant( ButtonVariant::Tertiary ).on_press( Msg::Cancel ) ) .action( button::( "Delete" ).variant( ButtonVariant::Primary ).on_press( Msg::Confirm ) ) .into() # } ``` `modal( false ) + dismiss_on_scrim( msg )` makes a tap on the dim background fire `msg`; combining `dismiss_on_scrim` with `modal( true )` panics when the dialog is converted to an `Element` — the `.into()` asserts with "dialog: dismiss_on_scrim is not valid when modal=true", since the two contracts contradict. `body( elem )` swaps a custom element in between the subtitle and the action row — the example app at `examples/dialog.rs` uses this for an in-dialog slider. `max_width( … )` caps the card width; it accepts any `Length` and defaults to `Length::fluid( 480.0 )` so the card scales with the same curve as the stock buttons inside it. **See also**: [`toast`](#toast) for non-blocking transient notifications, [`combo`](#combo) for a single-pick popup that does not require modal blocking. --- ## Pickers ### `date_picker` Calendar-grid date selector with month / year navigation. **When**: birthdays, deadlines, any single-date input. ```rust,no_run # use ltk::{ date_picker, Date, DatePicker }; # #[ derive( Clone ) ] enum Msg { Picked( Date ) } # fn _ex() -> DatePicker { date_picker( Date::new( 2026, 5, 7 ) ).on_change( Msg::Picked ) # } ``` ### `time_picker` Hour / minute selector. Wheel-style scrubbing on touch. **When**: alarms, scheduling, time-of-day input. ```rust,no_run # use ltk::{ time_picker, Time, TimePicker }; # #[ derive( Clone ) ] enum Msg { Picked( Time ) } # fn _ex( now: Time ) -> TimePicker { time_picker( now ).on_change( Msg::Picked ) # } ``` ### `color_picker` Hue + saturation/value picker with a hex/RGB readout. **When**: theming UIs, paint tools, accent customisation. ```rust,no_run # use ltk::{ color_picker, Color, ColorPicker }; # #[ derive( Clone ) ] enum Msg { Picked( Color ) } # fn _ex( current: Color ) -> ColorPicker { color_picker( current ).on_change( Msg::Picked ) # } ``` --- ## Layouts ### `column` Vertical flow. Children stack top-to-bottom with optional padding, spacing, alignment. ```rust,no_run # use ltk::{ button, column, spacer, text, Element }; # #[ derive( Clone ) ] enum Msg { Ok } # fn _ex() -> Element { column() .padding( 24.0 ) .spacing( 12.0 ) .push( text( "Title" ) ) .push( spacer() ) .push( button( "OK" ).on_press( Msg::Ok ) ) .into() # } ``` `max_width(px)` caps the inner content width; `fit_content()` reports the natural content width to the parent (otherwise the column claims the full available width). `center_y( true )` centres the content block vertically when there are no spacers. Watch the defaults: `column()` starts with **16 px padding** on every side (and 8 px spacing). A column nested inside an already-padded container silently doubles the inset — pass `.padding( 0.0 )` when the parent owns the margin. ### `row` Horizontal flow. Mirror of [`column`](#column) on the X axis — except its default padding is `0`, not `column`'s 16 px. ```rust,no_run # use ltk::{ flex, row, Element }; # #[ derive( Clone ) ] enum Msg {} # fn _ex( # label: Element, # field: Element, # submit_button: Element, # ) -> Element { row() .spacing( 8.0 ) .push( label ) .push( flex( field ) ) .push( submit_button ) .into() # } ``` ### `stack` Z-order overlay. Each child gets explicit horizontal and vertical alignment (`HAlign` / `VAlign`) plus an optional margin and pixel translation. Children are drawn in declaration order — the last child sits on top. ```rust,no_run # use std::sync::Arc; # use ltk::{ button, column, img_widget, stack, text, Element, HAlign, VAlign }; # #[ derive( Clone ) ] enum Msg { Add } # fn _ex( background: Arc>, w: u32, h: u32 ) -> Element { stack() .push( img_widget( background, w, h ) ) .push_aligned( column().push( text( "Heading" ) ), HAlign::Center, VAlign::Center, ) .push_aligned_margin( button( "+" ).on_press( Msg::Add ), HAlign::End, VAlign::Bottom, 16.0, ) .into() # } ``` **See also**: [`column`](#column) and [`row`](#row) for flow layout. ### `grid` Fixed-column-count grid that wraps its children into rows. **When**: icon launchers, photo galleries, app drawers. ```rust,no_run # use std::sync::Arc; # use ltk::{ grid, icon_button, Element }; # #[ derive( Clone ) ] enum Msg { OpenApp1, OpenApp2 } # fn _ex( app1_rgba: Arc>, app2_rgba: Arc>, w: u32, h: u32 ) -> Element { grid( 4 ) .padding( 16.0 ) .spacing( 12.0 ) .push( icon_button( app1_rgba, w, h ).on_press( Msg::OpenApp1 ) ) .push( icon_button( app2_rgba, w, h ).on_press( Msg::OpenApp2 ) ) // ... .into() # } ``` Wrap inside [`scroll`](#scroll) when the grid may overflow. `centre_last_row( true )` shifts a partial last row so its tiles sit centred under the full rows above instead of left-aligned — useful for app switchers and gallery layouts where a 7-of-9 leftover band reads better balanced. `grid_min_cell( width )` is the adaptive variant: instead of a fixed column count, it fits as many columns as the available width allows while keeping every cell at least `width` wide (never fewer than one), re-deriving the count on every layout — so the same grid shows more columns on a wide window and fewer on a phone. `width` accepts any `Length`; `max_columns( n )` caps the derived count so cells grow instead of multiplying on very wide surfaces. ### `spacer` An invisible flexible filler. Inside a column / row, absorbs leftover space along the parent's main axis. ```rust,no_run # use ltk::{ column, spacer, Element }; # #[ derive( Clone ) ] enum Msg {} # fn _ex( header: Element, footer: Element ) -> Element { column() .push( header ) .push( spacer() ) // pushes footer to the bottom .push( footer ) .into() # } ``` `weight( n )` sets the relative share; `height( px )` / `width( px )` pin the spacer to a fixed size on its respective axis. **See also**: [`flex`](#flex) for a non-empty filler.