diff --git a/v3/UNRELEASED_CHANGELOG.md b/v3/UNRELEASED_CHANGELOG.md index 928ae143f..88c4a4b3d 100644 --- a/v3/UNRELEASED_CHANGELOG.md +++ b/v3/UNRELEASED_CHANGELOG.md @@ -17,20 +17,22 @@ After processing, the content will be moved to the main changelog and this file ## Added - Add `XDG_SESSION_TYPE` to `wails3 doctor` output on Linux by @leaanthony -- Add additional WebKit2 load-change events for Linux: `WindowLoadStarted`, `WindowLoadRedirected`, `WindowLoadCommitted`, `WindowLoadFinished` (#3896) by @leaanthony ## Changed ## Fixed +- Fix window menu crash on Wayland caused by appmenu-gtk-module accessing unrealized window (#4769) by @leaanthony +- Fix GTK application crash when app name contains invalid characters (spaces, parentheses, etc.) by @leaanthony +- Fix "not enough memory" error when initializing drag and drop on Windows (#4701) by @overlordtm +- Fix systray context menu hiding attached window on Linux (#4494) by @leaanthony ## Deprecated ## Removed -- **BREAKING**: Remove `linux:WindowLoadChanged` event - use `linux:WindowLoadFinished` instead for detecting when WebView has finished loading (#3896) by @leaanthony ## Security diff --git a/v3/pkg/application/systemtray.go b/v3/pkg/application/systemtray.go index ef0525e54..f60ec80eb 100644 --- a/v3/pkg/application/systemtray.go +++ b/v3/pkg/application/systemtray.go @@ -101,6 +101,14 @@ func (s *SystemTray) Run() { if s.attachedWindow.Window != nil { // Setup listener s.attachedWindow.Window.OnWindowEvent(events.Common.WindowLostFocus, func(event *WindowEvent) { + // On Linux, don't hide when the systray menu is active (opening or open). + // The menu steals focus from the window, but we don't want to hide in this case. + s.attachedWindow.menuMutex.RLock() + menuActive := s.attachedWindow.menuActive + s.attachedWindow.menuMutex.RUnlock() + if menuActive { + return + } s.attachedWindow.Window.Hide() // Special handler for Windows if runtime.GOOS == "windows" { @@ -274,6 +282,13 @@ type WindowAttachConfig struct { // Used to ensure that the window state is read on first click initialClick sync.Once + + // Protects menuActive from concurrent access + menuMutex sync.RWMutex + + // Indicates that the systray menu is currently open or about to open. + // Used on Linux to prevent hiding the attached window when the menu steals focus. + menuActive bool } // AttachWindow attaches a window to the system tray. The window will be shown when the system tray icon is clicked. diff --git a/v3/pkg/application/systemtray_linux.go b/v3/pkg/application/systemtray_linux.go index d1319f4bd..fe629c4e3 100644 --- a/v3/pkg/application/systemtray_linux.go +++ b/v3/pkg/application/systemtray_linux.go @@ -620,6 +620,11 @@ func iconToPX(icon []byte) (PX, error) { // AboutToShow is an implementation of the com.canonical.dbusmenu.AboutToShow method. func (s *linuxSystemTray) AboutToShow(id int32) (needUpdate bool, err *dbus.Error) { + // Mark menu as active to prevent hiding attached window when menu steals focus. + // This is called by the desktop environment before showing the menu. + s.parent.attachedWindow.menuMutex.Lock() + s.parent.attachedWindow.menuActive = true + s.parent.attachedWindow.menuMutex.Unlock() return } @@ -646,6 +651,10 @@ func (s *linuxSystemTray) Event(id int32, eventID string, data dbus.Variant, tim InvokeAsync(item.menuItem.handleClick) } case "opened": + // Menu is now open - keep the flag active + s.parent.attachedWindow.menuMutex.Lock() + s.parent.attachedWindow.menuActive = true + s.parent.attachedWindow.menuMutex.Unlock() if s.parent.clickHandler != nil { s.parent.clickHandler() } @@ -653,6 +662,10 @@ func (s *linuxSystemTray) Event(id int32, eventID string, data dbus.Variant, tim s.parent.onMenuOpen() } case "closed": + // Menu is closed - clear the flag so normal focus behavior resumes + s.parent.attachedWindow.menuMutex.Lock() + s.parent.attachedWindow.menuActive = false + s.parent.attachedWindow.menuMutex.Unlock() if s.parent.onMenuClose != nil { s.parent.onMenuClose() } diff --git a/v3/test/4494-systray/Taskfile.yml b/v3/test/4494-systray/Taskfile.yml new file mode 100644 index 000000000..6e8e2dedd --- /dev/null +++ b/v3/test/4494-systray/Taskfile.yml @@ -0,0 +1,22 @@ +version: '3' + +vars: + APP_NAME: "4494-systray{{exeExt}}" + +tasks: + build: + summary: Builds the test application + cmds: + - go build -o bin/{{.APP_NAME}} . + + run: + summary: Runs the test application + deps: + - build + cmds: + - ./bin/{{.APP_NAME}} + + dev: + summary: Builds and runs the test application + cmds: + - go run . diff --git a/v3/test/4494-systray/assets/icon.png b/v3/test/4494-systray/assets/icon.png new file mode 100644 index 000000000..a6129a69f Binary files /dev/null and b/v3/test/4494-systray/assets/icon.png differ diff --git a/v3/test/4494-systray/assets/index.html b/v3/test/4494-systray/assets/index.html new file mode 100644 index 000000000..e1e1d1712 --- /dev/null +++ b/v3/test/4494-systray/assets/index.html @@ -0,0 +1,56 @@ + + + + + Systray Test (#4494) + + + +

Systray Test (#4494)

+ +
+

Test Steps:

+
    +
  1. Look for the systray icon in your system tray
  2. +
  3. Right-click the systray icon to open the context menu
  4. +
+ +

Expected: This window should stay visible when the context menu opens

+

Bug: On Linux (Wayland/KDE), this window hides when right-clicking the systray icon

+
+ +
+

Issue Details:

+

When an attached window loses focus (which happens when the context menu opens), + the WindowLostFocus event triggers and hides the window.

+

This is problematic because the user is still interacting with the systray, + not clicking away from it.

+
+ + diff --git a/v3/test/4494-systray/main.go b/v3/test/4494-systray/main.go new file mode 100644 index 000000000..82dc1cbd2 --- /dev/null +++ b/v3/test/4494-systray/main.go @@ -0,0 +1,64 @@ +package main + +import ( + "embed" + _ "embed" + "log" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +//go:embed assets/* +var assets embed.FS + +//go:embed assets/icon.png +var icon []byte + +func main() { + app := application.New(application.Options{ + Name: "Systray Test (#4494)", + Description: "Test for systray context menu hiding attached window", + Assets: application.AssetOptions{ + Handler: application.BundledAssetFileServer(assets), + }, + }) + + // Create the main window + window := app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "Systray Test (#4494)", + Width: 400, + Height: 300, + }) + + // Create a systray menu + menu := app.NewMenu() + menu.Add("Show Window").OnClick(func(ctx *application.Context) { + log.Println("Show Window clicked") + window.Show() + }) + menu.Add("Hide Window").OnClick(func(ctx *application.Context) { + log.Println("Hide Window clicked") + window.Hide() + }) + menu.AddSeparator() + menu.Add("Quit").OnClick(func(ctx *application.Context) { + log.Println("Quit clicked") + app.Quit() + }) + + // Create system tray with attached window + // Issue #4494: Right-clicking to open context menu hides the attached window + systemTray := app.SystemTray.New() + systemTray.SetIcon(icon) + systemTray.SetMenu(menu) + systemTray.AttachWindow(window) + + log.Println("Starting application...") + log.Println("TEST: Right-click the systray icon. The window should NOT hide when the context menu opens.") + log.Println("BUG: On Linux (especially Wayland/KDE), the window hides when right-clicking the systray icon.") + + err := app.Run() + if err != nil { + log.Fatal(err) + } +}