diff --git a/docs/src/content/docs/learn/windows.mdx b/docs/src/content/docs/learn/windows.mdx index 025942712..d9f3d9160 100644 --- a/docs/src/content/docs/learn/windows.mdx +++ b/docs/src/content/docs/learn/windows.mdx @@ -171,6 +171,37 @@ type Window interface { } ``` +### Common Window Methods + +Windows provide many methods for controlling their appearance and behavior: + +#### Display Control +- `Show()` - Shows the window +- `Hide()` - Hides the window +- `SetTitle(title string)` - Sets the window title +- `SetSize(width, height int)` - Sets the window size +- `SetPosition(x, y int)` - Sets the window position +- `Center()` - Centers the window on screen +- `SetAlwaysOnTop(bool)` - Sets whether window stays on top + +#### Window State +- `Minimise()` - Minimizes the window +- `Maximise()` - Maximizes the window +- `UnMinimise()` - Restores from minimized state +- `UnMaximise()` - Restores from maximized state +- `Fullscreen()` - Enters fullscreen mode +- `UnFullscreen()` - Exits fullscreen mode +- `Restore()` - Restores window to normal state +- `IsMinimised() bool` - Checks if window is minimized +- `IsMaximised() bool` - Checks if window is maximized +- `IsFullscreen() bool` - Checks if window is fullscreen + +#### Platform-Specific Methods + +**Windows Only:** +- `Flash(bool)` - Flashes the taskbar button +- `SnapAssist()` - Triggers Windows 11 Snap Assist (Win+Z) + ## Advanced Window Management ### Multi-Window Applications @@ -235,9 +266,18 @@ app.Event.On("data-updated", func(event *application.CustomEvent) { - Support custom window icons - Can be configured without system title bars - Follow Windows window management conventions - - Support Windows 11 snap layouts + - Support Windows 11 snap layouts via `SnapAssist()` method - Integrate with Windows taskbar features + **Windows 11 Snap Assist Example:** + ```go + // Trigger Windows 11 Snap Assist + window.SnapAssist() + + // This is equivalent to pressing Win+Z + // Shows snap layout options for the current window + ``` + diff --git a/v3/UNRELEASED_CHANGELOG.md b/v3/UNRELEASED_CHANGELOG.md index ab1258986..b680790f1 100644 --- a/v3/UNRELEASED_CHANGELOG.md +++ b/v3/UNRELEASED_CHANGELOG.md @@ -17,6 +17,7 @@ After processing, the content will be moved to the main changelog and this file ### Added - Added Run go mod tidy automatically after wails init [@triadmoko](https://github.com/triadmoko) in [PR](https://github.com/wailsapp/wails/pull/4286) +- Windows Snapassist feature by @leaanthony in [PR](https://github.dev/wailsapp/wails/pull/4463) ## Changed diff --git a/v3/examples/window/main.go b/v3/examples/window/main.go index d40a71cfa..75665f4c6 100644 --- a/v3/examples/window/main.go +++ b/v3/examples/window/main.go @@ -713,6 +713,14 @@ func main() { }) } + if runtime.GOOS == "windows" { + stateMenu.Add("Snap Assist").OnClick(func(ctx *application.Context) { + currentWindow(func(w *application.WebviewWindow) { + w.SnapAssist() + }) + }) + } + printMenu := menu.AddSubmenu("Print") printMenu.Add("Print").OnClick(func(ctx *application.Context) { currentWindow(func(w *application.WebviewWindow) { diff --git a/v3/internal/runtime/desktop/@wailsio/runtime/src/window.ts b/v3/internal/runtime/desktop/@wailsio/runtime/src/window.ts index 11c9c8b52..4279b4c44 100644 --- a/v3/internal/runtime/desktop/@wailsio/runtime/src/window.ts +++ b/v3/internal/runtime/desktop/@wailsio/runtime/src/window.ts @@ -60,6 +60,7 @@ const ZoomMethod = 45; const ZoomInMethod = 46; const ZoomOutMethod = 47; const ZoomResetMethod = 48; +const SnapAssistMethod = 49; /** * A record describing the position of a window. @@ -518,6 +519,14 @@ class Window { ZoomReset(): Promise { return this[callerSym](ZoomResetMethod); } + + /** + * Triggers Windows 11 Snap Assist feature (Windows only). + * This is equivalent to pressing Win+Z and shows snap layout options. + */ + SnapAssist(): Promise { + return this[callerSym](SnapAssistMethod); + } } /** diff --git a/v3/pkg/application/messageprocessor_window.go b/v3/pkg/application/messageprocessor_window.go index 6b9f2a9b2..e7fbb9962 100644 --- a/v3/pkg/application/messageprocessor_window.go +++ b/v3/pkg/application/messageprocessor_window.go @@ -56,6 +56,7 @@ const ( WindowZoomIn = 46 WindowZoomOut = 47 WindowZoomReset = 48 + WindowSnapAssist = 49 ) var windowMethodNames = map[int]string{ @@ -108,6 +109,7 @@ var windowMethodNames = map[int]string{ WindowZoomIn: "ZoomIn", WindowZoomOut: "ZoomOut", WindowZoomReset: "ZoomReset", + WindowSnapAssist: "SnapAssist", } func (m *MessageProcessor) processWindowMethod( @@ -421,6 +423,9 @@ func (m *MessageProcessor) processWindowMethod( case WindowZoomReset: window.ZoomReset() m.ok(rw) + case WindowSnapAssist: + window.SnapAssist() + m.ok(rw) default: m.httpError(rw, "Invalid window call:", fmt.Errorf("unknown method %d", method)) return diff --git a/v3/pkg/application/webview_window.go b/v3/pkg/application/webview_window.go index 67d125074..dbd187e2b 100644 --- a/v3/pkg/application/webview_window.go +++ b/v3/pkg/application/webview_window.go @@ -110,6 +110,7 @@ type ( hideMenuBar() toggleMenuBar() setMenu(menu *Menu) + snapAssist() } ) @@ -1428,3 +1429,12 @@ func (w *WebviewWindow) ToggleMenuBar() { } InvokeSync(w.impl.toggleMenuBar) } + +// SnapAssist triggers the Windows Snap Assist feature by simulating Win+Z key combination. +// On Windows, this opens the snap layout options. On Linux and macOS, this is a no-op. +func (w *WebviewWindow) SnapAssist() { + if w.impl == nil || w.isDestroyed() { + return + } + InvokeSync(w.impl.snapAssist) +} diff --git a/v3/pkg/application/webview_window_darwin.go b/v3/pkg/application/webview_window_darwin.go index eb3f83769..73eac6250 100644 --- a/v3/pkg/application/webview_window_darwin.go +++ b/v3/pkg/application/webview_window_darwin.go @@ -1438,3 +1438,4 @@ func (w *macosWebviewWindow) showMenuBar() {} func (w *macosWebviewWindow) hideMenuBar() {} func (w *macosWebviewWindow) toggleMenuBar() {} func (w *macosWebviewWindow) setMenu(_ *Menu) {} +func (w *macosWebviewWindow) snapAssist() {} // No-op on macOS diff --git a/v3/pkg/application/webview_window_linux.go b/v3/pkg/application/webview_window_linux.go index acf8a47c9..bbaba0ca1 100644 --- a/v3/pkg/application/webview_window_linux.go +++ b/v3/pkg/application/webview_window_linux.go @@ -427,3 +427,4 @@ func (w *linuxWebviewWindow) hide() { func (w *linuxWebviewWindow) showMenuBar() {} func (w *linuxWebviewWindow) hideMenuBar() {} func (w *linuxWebviewWindow) toggleMenuBar() {} +func (w *linuxWebviewWindow) snapAssist() {} // No-op on Linux diff --git a/v3/pkg/application/webview_window_windows.go b/v3/pkg/application/webview_window_windows.go index a39696943..aceb3a88d 100644 --- a/v3/pkg/application/webview_window_windows.go +++ b/v3/pkg/application/webview_window_windows.go @@ -2281,3 +2281,15 @@ func (w *windowsWebviewWindow) hideMenuBar() { w32.SetMenu(w.hwnd, 0) } } + +func (w *windowsWebviewWindow) snapAssist() { + // Simulate Win+Z key combination to trigger Snap Assist + // Press Windows key + w32.KeybdEvent(byte(w32.VK_LWIN), 0, 0, 0) + // Press Z key + w32.KeybdEvent(byte('Z'), 0, 0, 0) + // Release Z key + w32.KeybdEvent(byte('Z'), 0, w32.KEYEVENTF_KEYUP, 0) + // Release Windows key + w32.KeybdEvent(byte(w32.VK_LWIN), 0, w32.KEYEVENTF_KEYUP, 0) +} diff --git a/v3/pkg/application/window.go b/v3/pkg/application/window.go index 58b4c4bf5..f7cfc112b 100644 --- a/v3/pkg/application/window.go +++ b/v3/pkg/application/window.go @@ -86,4 +86,5 @@ type Window interface { ZoomOut() ZoomReset() Window SetMenu(menu *Menu) + SnapAssist() } diff --git a/v3/pkg/w32/constants.go b/v3/pkg/w32/constants.go index 83ed4b9d1..505d810b8 100644 --- a/v3/pkg/w32/constants.go +++ b/v3/pkg/w32/constants.go @@ -2900,6 +2900,13 @@ const ( INPUT_HARDWARE = 2 ) +const ( + KEYEVENTF_EXTENDEDKEY = 0x0001 + KEYEVENTF_KEYUP = 0x0002 + KEYEVENTF_SCANCODE = 0x0008 + KEYEVENTF_UNICODE = 0x0004 +) + const ( MOUSEEVENTF_ABSOLUTE = 0x8000 MOUSEEVENTF_HWHEEL = 0x01000 diff --git a/v3/pkg/w32/user32.go b/v3/pkg/w32/user32.go index 8988b786e..a6b8f2519 100644 --- a/v3/pkg/w32/user32.go +++ b/v3/pkg/w32/user32.go @@ -149,6 +149,7 @@ var ( procChangeDisplaySettingsEx = moduser32.NewProc("ChangeDisplaySettingsExW") procSetTimer = moduser32.NewProc("SetTimer") procKillTimer = moduser32.NewProc("KillTimer") + procKeybdEvent = moduser32.NewProc("keybd_event") procSendInput = moduser32.NewProc("SendInput") procSetWindowsHookEx = moduser32.NewProc("SetWindowsHookExW") procUnhookWindowsHookEx = moduser32.NewProc("UnhookWindowsHookEx") @@ -1492,3 +1493,17 @@ func TrackPopupMenu(hmenu HMENU, flags uint32, x, y int32, reserved int32, hwnd uintptr(unsafe.Pointer(prcRect))) return ret != 0 } + +// KeybdEvent synthesizes a keystroke. The system can use such a synthesized keystroke to generate a WM_KEYUP or WM_KEYDOWN message. +// bVk: Virtual-key code +// bScan: Hardware scan code +// dwFlags: Controls various aspects of function operation (KEYEVENTF_EXTENDEDKEY or KEYEVENTF_KEYUP) +// dwExtraInfo: Additional value associated with the keystroke +func KeybdEvent(bVk byte, bScan byte, dwFlags uint32, dwExtraInfo uintptr) { + procKeybdEvent.Call( + uintptr(bVk), + uintptr(bScan), + uintptr(dwFlags), + dwExtraInfo, + ) +}