From dfee5252a37b6036d2fc3ac35cdabd79eb525812 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20Papp?= Date: Mon, 1 Jun 2026 22:04:49 +0200 Subject: [PATCH] client/ui: open main window on tray left-click on Linux MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit KDE Plasma routes a tray left-click to the SNI Activate method (right-click opens the context menu), but NetBird wired no Activate action, so on KDE a left-click appeared completely dead while only right-click surfaced the menu. Bind the Linux tray OnClick handler to ShowWindow(). OpenMenu() is not an option on Linux: Wails v3 leaves linuxSystemTray.openMenu unimplemented (it only logs), so left-click→OpenMenu would still do nothing on KDE. ShowWindow() is the same call Windows already runs from its double-click handler, and it does not reproduce the macOS OpenMenu freeze (c77e5cef8) — that came from NSStatusItem's blocking embedded menu loop, whereas Show/Focus return immediately. Split the Linux click handler into its own tray_click_linux.go and narrow the macOS no-op bindTrayClick build tag accordingly. The context menu stays on right-click on every host. On hosts that already open the menu on left-click natively (GNOME Shell + AppIndicator) left-click now opens the window instead; the menu remains on right-click. --- client/ui/tray.go | 23 +++++++++++------------ client/ui/tray_click_linux.go | 32 ++++++++++++++++++++++++++++++++ client/ui/tray_click_other.go | 16 +++++++--------- 3 files changed, 50 insertions(+), 21 deletions(-) create mode 100644 client/ui/tray_click_linux.go diff --git a/client/ui/tray.go b/client/ui/tray.go index c54376454..17657c8fe 100644 --- a/client/ui/tray.go +++ b/client/ui/tray.go @@ -215,18 +215,17 @@ func NewTray(app *application.App, window *application.WebviewWindow, svc TraySe } t.menu = t.buildMenu() t.tray.SetMenu(t.menu) - // Left-click on the tray icon opens the menu, and the window is reached - // through the explicit "Open NetBird" entry. This matches macOS - // NSStatusItem convention (click → menu), the Linux StatusNotifierItem - // spec, and the legacy Fyne client. macOS and Linux give us click→menu - // natively, so bindTrayClick is a no-op there (binding OnClick→OpenMenu - // on macOS would freeze the tray — see tray_click_other.go). Windows has - // no native left-click handler, so bindTrayClick wires one explicitly - // (see tray_click_windows.go). On Linux we deliberately skip AttachWindow: - // it plus Wails3's applySmartDefaults would pop the window alongside the - // menu on environments like GNOME Shell with the AppIndicator extension. - // Right-click opens the menu through Wails' default rightClickHandler on - // every platform. + // Tray click handling is platform-specific (see the tray_click_*.go + // files): macOS auto-shows the menu on left-click natively, so its + // bindTrayClick is a no-op (binding OnClick→OpenMenu would freeze the + // tray — see tray_click_other.go). Windows has no native left-click + // handler, so it wires left→OpenMenu + double→ShowWindow. Linux hosts + // disagree on left-click (KDE routes it to Activate, which was unwired + // and appeared dead), so Linux binds left→ShowWindow. The context menu + // stays reachable via right-click on every platform, plus the explicit + // "Open NetBird" entry. AttachWindow is deliberately skipped everywhere: + // with Wails3's applySmartDefaults it would pop the window alongside the + // menu on GNOME Shell + AppIndicator. bindTrayClick(t) app.Event.On(services.EventStatusSnapshot, t.onStatusEvent) diff --git a/client/ui/tray_click_linux.go b/client/ui/tray_click_linux.go new file mode 100644 index 000000000..e4bf4ed7c --- /dev/null +++ b/client/ui/tray_click_linux.go @@ -0,0 +1,32 @@ +//go:build linux + +package main + +// bindTrayClick wires the tray icon's left-click handler on Linux. +// +// Different StatusNotifierItem hosts route a left-click differently. KDE +// Plasma maps left-click to the SNI Activate method and right-click to the +// context menu — but NetBird wired no Activate action, so on KDE a left-click +// appeared completely dead while only right-click surfaced the menu (the +// behaviour users reported as confusing). Wails' Linux SNI backend forwards +// Activate to the tray's OnClick handler (systemtray_linux.go Activate → +// clickHandler), so we bind one here. +// +// We open the main window rather than the menu. OpenMenu() is not an option +// on Linux: the Wails v3 backend leaves linuxSystemTray.openMenu unimplemented +// (it only logs), so a left-click→OpenMenu binding would still do nothing on +// KDE. ShowWindow() is the same call Windows already runs from its +// double-click handler, so it is a proven-safe click-handler action — and it +// does not reproduce the macOS OpenMenu freeze (commit c77e5cef8): that freeze +// came from NSStatusItem's blocking embedded menu loop, whereas Show/Focus +// return immediately. The context menu stays reachable via right-click through +// the host's own rendering. +// +// On hosts where left-click already opens the menu natively (e.g. GNOME Shell +// with the AppIndicator extension) this means left-click now opens the window +// instead — the menu remains on right-click. AttachWindow is deliberately not +// used: combined with Wails3's applySmartDefaults it pops the window alongside +// the menu on those hosts, which is not the UX we want. +func bindTrayClick(t *Tray) { + t.tray.OnClick(func() { t.ShowWindow() }) +} diff --git a/client/ui/tray_click_other.go b/client/ui/tray_click_other.go index 231986a47..cad878cb8 100644 --- a/client/ui/tray_click_other.go +++ b/client/ui/tray_click_other.go @@ -1,13 +1,11 @@ -//go:build !windows && !android && !ios && !freebsd && !js +//go:build !windows && !linux && !android && !ios && !freebsd && !js package main -// bindTrayClick is a no-op on macOS and Linux. On macOS the native -// NSStatusItem auto-shows the menu on left-click; on Linux the -// StatusNotifierItem host paints the menu independently. Binding an -// OnClick→OpenMenu handler is both unnecessary there and actively harmful on -// macOS, where OpenMenu routes through NSStatusItem's blocking [button -// mouseDown:] on the serial main GCD queue and freezes the tray and webview -// until the menu closes (commit c77e5cef8). Windows opts in via the sibling -// tray_click_windows.go file. +// bindTrayClick is a no-op on macOS. The native NSStatusItem auto-shows the +// menu on left-click, so binding an OnClick→OpenMenu handler is both +// unnecessary and actively harmful: OpenMenu routes through NSStatusItem's +// blocking [button mouseDown:] on the serial main GCD queue and freezes the +// tray and webview until the menu closes (commit c77e5cef8). Windows opts in +// via tray_click_windows.go; Linux via tray_click_linux.go. func bindTrayClick(*Tray) {}