gpuijs

Native shell

Menus

Menu and MenuItem follow Electron's API. Build menus from templates, set the application menu and the Dock menu, and pop up native context menus.

import { Menu, app } from "@gpuijs/core"

Menu.setApplicationMenu(
  Menu.buildFromTemplate([
    { role: "appMenu" },
    {
      label: "File",
      submenu: [
        { label: "New Note", accelerator: "CmdOrCtrl+N", click: () => createNote() },
        { label: "Open…", accelerator: "CmdOrCtrl+O", click: () => openFile() },
        { type: "separator" },
        { role: "close" },
      ],
    },
    { role: "editMenu" },
    {
      label: "View",
      submenu: [
        { id: "sidebar", label: "Show Sidebar", type: "checkbox", checked: true, click: (item) => setSidebar(item.checked) },
        { role: "togglefullscreen" },
      ],
    },
    { role: "windowMenu" },
  ]),
)

#Application menu

Menu.setApplicationMenu(menu) sets the macOS menu bar. On macOS the first top-level menu is the app menu, whatever its label, so start with { role: "appMenu" } or a submenu of your own.

Menu.getApplicationMenu() returns the menu you set, or null.

The menu bar is updated once per task, however many items changed, and only once the window is open. You can call setApplicationMenu before new Window().

A template entry is a plain object with MenuItem options, or a MenuItem instance.

OptionTypeDescription
labelstringThe item's text
type"normal" | "separator" | "submenu" | "checkbox" | "radio"Defaults to "submenu" with a submenu, else "normal"
click(item, window, event) => voidRuns when the item is chosen. event is { triggeredByAccelerator: false }.
acceleratorstringA keyboard shortcut, like "CmdOrCtrl+Shift+S". See below.
rolestringA predefined action. See Roles.
submenutemplate array or MenuA nested menu
enabledbooleanDefaults to true. Disabled items are greyed out.
visiblebooleanDefaults to true. Hidden items are left out.
checkedbooleanFor checkbox and radio items
idstringFor menu.getMenuItemById(id)
sublabelstringKept on the item, but not shown

#Changing items

enabled, visible and checked can be changed after the menu is set, and the change shows at once, as in Electron:

const menu = Menu.getApplicationMenu()
menu.getMenuItemById("sidebar").checked = false
menu.getMenuItemById("save").enabled = hasChanges

#Checkboxes and radio groups

Choosing a checkbox item toggles checked before click runs. Choosing a radio item checks it and unchecks the other radio items in its group. A group is the run of adjacent radio items around it.

{
  label: "Sort By",
  submenu: [
    { label: "Name", type: "radio", checked: true, click: () => sort("name") },
    { label: "Date", type: "radio", click: () => sort("date") },
    { label: "Size", type: "radio", click: () => sort("size") },
  ],
}

#Accelerators

Accelerators use Electron's syntax: modifiers and a key, joined by +.

ModifierMeaning on macOS
Command, Cmd, CommandOrControl, CmdOrCtrl, Super, Meta⌘
Control, Ctrl⌃
Alt, Option, AltGr⌥
Shift⇧

Keys can be letters, digits, punctuation (,, ., /, ...), F1–F24, Plus, Space, Tab, Backspace, Delete, Enter/Return, Esc/Escape, Up, Down, Left, Right, Home, End, PageUp and PageDown. Accelerators with other keys (media keys) or modifiers (Hyper) are ignored.

An accelerator works while the window is focused, even when the menu isn't open, and shows next to the item in the menu.

#Roles

A role gives an item Electron's default label and accelerator, and the action is carried out natively. Write roles in any case (selectAll or selectall).

RoleLabelAccelerator
undoUndo⌘Z
redoRedo⇧⌘Z
cutCut⌘X
copyCopy⌘C
pastePaste⌘V
selectAllSelect All⌘A
aboutAbout App
hideHide App⌘H
hideOthersHide Others⌥⌘H
unhideShow All
quitQuit App⌘Q
minimizeMinimize⌘M
zoomZoom
closeClose Window⌘W
togglefullscreenToggle Full Screen⌃⌘F
frontBring All to Front
servicesServices

You can override a role's label or accelerator.

These roles expand to a whole standard submenu:

RoleMenu
appMenuApp: About, Services, Hide, Hide Others, Show All, Quit
fileMenuFile: Close Window
editMenuEdit: Undo, Redo, Cut, Copy, Paste, Select All
viewMenuView: Toggle Full Screen
windowMenuWindow: Minimize, Zoom, Bring All to Front

App is the app's name. See Project structure: package.json.

#Dock menu

app.dock.setMenu(
  Menu.buildFromTemplate([
    { label: "New Note", click: () => createNote() },
    { label: "Quick Capture", click: () => capture() },
  ]),
)

app.dock.setMenu(menu) sets the menu shown when the Dock icon is right-clicked. app.dock.getMenu() returns it.

#Context menus

Build the menu and pop it up from a contextmenu handler:

const showNoteMenu = (note) =>
  Menu.buildFromTemplate([
    { label: "Rename", click: () => rename(note) },
    { label: "Duplicate", click: () => duplicate(note) },
    { type: "separator" },
    { label: "Delete", click: () => remove(note) },
  ]).popup({ callback: () => console.log("menu closed") })
<div onContextMenu={() => showNoteMenu(note)}>{note.title}</div>      // React, Solid
<div @contextmenu="showNoteMenu(note)">{{ note.title }}</div>        <!-- Vue -->

menu.popup(options?) shows the menu as a native context menu. options.callback runs once it closes. The menu emits menu-will-show when it opens and menu-will-close when it closes.

Note

The context menu always opens at the pointer. The x, y and window options are ignored. menu.closePopup() does nothing.

MemberDescription
Menu.buildFromTemplate(template)A menu from template objects or MenuItems
Menu.setApplicationMenu(menu)Sets the menu bar
Menu.getApplicationMenu()The menu bar's menu, or null
new Menu()An empty menu
menu.itemsIts MenuItems
menu.append(item)Adds an item at the end
menu.insert(pos, item)Inserts an item at pos
menu.getMenuItemById(id)Finds an item by id, searching submenus
menu.popup(options)Shows it as a context menu

new MenuItem(options) takes the options above. An item has label, type, role, accelerator, submenu, click, id, sublabel, enabled, visible, checked and menu (the menu it's in).