Skip to content

fix(desktop): cover the unpainted window frame and the Windows 10 drag stutter - #3217

Open
lml-muzi wants to merge 2 commits into
GCWing:mainfrom
lml-muzi:fix/windows-window-edge
Open

lml-muzi wants to merge 2 commits into
GCWing:mainfrom
lml-muzi:fix/windows-window-edge

Conversation

@lml-muzi

Copy link
Copy Markdown

Summary

Removes the window frame strips that render as a drop shadow around the main window, and stops Windows 10 from re-blurring the desktop on every window move.

The Windows main window is created with decorations(false), transparent(true) and the native sidebar material. tao sizes the WebView to the client rectangle and leaves the window class without a background brush, so the resizable-frame strips on the left, right and bottom edge (the top edge has none) stay unpainted and composite as bare window backdrop. On Windows 10 that backdrop can only be a live blur-behind, which re-blurs everything behind the window on every move, so dragging the window drops frames (issue #3128).

  • fix(desktop): cover the window frame strips with the WebView — collapses the non-client area so the client rectangle covers the window rectangle. Resizing, Aero Snap and the system move/size gestures are driven by the window rectangle, so they keep working; maximized windows keep the default handling.
  • fix(desktop): request the sidebar material only where the compositor hosts it — gates the material, and the frontend's transparent sidebar surfaces that depend on it, on backdrop support (DWMWA_SYSTEMBACKDROP_TYPE, Windows 11 22H2+). Windows 10 keeps the fallback surfaces the frontend already implements for reduced transparency and high contrast.

Fixes #3128

Type and Areas

Type: bug fix, UI/UX

Areas: Desktop/Tauri

Motivation / Impact

Measured on Windows 10 Pro for Workstations 22H2 (19045.6466), 150% scaling, OpenBitFun 1.0.1 (32e18ac), main window at 1635x1117 logical:

  • GetWindowRect = 289,188-2741,1863 device pixels; DWM DWMWA_EXTENDED_FRAME_BOUNDS = 299,188-2731,1853, i.e. the frame is inset by 10 device pixels on the left, right and bottom and 0 on the top.
  • ClientToScreen puts the WebView at 300,188, so those strips sit outside the client area and nothing paints them.
  • Sampling 1-13px inside the left and bottom edges returned the desktop wallpaper colour; app content starts 14px in. The top edge is app content from the first pixel, which is why the frame reads as a shadow on the left, right and bottom only (SM_CXSIZEFRAME + SM_CXPADDEDBORDER = 8 logical pixels).
  • DWMWA_SYSTEMBACKDROP_TYPE returns E_INVALIDARG on this build, so Effect::Acrylic falls back to ACCENT_ENABLE_ACRYLICBLURBEHIND, the live blur-behind that has to re-blur the desktop on every move.

User-facing: the window loses its translucent rim on the left, right and bottom edge, and dragging stays smooth on Windows 10. On Windows 10 the sidebar no longer shows the blurred desktop through its 10% transparency; it keeps the 90% theme tint plus the frontend CSS backdrop. Windows 11 and macOS behaviour is unchanged.

Verification

AI-assisted change. Testing level: compile-level only, not yet exercised on a real window.

Passed:

  • rustfmt --edition 2021 --check src/apps/desktop/src/appearance.rs src/apps/desktop/src/window_shell.rs
  • Isolated compile check of every Win32 call in window_shell.rs against the windows 0.61.3 the workspace locks (SetWindowSubclass, SUBCLASSPROC, DefSubclassProc, IsZoomed, SetWindowPos with SWP_FRAMECHANGED, WM_NCCALCSIZE, GetVersionExW/OSVERSIONINFOW, GetLastError): cargo check clean. It caught one error before review: GetVersionExW projects to Result<()>, not BOOL.
  • Handle type identity: Cargo.lock pins tauri 2.11.5, tao 0.36 and wry 0.56 on windows 0.61.3, the same crate the desktop app depends on, so WebviewWindow::hwnd() returns the HWND these Win32 calls take.

Not run:

  • cargo check -p openbitfun-desktop and a real build. The patch was prepared on a sparse checkout without node_modules, and tauri-build requires the resource paths src/mobile-web/dist and src/ext-host/extension-host.js, so the desktop check needs pnpm install + pnpm run prepare:mobile-web first. CI covers it.

Manual verification path (Windows):

  1. Build and start the app. The rim on the left/right/bottom edge must be gone and the app must draw edge-to-edge. Resize from every edge and corner, then check Aero Snap, double-click maximize and restore.
  2. On Windows 10, drag the window by the top bar: it must follow the cursor without the previous stutter (compare against 1.0.1).
  3. Switch appearance between light and dark: the sidebar keeps its theme tint and no desktop shows through its edges.
  4. On Windows 11, confirm the sidebar still shows the Mica/acrylic material.

Reviewer Notes

  • The strips could also be painted instead of removed, but painting needs a theme-aware colour kept in sync with the appearance, and a page cannot paint outside the viewport anyway. Removing the non-client area needs no colour and leaves every native interaction intact.
  • If keeping the desktop-blurred sidebar on Windows 10 matters more than the drag stutter, the alternative fix for [Bug]: Windows 10 拖动主窗口卡顿——窗口级原生材质在 Win10 只能走 blur-behind,且窗口拖动期间不关闭 backdrop-filter(前缀模板已给) #3128 is clearing the effects for the duration of a window move (WM_ENTERSIZEMOVE/WM_EXITSIZEMOVE, or SetWinEventHook(EVENT_SYSTEM_MOVESIZESTART/END)). That keeps the material but flashes while dragging; happy to follow up on top of this PR.
  • Rollback: reverting restores the unconditional Effect::Acrylic and drops the subclass, i.e. the previous behaviour on every platform.

Checklist

  • This PR is focused and does not include secrets, temporary prompts, generated scratch files, or unrelated artifacts.
  • Relevant verification is recorded above, or skipped checks are explained. (Recorded; the desktop build check is explained above.)
  • User-facing strings, docs, and locales are updated where applicable. (No user-facing strings or locales change.)

lml added 2 commits September 24, 2026 01:19
The undecorated main window keeps the Windows resizable frame so that resizing, Aero Snap and the system move/size gestures stay available, but tao sizes the WebView to the client rectangle and leaves the window class without a background brush. The strips between the client rectangle and the window rectangle therefore stay unpainted on the left, right and bottom edge, and a transparent window composites them as bare window backdrop: the frame reads as a drop shadow around the window, while the top edge has no such strip.

Collapse the non-client area so the client rectangle covers the window rectangle instead. Resizing, snapping and the move/size gestures are driven by the window rectangle, so the native interactions stay intact while the WebView paints the window edge-to-edge. Maximized windows keep the default handling: Windows inflates their window rectangle beyond the work area, which already moves the strips off-screen.

Measured before the change on Windows 10 19045 at 150% scaling: window rect 289,188-2741,1863 against DWM extended frame bounds 299,188-2731,1853 (10px inset on the left/right/bottom, 0px on the top), and the strips sampled the desktop instead of app content.
…hosts it

Windows 10 has no DWMWA_SYSTEMBACKDROP_TYPE, so the window material falls back to a live blur-behind that re-blurs everything behind the window on every move. Dragging the window then visibly drops frames (issue 3128). macOS vibrancy and the Windows 11 Mica/acrylic backdrops are compositor-owned and move smoothly.

Gate both the native material and the frontend's transparent sidebar surfaces on that capability, so Windows 10 keeps the opaque fallback surfaces that the frontend already implements for reduced transparency and high contrast.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant