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

Menus

Perry supports native menu bars, context menus, and toolbars across all platforms. Every snippet below is excerpted from docs/examples/ui/menus/snippets.ts — CI compiles and runs it on every PR.

The menu API is handle-based and free-function: build menus with menuCreate(), fill them with menuAddItem / menuAddItemWithShortcut, and attach them with menuBarAddMenu(bar, title, menu). Submenus go through menuAddSubmenu(parent, title, submenu).

// Menus are created independently, then attached. Build child menus first,
// then hand them to `menuBarAddMenu(bar, title, menu)`.
const menuBar = menuBarCreate()

// File menu
const fileMenu = menuCreate()
menuAddItemWithShortcut(fileMenu, "New",         "n", () => status.set("file/new"))
menuAddItemWithShortcut(fileMenu, "Open…",       "o", () => status.set("file/open"))
menuAddSeparator(fileMenu)
menuAddItemWithShortcut(fileMenu, "Save",        "s", () => status.set("file/save"))
menuAddItemWithShortcut(fileMenu, "Save As…",    "S", () => status.set("file/saveAs"))
menuBarAddMenu(menuBar, "File", fileMenu)

// Edit menu
const editMenu = menuCreate()
menuAddItemWithShortcut(editMenu, "Undo", "z", () => status.set("edit/undo"))
menuAddItemWithShortcut(editMenu, "Redo", "Z", () => status.set("edit/redo"))
menuAddSeparator(editMenu)
menuAddItemWithShortcut(editMenu, "Cut",   "x", () => status.set("edit/cut"))
menuAddItemWithShortcut(editMenu, "Copy",  "c", () => status.set("edit/copy"))
menuAddItemWithShortcut(editMenu, "Paste", "v", () => status.set("edit/paste"))
menuBarAddMenu(menuBar, "Edit", editMenu)

// Submenu: View → Zoom
const viewMenu = menuCreate()
const zoomSubmenu = menuCreate()
menuAddItemWithShortcut(zoomSubmenu, "Zoom In",     "+", () => status.set("zoom/in"))
menuAddItemWithShortcut(zoomSubmenu, "Zoom Out",    "-", () => status.set("zoom/out"))
menuAddItemWithShortcut(zoomSubmenu, "Actual Size", "0", () => status.set("zoom/reset"))
menuAddSubmenu(viewMenu, "Zoom", zoomSubmenu)
menuBarAddMenu(menuBar, "View", viewMenu)

menuBarAttach(menuBar)
FunctionDescription
menuBarCreate()Create a new (empty) menu bar
menuCreate()Create a new menu — used as a child of the bar or as a submenu
menuBarAddMenu(bar, title, menu)Attach a top-level menu under title
menuAddItem(menu, label, callback)Append an item without a shortcut
menuAddItemWithShortcut(menu, label, shortcut, callback)Append an item with a keyboard shortcut
menuAddSeparator(menu)Append a horizontal separator line
menuAddSubmenu(parent, title, submenu)Nest a previously-created menu under a label
menuBarAttach(bar)Install the bar as the application’s main menu

Keyboard Shortcuts

The third argument to menuAddItemWithShortcut is the shortcut key:

ShortcutmacOSOther
"n"Cmd+NCtrl+N
"S"Cmd+Shift+SCtrl+Shift+S
"+"Cmd++Ctrl++

Uppercase letters imply Shift.

Context Menus

Right-click menus are attached to widgets via widgetSetContextMenu(widget, menu). Build the menu the same way as a menu-bar entry, then bind it:

const label = Text("Right-click me")
const ctx = menuCreate()
menuAddItem(ctx, "Copy",   () => status.set("ctx/copy"))
menuAddItem(ctx, "Paste",  () => status.set("ctx/paste"))
menuAddSeparator(ctx)
menuAddItem(ctx, "Delete", () => status.set("ctx/delete"))
widgetSetContextMenu(label, ctx)

Toolbar

Add a toolbar to a window. toolbarAddItem takes an identifier (used by AppKit to deduplicate items) and a label:

const toolbar = toolbarCreate()
toolbarAddItem(toolbar, "new",  "New",  () => status.set("tb/new"))
toolbarAddItem(toolbar, "save", "Save", () => status.set("tb/save"))
toolbarAddItem(toolbar, "run",  "Run",  () => status.set("tb/run"))

// `toolbarAttach(toolbar, window)` mounts onto a specific window.
const win = Window("Toolbar Demo", 800, 600)
toolbarAttach(toolbar, win as unknown as number)

Platform Notes

PlatformMenu BarContext MenuToolbar
macOSNSMenuNSMenuNSToolbar
iOS— (no menu bar)UIMenuUIToolbar
WindowsHMENU/SetMenuHorizontal layout
LinuxGMenu/set_menubarHeaderBar
WebDOMDOMDOM

iOS: Menu bars are not applicable. Use toolbar and navigation patterns instead.

Next Steps

  • Events — Keyboard shortcuts and interactions
  • Dialogs — File dialogs and alerts
  • Layout — Toolbar and navigation patterns