コントリビュート

このドキュメントを編集

ドキュメントを翻訳するか、お使いのコンピューターでプロジェクト全体を開きます。

作業内容を送信するまで、何も公開されません。

アプリケーションメニュー

デスクトップアプリケーション用のネイティブメニューバーを作成する

課題

本格的なデスクトップアプリケーションには、ファイル、編集、表示、ヘルプなどのメニューバーが必要です。しかし、メニューの動作はプラットフォームごとに異なります。

  • macOS:画面上部のグローバルメニューバー
  • Windows:ウィンドウのタイトルバーにあるメニューバー
  • Linux:デスクトップ環境によって異なる

各プラットフォームに適したメニューを手作業で構築するのは煩雑で、エラーも起きやすくなります。

Wailsによる解決策

Wailsは、各プラットフォームのネイティブメニューを自動的に作成する統一APIを提供します。一度記述すれば、すべてのプラットフォームでネイティブな動作が得られます。

標準項目、チェックボックス項目、ラジオ項目、サブメニュー項目を含むmacOS上のWailsアプリケーションメニュー

macOSでは、アプリケーションメニューはグローバルメニューバーに配置されます。このキャプチャは、無効な項目、チェックボックス項目、ラジオ項目、サブメニュー項目を含め、WailsのメニューAPIによってレンダリングされたネイティブメニューを示しています。

クイックスタート

go
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()
}

これだけです! 標準項目を備えた、各プラットフォームのネイティブメニューが利用できるようになりました。UseApplicationMenuオプションにより、追加のコードなしでWindowsとLinuxのウィンドウにメニューが表示されます。

メニューの作成

基本的なメニューの作成

go
// 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()
})

メニューの設定

推奨される方法 — プラットフォーム間で一貫した動作を実現するには、UseApplicationMenuを使用します。

go
// 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
})

この方法では、次のように動作します。

  • macOS:メニューは画面上部に表示されます(標準動作)。
  • Windows/Linux:UseApplicationMenu: trueが設定された各ウィンドウにアプリケーションメニューが表示されます。

プラットフォーム固有の詳細:

グローバルメニューバー(アプリケーションごとに1つ):

go
app.Menu.Set(menu)

メニューは画面上部に表示され、すべてのウィンドウを閉じた後も表示され続けます。macOSではすべてのアプリがグローバルメニューを使用するため、UseApplicationMenuオプションは効果がありません。

ウィンドウごとのカスタムメニュー:

ウィンドウにアプリケーションメニューとは異なるメニューが必要な場合は、そのウィンドウに直接設定します。

go
window.SetMenu(customMenu)  // Overrides UseApplicationMenu

メニューのロール

Wailsは、各プラットフォームに適したメニュー構造を自動的に作成する定義済みのメニューロールを提供します。

利用可能なロール

ロール 説明 プラットフォームに関する注記
AppMenu 「このアプリケーションについて」、「環境設定」、「終了」を含むアプリケーションメニュー macOSのみ
FileMenu ファイル操作(新規、開く、保存など) すべてのプラットフォーム
EditMenu テキスト編集(取り消す、やり直す、カット、コピー、ペースト) すべてのプラットフォーム
WindowMenu ウィンドウ管理(しまう、拡大/縮小など) すべてのプラットフォーム
HelpMenu ヘルプと情報 すべてのプラットフォーム

ロールの使用

go
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)

作成される内容:

AppMenu(アプリ名を含む):

  • [App Name]について
  • 環境設定… (⌘,)

  • サービス

  • [App Name]を隠す (⌘H)
  • ほかを隠す (⌥⌘H)
  • すべてを表示

  • [App Name]を終了 (⌘Q)

FileMenu:

  • 新規 (⌘N)
  • 開く… (⌘O)

  • ウィンドウを閉じる (⌘W)

EditMenu:

  • 取り消す (⌘Z)
  • やり直す (⇧⌘Z)

  • カット(⌘X)
  • コピー(⌘C)
  • ペースト(⌘V)
  • すべてを選択(⌘A)

WindowMenu:

  • 最小化(⌘M)
  • 拡大/縮小

  • すべてを手前に移動

HelpMenu:

  • [App Name] ヘルプ

ロールメニューのカスタマイズ

Menu.AddRole(role) が返すのは、ロールのサブメニューではなく、レシーバーのメニュー(トップレベルメニュー)です。ロールのサブメニューに項目を追加するには、挿入されたロール項目を FindByRole で検索し、その項目に対して GetSubmenu() を呼び出します:

go
menu.AddRole(application.FileMenu)

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

カスタムメニュー

アプリケーション固有の機能用に独自のメニューを作成します:

go
// 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)

その他のメニュー項目タイプについては、メニューリファレンスを参照してください。

動的メニュー

アプリケーションの状態に応じてメニューを更新します:

項目の有効化/無効化

go
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!
}

ラベルの変更

go
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()
})

メニューの再構築

大幅な変更を行う場合は、メニュー全体を再構築します:

go
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)
}

メニューからのウィンドウ制御

メニュー項目からウィンドウを制御できます:

go
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
})

アクティブウィンドウを取得:

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

プラットフォーム固有の考慮事項

macOS

メニューバーの動作:

  • 画面上部に表示される(グローバル)
  • すべてのウィンドウを閉じても表示され続ける
  • 最初のメニューは常にアプリケーションメニューになる
  • 標準項目には menu.AddRole(application.AppMenu) を使用する

標準的な配置場所:

  • このアプリケーションについて:アプリケーションメニュー
  • 環境設定:アプリケーションメニュー(⌘,)
  • 終了:アプリケーションメニュー(⌘Q)
  • ヘルプ:ヘルプメニュー

例:

go
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
}

Windows

メニューバーの動作:

  • ウィンドウのタイトルバーに表示される
  • 各ウィンドウに固有のメニューがある
  • アプリケーションメニューはない

標準的な配置場所:

  • 終了:ファイルメニュー(Alt+F4)
  • 設定:ツールメニューまたは編集メニュー
  • バージョン情報:ヘルプメニュー

例:

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

Linux

メニューバーの動作:

  • 通常はウィンドウごとに表示される(Windows と同様)
  • 一部のデスクトップ環境では、グローバルメニューがサポートされています(Unity、拡張機能を導入した GNOME)
  • 外観はデスクトップ環境によって異なります

ベストプラクティス:Windows の慣例に従い、対象のデスクトップ環境でテストしてください。

完全な例

以下は、本番環境で使用できるメニュー構成です。

go
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
}

ベストプラクティス

✅ 推奨事項

  • 標準メニュー(ファイル、編集など)にはメニューロールを使用する
  • メニュー構成は各プラットフォームの慣例に従う
  • よく使う操作にはキーボードショートカットを追加する
  • メニューの状態を変更した後はmenu.Update() を呼び出す
  • 動作が異なるため、すべてのプラットフォームでテストする
  • メニュー階層を浅く保つ — 最大2-3階層
  • 明確なラベルを使用する — 「保存」ではなく「プロジェクトを保存」

❌ 非推奨事項

  • プラットフォーム固有のショートカットをハードコードしない — CmdOrCtrlを使用してください
  • macOS では「終了」を「ファイル」メニューに配置しない — 「アプリケーション」メニューに配置されます
  • macOS では「このアプリケーションについて」を「ヘルプ」メニューに配置しない — 「アプリケーション」メニューに配置されます
  • menu.Update() の呼び出しを忘れない — メニューが正しく動作しなくなります
  • 階層を深くしすぎない — ユーザーが迷ってしまいます
  • 専門用語を使用しない — ユーザーに分かりやすいラベルにしてください

次のステップ

メニューのリファレンス

メニュー項目の種類とプロパティに関する完全なリファレンスです。

詳細を見る →

コンテキストメニュー

右クリックで表示するコンテキストメニューを作成します。

詳細を見る →

システムトレイメニュー

システムトレイ/メニューバーとの連携を追加します。

詳細を見る →

メニューパターン

一般的なメニューパターンとベストプラクティスです。

詳細を見る →


ご質問がありますか?Discord で質問するか、メニューのサンプルを確認してください。

ページを編集

最終更新: