diff options
author | Tor Arne Vestbø <tor.arne.vestbo@qt.io> | 2021-01-22 16:00:17 +0100 |
---|---|---|
committer | Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> | 2021-01-25 21:59:04 +0000 |
commit | 7d3fee7ca9b8b0c47fc2cb8092bf21ba28d582e3 (patch) | |
tree | 5a7845510cca9afaac8808ba7cfb9c31ff19e20e /src/plugins/platforms | |
parent | 4b1153d1a24a5d32177c266c4adb197e97ecab00 (diff) |
macOS: Don't exec file dialogs via runModal unless they are app modal
Non-modal or window modal dialogs are shown at show(), via AppKit APIs
that are non-blocking. If we want to block execution at this point, we
need to spin our own event loop. The runModal API of NSSavePanel is not
meant to be used for blocking execution for already shown dialogs, but
is reserved for application modal dialogs.
This means we no longer trip over AppKit's understanding of what state
the dialog is in, which would result in the dialog not reporting back
any files. It also allows us to remove the guard for closing dialogs
twice.
We now also correctly close and end the application modal session if
the dialog is closed programmatically using Qt APIs.
Task-number: QTBUG-89959
Fixes: QTBUG-85547
Change-Id: Ida3dc404417789d4823822ecfbf0935591c23878
Reviewed-by: Morten Johan Sørvig <morten.sorvig@qt.io>
(cherry picked from commit aa3b42d6342c6756c3ce941db0bbf1122ae48224)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
Diffstat (limited to 'src/plugins/platforms')
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoafiledialoghelper.h | 2 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm | 58 |
2 files changed, 40 insertions, 20 deletions
diff --git a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.h b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.h index 1d01c0d1cf..d730a063a3 100644 --- a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.h +++ b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.h @@ -48,6 +48,7 @@ QT_DECLARE_NAMESPACED_OBJC_INTERFACE(QNSOpenSavePanelDelegate, NSObject<NSOpenSa QT_BEGIN_NAMESPACE +class QEventLoop; class QFileDialog; class QFileDialogPrivate; @@ -84,6 +85,7 @@ public: private: QNSOpenSavePanelDelegate *mDelegate; QUrl mDir; + QEventLoop *m_eventLoop = nullptr; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm index 0909b5e21a..541eabd3ed 100644 --- a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm +++ b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm @@ -179,15 +179,14 @@ static QString strippedText(QString s) - (void)closePanel { - // An already closed/closing panel has its return code set - if (mReturnCode != kReturnCodeNotSet) - return; - *mCurrentSelection = QString::fromNSString([[mSavePanel URL] path]).normalized(QString::NormalizationForm_C); - if ([mSavePanel respondsToSelector:@selector(close)]) + + if (mSavePanel.sheet) + [NSApp endSheet:mSavePanel]; + else if (NSApp.modalWindow == mSavePanel) + [NSApp stopModal]; + else [mSavePanel close]; - if ([mSavePanel isSheet]) - [NSApp endSheet: mSavePanel]; } - (void)showModelessPanel @@ -722,11 +721,14 @@ bool QCocoaFileDialogHelper::showCocoaFilePanel(Qt::WindowModality windowModalit createNSOpenSavePanelDelegate(); if (!mDelegate) return false; - if (windowModality == Qt::NonModal) - [mDelegate showModelessPanel]; - else if (windowModality == Qt::WindowModal && parent) + + if (windowModality == Qt::WindowModal && parent) [mDelegate showWindowModalSheet:parent]; - // no need to show a Qt::ApplicationModal dialog here, since it will be done in _q_platformRunNativeAppModalPanel() + else if (windowModality == Qt::ApplicationModal) + return true; // Defer until exec() + else + [mDelegate showModelessPanel]; + return true; } @@ -738,6 +740,10 @@ bool QCocoaFileDialogHelper::hideCocoaFilePanel() return false; } else { [mDelegate closePanel]; + + if (m_eventLoop) + m_eventLoop->exit(); + // Even when we hide it, we are still using a // native dialog, so return true: return true; @@ -746,16 +752,28 @@ bool QCocoaFileDialogHelper::hideCocoaFilePanel() void QCocoaFileDialogHelper::exec() { - // Note: If NSApp is not running (which is the case if e.g a top-most - // QEventLoop has been interrupted, and the second-most event loop has not - // yet been reactivated (regardless if [NSApp run] is still on the stack)), - // showing a native modal dialog will fail. - QMacAutoReleasePool pool; - if ([mDelegate runApplicationModalPanel]) - emit accept(); - else - emit reject(); + Q_ASSERT(mDelegate); + if (mDelegate->mSavePanel.visible) { + // WindowModal or NonModal, so already shown above + QEventLoop eventLoop; + m_eventLoop = &eventLoop; + eventLoop.exec(QEventLoop::DialogExec); + m_eventLoop = nullptr; + } else { + // ApplicationModal, so show and block using native APIs + + // Note: If NSApp is not running (which is the case if e.g a top-most + // QEventLoop has been interrupted, and the second-most event loop has not + // yet been reactivated (regardless if [NSApp run] is still on the stack)), + // showing a native modal dialog will fail. + + QMacAutoReleasePool pool; + if ([mDelegate runApplicationModalPanel]) + emit accept(); + else + emit reject(); + } } bool QCocoaFileDialogHelper::defaultNameFilterDisables() const |