From cb169611b14e7e6b8aa11881e6aca377c9b147ff Mon Sep 17 00:00:00 2001 From: Lea Anthony Date: Mon, 20 Jun 2022 17:47:13 +1000 Subject: [PATCH] Set Window Background colour if provided + debounce redraw option (#1466) * Set Window Background colour if provided. Added debounce option for Webview2 redraw on resize * [windows] Workaround resize flickering in frameless mode Co-authored-by: stffabi --- v2/go.mod | 1 + v2/go.sum | 2 + .../frontend/desktop/windows/frontend.go | 43 +++++++++++++------ .../frontend/desktop/windows/win32/consts.go | 6 +++ .../frontend/desktop/windows/win32/window.go | 20 +++++++++ .../frontend/desktop/windows/window.go | 14 +++++- v2/pkg/options/windows/windows.go | 4 ++ website/docs/reference/options.mdx | 9 ++++ 8 files changed, 84 insertions(+), 15 deletions(-) diff --git a/v2/go.mod b/v2/go.mod index f8e63331..039cae5a 100644 --- a/v2/go.mod +++ b/v2/go.mod @@ -42,6 +42,7 @@ require github.com/bitfield/script v0.19.0 require ( bitbucket.org/creachadair/shell v0.0.7 // indirect github.com/Microsoft/go-winio v0.4.16 // indirect + github.com/bep/debounce v1.2.1 // indirect github.com/emirpasic/gods v1.12.0 // indirect github.com/go-git/gcfg v1.5.0 // indirect github.com/go-ole/go-ole v1.2.6 // indirect diff --git a/v2/go.sum b/v2/go.sum index 090c749b..8af98058 100644 --- a/v2/go.sum +++ b/v2/go.sum @@ -11,6 +11,8 @@ github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY= +github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0= github.com/bitfield/script v0.19.0 h1:W24f+FQuPab9gXcW8bhcbo5qO8AtrXyu3XOnR4zhHN0= github.com/bitfield/script v0.19.0/go.mod h1:ana6F8YOSZ3ImT8SauIzuYSqXgFVkSUJ6kgja+WMmIY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= diff --git a/v2/internal/frontend/desktop/windows/frontend.go b/v2/internal/frontend/desktop/windows/frontend.go index 9f81dced..0d8bdf37 100644 --- a/v2/internal/frontend/desktop/windows/frontend.go +++ b/v2/internal/frontend/desktop/windows/frontend.go @@ -7,7 +7,18 @@ import ( "context" "encoding/json" "fmt" + "github.com/bep/debounce" + "github.com/wailsapp/wails/v2/internal/binding" + "github.com/wailsapp/wails/v2/internal/frontend" + "github.com/wailsapp/wails/v2/internal/frontend/assetserver" + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge" "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/win32" + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc" + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32" + "github.com/wailsapp/wails/v2/internal/logger" + "github.com/wailsapp/wails/v2/internal/system/operatingsystem" + "github.com/wailsapp/wails/v2/pkg/options" + "github.com/wailsapp/wails/v2/pkg/options/windows" "io" "log" "net/http" @@ -17,17 +28,7 @@ import ( "strconv" "strings" "text/template" - - "github.com/wailsapp/wails/v2/internal/binding" - "github.com/wailsapp/wails/v2/internal/frontend" - "github.com/wailsapp/wails/v2/internal/frontend/assetserver" - "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge" - "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc" - "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32" - "github.com/wailsapp/wails/v2/internal/logger" - "github.com/wailsapp/wails/v2/internal/system/operatingsystem" - "github.com/wailsapp/wails/v2/pkg/options" - "github.com/wailsapp/wails/v2/pkg/options/windows" + "time" ) const startURL = "http://wails.localhost/" @@ -54,7 +55,8 @@ type Frontend struct { hasStarted bool // Windows build number - versionInfo *operatingsystem.WindowsVersionInfo + versionInfo *operatingsystem.WindowsVersionInfo + resizeDebouncer func(f func()) } func NewFrontend(ctx context.Context, appoptions *options.App, myLogger *logger.Logger, appBindings *binding.Bindings, dispatcher frontend.Dispatcher) *Frontend { @@ -71,6 +73,12 @@ func NewFrontend(ctx context.Context, appoptions *options.App, myLogger *logger. versionInfo: versionInfo, } + if appoptions.Windows != nil { + if appoptions.Windows.ResizeDebounceMS > 0 { + result.resizeDebouncer = debounce.New(time.Duration(appoptions.Windows.ResizeDebounceMS) * time.Millisecond) + } + } + // We currently can't use wails://wails/ as other platforms do, therefore we map the assets sever onto the following url. result.startURL, _ = url.Parse(startURL) @@ -151,7 +159,15 @@ func (f *Frontend) Run(ctx context.Context) error { } } - f.chromium.Resize() + if f.resizeDebouncer != nil { + f.resizeDebouncer(func() { + f.mainWindow.Invoke(func() { + f.chromium.Resize() + }) + }) + } else { + f.chromium.Resize() + } }) mainWindow.OnClose().Bind(func(arg *winc.Event) { @@ -329,6 +345,7 @@ func (f *Frontend) WindowSetRGBA(col *options.RGBA) { log.Fatal(err) } }) + } func (f *Frontend) Quit() { diff --git a/v2/internal/frontend/desktop/windows/win32/consts.go b/v2/internal/frontend/desktop/windows/win32/consts.go index cc0a98a7..9b29bf2c 100644 --- a/v2/internal/frontend/desktop/windows/win32/consts.go +++ b/v2/internal/frontend/desktop/windows/win32/consts.go @@ -15,6 +15,7 @@ var ( moduser32 = syscall.NewLazyDLL("user32.dll") procSystemParametersInfo = moduser32.NewProc("SystemParametersInfoW") procGetWindowLong = moduser32.NewProc("GetWindowLongW") + procSetClassLongPtr = moduser32.NewProc("SetClassLongPtrW") procShowWindow = moduser32.NewProc("ShowWindow") ) var ( @@ -22,6 +23,11 @@ var ( procDwmSetWindowAttribute = moddwmapi.NewProc("DwmSetWindowAttribute") procDwmExtendFrameIntoClientArea = moddwmapi.NewProc("DwmExtendFrameIntoClientArea") ) +var ( + modwingdi = syscall.NewLazyDLL("gdi32.dll") + procCreateSolidBrush = modwingdi.NewProc("CreateSolidBrush") +) + var windowsVersion, _ = operatingsystem.GetWindowsVersionInfo() func IsWindowsVersionAtLeast(major, minor, buildNumber int) bool { diff --git a/v2/internal/frontend/desktop/windows/win32/window.go b/v2/internal/frontend/desktop/windows/win32/window.go index 3ad15d3c..3bd3f239 100644 --- a/v2/internal/frontend/desktop/windows/win32/window.go +++ b/v2/internal/frontend/desktop/windows/win32/window.go @@ -4,6 +4,7 @@ package win32 import ( "fmt" + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc" "log" "syscall" "unsafe" @@ -33,6 +34,10 @@ const ( SW_FORCEMINIMIZE = 11 ) +const ( + GCLP_HBRBACKGROUND int32 = -10 +) + // http://msdn.microsoft.com/en-us/library/windows/desktop/bb773244.aspx type MARGINS struct { CxLeftWidth, CxRightWidth, CyTopHeight, CyBottomHeight int32 @@ -74,6 +79,12 @@ func ShowWindowMinimised(hwnd uintptr) { showWindow(hwnd, SW_MINIMIZE) } +func SetBackgroundColour(hwnd uintptr, r, g, b uint8) { + col := winc.RGB(r, g, b) + hbrush, _, _ := procCreateSolidBrush.Call(uintptr(col)) + setClassLongPtr(hwnd, GCLP_HBRBACKGROUND, hbrush) +} + func dwmExtendFrameIntoClientArea(hwnd uintptr, margins *MARGINS) error { ret, _, _ := procDwmExtendFrameIntoClientArea.Call( hwnd, @@ -86,6 +97,15 @@ func dwmExtendFrameIntoClientArea(hwnd uintptr, margins *MARGINS) error { return nil } +func setClassLongPtr(hwnd uintptr, param int32, val uintptr) bool { + ret, _, _ := procSetClassLongPtr.Call( + hwnd, + uintptr(param), + val, + ) + return ret != 0 +} + func getWindowLong(hwnd uintptr, index int) int32 { ret, _, _ := procGetWindowLong.Call( hwnd, diff --git a/v2/internal/frontend/desktop/windows/window.go b/v2/internal/frontend/desktop/windows/window.go index 6948cff5..3464e4da 100644 --- a/v2/internal/frontend/desktop/windows/window.go +++ b/v2/internal/frontend/desktop/windows/window.go @@ -3,9 +3,10 @@ package windows import ( + "unsafe" + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/win32" "github.com/wailsapp/wails/v2/internal/system/operatingsystem" - "unsafe" "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc" "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32" @@ -66,6 +67,10 @@ func NewWindow(parent winc.Controller, appoptions *options.App, versionInfo *ope } } + if appoptions.RGBA != nil { + win32.SetBackgroundColour(result.Handle(), appoptions.RGBA.R, appoptions.RGBA.G, appoptions.RGBA.B) + } + result.SetSize(appoptions.Width, appoptions.Height) result.SetText(appoptions.Title) result.EnableSizable(!appoptions.DisableResize) @@ -191,6 +196,8 @@ func (w *Window) WndProc(msg uint32, wparam, lparam uintptr) uintptr { // This hides the titlebar and also disables the resizing from user interaction because the standard frame is not // shown. We still need the WS_THICKFRAME style to enable resizing from the frontend. if wparam != 0 { + rgrc := (*w32.RECT)(unsafe.Pointer(lparam)) + style := uint32(w32.GetWindowLong(w.Handle(), w32.GWL_STYLE)) if style&w32.WS_MAXIMIZE != 0 { // If the window is maximized we must adjust the client area to the work area of the monitor. Otherwise @@ -200,7 +207,6 @@ func (w *Window) WndProc(msg uint32, wparam, lparam uintptr) uintptr { var monitorInfo w32.MONITORINFO monitorInfo.CbSize = uint32(unsafe.Sizeof(monitorInfo)) if w32.GetMonitorInfo(monitor, &monitorInfo) { - rgrc := (*w32.RECT)(unsafe.Pointer(lparam)) *rgrc = monitorInfo.RcWork maxWidth := w.frontendOptions.MaxWidth @@ -220,6 +226,10 @@ func (w *Window) WndProc(msg uint32, wparam, lparam uintptr) uintptr { } } } + } else { + // This is needed to workaround the resize flickering in frameless mode with WindowDecorations + // See: https://stackoverflow.com/a/6558508 + rgrc.Bottom -= 1 } return 0 diff --git a/v2/pkg/options/windows/windows.go b/v2/pkg/options/windows/windows.go index fb80cee1..da1db640 100644 --- a/v2/pkg/options/windows/windows.go +++ b/v2/pkg/options/windows/windows.go @@ -82,6 +82,10 @@ type Options struct { // User messages that can be customised Messages *Messages + + // ResizeDebounceMS is the amount of time to debounce redraws of webview2 + // when resizing the window + ResizeDebounceMS uint16 } func DefaultMessages() *Messages { diff --git a/website/docs/reference/options.mdx b/website/docs/reference/options.mdx index eb317763..28807522 100644 --- a/website/docs/reference/options.mdx +++ b/website/docs/reference/options.mdx @@ -511,6 +511,15 @@ Type: `*windows.Messages` A struct of strings used by the webview2 installer if a valid webview2 runtime is not found. Customise this for any language you choose to support. +### ResizeDebounceMS + +Name: ResizeDebounceMS + +Type: uint16 + +ResizeDebounceMS is the amount of time to debounce redraws of webview2 when resizing the window. +The default value (0) will perform redraws as fast as it can. + ## Mac Specific Options ### TitleBar