diff --git a/v2/internal/platform/systray.go b/v2/internal/platform/systray.go index 317ea7a7a..2f047249c 100644 --- a/v2/internal/platform/systray.go +++ b/v2/internal/platform/systray.go @@ -3,13 +3,13 @@ package platform import ( "github.com/wailsapp/wails/v2/internal/platform/systray" "github.com/wailsapp/wails/v2/pkg/menu" + "github.com/wailsapp/wails/v2/pkg/options" ) import "github.com/samber/lo" type SysTray interface { // SetTitle sets the title of the tray menu SetTitle(title string) - SetIcons(lightModeIcon []byte, darkModeIcon []byte) error SetTooltip(tooltip string) error Show() error Hide() error @@ -18,6 +18,7 @@ type SysTray interface { AppendMenu(label string, callback menu.Callback) AppendMenuItem(item *menu.MenuItem) AppendSeparator() + SetIcons(lightModeIcon, darkModeIcon *options.SystemTrayIcon) error } func NewSysTray() SysTray { diff --git a/v2/internal/platform/systray/windows.go b/v2/internal/platform/systray/windows.go index fde7f3e3f..27ff447e1 100644 --- a/v2/internal/platform/systray/windows.go +++ b/v2/internal/platform/systray/windows.go @@ -6,9 +6,10 @@ package systray import ( "errors" + "github.com/samber/lo" "github.com/wailsapp/wails/v2/internal/platform/win32" "github.com/wailsapp/wails/v2/pkg/menu" - "golang.org/x/sys/windows" + "github.com/wailsapp/wails/v2/pkg/options" "syscall" "unsafe" ) @@ -22,17 +23,21 @@ var ( ) type Systray struct { - id uint32 - hwnd win32.HWND - hinst win32.HINSTANCE - lclick func() - rclick func() + id uint32 + hwnd win32.HWND + hinst win32.HINSTANCE + lclick func() + rclick func() + + appIcon win32.HICON lightModeIcon win32.HICON darkModeIcon win32.HICON + currentIcon win32.HICON Menu []*menu.MenuItem quit chan struct{} + icon *options.SystemTrayIcon } func (p *Systray) Close() { @@ -115,6 +120,9 @@ func New() (*Systray, error) { return nil, errors.New("shell notify version failed") } + ni.appIcon = win32.LoadIconWithResourceID(0, uintptr(win32.IDI_APPLICATION)) + ni.lightModeIcon = ni.appIcon + ni.darkModeIcon = ni.appIcon ni.id = nid.UID return ni, nil } @@ -218,16 +226,23 @@ func (p *Systray) setVisible(visible bool) error { return nil } -func (p *Systray) SetIcons(lightModeIcon []byte, darkModeIcon []byte) error { - p.lightModeIcon = p.getIcon(lightModeIcon) - p.darkModeIcon = p.getIcon(darkModeIcon) +func (p *Systray) SetIcons(lightModeIcon, darkModeIcon *options.SystemTrayIcon) error { + var newLightModeIcon, newDarkModeIcon win32.HICON + if lightModeIcon != nil && lightModeIcon.Data != nil { + newLightModeIcon = p.getIcon(lightModeIcon.Data) + } + if darkModeIcon != nil && darkModeIcon.Data != nil { + newDarkModeIcon = p.getIcon(darkModeIcon.Data) + } + p.lightModeIcon, _ = lo.Coalesce(newLightModeIcon, newDarkModeIcon, p.appIcon) + p.darkModeIcon, _ = lo.Coalesce(newDarkModeIcon, newLightModeIcon, p.appIcon) return p.updateIcon() } func (p *Systray) getIcon(icon []byte) win32.HICON { result, err := win32.CreateHIconFromPNG(icon) if err != nil { - result = win32.LoadIconWithResourceID(0, uintptr(win32.IDI_APPLICATION)) + result = p.appIcon } return result } @@ -269,7 +284,7 @@ func (p *Systray) WinProc(hwnd win32.HWND, msg uint32, wparam, lparam uintptr) u } } case win32.WM_SETTINGCHANGE: - settingChanged := windows.UTF16PtrToString((*uint16)(unsafe.Pointer(lparam))) + settingChanged := win32.UTF16PtrToString(lparam) if settingChanged == "ImmersiveColorSet" { err := p.updateIcon() if err != nil { @@ -313,10 +328,18 @@ func (p *Systray) Run() error { } func (p *Systray) updateIcon() error { + + var newIcon win32.HICON if win32.IsCurrentlyDarkMode() { - return p.setIcon(p.darkModeIcon) + newIcon = p.darkModeIcon + } else { + newIcon = p.lightModeIcon } - return p.setIcon(p.lightModeIcon) + if p.currentIcon == newIcon { + return nil + } + p.currentIcon = newIcon + return p.setIcon(newIcon) } func RegisterWindow(name string, proc win32.WindowProc) (win32.HINSTANCE, error) { diff --git a/v2/internal/platform/win32/window.go b/v2/internal/platform/win32/window.go index 62546ff23..7cf15cadb 100644 --- a/v2/internal/platform/win32/window.go +++ b/v2/internal/platform/win32/window.go @@ -3,6 +3,7 @@ package win32 import ( "fmt" "github.com/samber/lo" + "golang.org/x/sys/windows" "syscall" "unsafe" ) @@ -124,6 +125,10 @@ func MustUTF16FromString(input string) []uint16 { return ret } +func UTF16PtrToString(input uintptr) string { + return windows.UTF16PtrToString((*uint16)(unsafe.Pointer(input))) +} + func SetForegroundWindow(wnd HWND) bool { ret, _, _ := procSetForegroundWindow.Call( uintptr(wnd), diff --git a/v2/pkg/application/systray.go b/v2/pkg/application/systray.go index 74535b676..58cfdd34a 100644 --- a/v2/pkg/application/systray.go +++ b/v2/pkg/application/systray.go @@ -10,8 +10,8 @@ import ( type SystemTray struct { title string hidden bool - lightModeIcon []byte - darkModeIcon []byte + lightModeIcon *options.SystemTrayIcon + darkModeIcon *options.SystemTrayIcon tooltip string startHidden bool diff --git a/v2/pkg/options/options.go b/v2/pkg/options/options.go index 98d36d2c8..25f619b6b 100644 --- a/v2/pkg/options/options.go +++ b/v2/pkg/options/options.go @@ -170,12 +170,3 @@ func processDragOptions(appoptions *App) { appoptions.CSSDragProperty = html.EscapeString(appoptions.CSSDragProperty) appoptions.CSSDragValue = html.EscapeString(appoptions.CSSDragValue) } - -// SystemTray contains options for the system tray -type SystemTray struct { - LightModeIcon []byte - DarkModeIcon []byte - Title string - Tooltip string - StartHidden bool -} diff --git a/v2/pkg/options/systemtray.go b/v2/pkg/options/systemtray.go new file mode 100644 index 000000000..51463c534 --- /dev/null +++ b/v2/pkg/options/systemtray.go @@ -0,0 +1,15 @@ +package options + +// SystemTray contains options for the system tray +type SystemTray struct { + LightModeIcon *SystemTrayIcon + DarkModeIcon *SystemTrayIcon + Title string + Tooltip string + StartHidden bool +} + +// SystemTrayIcon represents a system tray icon +type SystemTrayIcon struct { + Data []byte +}