diff --git a/mkdocs-website/docs/en/changelog.md b/mkdocs-website/docs/en/changelog.md index eaa7d0dc9..c1a48b675 100644 --- a/mkdocs-website/docs/en/changelog.md +++ b/mkdocs-website/docs/en/changelog.md @@ -47,6 +47,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix replace line in `go.mod` to use relative paths. Fixes Windows paths with spaces - @leaanthony. - Fix MacOS systray click handling when no attached window by [thomas-senechal](https://github.com/thomas-senechal) in PR [#3207](https://github.com/wailsapp/wails/pull/3207) - Fix failing Windows build due to unknown option by [thomas-senechal](https://github.com/thomas-senechal) in PR [#3208](https://github.com/wailsapp/wails/pull/3208) +- Fix crash on windows left clicking the systray icon when not having an attached window [tw1nk](https://github.com/tw1nk) in PR [#3271](https://github.com/wailsapp/wails/pull/3271) - Fix wrong baseURL when open window twice by @5aaee9 in PR [#3273](https://github.com/wailsapp/wails/pull/3273) - Fix ordering of if branches in `WebviewWindow.Restore` method by [@fbbdev](https://github.com/fbbdev) in [#3279](https://github.com/wailsapp/wails/pull/3279) - Correctly compute `startURL` across multiple `GetStartURL` invocations when `FRONTEND_DEVSERVER_URL` is present. [#3299](https://github.com/wailsapp/wails/pull/3299) diff --git a/v3/pkg/application/systemtray_windows.go b/v3/pkg/application/systemtray_windows.go index 0915035c0..67786cae9 100644 --- a/v3/pkg/application/systemtray_windows.go +++ b/v3/pkg/application/systemtray_windows.go @@ -4,10 +4,11 @@ package application import ( "fmt" - "github.com/wailsapp/wails/v3/pkg/icons" "syscall" "unsafe" + "github.com/wailsapp/wails/v3/pkg/icons" + "github.com/samber/lo" "github.com/wailsapp/wails/v3/pkg/events" "github.com/wailsapp/wails/v3/pkg/w32" @@ -42,17 +43,9 @@ func (s *windowsSystemTray) openMenu() { // Show the menu at the tray bounds s.menu.ShowAt(trayBounds.X, trayBounds.Y) - } func (s *windowsSystemTray) positionWindow(window *WebviewWindow, offset int) error { - - // Get the trayBounds of this system tray - trayBounds, err := s.bounds() - if err != nil { - return err - } - // Get the current screen trayBounds currentScreen, err := s.getScreen() if err != nil { @@ -64,25 +57,45 @@ func (s *windowsSystemTray) positionWindow(window *WebviewWindow, offset int) er newX := screenBounds.Width - window.Width() newY := screenBounds.Height - window.Height() + // systray icons in windows can either be in the taskbar + // or in a flyout menu. + iconIsInTrayBounds, err := s.iconIsInTrayBounds() + if err != nil { + return err + } + + // we only need the traybounds if the icon is in the tray + var trayBounds *Rect + if iconIsInTrayBounds { + trayBounds, err = s.bounds() + if err != nil { + return err + } + } + taskbarBounds := w32.GetTaskbarPosition() + + // Set the window position based on the icon location + // if the icon is in the taskbar (traybounds) then we need + // to adjust the position so the window is centered on the icon switch taskbarBounds.UEdge { case w32.ABE_LEFT: - if trayBounds != nil && trayBounds.Y-(window.Height()/2) >= 0 { + if iconIsInTrayBounds && trayBounds.Y-(window.Height()/2) >= 0 { newY = trayBounds.Y - (window.Height() / 2) } window.SetRelativePosition(offset, newY) case w32.ABE_TOP: - if trayBounds != nil && trayBounds.X-(window.Width()/2) <= newX { + if iconIsInTrayBounds && trayBounds.X-(window.Width()/2) <= newX { newX = trayBounds.X - (window.Width() / 2) } window.SetRelativePosition(newX, offset) case w32.ABE_RIGHT: - if trayBounds != nil && trayBounds.Y-(window.Height()/2) <= newY { + if iconIsInTrayBounds && trayBounds.Y-(window.Height()/2) <= newY { newY = trayBounds.Y - (window.Height() / 2) } window.SetRelativePosition(screenBounds.Width-window.Width()-offset, newY) case w32.ABE_BOTTOM: - if trayBounds != nil && trayBounds.X-(window.Width()/2) <= newX { + if iconIsInTrayBounds && trayBounds.X-(window.Width()/2) <= newX { newX = trayBounds.X - (window.Width() / 2) } window.SetRelativePosition(newX, screenBounds.Height-window.Height()-offset) @@ -101,14 +114,6 @@ func (s *windowsSystemTray) bounds() (*Rect, error) { return nil, fmt.Errorf("failed to get monitor") } - // Get the taskbar rect - taskbarRect := w32.GetTaskbarPosition() - - flyoutOpen := !w32.RectInRect(bounds, &taskbarRect.Rc) - if flyoutOpen { - return nil, nil - } - return &Rect{ X: int(bounds.Left), Y: int(bounds.Top), @@ -117,6 +122,22 @@ func (s *windowsSystemTray) bounds() (*Rect, error) { }, nil } +func (s *windowsSystemTray) iconIsInTrayBounds() (bool, error) { + bounds, err := w32.GetSystrayBounds(s.hwnd, s.uid) + if err != nil { + return false, err + } + + taskbarRect := w32.GetTaskbarPosition() + + inTasksBar := w32.RectInRect(bounds, &taskbarRect.Rc) + if inTasksBar { + return true, nil + } + + return false, nil +} + func (s *windowsSystemTray) getScreen() (*Screen, error) { // Get the screen for this systray return getScreen(s.hwnd) @@ -188,7 +209,7 @@ func (s *windowsSystemTray) run() { if s.parent.rightClickHandler == nil { s.parent.rightClickHandler = func() { if s.menu != nil { - s.menu.ShowAtCursor() + s.openMenu() } } } @@ -203,11 +224,9 @@ func (s *windowsSystemTray) run() { // Register the system tray getNativeApplication().registerSystemTray(s) - } func (s *windowsSystemTray) updateIcon() { - var newIcon w32.HICON if w32.IsCurrentlyDarkMode() { newIcon = s.darkModeIcon @@ -252,6 +271,7 @@ func (s *windowsSystemTray) setIcon(icon []byte) { // Update the icon s.updateIcon() } + func (s *windowsSystemTray) setDarkModeIcon(icon []byte) { var err error s.darkModeIcon, err = w32.CreateSmallHIconFromImage(icon) @@ -301,7 +321,7 @@ func (s *windowsSystemTray) wndProc(msg uint32, wParam, lParam uintptr) uintptr s.parent.mouseLeaveHandler() } } - //println(w32.WMMessageToString(msg)) + // println(w32.WMMessageToString(msg)) // Menu processing case w32.WM_COMMAND: @@ -311,8 +331,8 @@ func (s *windowsSystemTray) wndProc(msg uint32, wParam, lParam uintptr) uintptr s.menu.ProcessCommand(cmdMsgID) } default: - //msg := int(wParam & 0xffff) - //println(w32.WMMessageToString(uintptr(msg))) + // msg := int(wParam & 0xffff) + // println(w32.WMMessageToString(uintptr(msg))) } return w32.DefWindowProc(s.hwnd, msg, wParam, lParam)