725 lines
14 KiB
Text
725 lines
14 KiB
Text
|
|
---
|
||
|
|
title: Menu API
|
||
|
|
description: Complete reference for the Menu API
|
||
|
|
sidebar:
|
||
|
|
order: 3
|
||
|
|
---
|
||
|
|
|
||
|
|
import { Card, CardGrid } from "@astrojs/starlight/components";
|
||
|
|
|
||
|
|
## Overview
|
||
|
|
|
||
|
|
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
|
||
|
|
|
||
|
|
## Creating Menus
|
||
|
|
|
||
|
|
### NewMenu()
|
||
|
|
|
||
|
|
Creates a new menu.
|
||
|
|
|
||
|
|
```go
|
||
|
|
func (a *App) NewMenu() *Menu
|
||
|
|
```
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
```go
|
||
|
|
menu := app.NewMenu()
|
||
|
|
```
|
||
|
|
|
||
|
|
## Menu Methods
|
||
|
|
|
||
|
|
### Add()
|
||
|
|
|
||
|
|
Adds a menu item to the menu.
|
||
|
|
|
||
|
|
```go
|
||
|
|
func (m *Menu) Add(label string) *MenuItem
|
||
|
|
```
|
||
|
|
|
||
|
|
**Parameters:**
|
||
|
|
- `label` - The text displayed for the menu item
|
||
|
|
|
||
|
|
**Returns:** The created menu item
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
```go
|
||
|
|
item := menu.Add("Open File")
|
||
|
|
item.OnClick(func(ctx *application.Context) {
|
||
|
|
// Handle click
|
||
|
|
})
|
||
|
|
```
|
||
|
|
|
||
|
|
### AddSubmenu()
|
||
|
|
|
||
|
|
Adds a submenu to the menu.
|
||
|
|
|
||
|
|
```go
|
||
|
|
func (m *Menu) AddSubmenu(label string) *Menu
|
||
|
|
```
|
||
|
|
|
||
|
|
**Parameters:**
|
||
|
|
- `label` - The submenu label
|
||
|
|
|
||
|
|
**Returns:** The created submenu
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
```go
|
||
|
|
fileMenu := menu.AddSubmenu("File")
|
||
|
|
fileMenu.Add("New")
|
||
|
|
fileMenu.Add("Open")
|
||
|
|
fileMenu.Add("Save")
|
||
|
|
```
|
||
|
|
|
||
|
|
### AddSeparator()
|
||
|
|
|
||
|
|
Adds a visual separator line between menu items.
|
||
|
|
|
||
|
|
```go
|
||
|
|
func (m *Menu) AddSeparator()
|
||
|
|
```
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
```go
|
||
|
|
menu.Add("Copy")
|
||
|
|
menu.Add("Paste")
|
||
|
|
menu.AddSeparator()
|
||
|
|
menu.Add("Select All")
|
||
|
|
```
|
||
|
|
|
||
|
|
**Best practice:** Use separators to group related menu items.
|
||
|
|
|
||
|
|
### AddCheckbox()
|
||
|
|
|
||
|
|
Adds a checkable menu item.
|
||
|
|
|
||
|
|
```go
|
||
|
|
func (m *Menu) AddCheckbox(label string, checked bool) *MenuItem
|
||
|
|
```
|
||
|
|
|
||
|
|
**Parameters:**
|
||
|
|
- `label` - The checkbox label
|
||
|
|
- `checked` - Initial checked state
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
```go
|
||
|
|
darkMode := menu.AddCheckbox("Dark Mode", false)
|
||
|
|
darkMode.OnClick(func(ctx *application.Context) {
|
||
|
|
isChecked := darkMode.Checked()
|
||
|
|
// Toggle dark mode
|
||
|
|
})
|
||
|
|
```
|
||
|
|
|
||
|
|
### AddRadio()
|
||
|
|
|
||
|
|
Adds a radio menu item (mutually exclusive group).
|
||
|
|
|
||
|
|
```go
|
||
|
|
func (m *Menu) AddRadio(label string, checked bool) *MenuItem
|
||
|
|
```
|
||
|
|
|
||
|
|
**Parameters:**
|
||
|
|
- `label` - The radio button label
|
||
|
|
- `checked` - Initial checked state
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
```go
|
||
|
|
// 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")
|
||
|
|
})
|
||
|
|
```
|
||
|
|
|
||
|
|
### Update()
|
||
|
|
|
||
|
|
Updates the menu to reflect any changes made to menu items.
|
||
|
|
|
||
|
|
```go
|
||
|
|
func (m *Menu) Update()
|
||
|
|
```
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
```go
|
||
|
|
item.SetEnabled(false)
|
||
|
|
menu.Update() // Must call to apply changes
|
||
|
|
```
|
||
|
|
|
||
|
|
**Important:** Always call `Update()` after modifying menu item properties.
|
||
|
|
|
||
|
|
## Menu Item Methods
|
||
|
|
|
||
|
|
### OnClick()
|
||
|
|
|
||
|
|
Registers a click handler for the menu item.
|
||
|
|
|
||
|
|
```go
|
||
|
|
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:**
|
||
|
|
```go
|
||
|
|
item.OnClick(func(ctx *application.Context) {
|
||
|
|
fmt.Println("Menu item clicked")
|
||
|
|
app.Logger.Info("User clicked menu item")
|
||
|
|
})
|
||
|
|
```
|
||
|
|
|
||
|
|
### SetLabel()
|
||
|
|
|
||
|
|
Changes the menu item's label.
|
||
|
|
|
||
|
|
```go
|
||
|
|
func (mi *MenuItem) SetLabel(label string) *MenuItem
|
||
|
|
```
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
```go
|
||
|
|
item.SetLabel("Save As...")
|
||
|
|
menu.Update()
|
||
|
|
```
|
||
|
|
|
||
|
|
### SetEnabled()
|
||
|
|
|
||
|
|
Enables or disables the menu item.
|
||
|
|
|
||
|
|
```go
|
||
|
|
func (mi *MenuItem) SetEnabled(enabled bool) *MenuItem
|
||
|
|
```
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
```go
|
||
|
|
// Disable save when no document is open
|
||
|
|
saveItem.SetEnabled(hasOpenDocument)
|
||
|
|
menu.Update()
|
||
|
|
```
|
||
|
|
|
||
|
|
**Common pattern:**
|
||
|
|
```go
|
||
|
|
// Update menu state based on application state
|
||
|
|
func updateMenuState() {
|
||
|
|
saveItem.SetEnabled(hasUnsavedChanges)
|
||
|
|
undoItem.SetEnabled(canUndo)
|
||
|
|
redoItem.SetEnabled(canRedo)
|
||
|
|
menu.Update()
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### SetChecked()
|
||
|
|
|
||
|
|
Sets the checked state for checkbox/radio menu items.
|
||
|
|
|
||
|
|
```go
|
||
|
|
func (mi *MenuItem) SetChecked(checked bool) *MenuItem
|
||
|
|
```
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
```go
|
||
|
|
darkModeItem.SetChecked(isDarkModeEnabled)
|
||
|
|
menu.Update()
|
||
|
|
```
|
||
|
|
|
||
|
|
### Checked()
|
||
|
|
|
||
|
|
Returns the current checked state.
|
||
|
|
|
||
|
|
```go
|
||
|
|
func (mi *MenuItem) Checked() bool
|
||
|
|
```
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
```go
|
||
|
|
if darkModeItem.Checked() {
|
||
|
|
// Dark mode is enabled
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### SetAccelerator()
|
||
|
|
|
||
|
|
Sets a keyboard shortcut for the menu item.
|
||
|
|
|
||
|
|
```go
|
||
|
|
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:**
|
||
|
|
```go
|
||
|
|
saveItem.SetAccelerator("Ctrl+S")
|
||
|
|
quitItem.SetAccelerator("Ctrl+Q")
|
||
|
|
newItem.SetAccelerator("Ctrl+N")
|
||
|
|
```
|
||
|
|
|
||
|
|
**Platform-aware example:**
|
||
|
|
```go
|
||
|
|
import "runtime"
|
||
|
|
|
||
|
|
var quitShortcut string
|
||
|
|
if runtime.GOOS == "darwin" {
|
||
|
|
quitShortcut = "Cmd+Q"
|
||
|
|
} else {
|
||
|
|
quitShortcut = "Ctrl+Q"
|
||
|
|
}
|
||
|
|
quitItem.SetAccelerator(quitShortcut)
|
||
|
|
```
|
||
|
|
|
||
|
|
### SetTooltip()
|
||
|
|
|
||
|
|
Sets a tooltip that appears when hovering over the menu item.
|
||
|
|
|
||
|
|
```go
|
||
|
|
func (mi *MenuItem) SetTooltip(tooltip string) *MenuItem
|
||
|
|
```
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
```go
|
||
|
|
item.SetTooltip("Opens a file from disk")
|
||
|
|
```
|
||
|
|
|
||
|
|
### SetHidden()
|
||
|
|
|
||
|
|
Shows or hides the menu item.
|
||
|
|
|
||
|
|
```go
|
||
|
|
func (mi *MenuItem) SetHidden(hidden bool) *MenuItem
|
||
|
|
```
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
```go
|
||
|
|
// Hide debug menu in production
|
||
|
|
debugItem.SetHidden(!isDevelopment)
|
||
|
|
menu.Update()
|
||
|
|
```
|
||
|
|
|
||
|
|
## Application Menu
|
||
|
|
|
||
|
|
### app.Menu.Set()
|
||
|
|
|
||
|
|
Sets the application's main menu bar.
|
||
|
|
|
||
|
|
```go
|
||
|
|
func (mm *MenuManager) Set(menu *Menu)
|
||
|
|
```
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
```go
|
||
|
|
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
|
||
|
|
|
||
|
|
## Context Menus
|
||
|
|
|
||
|
|
### app.ContextMenu.Add()
|
||
|
|
|
||
|
|
Registers a context menu (right-click menu) with a specific name.
|
||
|
|
|
||
|
|
```go
|
||
|
|
func (cm *ContextMenuManager) Add(name string, menu *Menu)
|
||
|
|
```
|
||
|
|
|
||
|
|
**Parameters:**
|
||
|
|
- `name` - Unique identifier for the context menu
|
||
|
|
- `menu` - The menu to show
|
||
|
|
|
||
|
|
**Go:**
|
||
|
|
|
||
|
|
```go
|
||
|
|
// Create context menu
|
||
|
|
contextMenu := app.NewMenu()
|
||
|
|
contextMenu.Add("Cut").OnClick(cut)
|
||
|
|
contextMenu.Add("Copy").OnClick(copy)
|
||
|
|
contextMenu.Add("Paste").OnClick(paste)
|
||
|
|
contextMenu.AddSeparator()
|
||
|
|
contextMenu.Add("Select All").OnClick(selectAll)
|
||
|
|
|
||
|
|
// Register it
|
||
|
|
app.RegisterContextMenu("editor", contextMenu)
|
||
|
|
```
|
||
|
|
|
||
|
|
**HTML:**
|
||
|
|
|
||
|
|
```html
|
||
|
|
<!-- Trigger context menu on right-click -->
|
||
|
|
<div data-wails-context-menu="editor">
|
||
|
|
Right-click here for context menu
|
||
|
|
</div>
|
||
|
|
```
|
||
|
|
|
||
|
|
**Dynamic context menus:**
|
||
|
|
```go
|
||
|
|
// Update context menu based on selection
|
||
|
|
func updateContextMenu() {
|
||
|
|
contextMenu := app.NewMenu()
|
||
|
|
|
||
|
|
if hasSelection {
|
||
|
|
contextMenu.Add("Cut").OnClick(cut)
|
||
|
|
contextMenu.Add("Copy").OnClick(copy)
|
||
|
|
}
|
||
|
|
|
||
|
|
contextMenu.Add("Paste").SetEnabled(hasClipboardContent).OnClick(paste)
|
||
|
|
|
||
|
|
app.RegisterContextMenu("editor", contextMenu)
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## System Tray Menu
|
||
|
|
|
||
|
|
### app.SystemTray.New()
|
||
|
|
|
||
|
|
Creates a new system tray icon.
|
||
|
|
|
||
|
|
```go
|
||
|
|
func (sm *SystemTrayManager) New() *SystemTray
|
||
|
|
```
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
```go
|
||
|
|
tray := app.SystemTray.New()
|
||
|
|
```
|
||
|
|
|
||
|
|
### SetIcon()
|
||
|
|
|
||
|
|
Sets the system tray icon.
|
||
|
|
|
||
|
|
```go
|
||
|
|
func (st *SystemTray) SetIcon(icon []byte) *SystemTray
|
||
|
|
```
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
```go
|
||
|
|
iconData, _ := os.ReadFile("icon.png")
|
||
|
|
tray.SetIcon(iconData)
|
||
|
|
```
|
||
|
|
|
||
|
|
### SetMenu()
|
||
|
|
|
||
|
|
Sets the menu for the system tray.
|
||
|
|
|
||
|
|
```go
|
||
|
|
func (st *SystemTray) SetMenu(menu *Menu) *SystemTray
|
||
|
|
```
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
```go
|
||
|
|
trayMenu := app.NewMenu()
|
||
|
|
trayMenu.Add("Show Window").OnClick(func(ctx *application.Context) {
|
||
|
|
window.Show()
|
||
|
|
window.SetFocus()
|
||
|
|
})
|
||
|
|
trayMenu.Add("Settings").OnClick(openSettings)
|
||
|
|
trayMenu.AddSeparator()
|
||
|
|
trayMenu.Add("Quit").OnClick(func(ctx *application.Context) {
|
||
|
|
app.Quit()
|
||
|
|
})
|
||
|
|
|
||
|
|
tray.SetMenu(trayMenu)
|
||
|
|
```
|
||
|
|
|
||
|
|
### SetTooltip()
|
||
|
|
|
||
|
|
Sets the tooltip shown when hovering over the tray icon.
|
||
|
|
|
||
|
|
```go
|
||
|
|
func (st *SystemTray) SetTooltip(tooltip string) *SystemTray
|
||
|
|
```
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
```go
|
||
|
|
tray.SetTooltip("My Application - Running")
|
||
|
|
```
|
||
|
|
|
||
|
|
### OnClick()
|
||
|
|
|
||
|
|
Handles left-click on the tray icon.
|
||
|
|
|
||
|
|
```go
|
||
|
|
func (st *SystemTray) OnClick(callback func()) *SystemTray
|
||
|
|
```
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
```go
|
||
|
|
tray.OnClick(func() {
|
||
|
|
if window.IsVisible() {
|
||
|
|
window.Hide()
|
||
|
|
} else {
|
||
|
|
window.Show()
|
||
|
|
window.SetFocus()
|
||
|
|
}
|
||
|
|
})
|
||
|
|
```
|
||
|
|
|
||
|
|
## Complete Examples
|
||
|
|
|
||
|
|
### Standard Application Menu
|
||
|
|
|
||
|
|
```go
|
||
|
|
package main
|
||
|
|
|
||
|
|
import (
|
||
|
|
"github.com/wailsapp/wails/v3/pkg/application"
|
||
|
|
)
|
||
|
|
|
||
|
|
func createMenu(app *application.Application) *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()
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### System Tray Application
|
||
|
|
|
||
|
|
```go
|
||
|
|
func setupSystemTray(app *application.Application, 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.SetFocus()
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
// Create tray menu
|
||
|
|
trayMenu := app.NewMenu()
|
||
|
|
|
||
|
|
showItem := trayMenu.Add("Show Window")
|
||
|
|
showItem.OnClick(func(ctx *application.Context) {
|
||
|
|
window.Show()
|
||
|
|
window.SetFocus()
|
||
|
|
})
|
||
|
|
|
||
|
|
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)
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Dynamic Menu Updates
|
||
|
|
|
||
|
|
```go
|
||
|
|
type Editor struct {
|
||
|
|
app *application.Application
|
||
|
|
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()
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Best Practices
|
||
|
|
|
||
|
|
### ✅ Do
|
||
|
|
|
||
|
|
- **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
|
||
|
|
|
||
|
|
- **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
|
||
|
|
|
||
|
|
## Platform-Specific Notes
|
||
|
|
|
||
|
|
### macOS
|
||
|
|
|
||
|
|
- Application menu automatically added with app name
|
||
|
|
- Use `Cmd` instead of `Ctrl` for accelerators
|
||
|
|
- "About", "Preferences", and "Quit" in application menu by default
|
||
|
|
|
||
|
|
### Windows/Linux
|
||
|
|
|
||
|
|
- No automatic application menu
|
||
|
|
- Use `Ctrl` for accelerators
|
||
|
|
- "Exit" typically in File menu
|