Pular para o conteúdo

Menu API

Este conteúdo não está disponível em sua língua ainda.

The Menu API provides methods to create and manage application menus, context menus, and system tray menus.

Menu Types:

  • Application Menus - Top menu bar (File, Edit, etc.)
  • Context Menus - Right-click menus
  • System Tray Menus - Menus in the system tray/notification area

Creates a new menu.

func (a *App) NewMenu() *Menu

Example:

menu := app.NewMenu()

Adds a menu item to the menu.

func (m *Menu) Add(label string) *MenuItem

Parameters:

  • label - The text displayed for the menu item

Returns: The created menu item

Example:

item := menu.Add("Open File")
item.OnClick(func(ctx *application.Context) {
// Handle click
})

Adds a submenu to the menu.

func (m *Menu) AddSubmenu(label string) *Menu

Parameters:

  • label - The submenu label

Returns: The created submenu

Example:

fileMenu := menu.AddSubmenu("File")
fileMenu.Add("New")
fileMenu.Add("Open")
fileMenu.Add("Save")

Adds a visual separator line between menu items.

func (m *Menu) AddSeparator()

Example:

menu.Add("Copy")
menu.Add("Paste")
menu.AddSeparator()
menu.Add("Select All")

Best practice: Use separators to group related menu items.

Adds a checkable menu item.

func (m *Menu) AddCheckbox(label string, checked bool) *MenuItem

Parameters:

  • label - The checkbox label
  • checked - Initial checked state

Example:

darkMode := menu.AddCheckbox("Dark Mode", false)
darkMode.OnClick(func(ctx *application.Context) {
isChecked := darkMode.Checked()
// Toggle dark mode
})

Adds a radio menu item (mutually exclusive group).

func (m *Menu) AddRadio(label string, checked bool) *MenuItem

Parameters:

  • label - The radio button label
  • checked - Initial checked state

Example:

// Create radio group for view modes
viewMenu := menu.AddSubmenu("View")
listView := viewMenu.AddRadio("List View", true)
gridView := viewMenu.AddRadio("Grid View", false)
treeView := viewMenu.AddRadio("Tree View", false)
listView.OnClick(func(ctx *application.Context) {
setViewMode("list")
})
gridView.OnClick(func(ctx *application.Context) {
setViewMode("grid")
})

Updates the menu to reflect any changes made to menu items.

func (m *Menu) Update()

Example:

item.SetEnabled(false)
menu.Update() // Must call to apply changes

Important: Always call Update() after modifying menu item properties.

Registers a click handler for the menu item.

func (mi *MenuItem) OnClick(callback func(ctx *application.Context)) *MenuItem

Parameters:

  • callback - Function called when item is clicked

Returns: The menu item (for chaining)

Example:

item.OnClick(func(ctx *application.Context) {
fmt.Println("Menu item clicked")
app.Logger.Info("User clicked menu item")
})

Changes the menu item’s label.

func (mi *MenuItem) SetLabel(label string) *MenuItem

Example:

item.SetLabel("Save As...")
menu.Update()

Enables or disables the menu item.

func (mi *MenuItem) SetEnabled(enabled bool) *MenuItem

Example:

// Disable save when no document is open
saveItem.SetEnabled(hasOpenDocument)
menu.Update()

Common pattern:

// Update menu state based on application state
func updateMenuState() {
saveItem.SetEnabled(hasUnsavedChanges)
undoItem.SetEnabled(canUndo)
redoItem.SetEnabled(canRedo)
menu.Update()
}

Sets the checked state for checkbox/radio menu items.

func (mi *MenuItem) SetChecked(checked bool) *MenuItem

Example:

darkModeItem.SetChecked(isDarkModeEnabled)
menu.Update()

Returns the current checked state.

func (mi *MenuItem) Checked() bool

Example:

if darkModeItem.Checked() {
// Dark mode is enabled
}

Sets a keyboard shortcut for the menu item.

func (mi *MenuItem) SetAccelerator(accelerator string) *MenuItem

Parameters:

  • accelerator - Keyboard shortcut (e.g., “Ctrl+S”, “Cmd+Q”)

Accelerator format:

  • Modifiers: Ctrl, Cmd, Alt, Shift
  • Keys: A-Z, 0-9, F1-F12, Enter, Backspace, etc.
  • Platform: Use Cmd on macOS, Ctrl on Windows/Linux

Example:

saveItem.SetAccelerator("Ctrl+S")
quitItem.SetAccelerator("Ctrl+Q")
newItem.SetAccelerator("Ctrl+N")

Platform-aware example:

import "runtime"
var quitShortcut string
if runtime.GOOS == "darwin" {
quitShortcut = "Cmd+Q"
} else {
quitShortcut = "Ctrl+Q"
}
quitItem.SetAccelerator(quitShortcut)

Sets a tooltip that appears when hovering over the menu item.

func (mi *MenuItem) SetTooltip(tooltip string) *MenuItem

Example:

item.SetTooltip("Opens a file from disk")

Shows or hides the menu item.

func (mi *MenuItem) SetHidden(hidden bool) *MenuItem

Example:

// Hide debug menu in production
debugItem.SetHidden(!isDevelopment)
menu.Update()

Sets the application’s main menu bar.

func (mm *MenuManager) Set(menu *Menu)

Example:

menu := app.NewMenu()
// File menu
fileMenu := menu.AddSubmenu("File")
fileMenu.Add("New").SetAccelerator("Ctrl+N").OnClick(newFile)
fileMenu.Add("Open").SetAccelerator("Ctrl+O").OnClick(openFile)
fileMenu.Add("Save").SetAccelerator("Ctrl+S").OnClick(saveFile)
fileMenu.AddSeparator()
fileMenu.Add("Exit").SetAccelerator("Ctrl+Q").OnClick(func(ctx *application.Context) {
app.Quit()
})
// Edit menu
editMenu := menu.AddSubmenu("Edit")
editMenu.Add("Undo").SetAccelerator("Ctrl+Z").OnClick(undo)
editMenu.Add("Redo").SetAccelerator("Ctrl+Y").OnClick(redo)
editMenu.AddSeparator()
editMenu.Add("Cut").SetAccelerator("Ctrl+X").OnClick(cut)
editMenu.Add("Copy").SetAccelerator("Ctrl+C").OnClick(copy)
editMenu.Add("Paste").SetAccelerator("Ctrl+V").OnClick(paste)
app.Menu.Set(menu)

Platform notes:

  • macOS: Menu appears in the top menu bar
  • Windows/Linux: Menu appears in the window title bar
  • macOS: Automatically adds application menu with app name

app.ContextMenu.New() / app.ContextMenu.Add()

Section titled “app.ContextMenu.New() / app.ContextMenu.Add()”

Create a *ContextMenu via the manager and register it under a name. ContextMenuManager.Add takes *ContextMenunot *Menu, and there is no app.RegisterContextMenu method.

func (cm *ContextMenuManager) New() *ContextMenu
func (cm *ContextMenuManager) Add(name string, menu *ContextMenu)
func (cm *ContextMenuManager) Get(name string) (*ContextMenu, bool)
func (cm *ContextMenuManager) Remove(name string)

Alternatively, the package-level application.NewContextMenu(name string) *ContextMenu creates and registers a context menu in one step.

Go:

// Build the context menu via the manager
contextMenu := app.ContextMenu.New()
contextMenu.Add("Cut").OnClick(cut)
contextMenu.Add("Copy").OnClick(copy)
contextMenu.Add("Paste").OnClick(paste)
contextMenu.AddSeparator()
contextMenu.Add("Select All").OnClick(selectAll)
// Register under a name; HTML opts into it via the CSS custom property below.
app.ContextMenu.Add("editor", contextMenu)

HTML / CSS:

The runtime triggers a registered context menu when the right-click target (or any ancestor) has the --custom-contextmenu CSS custom property set to that name. The optional --custom-contextmenu-data property is passed to the Go callback via ctx.ContextMenuData(). To suppress the default browser context menu, set --default-contextmenu: hide (or auto/show).

<!-- Trigger context menu on right-click -->
<div style="--custom-contextmenu: editor; --default-contextmenu: hide">
Right-click here for context menu
</div>

There is no data-wails-context-menu="..." attribute — that was never wired in the runtime.

Dynamic context menus:

// Update context menu based on selection
func updateContextMenu() {
contextMenu := app.ContextMenu.New()
if hasSelection {
contextMenu.Add("Cut").OnClick(cut)
contextMenu.Add("Copy").OnClick(copy)
}
contextMenu.Add("Paste").SetEnabled(hasClipboardContent).OnClick(paste)
app.ContextMenu.Add("editor", contextMenu)
}

Creates a new system tray icon.

func (sm *SystemTrayManager) New() *SystemTray

Example:

tray := app.SystemTray.New()

Sets the system tray icon.

func (st *SystemTray) SetIcon(icon []byte) *SystemTray

Example:

iconData, _ := os.ReadFile("icon.png")
tray.SetIcon(iconData)

Sets the menu for the system tray.

func (st *SystemTray) SetMenu(menu *Menu) *SystemTray

Example:

trayMenu := app.NewMenu()
trayMenu.Add("Show Window").OnClick(func(ctx *application.Context) {
window.Show()
window.Focus()
})
trayMenu.Add("Settings").OnClick(openSettings)
trayMenu.AddSeparator()
trayMenu.Add("Quit").OnClick(func(ctx *application.Context) {
app.Quit()
})
tray.SetMenu(trayMenu)

Sets the tooltip shown when hovering over the tray icon. Returns nothing.

func (st *SystemTray) SetTooltip(tooltip string)

Example:

tray.SetTooltip("My Application - Running")

Handles left-click on the tray icon.

func (st *SystemTray) OnClick(callback func()) *SystemTray

Example:

tray.OnClick(func() {
if window.IsVisible() {
window.Hide()
} else {
window.Show()
window.Focus()
}
})
package main
import (
"github.com/wailsapp/wails/v3/pkg/application"
)
func createMenu(app *application.App) *application.Menu {
menu := app.NewMenu()
// File menu
fileMenu := menu.AddSubmenu("File")
fileMenu.Add("New").
SetAccelerator("Ctrl+N").
OnClick(func(ctx *application.Context) {
// Create new document
})
fileMenu.Add("Open").
SetAccelerator("Ctrl+O").
OnClick(func(ctx *application.Context) {
// Open file dialog
})
fileMenu.Add("Save").
SetAccelerator("Ctrl+S").
OnClick(func(ctx *application.Context) {
// Save document
})
fileMenu.AddSeparator()
fileMenu.Add("Exit").OnClick(func(ctx *application.Context) {
app.Quit()
})
// Edit menu
editMenu := menu.AddSubmenu("Edit")
editMenu.Add("Undo").SetAccelerator("Ctrl+Z")
editMenu.Add("Redo").SetAccelerator("Ctrl+Y")
editMenu.AddSeparator()
editMenu.Add("Cut").SetAccelerator("Ctrl+X")
editMenu.Add("Copy").SetAccelerator("Ctrl+C")
editMenu.Add("Paste").SetAccelerator("Ctrl+V")
// View menu
viewMenu := menu.AddSubmenu("View")
darkMode := viewMenu.AddCheckbox("Dark Mode", false)
darkMode.OnClick(func(ctx *application.Context) {
// Toggle dark mode
isChecked := darkMode.Checked()
app.Logger.Info("Dark mode", "enabled", isChecked)
})
viewMenu.AddSeparator()
viewMenu.AddRadio("List View", true)
viewMenu.AddRadio("Grid View", false)
viewMenu.AddRadio("Detail View", false)
// Help menu
helpMenu := menu.AddSubmenu("Help")
helpMenu.Add("Documentation").OnClick(func(ctx *application.Context) {
// Open docs
})
helpMenu.Add("About").OnClick(func(ctx *application.Context) {
// Show about dialog
})
return menu
}
func main() {
app := application.New(application.Options{
Name: "Menu Demo",
})
menu := createMenu(app)
app.Menu.Set(menu)
window := app.Window.New()
window.Show()
app.Run()
}
func setupSystemTray(app *application.App, window application.Window) {
// Create system tray
tray := app.SystemTray.New()
// Set icon
iconData, _ := os.ReadFile("icon.png")
tray.SetIcon(iconData)
tray.SetTooltip("My App - Running")
// Handle left-click on tray icon
tray.OnClick(func() {
if window.IsVisible() {
window.Hide()
} else {
window.Show()
window.Focus()
}
})
// Create tray menu
trayMenu := app.NewMenu()
showItem := trayMenu.Add("Show Window")
showItem.OnClick(func(ctx *application.Context) {
window.Show()
window.Focus()
})
trayMenu.AddSeparator()
trayMenu.Add("Settings").OnClick(func(ctx *application.Context) {
// Open settings window
})
trayMenu.Add("About").OnClick(func(ctx *application.Context) {
// Show about dialog
})
trayMenu.AddSeparator()
trayMenu.Add("Quit").OnClick(func(ctx *application.Context) {
app.Quit()
})
tray.SetMenu(trayMenu)
}
type Editor struct {
app *application.App
menu *application.Menu
undoItem *application.MenuItem
redoItem *application.MenuItem
saveItem *application.MenuItem
undoStack []string
redoStack []string
hasChanges bool
}
func (e *Editor) createMenu() {
e.menu = e.app.NewMenu()
fileMenu := e.menu.AddSubmenu("File")
e.saveItem = fileMenu.Add("Save").SetAccelerator("Ctrl+S")
e.saveItem.OnClick(func(ctx *application.Context) {
e.save()
})
editMenu := e.menu.AddSubmenu("Edit")
e.undoItem = editMenu.Add("Undo").SetAccelerator("Ctrl+Z")
e.undoItem.OnClick(func(ctx *application.Context) {
e.undo()
})
e.redoItem = editMenu.Add("Redo").SetAccelerator("Ctrl+Y")
e.redoItem.OnClick(func(ctx *application.Context) {
e.redo()
})
e.updateMenuState()
e.app.Menu.Set(e.menu)
}
func (e *Editor) updateMenuState() {
// Update menu items based on current state
e.saveItem.SetEnabled(e.hasChanges)
e.undoItem.SetEnabled(len(e.undoStack) > 0)
e.redoItem.SetEnabled(len(e.redoStack) > 0)
e.menu.Update()
}
func (e *Editor) onChange() {
e.hasChanges = true
e.updateMenuState()
}
func (e *Editor) save() {
// Save logic
e.hasChanges = false
e.updateMenuState()
}
  • Use standard accelerators - Follow platform conventions (Ctrl+C for copy, etc.)
  • Call Update() after changes - Menu won’t reflect changes otherwise
  • Group related items - Use separators to organize menu items
  • Disable unavailable actions - Don’t hide, disable with SetEnabled(false)
  • Use clear labels - Be concise and descriptive
  • Follow platform conventions - macOS vs Windows/Linux menu patterns
  • Don’t forget Update() - Most common mistake
  • Don’t nest too deeply - Keep menus 2-3 levels maximum
  • Don’t use ambiguous labels - “Process” vs “Process Document”
  • Don’t overcomplicate - Keep menus simple and focused
  • Don’t mix metaphors - Consistent naming and organization
  • Application menu automatically added with app name
  • Use Cmd instead of Ctrl for accelerators
  • “About”, “Preferences”, and “Quit” in application menu by default
  • No automatic application menu
  • Use Ctrl for accelerators
  • “Exit” typically in File menu