Application menus
Access the application's native menu bar through app.menus. Configure its structure before app.run; update retained item properties on the UI context. Native menus support macOS.
Manager API
| Method | Returns | Purpose |
|---|---|---|
register(MenuClass) | CryKit::Menus::Menu | Install one reusable definition |
register(MenuClass, OtherClass, ...) | Nil | Install definitions in order |
create(id, text, role: nil) { |menu| ... } | CryKit::Menus::Menu | Build a menu inline |
[id] / [id]? | Menu / Menu? | Look up a menu; [] raises if missing |
find_by(menu_id, action_id) | Item? | Look up an item, including nested items |
each { |menu| ... } | — | Enumerate menus in registration order |
size / empty? | Int32 / Bool | Inspect the registry |
Menu builder
Use the same operations in a CryKit::Menu definition, an inline menu, or a nested submenu. Each operation returns a CryKit::Menus::Item.
| Operation | Purpose |
|---|---|
text(id, text, shortcut: nil, enabled: true) | Add an application action |
checkbox(id, text, checked: false, shortcut: nil, enabled: true) | Add a toggle |
radio(id, text, checked: false, shortcut: nil, enabled: true) | Add a choice in an adjacent radio group |
separator | Divide items and end an adjacent radio group |
submenu(id, text, enabled: true) { |submenu| ... } | Add nested items |
command(:close) | Add a native command with its standard label and shortcut |
Menu and item API
| Receiver | Member | Purpose |
|---|---|---|
menu | id, text, role | Read identity and native role |
menu | items | Read a snapshot of top-level items |
menu | [action_id] / [action_id]? | Look up any nested item; [] raises if missing |
menu | on_click { |event| ... } | Subscribe to action activation |
menu | on_event(:click) { |event| ... } / events { |event| ... } | Subscribe through the common event API |
item | text, text= | Read or change its label |
item | enabled, enabled= | Read or change its enabled state |
item | checked, checked= | Read state; change it on checkbox/radio items |
item | id, kind, shortcut, role, children | Inspect the item and a snapshot of its children |
Changing item properties updates native items immediately and emits no click event. Setting checked = true on a radio item clears its adjacent peers. The registered structure is fixed; item insertion, context menus, and per-window menu selection are not exposed.
Define and compose
class ClockMenu
include CryKit::Menu
menu :clock, "Clock" do
text :time_control, "Pause", shortcut: "CmdOrCtrl+Shift+P"
end
end
app.menus.register(
CryKit::Menus::AppMenu,
CryKit::Menus::FileMenu,
CryKit::Menus::EditMenu,
ClockMenu,
CryKit::Menus::WindowMenu,
CryKit::Menus::HelpMenu,
)For a small menu, use the builder directly:
app.menus.create(:tools, "Tools") do |menu|
menu.checkbox(:show_seconds, "Show seconds", checked: true)
menu.separator
menu.radio(:local_time, "Local time", checked: true)
menu.radio(:utc_time, "UTC")
menu.submenu(:advanced, "Advanced") do |submenu|
submenu.text(:reset_clock, "Reset clock", enabled: false)
end
end| Definition rule | Behavior |
|---|---|
| Menu IDs | Unique within the application |
| Action IDs | Unique within the top-level menu, including nested submenus |
shortcut: | Key and modifiers, for example CmdOrCtrl+Shift+P; active application shortcuts, including while a WebKit input has focus |
application | The concrete application injected into each registered definition |
menu | This registration's retained menu, available before on_register |
on_register / on_unregister | Optional private hooks to acquire and release subscriptions or resources |
Construction and registration listeners stay attached to the menu until its release, including when registration occurs in a view callback. Custom-action definitions can also be registered with trays.
Shortcuts
Pass shortcut: "CmdOrCtrl+Shift+P" to text, checkbox, or radio. Join modifiers and a final key with +; names are case-insensitive.
| Modifier | Accepted names |
|---|---|
| Command | Cmd, Command, Super |
| Control | Ctrl, Control |
| Option | Alt, Option |
| Shift | Shift |
| Platform command key | CmdOrCtrl, CommandOrControl (Command on macOS, Control elsewhere) |
| Key | Accepted names |
|---|---|
| Character | A single character, such as P or 1; use Plus for + |
| Function key | F1–F35 |
| Arrow | Up, Down, Left, Right |
| Navigation | Home, End, PageUp, PageDown |
| Return and whitespace | Return / Enter, Tab, Space |
| Escape and deletion | Escape / Esc, Backspace, Delete / ForwardDelete |
Unknown keys or modifiers, repeated modifiers, and missing keys raise ArgumentError.
Standard menus and native roles
| Definition | ID | Native role |
|---|---|---|
CryKit::Menus::AppMenu | :app_menu | :app |
CryKit::Menus::FileMenu | :file_menu | — |
CryKit::Menus::EditMenu | :edit_menu | — |
CryKit::Menus::WindowMenu | :window_menu | :window |
CryKit::Menus::HelpMenu | :help_menu | :help |
Replace a built-in in the registration list to customize it. Declare menu :window_menu, "Window", role: :window in a definition, or pass role: :window to inline create. Each role can belong to one menu and does not populate items. On macOS the application menu occupies the first native slot; custom-only menus retain a separate native application slot.
Native commands
| Command IDs | Behavior |
|---|---|
:about, :services | About panel and Services submenu |
:hide, :hide_others, :show_all, :quit | Application visibility and termination |
:close | Close the current window |
:undo, :redo, :cut, :copy, :paste, :select_all | Native editing |
:minimize, :zoom, :bring_all_to_front | Native window management |
command :close supplies native behavior; text :close, "Close" creates an application action to handle yourself. Native commands use AppKit's responder chain. Close and Quit preserve window/application close approval. Application New/Open/Save actions use text and ordinary menu subscriptions. Tray menus reject native roles and native commands.
Subscribe and update through the application
menu = app.menus[:clock]
unsubscribe = menu.on_click do |event|
if event.action == :time_control
app.state.clock.toggle_pause
event.menu[:time_control].text = app.state.clock.paused? ? "Resume" : "Pause"
end
endThis example assumes application state with a clock model and toggle_pause.
CryKit::Menus::Menu::Event member | Value |
|---|---|
menu | Retained menu that owns the action |
type | :click |
action | Activated action's Symbol ID |
click? | Whether this is a click event |
on_click, on_event(:click), and events use the same ordered event registry and return an idempotent unsubscribe function. Call unsubscribe.call to stop a listener. Events are observational: checkbox/radio state updates before listeners run; events cannot cancel that update.
Subscription lifetime and dispatch
Listeners run in registration order on the UI context. A failing listener is logged and does not prevent later listeners. Delivery is not filtered by window focus, mounted page, or subscriber count.
Subscriptions registered in a view's on_mount, UI callbacks, or job.notify blocks are removed on unmount. Other subscriptions remain until explicitly removed or the menu closes. Menus survive navigation and view replacement; native resources live from startup to shutdown. Live updates from background work must return through job.notify. Unsupported native platforms raise CryKit::Menus::UnsupportedError when installing menus.
Clock demonstration
The demo's Clock menu and tray menu share application clock state. Both update their Pause/Resume labels and remain active across navigation. The application publishes timestamps; mounted views subscribe while they are present. See application jobs and channels for those lifetimes.