Menu Reference
Menu Reference
Section titled “Menu Reference”Complete reference for menu item types, properties, and dynamic behaviour. Build professional, responsive menus with checkboxes, radio groups, separators, and dynamic updates.
Menu Item Types
Section titled “Menu Item Types”Regular Menu Items
Section titled “Regular Menu Items”The most common type—displays text and triggers an action:
menuItem := menu.Add("Click Me")menuItem.OnClick(func(ctx *application.Context) { fmt.Println("Menu item clicked!")})Use for: Commands, actions, opening windows
Checkboxes
Section titled “Checkboxes”Toggle-able menu items with checked/unchecked state:
checkbox := menu.AddCheckbox("Enable Feature", true) // true = initially checkedcheckbox.OnClick(func(ctx *application.Context) { isChecked := ctx.ClickedMenuItem().Checked() fmt.Printf("Feature is now: %v\n", isChecked)})Use for: Boolean settings, feature toggles, view options
Important: The checked state toggles automatically when clicked.
Radio Groups
Section titled “Radio Groups”Mutually exclusive options—only one can be selected:
menu.AddRadio("Small", true) // true = initially selectedmenu.AddRadio("Medium", false)menu.AddRadio("Large", false)Use for: Mutually exclusive choices (size, theme, mode)
How grouping works:
- Adjacent radio items form a group automatically
- Selecting one deselects others in the group
- Separate groups with a separator or regular item
Example with multiple groups:
// Group 1: Sizemenu.AddRadio("Small", true)menu.AddRadio("Medium", false)menu.AddRadio("Large", false)
menu.AddSeparator()
// Group 2: Thememenu.AddRadio("Light", true)menu.AddRadio("Dark", false)Submenus
Section titled “Submenus”Nested menu structures for organisation:
submenu := menu.AddSubmenu("More Options")submenu.Add("Submenu Item 1").OnClick(func(ctx *application.Context) { // Handle click})submenu.Add("Submenu Item 2")Use for: Grouping related items, reducing clutter
Nesting limit: Most platforms support 2-3 levels. Avoid deeper nesting.
Separators
Section titled “Separators”Visual dividers between menu items:
menu.Add("Item 1")menu.AddSeparator()menu.Add("Item 2")Use for: Grouping related items visually
Best practice: Don’t start or end menus with separators.
Menu Item Properties
Section titled “Menu Item Properties”The text displayed for the menu item:
menuItem := menu.Add("Initial Label")menuItem.SetLabel("New Label")
// Get current labellabel := menuItem.Label()Dynamic labels:
updateMenuItem := menu.Add("Check for Updates")updateMenuItem.OnClick(func(ctx *application.Context) { updateMenuItem.SetLabel("Checking...") menu.Update() // Important on Windows!
// Perform update check checkForUpdates()
updateMenuItem.SetLabel("Check for Updates") menu.Update()})Enabled State
Section titled “Enabled State”Control whether the menu item can be interacted with:
menuItem := menu.Add("Save")menuItem.SetEnabled(false) // Greyed out, can't click
// Enable it latermenuItem.SetEnabled(true)menu.Update() // Important: Call this after changing enabled state!
// Check current stateisEnabled := menuItem.Enabled()Example: Dynamic enable/disable
var hasSelection bool
cutMenuItem := menu.Add("Cut")cutMenuItem.SetEnabled(false) // Initially disabled
copyMenuItem := menu.Add("Copy")copyMenuItem.SetEnabled(false)
// When selection changesfunc onSelectionChanged(selected bool) { hasSelection = selected cutMenuItem.SetEnabled(hasSelection) copyMenuItem.SetEnabled(hasSelection) menu.Update() // Critical on Windows!}Common pattern: Enable on condition
saveMenuItem := menu.Add("Save")
func updateSaveMenuItem() { canSave := hasUnsavedChanges() && !isSaving() saveMenuItem.SetEnabled(canSave) menu.Update()}
// Call whenever state changesonDocumentChanged(func() { updateSaveMenuItem()})Checked State
Section titled “Checked State”For checkbox and radio items, control or query their checked state:
checkbox := menu.AddCheckbox("Feature", false)checkbox.SetChecked(true)menu.Update()
// Query stateisChecked := checkbox.Checked()Auto-toggle: Checkboxes toggle automatically when clicked. You don’t need to call SetChecked() in the click handler.
Manual control:
checkbox := menu.AddCheckbox("Auto-save", false)
// Sync with external statefunc syncAutoSave(enabled bool) { checkbox.SetChecked(enabled) menu.Update()}Accelerators (Keyboard Shortcuts)
Section titled “Accelerators (Keyboard Shortcuts)”Add keyboard shortcuts to menu items:
saveMenuItem := menu.Add("Save")saveMenuItem.SetAccelerator("CmdOrCtrl+S")
quitMenuItem := menu.Add("Quit")quitMenuItem.SetAccelerator("CmdOrCtrl+Q")Accelerator format:
CmdOrCtrl- Cmd on macOS, Ctrl on Windows/LinuxShift,Alt,Option- Modifier keysA-Z,0-9- Letter/number keysF1-F12- Function keysEnter,Space,Backspace, etc. - Special keys
Examples:
"CmdOrCtrl+S" // Save"CmdOrCtrl+Shift+S" // Save As"CmdOrCtrl+W" // Close Window"CmdOrCtrl+Q" // Quit"F5" // Refresh"CmdOrCtrl+," // Preferences (macOS convention)"Alt+F4" // Close (Windows convention)Platform-specific accelerators:
if runtime.GOOS == "darwin" { prefsMenuItem.SetAccelerator("Cmd+,")} else { prefsMenuItem.SetAccelerator("Ctrl+P")}Tooltip
Section titled “Tooltip”Add hover text to menu items (platform support varies):
menuItem := menu.Add("Advanced Options")menuItem.SetTooltip("Configure advanced settings")Platform support:
- Windows: ✅ Supported
- macOS: ❌ Not supported (tooltips not standard for menus)
- Linux: ⚠️ Varies by desktop environment
Hidden State
Section titled “Hidden State”Hide menu items without removing them:
debugMenuItem := menu.Add("Debug Mode")debugMenuItem.SetHidden(true) // Hidden
// Show in debug buildsif isDebugBuild { debugMenuItem.SetHidden(false) menu.Update()}Use for: Debug options, feature flags, conditional features
Event Handling
Section titled “Event Handling”OnClick Handler
Section titled “OnClick Handler”Execute code when menu item is clicked:
menuItem := menu.Add("Click Me")menuItem.OnClick(func(ctx *application.Context) { // Handle click fmt.Println("Clicked!")})Context provides:
ctx.ClickedMenuItem()- The menu item that was clicked- Window context (if from window menu)
- Application context
Example: Access menu item in handler
checkbox := menu.AddCheckbox("Feature", false)checkbox.OnClick(func(ctx *application.Context) { item := ctx.ClickedMenuItem() isChecked := item.Checked() fmt.Printf("Feature is now: %v\n", isChecked)})Multiple Handlers
Section titled “Multiple Handlers”You can set multiple handlers (last one wins):
menuItem := menu.Add("Action")menuItem.OnClick(func(ctx *application.Context) { fmt.Println("First handler")})
// This replaces the first handlermenuItem.OnClick(func(ctx *application.Context) { fmt.Println("Second handler - this one runs")})Best practice: Set handler once, use conditional logic inside if needed.
Dynamic Menus
Section titled “Dynamic Menus”Updating Menu Items
Section titled “Updating Menu Items”The golden rule: Always call menu.Update() after changing menu state.
// ✅ CorrectmenuItem.SetEnabled(true)menu.Update()
// ❌ Wrong (especially on Windows)menuItem.SetEnabled(true)// Forgot to call Update() - click handlers may not work!Why this matters:
- Windows: Menus are reconstructed when updated
- macOS/Linux: Less critical but still recommended
- Click handlers: Won’t fire properly without Update()
Rebuilding Menus
Section titled “Rebuilding Menus”For major changes, rebuild the entire menu:
func rebuildFileMenu() { menu := app.Menu.New()
menu.Add("New").OnClick(handleNew) menu.Add("Open").OnClick(handleOpen)
if hasRecentFiles() { recentMenu := menu.AddSubmenu("Open Recent") for _, file := range getRecentFiles() { recentMenu.Add(file).OnClick(func(ctx *application.Context) { openFile(file) }) } }
menu.AddSeparator() menu.Add("Quit").OnClick(handleQuit)
// Set the new menu window.SetMenu(menu)}When to rebuild:
- Recent files list changes
- Plugin menus change
- Major state transitions
When to update:
- Enable/disable items
- Change labels
- Toggle checkboxes
Context-Sensitive Menus
Section titled “Context-Sensitive Menus”Adjust menus based on application state:
func updateEditMenu() { cutMenuItem.SetEnabled(hasSelection()) copyMenuItem.SetEnabled(hasSelection()) pasteMenuItem.SetEnabled(hasClipboardContent()) undoMenuItem.SetEnabled(canUndo()) redoMenuItem.SetEnabled(canRedo()) menu.Update()}
// Call whenever state changesonSelectionChanged(updateEditMenu)onClipboardChanged(updateEditMenu)onUndoStackChanged(updateEditMenu)Platform Differences
Section titled “Platform Differences”Menu Bar Location
Section titled “Menu Bar Location”| Platform | Location | Notes |
|---|---|---|
| macOS | Top of screen | Global menu bar |
| Windows | Top of window | Per-window menu |
| Linux | Top of window | Per-window (usually) |
Standard Menus
Section titled “Standard Menus”macOS:
- Has “Application” menu (with app name)
- “Preferences” in Application menu
- “Quit” in Application menu
Windows/Linux:
- No Application menu
- “Preferences” in Edit or Tools menu
- “Exit” in File menu
Example: Platform-appropriate structure
menu := app.Menu.New()
// macOS gets Application menuif runtime.GOOS == "darwin" { menu.AddRole(application.AppMenu)}
// File menufileMenu := menu.AddSubmenu("File")fileMenu.Add("New")fileMenu.Add("Open")
// Preferences location variesif runtime.GOOS == "darwin" { // On macOS, preferences are in Application menu (added by AppMenu role)} else { // On Windows/Linux, add to Edit or Tools menu editMenu := menu.AddSubmenu("Edit") editMenu.Add("Preferences")}Accelerator Conventions
Section titled “Accelerator Conventions”macOS:
Cmd+for most shortcutsCmd+,for PreferencesCmd+Qfor Quit
Windows:
Ctrl+for most shortcutsCtrl+PorCtrl+,for PreferencesAlt+F4for Exit (orCtrl+Q)
Linux:
- Generally follows Windows conventions
- Desktop environment may override
Best Practices
Section titled “Best Practices”- Call menu.Update() after changing menu state (especially on Windows)
- Use radio groups for mutually exclusive options
- Use checkboxes for toggleable features
- Add accelerators to common actions
- Group related items with separators
- Test on all platforms - behaviour varies
❌ Don’t
Section titled “❌ Don’t”- Don’t forget menu.Update() - Click handlers won’t work properly
- Don’t nest too deeply - 2-3 levels maximum
- Don’t start/end with separators - Looks unprofessional
- Don’t use tooltips on macOS - Not supported
- Don’t hardcode platform shortcuts - Use
CmdOrCtrl
Troubleshooting
Section titled “Troubleshooting”Menu Items Not Responding
Section titled “Menu Items Not Responding”Symptom: Click handlers don’t fire
Cause: Forgot to call menu.Update() after enabling item
Solution:
menuItem.SetEnabled(true)menu.Update() // Add this!Menu Items Greyed Out
Section titled “Menu Items Greyed Out”Symptom: Can’t click menu items
Cause: Items are disabled
Solution:
menuItem.SetEnabled(true)menu.Update()Accelerators Not Working
Section titled “Accelerators Not Working”Symptom: Keyboard shortcuts don’t trigger menu items
Causes:
- Accelerator format incorrect
- Conflict with system shortcuts
- Window doesn’t have focus
Solution:
// Check formatmenuItem.SetAccelerator("CmdOrCtrl+S") // ✅ CorrectmenuItem.SetAccelerator("Ctrl+S") // ❌ Wrong (macOS uses Cmd)
// Avoid conflicts// ❌ Cmd+H (Hide Window on macOS - system shortcut)// ✅ Cmd+Shift+H (Custom shortcut)Next Steps
Section titled “Next Steps”- Application Menus - Create application menu bars
- Context Menus - Right-click context menus
- System Tray Menus - System tray/menu bar menus
- Menu Patterns - Common menu patterns and best practices
Questions? Ask in Discord or check the menu examples.