Skip to content

修复 Win32 原生窗口重建后的 frame 位置漂移 - #243

Merged
wangwenx190 merged 1 commit into
stdware:mainfrom
hesphoros:fix/qwk-pin
Aug 1, 2026
Merged

修复 Win32 原生窗口重建后的 frame 位置漂移#243
wangwenx190 merged 1 commit into
stdware:mainfrom
hesphoros:fix/qwk-pin

Conversation

@hesphoros

Copy link
Copy Markdown
Contributor

概要

修复 QWindowKit 托管窗口在 Windows 上发生原生 HWND 重建后,窗口 frame 可能向下漂移的问题。

当 Qt 重新创建顶层窗口的 native window 时,QWindowKit 的 Win32 hook 需要重新绑定到新的 HWND。在旧 HWND 失效、新 HWND 完整接管之间,Qt/Windows 可能先按系统标题栏参与一次 frame/client 坐标换算。随后 Qt 会把这个带系统标题栏偏移的 client geometry 当作新的窗口位置继续应用,导致窗口整体向下偏移约一个标题栏高度。

本 PR 在 QWK Win32 层缓存 native window 重建前的稳定 frame rect,并在 WM_WINDOWPOSCHANGING 阶段识别和修正这类漂移,避免错误位置真正应用到窗口。

问题

一个典型触发路径是示例程序中切换置顶状态:

setWindowFlag(Qt::WindowStaysOnTopHint, pin);
show();

Copilot AI review requested due to automatic review settings July 31, 2026 15:15
@hesphoros

Copy link
Copy Markdown
Contributor Author

改动

  • 在旧 HWND 仍有效时缓存 native frame rect。
  • 允许缓存跨过短暂的 old HWND -> null -> new HWND 过渡。
  • 在新 HWND 接管后记录 pending restore 状态。
  • WM_WINDOWPOSCHANGING 阶段提前检查即将应用的窗口位置。
  • 仅在 proposed geometry 符合以下特征时修正:
    • x 基本不变
    • 宽高基本不变
    • y 发生正向偏移
    • 偏移量接近 titleBarHeight + visibleFrameBorder
  • 在 Windows 应用错误位置前修改 WINDOWPOS::x/y,避免窗口先漂移再被拉回。
  • 保留一拍后的 fallback restore,作为未命中 WM_WINDOWPOSCHANGING 路径时的保险。
  • 避免对最小化、最大化、全屏窗口应用该 workaround。

说明

本 PR 不包含调试日志代码,也不修改示例程序的 pin 逻辑。

该 workaround 是保守触发的:只有在 WinIdChange 后检测到明确符合标题栏高度级别漂移特征的 proposed geometry 时才会修正位置,避免影响正常窗口移动或 resize。

这个修复主要解决 QWK 托管窗口在 native window recreate 后的 frame 漂移问题。对于 pin/topmost 功能,更理想的后续方案是由 QWK 提供 stay-on-top 属性,并在 Windows backend 内部使用 SetWindowPos(HWND_TOPMOST/HWND_NOTOPMOST) 实现,从源头避免 Qt flag 重建 native window。

验证

  • 验证重复触发 pin/unpin 导致的 native frame 下移不再累加。
  • 验证 WM_WINDOWPOSCHANGING 阶段可以提前识别约 32px 的漂移并修正 proposed position。

@hesphoros

Copy link
Copy Markdown
Contributor Author

#241

@hesphoros

Copy link
Copy Markdown
Contributor Author

@wangwenx190 用AI写的一个简短的方案,基本测试通过.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

该 PR 针对 QWindowKit 在 Windows 上顶层窗口发生原生 HWND 重建后,Qt 可能在重建窗口的几何换算路径中引入一次“系统标题栏参与的偏移”,从而导致 frame 位置向下漂移的问题,在 Win32 hook 层增加了“重建前稳定 frame rect 缓存 + 重建后漂移识别与纠正”的防护逻辑。

Changes:

  • 在 Win32 上新增“重建前 frame rect”缓存,并在 HWND 变化后基于该 rect 生成“待纠正目标 rect”。
  • 增加漂移识别逻辑:通过 WM_WINDOWPOSCHANGING 与异步 SetWindowPos 两条路径拦截并纠正疑似漂移。
  • 在私有上下文结构中新增相关状态字段以跟踪一次重建周期内的纠正流程。

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/core/contexts/win32windowcontext.cpp 新增漂移判定函数,并在 winIdChanged/WM_WINDOWPOSCHANGING 路径中缓存与纠正 frame 漂移
src/core/contexts/win32windowcontext_p.h 为一次 native handle 重建周期增加 frame rect 缓存与纠正状态字段

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +897 to 904
const auto oldHWnd = reinterpret_cast<HWND>(oldWinId);
if (!restoringFrameRectAfterWinIdChange && isValidWindow(oldHWnd, false, true) &&
isWindowNoState(oldHWnd)) {
hasFrameRectBeforeWinIdChange =
::GetWindowRect(oldHWnd, &frameRectBeforeWinIdChange) != FALSE;
}
removeManagedWindow(oldHWnd);
}
@wangwenx190

Copy link
Copy Markdown
Collaborator

非常感谢!我暂时也没看出什么问题,先合了。有问题我们后面再修复

@wangwenx190
wangwenx190 merged commit 2813c1f into stdware:main Aug 1, 2026
@hesphoros
hesphoros deleted the fix/qwk-pin branch August 1, 2026 14:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants