From ff3dcc49c4a1912189091e35e87cb61af2f62d47 Mon Sep 17 00:00:00 2001 From: Gabriel de Dietrich Date: Mon, 17 Mar 2014 19:21:49 +0100 Subject: Cocoa: Don't stop NSApp when showing a modal dialog We manage embedded modal sessions with a stack and only run the top-most session. We also stop the last modal session before starting a new one. However, if there is no modal session running yet, we end up stopping NSApp. This seems to cause ill side effects on OS X 10.9. Notably, starting a new modal session outside QCocoaEventDispatcher, like when opening a native file dialog, makes this last modal session impossible for the user to quit. In this patch, we make sure NSApp is kept running if there's no modal session running yet, akin to calling QDialog::exec() at the event dispatcher level. The behavior for ensuing modal sessions remains unchanged. Task-number: QTBUG-34677 Change-Id: I6a23b191e4dce18514504b8e953f8caa7fad8731 Reviewed-by: Gabriel de Dietrich Reviewed-by: Richard Moe Gustavsen --- src/plugins/platforms/cocoa/qcocoaeventdispatcher.h | 1 + src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) (limited to 'src/plugins') diff --git a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h index 33d7dcbcf4..0274ed8201 100644 --- a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h +++ b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h @@ -163,6 +163,7 @@ public: // The following variables help organizing modal sessions: QStack cocoaModalSessionStack; bool currentExecIsNSAppRun; + bool modalSessionOnNSAppRun; bool nsAppRunCalledByQt; bool cleanupModalSessionsNeeded; uint processEventsCalled; diff --git a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm index 91b631bff9..495a54cac4 100644 --- a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm +++ b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm @@ -721,7 +721,6 @@ void QCocoaEventDispatcherPrivate::beginModalSession(QWindow *window) // setting currentModalSessionCached to zero, so that interrupt() calls // [NSApp abortModal] if another modal session is currently running Q_Q(QCocoaEventDispatcher); - q->interrupt(); // Add a new, empty (null), NSModalSession to the stack. // It will become active the next time QEventDispatcher::processEvents is called. @@ -734,6 +733,12 @@ void QCocoaEventDispatcherPrivate::beginModalSession(QWindow *window) cocoaModalSessionStack.push(info); updateChildrenWorksWhenModal(); currentModalSessionCached = 0; + if (currentExecIsNSAppRun) { + modalSessionOnNSAppRun = true; + q->wakeUp(); + } else { + q->interrupt(); + } } void QCocoaEventDispatcherPrivate::endModalSession(QWindow *window) @@ -772,6 +777,7 @@ QCocoaEventDispatcherPrivate::QCocoaEventDispatcherPrivate() runLoopTimerRef(0), blockSendPostedEvents(false), currentExecIsNSAppRun(false), + modalSessionOnNSAppRun(false), nsAppRunCalledByQt(false), cleanupModalSessionsNeeded(false), processEventsCalled(0), @@ -902,6 +908,14 @@ void QCocoaEventDispatcherPrivate::postedEventsSourceCallback(void *info) // processEvents() was called "manually," ignore this source for now d->maybeCancelWaitForMoreEvents(); return; + } else if (d->modalSessionOnNSAppRun) { + // We're about to spawn the 1st modal session on top of the main runloop. + // Instead of calling processPostedEvents(), which would need us stop + // NSApp, we just re-enter processEvents(). This is equivalent to calling + // QDialog::exec() except that it's done in a non-blocking way. + d->modalSessionOnNSAppRun = false; + d->q_func()->processEvents(QEventLoop::DialogExec | QEventLoop::EventLoopExec | QEventLoop::WaitForMoreEvents); + return; } d->processPostedEvents(); d->maybeCancelWaitForMoreEvents(); -- cgit v1.2.3