Windows: Set window style in correct order when reparenting foreign windows

The SetParent() documentation notes that when making a window a child
window the window style should be set before calling SetParent(), while
the opposite order should be used when making a window top level.

We were exclusively doing the latter, which resulted in child windows
getting activated and focused as part of the reparenting. Being the
active window was resulting in the child window receiving WM_CLOSE
and being destroyed if the user pressed Alt+F4, which was very
surprising.

The child window can still be focused later on, via e.g. an explicit
call to SetFocus(), so it can receive keyboard input.

The QWindowsWindow::setParent_sys() logic, used for non-foreign
windows has the same fundamental issue with not respecting the
order of the style update, but the interactions with the drop
site logic makes it harder to update in similar fashion as this
patch does for QWindowsForeignWindow.

Pick-to: 6.8 6.8.0
Change-Id: Id88f5981daaf121a39aba9319d02aebefb6aa07b
Reviewed-by: Jøger Hansegård <joger.hansegard@qt.io>
Reviewed-by: Oliver Wolff <oliver.wolff@qt.io>
This commit is contained in:
Tor Arne Vestbø 2024-08-30 16:40:31 +02:00
parent eff8e6b885
commit a90d99d8da

View File

@ -1357,10 +1357,12 @@ void QWindowsForeignWindow::setParent(const QPlatformWindow *newParentWindow)
const HWND newParent = newParentWindow ? reinterpret_cast<HWND>(newParentWindow->winId()) : HWND(nullptr);
const bool isTopLevel = !newParent;
const DWORD oldStyle = style();
qCDebug(lcQpaWindow) << __FUNCTION__ << window() << "newParent="
<< newParentWindow << newParent << "oldStyle=" << debugWinStyle(oldStyle);
SetParent(m_hwnd, newParent);
if (wasTopLevel != isTopLevel) { // Top level window flags need to be set/cleared manually.
auto updateWindowFlags = [=]{
// Top level window flags need to be set/cleared manually.
DWORD newStyle = oldStyle;
if (isTopLevel) {
newStyle = m_topLevelStyle;
@ -1370,6 +1372,20 @@ void QWindowsForeignWindow::setParent(const QPlatformWindow *newParentWindow)
newStyle |= WS_CHILD;
}
SetWindowLongPtr(m_hwnd, GWL_STYLE, newStyle);
};
if (wasTopLevel && !isTopLevel) {
// Becoming a child window requires the style
// flags to be updated before reparenting.
updateWindowFlags();
}
SetParent(m_hwnd, newParent);
if (!wasTopLevel && isTopLevel) {
// Becoming a top level window requires the style
// flags to be updated after reparenting.
updateWindowFlags();
}
}