[client/ui] Refresh tray menu after status-indicator bitmap change

Wails v3 alpha's setMenuItemBitmap on darwin calls NSMenuItem.setImage
from whichever thread invokes SetBitmap — unlike the sibling setters
for label/disabled/hidden/checked, which dispatch_sync onto the main
queue. The off-thread AppKit call doesn't redraw, so the coloured
status dot stayed stale until the user closed and reopened the menu.

Force a tray.SetMenu rebuild after updating the bitmap; the rebuild
runs processMenu inside InvokeSync, which applies the bitmap to a fresh
NSMenuItem on the main thread and macOS picks it up immediately.
This commit is contained in:
Zoltan Papp
2026-05-12 21:46:05 +02:00
parent af40ee52f8
commit e1bf362675

View File

@@ -571,11 +571,22 @@ func (t *Tray) rebuildExitNodes(nodes []string) {
// palette: green for Connected, yellow for Connecting, blue for the
// login states, red for hard errors, grey for the idle/disconnected
// pair and a darker grey when the daemon socket is unreachable.
//
// Wails v3 alpha's setMenuItemBitmap calls NSMenuItem.setImage from
// whichever thread invoked SetBitmap — unlike setMenuItemLabel/Disabled/
// Hidden/Checked which dispatch_sync onto the main queue. The off-thread
// AppKit call leaves the visible dot stale until the next time the menu
// is reopened (close+reopen workaround). Rebuilding via tray.SetMenu
// reruns processMenu inside InvokeSync, so the bitmap is applied to a
// fresh NSMenuItem on the main thread and macOS picks it up.
func (t *Tray) applyStatusIndicator(status string) {
if t.statusItem == nil {
return
}
t.statusItem.SetBitmap(statusIndicatorBitmap(status))
if t.menu != nil {
t.tray.SetMenu(t.menu)
}
}
func statusIndicatorBitmap(status string) []byte {