Reusable commands

Define a Command { id, label, action } when several controls perform the same operation. Call .build() to obtain a cloneable CommandHandle, then configure availability, optional checked state, icon, and keyboard shortcut with its modifiers. A menu item that is still visible after the operation becomes unavailable cannot bypass the command’s guard.

use day::prelude::*;

let starred = Signal::new(false);
let star = Command {
    id: "star",
    label: move || {
        if starred.get() { "Unstar" } else { "Star" }.to_string()
    },
    action: move || starred.update(|on| *on = !*on),
}
.build()
.checked(starred)
.icon(Symbol::Star)
.shortcut(Shortcut::new("d"));

let menu = star.clone();
let bar = star.clone();
let content = star.button()
    .context_menu_fn(move |_| vec![menu.menu_item()])
    .toolbar(move || vec![bar.toolbar_item()]);

Use localized resources in application titles, just as for a normal button. Add .enabled(move || ...) for availability, and call .invoke() from a custom gesture or another caller. It returns whether the handler ran; it does not report the result of asynchronous work.

Choose the owning scope and target

Handle clones share behavior and reactive sources. .build() captures the owning reactive scope; the struct literal alone does not. Call .build() in the scope that owns the operation; the handle stops accepting invocations after that scope is disposed. Captured signals must still outlive their use. Command factory functions return CommandHandle. Capture a window’s scene for a window command, or resolve the focused scene inside an app-menu command. Commands do not implicitly select a window or redirect an operation to whichever document is frontmost.

A command’s id defaults each presentation’s id. Override it when two controls in one tree need distinct ids, or to preserve an existing Dayscript target:

star.button().id("star-footer");
star.toolbar_item().id("star-toolbar");

Keep the presentations reactive

Buttons bind automatically. Use a derived .toolbar(move || ...) for changing titles, and app_menu_reactive or context_menu_fn for current menu state. A fixed menu remains a snapshot; its handler still rechecks availability. Toolbar check and enabled state bind live even in fixed lists. The application handler owns check changes; native toggle activation does not write a second mirror signal.

What this does and does not abstract

The adapters retain each toolkit’s native controls and existing menu/toolbar behavior. They do not expose a Qt QAction or Windows ICommand, introduce a new command palette, or register OS-wide shortcuts. Shortcuts take effect through installed menu items, not a standalone button. Continue using menu_role for native Copy/Paste/Undo and responder-chain targeting.

  • AppKit, GTK, Qt, and XAML retain their native menu and toolbar presentation; primary modifiers are Command on Apple and Ctrl elsewhere. GTK uses header-bar controls; XAML placement remains constrained by CommandBar.
  • UIKit contributes commands to navigation bars and context menus. An iPhone app must provide a visible route to operations instead of relying on the application menu.
  • Android keeps Material app-bar/overflow behavior, including restricted nesting and optional omission of menu icons. Menu shortcut metadata is not a cross-platform hardware-keyboard API.
  • Web uses DOM buttons and composed toolbar/context-menu surfaces. Browser-reserved shortcuts stay reserved, and there is no native application menu.
  • ArkUI supports the button path. A reusable command does not add unsupported application-menu or toolbar chrome; keep content controls available.

See the full design, lifecycle rules, platform notes, and app audit, plus menus and toolbars. Day-Showcase’s Menus & dialogs page demonstrates one counter through content buttons, toolbar items, and a context menu. Its page stars and recorder playback also use the framework abstraction.

For other UI components, named definitions are most useful for helpers with several required arguments. Existing one-argument functions and fluent modifiers remain the normal API. See the assessment of additional named component definitions for candidates, coexistence with functions, and the distinction between a definition and a runtime piece. Those additional wrappers are proposals, not implemented APIs.

Implementation: crates/day-pieces/src/commands.rs.