Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Toolbar Reference

teksilo_widgets::toolbar ships Toolbar — a command bar with automatic overflow: excess commands collapse into a trailing chevron () that opens a drop-down menu, mirroring Qt's QToolBar extension button, macOS NSToolbar's overflow menu, and WinUI CommandBar.

Mental model in one line:

Toolbar::new().action(…).item(ToolbarItem::custom(…).overflow_widget(…)) → it fits itself to the available width

A Toolbar fills the width it is offered and decides, every layout pass, which commands stay inline and which collapse into the chevron menu — so it never spills outside its container and never truncates an action label (a truncated action reads poorly; the desktop convention is to overflow excess commands into a menu instead).

End-to-end demo: cargo run -p over-constraint (section 1). Source: examples/over_constraint/src/main.rs.


Quickstart

#![allow(unused)]
fn main() {
use teksilo::prelude::*;
use teksilo::widgets::{Toolbar, ToolbarAction};

Toolbar::new()
    .action(
        ToolbarAction::new(tr!(new_doc()))
            .icon(|| IconWidget::doc_add(16.0))
            .on_activate(|ctx| ctx.send_intent(AppIntent::NewDocument)),
    )
    .action(ToolbarAction::new(tr!(open())).on_activate(|ctx| ctx.send_intent(AppIntent::Open)))
    .action(ToolbarAction::new(tr!(save())).on_activate(|ctx| ctx.send_intent(AppIntent::Save)))
}

That's the 90% case: a row of ToolbarActions. When the bar is too narrow to show them all, the lowest-priority ones collapse into the chevron menu and reappear as it widens.


Items

A toolbar is a sequence of ToolbarItems, added with .item(...) (or the .action(...) / .child(...) sugar). There are four kinds:

ItemConstructorCollapses?Renders inline asRenders in the menu as
ActionToolbarItem::action(a) / .action(a)yes (by priority)a Buttona MenuItem (with the action's icon)
Pinned widgetToolbarItem::custom(w) / .child(w)nothe widget itself— (never collapses)
Collapsible widgetToolbarItem::custom(w).overflow_as(...) / .overflow_widget(...) / ToolbarItem::collapsible(w)yesthe widget itselfits declared overflow form
Separator / flexible spaceToolbarItem::separator() / flexible_space()noa Divider / a Spacer

Actions

ToolbarAction is a command with a label and an activation handler, plus optional refinements:

#![allow(unused)]
fn main() {
ToolbarAction::new(tr!(bold()))
    .icon(|| IconWidget::bold(16.0))   // icon FACTORY — reused inline AND in the menu
    .tooltip(tr!(bold_tooltip()))      // also the accessible name in IconOnly mode
    .enabled(true)
    .toggle(is_bold)                   // checkable: pressed inline, checkmark in the menu
    .priority(10)                      // higher priority collapses LAST (NSToolbar semantics)
    .always_overflow()                 // WinUI secondary command — lives in the menu, never inline
    .on_activate(|ctx| ctx.send_intent(Editor::ToggleBold))
}

The icon is a factory (Fn() -> IconWidget) because IconWidget isn't Clone and the toolbar may build it twice — once for the inline Button, once for the menu MenuItem.

Pinned widgets

ToolbarItem::custom(widget) (sugar: .child(widget)) embeds an arbitrary widget — a search field, a SegmentedControl, a zoom SpinBox — that never collapses. Use it for controls that must always stay reachable.

#![allow(unused)]
fn main() {
Toolbar::new()
    .child(SearchField::new(query))           // pinned: always visible
    .action(ToolbarAction::new(tr!(filter())).on_activate(…))
}

Collapsible widgets — overflow_as, overflow_widget, ToolbarOverflow

A custom widget becomes collapsible by declaring its overflow representation (NSToolbar menuFormRepresentation / Qt QWidgetAction). Pick the form that reads best in a menu:

1. .overflow_as(action) — a menu row

Best when the control's menu form is a single command. An icon-only inline control reuses its icon as the menu item's leading glyph:

#![allow(unused)]
fn main() {
ToolbarItem::custom(IconButton::new(IconWidget::checkmark(16.0)).tooltip(tr!(confirm())))
    .overflow_as(
        ToolbarAction::new(tr!(confirm()))
            .icon(|| IconWidget::checkmark(16.0))   // shown as the MenuItem icon
            .on_activate(|ctx| ctx.send_intent(App::Confirm)),
    )
}

2. .overflow_widget(factory) — a live widget in the menu

Best for stateful inputs (a combo box, a slider) that have no meaningful single-command form. The factory rebuilds the control inside the menu, bound to the same signal as the inline instance, so it stays fully usable while collapsed — selecting in the menu copy updates the inline copy and vice-versa:

#![allow(unused)]
fn main() {
let view_mode = Signal::new(Some("List".to_string()));
let menu_mode = view_mode.clone();

ToolbarItem::custom(ComboBox::new(["List", "Grid", "Columns"], view_mode))
    .overflow_widget(move || {
        Box::new(ComboBox::new(["List", "Grid", "Columns"], menu_mode.clone()))
    })
}

When the bar is too narrow, the inline ComboBox is hidden and an equivalent, live ComboBox appears in the chevron menu. State is shared through the cloned Signal, so the two are never out of sync.

A factory (Fn() -> Box<dyn Widget>) is required rather than a value because widgets aren't Clone and the menu builds its row lazily.

3. ToolbarOverflow trait — a widget that knows its own menu form

When a reusable widget always overflows the same way, implement ToolbarOverflow on it and add it with ToolbarItem::collapsible(w) — no per-call overflow_as:

#![allow(unused)]
fn main() {
impl ToolbarOverflow for ZoomControl {
    fn toolbar_menu_form(&self) -> ToolbarAction {
        ToolbarAction::new(tr!(zoom())).on_activate(/* … */)
    }
}

Toolbar::new().item(ToolbarItem::collapsible(ZoomControl::new(zoom)))
}

Separators & flexible space

ToolbarItem::separator() draws a Divider between groups. ToolbarItem::flexible_space() inserts a Spacer that pushes the following items to the trailing edge (NSToolbar flexibleSpace).


How overflow is computed

Every layout pass, the toolbar measures each item's intrinsic size — even the currently-collapsed ones, via LayoutContext::measure_intrinsic — so a collapsed command reappears at exactly the right width as the bar grows (no stale-width glitch). It then runs a greedy priority algorithm:

  1. If everything fits, nothing collapses and no chevron is shown.
  2. Otherwise the chevron is reserved, and the lowest-priority inline commands collapse into the menu until the rest fit (ties: the later-declared one collapses first).
  3. always_overflow commands start collapsed regardless of room.

Pinned widgets and separators reduce the room available to collapsible commands but never collapse themselves.

Toolbar::is_overflowing() returns a Signal<bool> that is true whenever any command is currently collapsed — useful for adaptive UI (WinUI IsOverflowOpen-adjacent introspection).


The overflow menu

The chevron's drop-down is a real MenuList, not a bare list, so it:

  • sizes compactly to the currently-collapsed rows (size-to-content, standard menu chrome — no fixed width/height);
  • takes focus when opened (keyboard or pointer) and supports arrow / Home / End / Enter navigation, skipping the rows that aren't currently collapsed;
  • hosts both ordinary menu rows (from overflow_as actions) and live embedded widgets (from overflow_widget).

It is driven by MenuList::item_when — a conditionally-visible menu row that collapses to zero height (no gap) and is skipped by keyboard navigation while hidden. That is the general primitive any app can use for a menu whose rows come and go.


Display mode & orientation

#![allow(unused)]
fn main() {
Toolbar::new()
    .display_mode(ToolbarDisplayMode::IconOnly)   // IconAndText (default) / IconOnly / TextOnly
    .orientation(ToolbarOrientation::Vertical)    // Horizontal (default) / Vertical
    .spacing(6.0)
}

In IconOnly mode the label becomes the control's accessible name + tooltip. Vertical toolbars collapse along the vertical axis and the roving arrow keys become Up/Down.


Accessibility — the ARIA toolbar pattern

Toolbar implements the WAI-ARIA toolbar pattern:

  • It emits Role::Toolbar with its orientation and name (.label(...) overrides the default localized "Toolbar").
  • It is a single Tab stop with roving tab-index: Tab enters the toolbar (landing on the last-focused control) and leaves it; the / (or / when vertical) arrow keys move focus among the visible controls, and Home/End jump to the ends. The roving suppression reaches composite controls (a ComboBox, an IconButton) correctly — Tab doesn't get stuck on one. Under RTL the horizontal arrows mirror ( advances, steps back), resolved live so a locale change flips them.
  • All localizable strings — the chevron's "More" tooltip and the accessible name — flow through the framework's Fluent bundle (en-US + fr-FR shipped), so they translate and update reactively on a locale change.
  • The chevron announces HasPopup::Menu and its expanded state.
  • Collapsed commands are dormant (absent from the accessibility tree) — they are represented by their menu rows instead, so no command is announced twice.
  • Toggle actions carry Toggled.

API surface

Pull the full, current signatures with:

python3 tools/extract_widget_api.py Toolbar

Key types (crates/teksilo-widgets/src/toolbar.rs):

  • Toolbarnew, item, action, child, add_child, orientation, display_mode, spacing, label, is_overflowing.
  • ToolbarActionnew, icon, tooltip, enabled, on_activate, toggle, priority, always_overflow.
  • ToolbarItemaction, custom, custom_id, collapsible, overflow_as, overflow_widget, separator, flexible_space.
  • ToolbarOverflowtoolbar_menu_form (implement on a widget for ToolbarItem::collapsible).
  • ToolbarDisplayMode { IconAndText, IconOnly, TextOnly }, ToolbarOrientation { Horizontal, Vertical }.