Skip to content

Fix:pinning-induced window position drift on Windows - #241

Open
hesphoros wants to merge 2 commits into
stdware:mainfrom
hesphoros:fix/pin
Open

Fix:pinning-induced window position drift on Windows#241
hesphoros wants to merge 2 commits into
stdware:mainfrom
hesphoros:fix/pin

Conversation

@hesphoros

Copy link
Copy Markdown

Fix pinning-induced window position drift on Windows

Summary

Fix the Widgets MainWindow example moving downward when the Pin button is toggled repeatedly on Windows.

The Pin action now changes only the native window z-order instead of changing Qt::WindowStaysOnTopHint and showing the window again. The selected Pin state is cached and restored if Qt recreates the native window handle.

Problem

The previous Pin callback used:

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

Changing a top-level window flag can recreate its native HWND. For a frameless window with custom non-client handling, recreating and showing the window can alter the conversion between client and frame coordinates. Repeated Pin/Unpin operations therefore caused the window to drift downward by approximately one title-bar height per toggle.

Changes

  • Use SetWindowPos() with HWND_TOPMOST or HWND_NOTOPMOST on Windows.
  • Pass SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOOWNERZORDER so the operation changes only z-order.
  • Use internalWinId() to avoid creating a native handle as a side effect.
  • Cache the current stay-on-top state in the example window.
  • Reapply the cached state after QEvent::WinIdChange so pinning survives unrelated native handle recreation.
  • Guard against reentrant state application while handling native window changes.
  • Restore the Pin button's checked state when the request cannot be applied, including requests made while the window is hidden, minimized, maximized, or full screen.
  • Keep a geometry-preserving Qt fallback for non-Windows platforms.

This change is limited to the Widgets example and does not modify QWindowKit's public API.

Verification

  • Built QWKExample_MainWindow in Release mode with Qt 6.8.3 and MSVC.
  • On Windows, toggle Pin/Unpin and confirm the frame position and size do not change.
  • Confirm the window becomes topmost when pinned and returns to non-topmost when unpinned
  • Confirm the Pin button remains synchronized with the applied native state.
  • Confirm pinning is restored after native handle recreation.
  • Confirm minimize, maximize, restore, close, title-bar hit testing, and menu activation styling still work.

Scope

No core WindowAgent behavior or public API is changed. The fix only replaces the example application's Pin policy implementation.

Copilot AI review requested due to automatic review settings July 28, 2026 11:26

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

This PR updates the Widgets MainWindow example’s “Pin” (stay-on-top) behavior on Windows to avoid repeated top-level flag changes that can recreate the native window handle and cause gradual frame position drift.

Changes:

  • On Windows, implement pin/unpin by changing native z-order via SetWindowPos(HWND_TOPMOST/HWND_NOTOPMOST) instead of toggling Qt::WindowStaysOnTopHint + show().
  • Cache the requested stay-on-top state and reapply it on QEvent::WinIdChange to survive native handle recreation.
  • Keep a geometry-preserving Qt-based fallback for non-Windows platforms and improve UI synchronization when pin requests can’t be applied.

Reviewed changes

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

File Description
examples/mainwindow/mainwindow.h Adds helper API + state flags to track and reapply stay-on-top behavior.
examples/mainwindow/mainwindow.cpp Implements Windows z-order pinning, WinIdChange reapply, and updated pin button handling.
Comments suppressed due to low confidence (2)

examples/mainwindow/mainwindow.cpp:141

  • This Windows-only event handling is guarded by Q_OS_WINDOWS, but other Windows-specific blocks in this file use Q_OS_WIN. Using a single Windows macro consistently within the file avoids platform-guard mismatches.
#ifdef Q_OS_WINDOWS
        case QEvent::WinIdChange:
            if (!applyingStayOnTop) {
                setStayOnTop(stayOnTop);
            }
            break;
#endif

examples/mainwindow/mainwindow.cpp:476

  • setStayOnTop() is currently compiled under Q_OS_WINDOWS while other Windows-specific logic in this file is under Q_OS_WIN. Consider using the same macro consistently to prevent any platform-guard mismatches.
bool MainWindow::setStayOnTop(bool enabled) {
#ifdef Q_OS_WINDOWS
    if (applyingStayOnTop) {
        return false;

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

Comment thread examples/mainwindow/mainwindow.h
Comment thread examples/mainwindow/mainwindow.cpp Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@wangwenx190

Copy link
Copy Markdown
Collaborator

看起来有道理,但我觉得应该是Qt内部有bug,你这样实际上只是从表层进行workaround吧?

@hesphoros

Copy link
Copy Markdown
Author

@wangwenx190 是的,这个改动属于 example 层的 workaround。问题发生在一个已经显示的顶层 QWidget 上调用 setWindowFlag(Qt::WindowStaysOnTopHint, ...) 并重新 show() 时;该过程可能重建 HWND。在 QWindowKit 自定义 non-client frame 的场景下,重建前后的 frame/client 坐标换算会导致窗口位置发生偏移,这部分可能是 Qt Windows platform plugin 的问题

@wangwenx190

Copy link
Copy Markdown
Collaborator

你这个PR其实改了不少地方,我有点不确定为了workaround示例程序的问题,引入这种改动是否有必要

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