feat(v3): Add UseApplicationMenu option for cross-platform menu support (#4944)

* feat(v3): Add UseApplicationMenu option for cross-platform menu support

Add `UseApplicationMenu` option to `WebviewWindowOptions` that allows
windows on Windows and Linux to inherit the application menu set via
`app.Menu.Set()`.

This provides a simpler cross-platform approach:
- On macOS: No effect (app menu is always global)
- On Windows/Linux: Window displays the application menu

Benefits:
- Eliminates need for platform-specific menu code
- Per-window opt-in maintains backwards compatibility
- Explicit window menus still take priority

Updated:
- webview_window_options.go: Added UseApplicationMenu bool field
- webview_window_windows.go: Check UseApplicationMenu when no window menu set
- webview_window_linux.go: Check UseApplicationMenu when no window menu set
- examples/dialogs: Use UseApplicationMenu instead of conditional SetMenu
- examples/menu: Use UseApplicationMenu instead of explicit SetMenu
- docs: Updated menu and window options documentation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* docs: Remove incorrect 'Menu Options' section header

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Lea Anthony 2026-02-02 19:51:31 +11:00 committed by GitHub
commit fa06d48497
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 144 additions and 33 deletions

View file

@ -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:**
<Tabs syncKey="platform">
<TabItem label="macOS" icon="apple">
**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.
</TabItem>
<TabItem label="Windows" icon="seti:windows">
**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.
</TabItem>
<TabItem label="Linux" icon="linux">
**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:
</TabItem>
</Tabs>
**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) {

View file

@ -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

View file

@ -17,6 +17,7 @@ After processing, the content will be moved to the main changelog and this file
## Added
<!-- New features, capabilities, or enhancements -->
- Add `UseApplicationMenu` option to `WebviewWindowOptions` allowing windows on Windows/Linux to inherit the application menu set via `app.Menu.Set()` by @leaanthony
## Changed
<!-- Changes in existing functionality -->

View file

@ -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()

View file

@ -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()

View file

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

View file

@ -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 {

View file

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