summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms
diff options
context:
space:
mode:
authorTor Arne Vestbø <tor.arne.vestbo@qt.io>2021-01-22 16:00:17 +0100
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2021-01-25 21:59:04 +0000
commit7d3fee7ca9b8b0c47fc2cb8092bf21ba28d582e3 (patch)
tree5a7845510cca9afaac8808ba7cfb9c31ff19e20e /src/plugins/platforms
parent4b1153d1a24a5d32177c266c4adb197e97ecab00 (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.h2
-rw-r--r--src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm58
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