diff --git a/docs/src/content/docs/features/menus/application.mdx b/docs/src/content/docs/features/menus/application.mdx index 9e39e5caf..26b7e71a0 100644 --- a/docs/src/content/docs/features/menus/application.mdx +++ b/docs/src/content/docs/features/menus/application.mdx @@ -56,16 +56,19 @@ func main() { menu.AddRole(application.WindowMenu) menu.AddRole(application.HelpMenu) - // Set the menu - app.SetMenu(menu) + // 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, + }) - // Create window and run - app.Window.New() app.Run() } ``` -**That's it!** You now have platform-native menus with standard items. +**That's it!** You now have platform-native menus with standard items. The `UseApplicationMenu` option ensures Windows and Linux windows display the menu without additional code. ## Creating Menus @@ -96,33 +99,63 @@ fileMenu.Add("Quit").OnClick(func(ctx *application.Context) { ### Setting the Menu -Platform-specific API: +**Recommended approach** — Use `UseApplicationMenu` for cross-platform consistency: + +```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 +}) +``` + +This approach: +- On **macOS**: The menu appears at the top of screen (standard behaviour) +- On **Windows/Linux**: Each window with `UseApplicationMenu: true` displays the app menu + +**Platform-specific details:** **Global menu bar** (one per application): ```go - app.SetMenu(menu) + app.Menu.Set(menu) ``` - The menu appears at the top of the screen and persists even when all windows are closed. + The menu appears at the top of the screen and persists even when all windows are closed. The `UseApplicationMenu` option has no effect on macOS since all apps use the global menu. **Per-window menu bar**: ```go + // Option 1: Use application menu (recommended) + app.Menu.Set(menu) + window := app.Window.NewWithOptions(application.WebviewWindowOptions{ + UseApplicationMenu: true, + }) + + // Option 2: Set menu directly on window window.SetMenu(menu) ``` - Each window can have its own menu. The menu appears in the window's title bar. + Each window can have its own menu, or inherit the application menu. The menu appears in the window's title bar. **Per-window menu bar** (usually): ```go + // Option 1: Use application menu (recommended) + app.Menu.Set(menu) + window := app.Window.NewWithOptions(application.WebviewWindowOptions{ + UseApplicationMenu: true, + }) + + // Option 2: Set menu directly on window window.SetMenu(menu) ``` @@ -130,16 +163,24 @@ Platform-specific API: -**Cross-platform helper:** +:::tip[Simplify Cross-Platform Menus] +Using `UseApplicationMenu: true` eliminates the need for platform-specific code like: +```go +// Old approach - no longer needed +if runtime.GOOS == "darwin" { + app.Menu.Set(menu) +} else { + window.SetMenu(menu) +} +``` +::: + +**Per-window custom menus:** + +If a window needs a different menu than the application menu, set it directly: ```go -func setMenuForPlatform(app *application.Application, window *application.WebviewWindow, menu *application.Menu) { - if runtime.GOOS == "darwin" { - app.SetMenu(menu) - } else { - window.SetMenu(menu) - } -} +window.SetMenu(customMenu) // Overrides UseApplicationMenu ``` ## Menu Roles @@ -486,8 +527,10 @@ func main() { // Create and set menu createMenu(app) - // Create main window - app.Window.New() + // Create main window with UseApplicationMenu for cross-platform menu support + app.Window.NewWithOptions(application.WebviewWindowOptions{ + UseApplicationMenu: true, + }) app.Run() } @@ -543,8 +586,8 @@ func createMenu(app *application.Application) { helpMenu.Add("About").OnClick(showAbout) } - // Set the menu - app.SetMenu(menu) + // Set the application menu + app.Menu.Set(menu) } func handleImport(ctx *application.Context) { diff --git a/docs/src/content/docs/features/windows/options.mdx b/docs/src/content/docs/features/windows/options.mdx index b7cea7f85..15df1ec71 100644 --- a/docs/src/content/docs/features/windows/options.mdx +++ b/docs/src/content/docs/features/windows/options.mdx @@ -52,11 +52,14 @@ type WebviewWindowOptions struct { // Security ContentProtectionEnabled bool - + + // Menu + UseApplicationMenu bool + // Lifecycle OnClose func() bool OnDestroy func() - + // Platform-Specific Mac MacOptions Windows WindowsOptions @@ -546,6 +549,48 @@ Assets: application.AssetOptions{ **See [Build System](/concepts/build-system) for details.** +### UseApplicationMenu + +**Type:** `bool` +**Default:** `false` +**Platform:** Windows, Linux (no effect on macOS) + +```go +UseApplicationMenu: true, +``` + +**Purpose:** Use the application menu (set via `app.Menu.Set()`) for this window. + +On **macOS**, this option has no effect because macOS always uses a global application menu at the top of the screen. + +On **Windows** and **Linux**, windows don't display a menu by default. Setting `UseApplicationMenu: true` tells the window to use the application-level menu, providing a simple cross-platform solution. + +**Example:** + +```go +// Set the application menu once +menu := app.NewMenu() +menu.AddRole(application.FileMenu) +menu.AddRole(application.EditMenu) +app.Menu.Set(menu) + +// All windows with UseApplicationMenu will display this menu +app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "Main Window", + UseApplicationMenu: true, +}) + +app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "Second Window", + UseApplicationMenu: true, // Also gets the app menu +}) +``` + +**Notes:** +- If both `UseApplicationMenu` and a window-specific menu are set, the window-specific menu takes priority +- This simplifies cross-platform code by eliminating the need for runtime OS checks +- See [Application Menus](/features/menus/application) for complete menu documentation + ## Input Options ### EnableFileDrop diff --git a/v3/UNRELEASED_CHANGELOG.md b/v3/UNRELEASED_CHANGELOG.md index 43a05ba7b..69e114900 100644 --- a/v3/UNRELEASED_CHANGELOG.md +++ b/v3/UNRELEASED_CHANGELOG.md @@ -17,6 +17,7 @@ After processing, the content will be moved to the main changelog and this file ## Added +- Add `UseApplicationMenu` option to `WebviewWindowOptions` allowing windows on Windows/Linux to inherit the application menu set via `app.Menu.Set()` by @leaanthony ## Changed diff --git a/v3/examples/dialogs/main.go b/v3/examples/dialogs/main.go index dbc6a218f..e99878941 100644 --- a/v3/examples/dialogs/main.go +++ b/v3/examples/dialogs/main.go @@ -363,13 +363,13 @@ func main() { } }) - window := app.Window.New() + // Set the application menu + app.Menu.Set(menu) - if runtime.GOOS == "darwin" { - app.Menu.Set(menu) - } else { - window.SetMenu(menu) - } + // Create window with UseApplicationMenu to inherit the app menu on Windows/Linux + app.Window.NewWithOptions(application.WebviewWindowOptions{ + UseApplicationMenu: true, + }) err = app.Run() diff --git a/v3/examples/menu/main.go b/v3/examples/menu/main.go index 53981cbad..b034cbb68 100644 --- a/v3/examples/menu/main.go +++ b/v3/examples/menu/main.go @@ -146,11 +146,12 @@ func main() { app.Menu.Set(menu) - window := app.Window.NewWithOptions(application.WebviewWindowOptions{ - Name: "menu-example", - Title: "Menu Example", + // UseApplicationMenu allows Windows/Linux to inherit the app menu + app.Window.NewWithOptions(application.WebviewWindowOptions{ + Name: "menu-example", + Title: "Menu Example", + UseApplicationMenu: true, }).SetBackgroundColour(application.NewRGB(33, 37, 41)) - window.SetMenu(menu) err := app.Run() diff --git a/v3/pkg/application/webview_window_linux.go b/v3/pkg/application/webview_window_linux.go index 510905a20..ed664b49f 100644 --- a/v3/pkg/application/webview_window_linux.go +++ b/v3/pkg/application/webview_window_linux.go @@ -273,10 +273,17 @@ func (w *linuxWebviewWindow) run() { var menu = w.parent.options.Linux.Menu if menu != nil { + // Explicit window menu takes priority InvokeSync(func() { menu.Update() }) w.gtkmenu = (menu.impl).(*linuxMenu).native + } else if w.parent.options.UseApplicationMenu && globalApplication.applicationMenu != nil { + // Use the global application menu if opted in + InvokeSync(func() { + globalApplication.applicationMenu.Update() + }) + w.gtkmenu = (globalApplication.applicationMenu.impl).(*linuxMenu).native } w.window, w.webview, w.vbox = windowNew(app.application, w.gtkmenu, w.parent.id, w.parent.options.Linux.WebviewGpuPolicy) diff --git a/v3/pkg/application/webview_window_options.go b/v3/pkg/application/webview_window_options.go index 3ae49157d..8ba248e23 100644 --- a/v3/pkg/application/webview_window_options.go +++ b/v3/pkg/application/webview_window_options.go @@ -142,6 +142,13 @@ type WebviewWindowOptions struct { // Effective on Windows and macOS only; no-op on Linux. // Best-effort protection with platform-specific caveats (see docs). ContentProtectionEnabled bool + + // UseApplicationMenu indicates this window should use the application menu + // set via app.Menu.Set() instead of requiring a window-specific menu. + // On macOS this has no effect as the application menu is always global. + // On Windows/Linux, if true and no explicit window menu is set, the window + // will use the application menu. Defaults to false for backwards compatibility. + UseApplicationMenu bool } type RGBA struct { diff --git a/v3/pkg/application/webview_window_windows.go b/v3/pkg/application/webview_window_windows.go index b2bb3c04d..e0581bbb3 100644 --- a/v3/pkg/application/webview_window_windows.go +++ b/v3/pkg/application/webview_window_windows.go @@ -362,10 +362,17 @@ func (w *windowsWebviewWindow) run() { if !options.Frameless { userMenu := w.parent.options.Windows.Menu if userMenu != nil { + // Explicit window menu takes priority userMenu.Update() w.menu = NewApplicationMenu(w, userMenu) w.menu.parentWindow = w appMenu = w.menu.menu + } else if options.UseApplicationMenu && globalApplication.applicationMenu != nil { + // Use the global application menu if opted in + globalApplication.applicationMenu.Update() + w.menu = NewApplicationMenu(w, globalApplication.applicationMenu) + w.menu.parentWindow = w + appMenu = w.menu.menu } }