macOS: Don't refuse key window for direct transient parent of modal window

As explain in detail in 1bde2036051b3aaf5896ef1ff50c08c11ea1eba9, AppKit
doesn't treat non-direct transient parents of a window modal window as
being blocked by that modal window, but Qt does. To align with Qt, we
worked around it by returning NO from canBecomeKeyWindow for windows
that were blocked by a modal window.

This however had an unintended side effect for native dialogs, due
to the way we hide these dialogs. When a native dialog is closed,
AppKit will look for another window to make key, and as part of that
it checks canBecomeKeyWindow. The problem is that the modal blocked
status has not been updated in QGuiApplicationPrivate at that point,
so we tell AppKit it can't make the transient parent key.

The modal blocked status is only updated once we hit QWidget::setVisible,
at which point dialogs like QMessageBox and QColorDialog has already
called setNativeDialogVisible(false) in its setVisible() override,
triggering the close logic described above.

To fix this properly we need to have the dialogs call the base class
first when hiding, and then setNativeDialogVisible(), and also have
the macOS native dialog helpers react to hide() by ordering out the
window in case it didn't work at an earlier point. This kind of
change is risky for Qt 6.5/6.6 however, so we opt for a simpler
solution for now.

By limiting the original workaround for non-direct transient parents
not being blocked to just those transient parents, and only for window
modal dialogs, we avoid the issue with failing to transfer key status.

Task-number: QTBUG-104905
Fixes: QTBUG-118320
Pick-to: 6.5
Change-Id: Iabbca0b74a7db4e9821a9b60730d01fbad1425db
Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
(cherry picked from commit 4a16e51bc3e248c6f82b70e176d605d54b9b2cad)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Tor Arne Vestbø 2023-10-20 19:08:32 +02:00 committed by Qt Cherry-pick Bot
parent f89a2f41e7
commit ed1def47a2

View File

@ -1983,8 +1983,21 @@ bool QCocoaWindow::shouldRefuseKeyWindowAndFirstResponder()
if (window()->flags() & (Qt::WindowDoesNotAcceptFocus | Qt::WindowTransparentForInput))
return true;
if (QWindowPrivate::get(window())->blockedByModalWindow)
return true;
// For application modal windows, as well as direct parent windows
// of window modal windows, AppKit takes care of blocking interaction.
// The Qt expectation however, is that all transient parents of a
// window modal window is blocked, as reflected by QGuiApplication.
// We reflect this by returning false from this function for transient
// parents blocked by a modal window, but limit it to the cases not
// covered by AppKit to avoid potential unwanted side effects.
QWindow *modalWindow = nullptr;
if (QGuiApplicationPrivate::instance()->isWindowBlocked(window(), &modalWindow)) {
if (modalWindow->modality() == Qt::WindowModal && modalWindow->transientParent() != window()) {
qCDebug(lcQpaWindow) << "Refusing key window for" << this << "due to being"
<< "blocked by" << modalWindow;
return true;
}
}
if (m_inSetVisible) {
QVariant showWithoutActivating = window()->property("_q_showWithoutActivating");