選單 API
選單 API 完整參考資料
概觀
選單 API 提供建立及管理應用程式選單、快顯選單和系統匣選單的方法。
選單類型:
- 應用程式選單——頂端選單列(檔案、編輯等)
- 快顯選單——按一下滑鼠右鍵時顯示的選單
- 系統匣選單——系統匣/通知區域中的選單
建立選單
NewMenu()
建立新選單。
func (a *App) NewMenu() *Menu範例:
menu := app.NewMenu()選單方法
Add()
將選單項目新增至選單。
func (m *Menu) Add(label string) *MenuItem參數:
label——選單項目所顯示的文字
傳回值:建立的選單項目
範例:
item := menu.Add("Open File")
item.OnClick(func(ctx *application.Context) {
// Handle click
})AddSubmenu()
將子選單新增至選單。
func (m *Menu) AddSubmenu(label string) *Menu參數:
label——子選單標籤
傳回值:建立的子選單
範例:
fileMenu := menu.AddSubmenu("File")
fileMenu.Add("New")
fileMenu.Add("Open")
fileMenu.Add("Save")AddSeparator()
在選單項目之間新增一條視覺分隔線。
func (m *Menu) AddSeparator()範例:
menu.Add("Copy")
menu.Add("Paste")
menu.AddSeparator()
menu.Add("Select All")最佳實務:使用分隔線將相關的選單項目分組。
AddSectionHeader()
在 macOS 14 及更新版本上新增原生、不可互動的區段標題。在較舊版本的 macOS 和所有其他平台(包括伺服器建置)上,此呼叫不會執行任何操作:不會插入預留位置項目。
func (m *Menu) AddSectionHeader(label string)使用標題為系統匣選單、快顯選單或應用程式選單中的群組加上標籤:
menu.AddSectionHeader("mac-node1")
menu.Add("Windows VM").OnClick(openWindowsVM)
menu.Add("Linux VM").OnClick(openLinuxVM)
menu.AddSectionHeader("ai-master")
menu.Add("Build VM").OnClick(openBuildVM)標題使用 AppKit 標準的區段標題外觀。重新建構已顯示的選單後,與其他結構變更一樣,請呼叫 menu.Update()。如需使用 os.Hostname() 取得標題的範例,請參閱 systray-menu。
省略的標題不會分隔單選群組。若群組必須在每個平台上保持獨立,請使用 AddSeparator()。
AddCheckbox()
新增可勾選的選單項目。
func (m *Menu) AddCheckbox(label string, checked bool) *MenuItem參數:
label——核取方塊標籤checked——初始勾選狀態
範例:
darkMode := menu.AddCheckbox("Dark Mode", false)
darkMode.OnClick(func(ctx *application.Context) {
isChecked := darkMode.Checked()
// Toggle dark mode
})AddRadio()
新增單選選單項目(互斥群組)。
func (m *Menu) AddRadio(label string, checked bool) *MenuItem參數:
label——選項按鈕標籤checked——初始勾選狀態
範例:
// 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()
更新選單,以反映對選單項目所做的任何變更。
func (m *Menu) Update()範例:
item.SetEnabled(false)
menu.Update() // Must call to apply changes重要:修改選單項目的屬性後,一律呼叫Update()。
選單項目方法
OnClick()
為選單項目註冊點擊處理常式。
func (mi *MenuItem) OnClick(callback func(ctx *application.Context)) *MenuItem參數:
callback——點擊項目時呼叫的函式
傳回值:選單項目(用於鏈式呼叫)
範例:
item.OnClick(func(ctx *application.Context) {
fmt.Println("Menu item clicked")
app.Logger.Info("User clicked menu item")
})SetLabel()
變更選單項目的標籤。
func (mi *MenuItem) SetLabel(label string) *MenuItem範例:
item.SetLabel("Save As...")
menu.Update()SetEnabled()
啟用或停用選單項目。
func (mi *MenuItem) SetEnabled(enabled bool) *MenuItem範例:
// Disable save when no document is open
saveItem.SetEnabled(hasOpenDocument)
menu.Update()常見模式:
// Update menu state based on application state
func updateMenuState() {
saveItem.SetEnabled(hasUnsavedChanges)
undoItem.SetEnabled(canUndo)
redoItem.SetEnabled(canRedo)
menu.Update()
}SetChecked()
設定核取方塊/單選選單項目的勾選狀態。
func (mi *MenuItem) SetChecked(checked bool) *MenuItem範例:
darkModeItem.SetChecked(isDarkModeEnabled)
menu.Update()Checked()
傳回目前的勾選狀態。
func (mi *MenuItem) Checked() bool範例:
if darkModeItem.Checked() {
// Dark mode is enabled
}SetAccelerator()
為選單項目設定鍵盤快速鍵。
func (mi *MenuItem) SetAccelerator(accelerator string) *MenuItem參數:
accelerator——鍵盤快速鍵(例如「Ctrl+S」、「Cmd+Q」)
快速鍵格式:
- 輔助按鍵:
Ctrl、Cmd、Alt、Shift - 按鍵:
A-Z、0-9、F1-F12、Enter、Backspace等 - 平台:macOS 使用
Cmd,Windows/Linux 使用Ctrl
範例:
saveItem.SetAccelerator("Ctrl+S")
quitItem.SetAccelerator("Ctrl+Q")
newItem.SetAccelerator("Ctrl+N")平台感知範例:
import "runtime"
var quitShortcut string
if runtime.GOOS == "darwin" {
quitShortcut = "Cmd+Q"
} else {
quitShortcut = "Ctrl+Q"
}
quitItem.SetAccelerator(quitShortcut)SetTooltip()
設定將游標停留在功能表項目上時顯示的工具提示。
func (mi *MenuItem) SetTooltip(tooltip string) *MenuItem範例:
item.SetTooltip("Opens a file from disk")SetHidden()
顯示或隱藏功能表項目。
func (mi *MenuItem) SetHidden(hidden bool) *MenuItem範例:
// Hide debug menu in production
debugItem.SetHidden(!isDevelopment)
menu.Update()應用程式功能表
app.Menu.Set()
設定應用程式的主功能表列。
func (mm *MenuManager) Set(menu *Menu)範例:
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)平台注意事項:
- macOS:功能表會顯示在畫面頂端的功能表列中
- Windows/Linux:功能表會顯示在視窗標題列中
- macOS:自動加入以應用程式名稱命名的應用程式功能表
快顯功能表
app.ContextMenu.New() / app.ContextMenu.Add()
透過管理員建立*ContextMenu,並以名稱註冊。ContextMenuManager.Add接受*ContextMenu,而不是*Menu;此外,沒有app.RegisterContextMenu方法。
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)或者,也可以使用套件層級的application.NewContextMenu(name string) *ContextMenu,一次完成快顯功能表的建立與註冊。
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:
當按滑鼠右鍵的目標(或其任一祖先元素)將--custom-contextmenu CSS 自訂屬性設為該名稱時,執行階段便會觸發已註冊的快顯功能表。選用的--custom-contextmenu-data屬性會透過ctx.ContextMenuData()傳給 Go 回呼函式。若要停用瀏覽器的預設快顯功能表,請設定--default-contextmenu: hide(或auto/show)。
<!-- Trigger context menu on right-click -->
<div style="--custom-contextmenu: editor; --default-contextmenu: hide">
Right-click here for context menu
</div>不存在data-wails-context-menu="..."屬性,因為執行階段從未接入該屬性。
動態快顯功能表:
// 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)
}系統匣功能表
app.SystemTray.New()
建立新的系統匣圖示。
func (sm *SystemTrayManager) New() *SystemTray範例:
tray := app.SystemTray.New()SetIcon()
設定系統匣圖示。
func (st *SystemTray) SetIcon(icon []byte) *SystemTray範例:
iconData, _ := os.ReadFile("icon.png")
tray.SetIcon(iconData)SetMenu()
設定系統匣的功能表。
func (st *SystemTray) SetMenu(menu *Menu) *SystemTray範例:
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)SetTooltip()
設定將游標停留在系統匣圖示上時顯示的工具提示。此方法不會傳回任何值。
func (st *SystemTray) SetTooltip(tooltip string)範例:
tray.SetTooltip("My Application - Running")OnClick()
處理系統匣圖示上的滑鼠左鍵點擊。
func (st *SystemTray) OnClick(callback func()) *SystemTray範例:
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()
}最佳實務
✅ 建議做法
- 使用標準快速鍵——遵循平台慣例(例如使用 Ctrl+C 複製)
- 變更後呼叫 Update()——否則功能表不會反映變更
- 將相關項目分組——使用分隔線整理功能表項目
- 停用目前無法使用的動作——不要隱藏,請使用 SetEnabled(false) 停用
- 使用清楚的標籤——保持簡潔且具描述性
- 遵循平台慣例——注意 macOS 與 Windows/Linux 的功能表模式差異
❌ 避免事項
- 不要忘記呼叫 Update()——這是最常見的錯誤
- 不要巢狀化得太深——功能表最多維持在2-3層
- 不要使用含糊的標籤——例如「處理」與「處理文件」
- 不要過度複雜化——讓功能表保持簡單且聚焦
- 不要混用不同概念——命名與編排方式應保持一致
平台特定注意事項
macOS
- 自動加入以應用程式名稱命名的應用程式功能表
- 快速鍵請使用
Cmd,而非Ctrl - 應用程式功能表預設包含「關於」、「偏好設定」和「結束」
Windows/Linux
- 不會自動建立應用程式選單
- 使用
Ctrl作為快速鍵 - 「結束」通常位於「檔案」選單中