macOS: Clear event dispatcher interrupt before running NSAlert modal session

If the event dispatcher is interrupted we propagate the interrupt to
lower event loop levels, in case they too need to be interrupted. And
we defer the actual interrupt of the NSApplication to the next time we
process Qt events, to avoid AppKit dropping queued events on the floor.

This logic relies on QCocoaEventDispatcher::processEvents() setting the
interrupt flag to false, which signals that we should not continue to
tear down any further event loops.

Unfortunately, native run loops such as running application modal
sessions, are not driven by QCocoaEventDispatcher::processEvents(),
so we never reset the interrupt, and end up ending the session
immediately.

To work around this we need to explicitly clear the interrupt flag
before starting native modal sessions. This also fixes the issue
seen in QTBUG-111524 with showing native alerts from nested event
loops.

Fixes: QTBUG-112697
Task-number: QTBUG-111524
Pick-to: 6.5 6.5.1
Change-Id: I6aaec97011fd18c4a513c1dde3173b1cc4d50112
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
This commit is contained in:
Tor Arne Vestbø 2023-04-28 15:07:50 +02:00
parent e51ff5c493
commit dcff882f30

View File

@ -5,6 +5,7 @@
#include "qcocoawindow.h"
#include "qcocoahelpers.h"
#include "qcocoaeventdispatcher.h"
#include <QtCore/qmetaobject.h>
#include <QtCore/qscopedvaluerollback.h>
@ -90,10 +91,6 @@ bool QCocoaMessageDialog::show(Qt::WindowFlags windowFlags, Qt::WindowModality w
if (!options())
return false;
if (windowModality == Qt::ApplicationModal && QThread::currentThread()->loopLevel() > 1) {
qCWarning(lcQpaDialogs, "Cannot use native application modal dialog from nested event loop");
return false;
}
Q_ASSERT(!m_alert);
m_alert = [NSAlert new];
@ -224,6 +221,7 @@ bool QCocoaMessageDialog::show(Qt::WindowFlags windowFlags, Qt::WindowModality w
QTimer::singleShot(0, this, [this]{
if (m_alert && NSApp.modalWindow != m_alert.window) {
qCDebug(lcQpaDialogs) << "Running deferred modal" << m_alert;
QCocoaEventDispatcher::clearCurrentThreadCocoaEventDispatcherInterruptFlag();
processResponse([m_alert runModal]);
}
});
@ -243,6 +241,7 @@ void QCocoaMessageDialog::exec()
m_eventLoop->exec(QEventLoop::DialogExec);
} else {
qCDebug(lcQpaDialogs) << "Running modal" << m_alert;
QCocoaEventDispatcher::clearCurrentThreadCocoaEventDispatcherInterruptFlag();
processResponse([m_alert runModal]);
}
}