Lewati ke konten

Menu Aplikasi

Aplikasi desktop profesional memerlukan menu bar—File, Edit, View, Help. Namun menu bekerja berbeda di setiap platform:

  • macOS: Menu bar global di bagian atas layar
  • Windows: Menu bar di title bar window
  • Linux: Bervariasi menurut desktop environment

Membangun menu yang sesuai platform secara manual membosankan dan rentan error.

Wails menyediakan API terpadu yang membuat menu native platform secara otomatis. Tulis sekali, dapatkan perilaku native di semua platform.

package main
import (
"runtime"
"github.com/wailsapp/wails/v3/pkg/application"
)
func main() {
app := application.New(application.Options{
Name: "My App",
})
// Create menu
menu := app.NewMenu()
// Add standard menus (platform-appropriate)
if runtime.GOOS == "darwin" {
menu.AddRole(application.AppMenu) // macOS only
}
menu.AddRole(application.FileMenu)
menu.AddRole(application.EditMenu)
menu.AddRole(application.WindowMenu)
menu.AddRole(application.HelpMenu)
// Set the application menu
app.Menu.Set(menu)
// Create window with UseApplicationMenu to inherit the menu on Windows/Linux
app.Window.NewWithOptions(application.WebviewWindowOptions{
UseApplicationMenu: true,
})
app.Run()
}

Itu saja! Anda sekarang memiliki menu native platform dengan item standar. Opsi UseApplicationMenu memastikan window Windows dan Linux menampilkan menu tanpa kode tambahan.

// Create a new menu
menu := app.NewMenu()
// Add a top-level menu
fileMenu := menu.AddSubmenu("File")
// Add menu items
fileMenu.Add("New").OnClick(func(ctx *application.Context) {
// Handle New
})
fileMenu.Add("Open").OnClick(func(ctx *application.Context) {
// Handle Open
})
fileMenu.AddSeparator()
fileMenu.Add("Quit").OnClick(func(ctx *application.Context) {
app.Quit()
})

Pendekatan yang direkomendasikan — Gunakan UseApplicationMenu untuk konsistensi lintas platform:

// Set the application menu once
app.Menu.Set(menu)
// Create windows that inherit the menu on Windows/Linux
app.Window.NewWithOptions(application.WebviewWindowOptions{
UseApplicationMenu: true, // Window uses the app menu
})

Pendekatan ini:

  • Di macOS: Menu muncul di bagian atas layar (perilaku standar)
  • Di Windows/Linux: Setiap window dengan UseApplicationMenu: true menampilkan menu aplikasi

Detail spesifik platform:

Menu bar global (satu per aplikasi):

app.Menu.Set(menu)

Menu muncul di bagian atas layar dan tetap ada bahkan saat semua window ditutup. Opsi UseApplicationMenu tidak berpengaruh di macOS karena semua aplikasi menggunakan menu global.

Menu kustom per window:

Jika window memerlukan menu berbeda dari menu aplikasi, atur langsung:

window.SetMenu(customMenu) // Overrides UseApplicationMenu

Wails menyediakan role menu yang telah ditentukan yang membuat struktur menu sesuai platform secara otomatis.

RoleDeskripsiCatatan Platform
AppMenuMenu aplikasi dengan About, Preferences, QuitmacOS saja
FileMenuOperasi file (New, Open, Save, dll.)Semua platform
EditMenuPengeditan teks (Undo, Redo, Cut, Copy, Paste)Semua platform
WindowMenuManajemen window (Minimise, Zoom, dll.)Semua platform
HelpMenuBantuan dan informasiSemua platform
menu := app.NewMenu()
// macOS: Add application menu
if runtime.GOOS == "darwin" {
menu.AddRole(application.AppMenu)
}
// All platforms: Add standard menus
menu.AddRole(application.FileMenu)
menu.AddRole(application.EditMenu)
menu.AddRole(application.WindowMenu)
menu.AddRole(application.HelpMenu)

Yang Anda dapatkan:

AppMenu (dengan nama app):

  • About [App Name]
  • Preferences… (⌘,)

  • Services

  • Hide [App Name] (⌘H)
  • Hide Others (⌥⌘H)
  • Show All

  • Quit [App Name] (⌘Q)

FileMenu:

  • New (⌘N)
  • Open… (⌘O)

  • Close Window (⌘W)

EditMenu:

  • Undo (⌘Z)
  • Redo (⇧⌘Z)

  • Cut (⌘X)
  • Copy (⌘C)
  • Paste (⌘V)
  • Select All (⌘A)

WindowMenu:

  • Minimise (⌘M)
  • Zoom

  • Bring All to Front

HelpMenu:

  • [App Name] Help

Menu.AddRole(role) mengembalikan menu receiver (menu tingkat atas), bukan submenu role. Untuk menambahkan item ke submenu role, cari item role yang dimasukkan dengan FindByRole dan panggil GetSubmenu():

menu.AddRole(application.FileMenu)
fileMenu := menu.FindByRole(application.FileMenu).GetSubmenu()
fileMenu.Add("Import...").OnClick(handleImport)
fileMenu.Add("Export...").OnClick(handleExport)

Buat menu sendiri untuk fitur spesifik aplikasi:

// Add a custom top-level menu
toolsMenu := menu.AddSubmenu("Tools")
// Add items
toolsMenu.Add("Settings").OnClick(func(ctx *application.Context) {
showSettingsWindow()
})
toolsMenu.AddSeparator()
// Add checkbox
toolsMenu.AddCheckbox("Dark Mode", false).OnClick(func(ctx *application.Context) {
isDark := ctx.ClickedMenuItem().Checked()
setTheme(isDark)
})
// Add radio group
toolsMenu.AddRadio("Small", true).OnClick(handleFontSize)
toolsMenu.AddRadio("Medium", false).OnClick(handleFontSize)
toolsMenu.AddRadio("Large", false).OnClick(handleFontSize)
// Add submenu
advancedMenu := toolsMenu.AddSubmenu("Advanced")
advancedMenu.Add("Configure...").OnClick(showAdvancedSettings)

Untuk tipe item menu lainnya, lihat Referensi Menu.

Perbarui menu berdasarkan status aplikasi:

var saveMenuItem *application.MenuItem
func createMenu() {
menu := app.NewMenu()
fileMenu := menu.AddSubmenu("File")
saveMenuItem = fileMenu.Add("Save")
saveMenuItem.SetEnabled(false) // Initially disabled
saveMenuItem.OnClick(handleSave)
app.Menu.Set(menu)
}
func onDocumentChanged() {
saveMenuItem.SetEnabled(hasUnsavedChanges())
menu.Update() // Important!
}
updateMenuItem := menu.Add("Check for Updates")
updateMenuItem.OnClick(func(ctx *application.Context) {
updateMenuItem.SetLabel("Checking...")
menu.Update()
checkForUpdates()
updateMenuItem.SetLabel("Check for Updates")
menu.Update()
})

Untuk perubahan besar, rebuild seluruh menu:

func rebuildFileMenu() {
menu := app.NewMenu()
fileMenu := menu.AddSubmenu("File")
fileMenu.Add("New").OnClick(handleNew)
fileMenu.Add("Open").OnClick(handleOpen)
// Add recent files dynamically
if hasRecentFiles() {
recentMenu := fileMenu.AddSubmenu("Open Recent")
for _, file := range getRecentFiles() {
filePath := file // Capture for closure
recentMenu.Add(filepath.Base(file)).OnClick(func(ctx *application.Context) {
openFile(filePath)
})
}
recentMenu.AddSeparator()
recentMenu.Add("Clear Recent").OnClick(clearRecentFiles)
}
fileMenu.AddSeparator()
fileMenu.Add("Quit").OnClick(func(ctx *application.Context) {
app.Quit()
})
app.Menu.Set(menu)
}

Item menu dapat mengendalikan window:

viewMenu := menu.AddSubmenu("View")
// Toggle fullscreen
viewMenu.Add("Toggle Fullscreen").OnClick(func(ctx *application.Context) {
if window, ok := app.Window.GetByName("main"); ok {
window.ToggleFullscreen()
}
})
// Zoom controls
viewMenu.Add("Zoom In").SetAccelerator("CmdOrCtrl++").OnClick(func(ctx *application.Context) {
// Increase zoom
})
viewMenu.Add("Zoom Out").SetAccelerator("CmdOrCtrl+-").OnClick(func(ctx *application.Context) {
// Decrease zoom
})
viewMenu.Add("Reset Zoom").SetAccelerator("CmdOrCtrl+0").OnClick(func(ctx *application.Context) {
// Reset zoom
})

Dapatkan window aktif:

menuItem.OnClick(func(ctx *application.Context) {
window := application.Get().Window.Current() // the window the menu was invoked from
// Use window
})

Perilaku menu bar:

  • Muncul di bagian atas layar (global)
  • Tetap ada saat semua window ditutup
  • Menu pertama selalu menu aplikasi
  • Gunakan menu.AddRole(application.AppMenu) untuk item standar

Lokasi standar:

  • About: Menu aplikasi
  • Preferences: Menu aplikasi (⌘,)
  • Quit: Menu aplikasi (⌘Q)
  • Help: Menu Help

Contoh:

if runtime.GOOS == "darwin" {
menu.AddRole(application.AppMenu) // Adds About, Preferences, Quit
// Don't add Quit to File menu on macOS
// Don't add About to Help menu on macOS
}

Perilaku menu bar:

  • Muncul di title bar window
  • Setiap window memiliki menu sendiri
  • Tidak ada menu aplikasi

Lokasi standar:

  • Exit: Menu File (Alt+F4)
  • Settings: Menu Tools atau Edit
  • About: Menu Help

Contoh:

if runtime.GOOS == "windows" {
menu.AddRole(application.FileMenu) // Exit is added automatically
menu.AddRole(application.HelpMenu) // About is added automatically
}

Perilaku menu bar:

  • Biasanya per-window (seperti Windows)
  • Beberapa DE mendukung menu global (Unity, GNOME dengan extension)
  • Tampilan bervariasi menurut desktop environment

Praktik terbaik: Ikuti konvensi Windows, uji di DE target.

Berikut struktur menu siap produksi:

package main
import (
"runtime"
"github.com/wailsapp/wails/v3/pkg/application"
)
func main() {
app := application.New(application.Options{
Name: "My Application",
})
// Create and set menu
createMenu(app)
// Create main window with UseApplicationMenu for cross-platform menu support
app.Window.NewWithOptions(application.WebviewWindowOptions{
UseApplicationMenu: true,
})
app.Run()
}
func createMenu(app *application.App) {
menu := app.NewMenu()
// Platform-specific application menu (macOS only)
if runtime.GOOS == "darwin" {
menu.AddRole(application.AppMenu)
}
// File menu — AddRole returns the receiver menu, not the role submenu.
// To add items into the File submenu, look it up via FindByRole + GetSubmenu.
menu.AddRole(application.FileMenu)
fileMenu := menu.FindByRole(application.FileMenu).GetSubmenu()
fileMenu.Add("Import...").SetAccelerator("CmdOrCtrl+I").OnClick(handleImport)
fileMenu.Add("Export...").SetAccelerator("CmdOrCtrl+E").OnClick(handleExport)
// Edit menu
menu.AddRole(application.EditMenu)
// View menu
viewMenu := menu.AddSubmenu("View")
viewMenu.Add("Toggle Fullscreen").SetAccelerator("F11").OnClick(toggleFullscreen)
viewMenu.AddSeparator()
viewMenu.AddCheckbox("Show Sidebar", true).OnClick(toggleSidebar)
viewMenu.AddCheckbox("Show Toolbar", true).OnClick(toggleToolbar)
// Tools menu
toolsMenu := menu.AddSubmenu("Tools")
// Settings location varies by platform
if runtime.GOOS == "darwin" {
// On macOS, Preferences is in Application menu (added by AppMenu role)
} else {
toolsMenu.Add("Settings").SetAccelerator("CmdOrCtrl+,").OnClick(showSettings)
}
toolsMenu.AddSeparator()
toolsMenu.AddCheckbox("Dark Mode", false).OnClick(toggleDarkMode)
// Window menu
menu.AddRole(application.WindowMenu)
// Help menu
helpMenu := menu.AddRole(application.HelpMenu)
helpMenu.Add("Documentation").OnClick(openDocumentation)
// About location varies by platform
if runtime.GOOS == "darwin" {
// On macOS, About is in Application menu (added by AppMenu role)
} else {
helpMenu.AddSeparator()
helpMenu.Add("About").OnClick(showAbout)
}
// Set the application menu
app.Menu.Set(menu)
}
func handleImport(ctx *application.Context) {
// Implementation
}
func handleExport(ctx *application.Context) {
// Implementation
}
func toggleFullscreen(ctx *application.Context) {
window := application.Get().Window.Current()
window.ToggleFullscreen()
}
func toggleSidebar(ctx *application.Context) {
// Implementation
}
func toggleToolbar(ctx *application.Context) {
// Implementation
}
func showSettings(ctx *application.Context) {
// Implementation
}
func toggleDarkMode(ctx *application.Context) {
isDark := ctx.ClickedMenuItem().Checked()
// Apply theme
}
func openDocumentation(ctx *application.Context) {
// Open browser
}
func showAbout(ctx *application.Context) {
// Show about dialog
}
  • Gunakan role menu untuk menu standar (File, Edit, dll.)
  • Ikuti konvensi platform untuk struktur menu
  • Tambahkan keyboard shortcut ke aksi umum
  • Panggil menu.Update() setelah mengubah status menu
  • Uji di semua platform - perilaku bervariasi
  • Jaga menu dangkal - maksimum 2-3 level
  • Gunakan label jelas - “Save Project” bukan “Save”
  • Jangan hardcode shortcut platform - Gunakan CmdOrCtrl
  • Jangan taruh Quit di menu File di macOS - Ada di menu Application
  • Jangan taruh About di menu Help di macOS - Ada di menu Application
  • Jangan lupa menu.Update() - Menu tidak akan berfungsi dengan benar
  • Jangan nest terlalu dalam - Pengguna akan tersesat
  • Jangan gunakan jargon - Jaga label ramah pengguna

Pertanyaan? Tanyakan di Discord atau lihat contoh menu.