diff --git a/v3/pkg/application/webview_window_windows.go b/v3/pkg/application/webview_window_windows.go index 434723fb4..f6a3c204d 100644 --- a/v3/pkg/application/webview_window_windows.go +++ b/v3/pkg/application/webview_window_windows.go @@ -362,10 +362,11 @@ func (w *windowsWebviewWindow) run() { if options.AlwaysOnTop { exStyle |= w32.WS_EX_TOPMOST } - // If we're frameless, we need to add the WS_EX_TOOLWINDOW style to hide the window from the taskbar + // WS_EX_TOOLWINDOW hides the window from the taskbar without blocking keyboard focus. + // WS_EX_NOACTIVATE (previously used here) prevents the window from being activated, + // which blocks keyboard focus and input. if options.Windows.HiddenOnTaskbar { - //exStyle |= w32.WS_EX_TOOLWINDOW - exStyle |= w32.WS_EX_NOACTIVATE + exStyle |= w32.WS_EX_TOOLWINDOW } else { exStyle |= w32.WS_EX_APPWINDOW } @@ -692,6 +693,13 @@ func (w *windowsWebviewWindow) setPhysicalBounds(physicalBounds Rect) { w32.SWP_NOZORDER|w32.SWP_NOACTIVATE, ) w.ignoreDPIChangeResizing = previousFlag + + // For WS_EX_LAYERED windows (frameless+transparent or IgnoreMouseEvents), the hit-test + // region is not updated by SetWindowPos alone. Calling SetLayeredWindowAttributes refreshes + // the layered region so that the full new window area responds to mouse events. + if exStyle := w32.GetWindowLong(w.hwnd, w32.GWL_EXSTYLE); exStyle&w32.WS_EX_LAYERED != 0 { + w32.SetLayeredWindowAttributes(w.hwnd, 0, 255, w32.LWA_ALPHA) + } } // Get window dip bounds diff --git a/v3/pkg/w32/constants.go b/v3/pkg/w32/constants.go index 50085a6e6..2ce60aed6 100644 --- a/v3/pkg/w32/constants.go +++ b/v3/pkg/w32/constants.go @@ -397,6 +397,12 @@ const ( WS_EX_NOACTIVATE = 0x08000000 ) +// SetLayeredWindowAttributes flags +const ( + LWA_COLORKEY = 0x00000001 + LWA_ALPHA = 0x00000002 +) + // Window message constants const ( WM_APP = 32768 diff --git a/v3/pkg/w32/user32.go b/v3/pkg/w32/user32.go index 95463d86b..339428fa4 100644 --- a/v3/pkg/w32/user32.go +++ b/v3/pkg/w32/user32.go @@ -156,6 +156,7 @@ var ( procCallNextHookEx = moduser32.NewProc("CallNextHookEx") procGetForegroundWindow = moduser32.NewProc("GetForegroundWindow") procUpdateLayeredWindow = moduser32.NewProc("UpdateLayeredWindow") + procSetLayeredWindowAttributes = moduser32.NewProc("SetLayeredWindowAttributes") getDisplayConfig = moduser32.NewProc("GetDisplayConfigBufferSizes") queryDisplayConfig = moduser32.NewProc("QueryDisplayConfig") @@ -317,6 +318,19 @@ func UpdateLayeredWindow(hwnd HWND, hdcDst HDC, pptDst *POINT, psize *SIZE, return ret != 0 } +// SetLayeredWindowAttributes sets the opacity and transparency color key of a layered window. +// crKey: the transparency color key (use 0 if not using LWA_COLORKEY) +// bAlpha: the opacity value (0 = transparent, 255 = opaque); used when dwFlags includes LWA_ALPHA +// dwFlags: LWA_COLORKEY and/or LWA_ALPHA +func SetLayeredWindowAttributes(hwnd HWND, crKey COLORREF, bAlpha byte, dwFlags DWORD) bool { + ret, _, _ := procSetLayeredWindowAttributes.Call( + uintptr(hwnd), + uintptr(crKey), + uintptr(bAlpha), + uintptr(dwFlags)) + return ret != 0 +} + func PostThreadMessage(threadID HANDLE, msg int, wp, lp uintptr) { procPostThreadMessageW.Call(threadID, uintptr(msg), wp, lp) }