From 77a306beb2fdd2066d606cb05bea2f9067458489 Mon Sep 17 00:00:00 2001 From: Syngnat Date: Fri, 12 Jun 2026 15:58:30 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix(window):=20=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E5=A4=96=E6=8E=A5=E6=98=BE=E7=A4=BA=E5=99=A8=E6=81=A2?= =?UTF-8?q?=E5=A4=8D=E5=90=8E=E5=AD=97=E4=BD=93=E6=A8=A1=E7=B3=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 恢复策略:Windows 最小化恢复时不再依赖 viewport drift 才触发修复 - 渲染刷新:普通窗口执行 1px 尺寸 nudge,强制 WebView2/DWM 重建渲染 surface - 体验保护:最大化窗口继续保留 zoom reset + resize,避免可见重复最大化动画 - 测试覆盖:补充 restore 无 drift 场景与自动修复路径断言 Refs #495 --- frontend/src/App.tool-center.test.ts | 3 +++ frontend/src/utils/windowStateUi.test.ts | 6 ++++-- frontend/src/utils/windowStateUi.ts | 8 ++++---- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/frontend/src/App.tool-center.test.ts b/frontend/src/App.tool-center.test.ts index 573f19a..7be5fad 100644 --- a/frontend/src/App.tool-center.test.ts +++ b/frontend/src/App.tool-center.test.ts @@ -200,6 +200,9 @@ describe('tool center menu entries', () => { expect(appSource).toContain('const shouldResetWebViewZoom = shouldResetWebViewZoomForScaleFix(reason, hasViewportScaleDrift);'); expect(appSource).toContain('if (shouldResetWebViewZoom && !isMaximised)'); expect(appSource).toContain('const res = await (window as any).go?.app?.App?.ResetWebViewZoom?.();'); + expect(appSource).toContain('if (!shouldApplyWindowsScaleFix(reason, hasViewportScaleDrift))'); + expect(appSource).toContain('const nudgedWidth = getWindowsScaleFixNudgedWidth(width);'); + expect(appSource).toContain('WindowSetSize(nudgedWidth, height);'); expect(appSource).toContain('该异常不一定表现为 viewport ratio drift'); }); diff --git a/frontend/src/utils/windowStateUi.test.ts b/frontend/src/utils/windowStateUi.test.ts index f197d73..ee68062 100644 --- a/frontend/src/utils/windowStateUi.test.ts +++ b/frontend/src/utils/windowStateUi.test.ts @@ -18,9 +18,11 @@ describe('windowStateUi', () => { expect(shouldApplyWindowsScaleFix('ratio-change', true)).toBe(true); }); - it('applies the Windows scale fix when a minimized taskbar window is restored with viewport drift', () => { + it('applies the Windows scale fix whenever a minimized taskbar window is restored', () => { expect(shouldApplyWindowsScaleFix('restore', true)).toBe(true); - expect(shouldApplyWindowsScaleFix('restore', false)).toBe(false); + // 外接显示器恢复后的 WebView2/DWM backing surface 可能被旧 DPI 缩放, + // 但不一定表现为 viewport ratio drift;restore 仍要触发 1px 轻量重绘。 + expect(shouldApplyWindowsScaleFix('restore', false)).toBe(true); // 关键:restore 场景刻意不再触发 maximised 窗口的 toggle —— Unmaximise → Maximise 在 // 任务栏恢复的真实交互里会被用户肉眼看见为"重复最大化"动画,比偶发字体变大更糟。 // 这是 9848b8b2 已有的取舍,禁止再次被"修复"成 true。 diff --git a/frontend/src/utils/windowStateUi.ts b/frontend/src/utils/windowStateUi.ts index 5f1d8e2..c5a05f3 100644 --- a/frontend/src/utils/windowStateUi.ts +++ b/frontend/src/utils/windowStateUi.ts @@ -6,15 +6,15 @@ export type TitleBarToggleIconKey = 'maximize' | 'restore'; export const shouldApplyWindowsScaleFix = ( reason: WindowScaleFixReason, hasViewportScaleDrift: boolean, -): boolean => (reason === 'ratio-change' || reason === 'restore') && hasViewportScaleDrift; +): boolean => reason === 'restore' || (reason === 'ratio-change' && hasViewportScaleDrift); // 关于 restore 场景为何刻意不走 toggle(见 9848b8b2): // maximised 窗口在 Windows 上无法通过 SetSize nudge 修复 viewport drift(OS 拒绝 resize), // 唯一能让 WebView2 重新计算缩放的办法是 Unmaximise → Maximise,但在任务栏图标点击恢复的 // 真实场景下,用户会肉眼看到窗口"被弹两次"的重复最大化动画——比偶发字体变大更糟。 -// 取舍:restore 时只 dispatch resize 让 React 重算布局,宁可字体保持当前缩放, -// 也不要可见的重复最大化抖动。ratio-change(DPR 变化,例如把窗口拖到另一块显示器)则 -// 允许 toggle,因为那种场景下用户预期会有视觉过渡。 +// 取舍:restore 时普通窗口走 1px SetSize nudge 迫使 WebView2/DWM 重新分配 backing surface; +// maximised 窗口只做 WebView2 zoom reset + resize,避免可见的重复最大化抖动。 +// ratio-change(DPR 变化,例如把窗口拖到另一块显示器)则允许 toggle,因为那种场景下用户预期会有视觉过渡。 export const shouldToggleMaximisedWindowForScaleFix = ( reason: WindowScaleFixReason, hasViewportScaleDrift: boolean,