From 5c37258b06107d247f254fc7e3e852d2838e0a97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Tue, 8 Oct 2019 18:31:33 +0200 Subject: macOS: Don't dismiss menu during applicationShouldTerminate The logic was a leftover from the Carbon days and is no longer needed. Change-Id: I557b669eadea902fa439c43162218c5864077df9 Reviewed-by: Volker Hilsheimer --- src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm | 2 -- 1 file changed, 2 deletions(-) (limited to 'src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm') diff --git a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm index 00f5a1bf09..9daf65abcf 100644 --- a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm +++ b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm @@ -142,8 +142,6 @@ QT_USE_NAMESPACE - (BOOL)canQuit { - [[NSApp mainMenu] cancelTracking]; - bool handle_quit = true; NSMenuItem *quitMenuItem = [[QCocoaMenuLoader sharedMenuLoader] quitMenuItem]; if (!QGuiApplicationPrivate::instance()->modalWindowList.isEmpty() -- cgit v1.2.3 From ff2ba8b9d2798c4b7a77734df723d6312b983f1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Mon, 14 Oct 2019 14:07:20 +0200 Subject: macOS: Let system decide whether modal window should prevent app termination The logic for preventing application termination when there are modal windows active was a leftover from the Carbon to Cocoa port, and is no longer needed. AppKit will deal with this on its own, by checking the preventsApplicationTerminationWhenModal property of each NSWindow. In some cases AppKit will also ignore this property, such as when quitting the application from the menu entry, which means we now get the default system behavior for this use-case. Change-Id: Iac5d8d8e17eb0974448f7ee6f39c9b7761bf4d90 Reviewed-by: Shawn Rutledge Reviewed-by: Volker Hilsheimer --- .../platforms/cocoa/qcocoaapplicationdelegate.mm | 25 +++------------------- 1 file changed, 3 insertions(+), 22 deletions(-) (limited to 'src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm') diff --git a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm index 9daf65abcf..33a45985a8 100644 --- a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm +++ b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm @@ -142,28 +142,9 @@ QT_USE_NAMESPACE - (BOOL)canQuit { - bool handle_quit = true; - NSMenuItem *quitMenuItem = [[QCocoaMenuLoader sharedMenuLoader] quitMenuItem]; - if (!QGuiApplicationPrivate::instance()->modalWindowList.isEmpty() - && [quitMenuItem isEnabled]) { - int visible = 0; - const QWindowList tlws = QGuiApplication::topLevelWindows(); - for (int i = 0; i < tlws.size(); ++i) { - if (tlws.at(i)->isVisible()) - ++visible; - } - handle_quit = (visible <= 1); - } - - if (handle_quit) { - QCloseEvent ev; - QGuiApplication::sendEvent(qGuiApp, &ev); - if (ev.isAccepted()) { - return YES; - } - } - - return NO; + QCloseEvent ev; + QGuiApplication::sendEvent(qGuiApp, &ev); + return ev.isAccepted(); } // This function will only be called when NSApp is actually running. -- cgit v1.2.3 From 2967510213373fa92db94385e3904196637e8df1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Mon, 14 Oct 2019 14:14:01 +0200 Subject: macOS: Merge [QCocoaApplicationDelegate canQuit] into callsite The logic for handling termination without any event loops has been moved up as an early exit, and the code has been modernized. Change-Id: I202720b9923e35732cffea656e1ce108ef11953d Reviewed-by: Paul Olav Tvete --- .../platforms/cocoa/qcocoaapplicationdelegate.mm | 54 ++++++++++------------ 1 file changed, 24 insertions(+), 30 deletions(-) (limited to 'src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm') diff --git a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm index 33a45985a8..01abeb1a0e 100644 --- a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm +++ b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm @@ -140,46 +140,40 @@ QT_USE_NAMESPACE return [[self.dockMenu retain] autorelease]; } -- (BOOL)canQuit -{ - QCloseEvent ev; - QGuiApplication::sendEvent(qGuiApp, &ev); - return ev.isAccepted(); -} - // This function will only be called when NSApp is actually running. - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender { if ([reflectionDelegate respondsToSelector:_cmd]) return [reflectionDelegate applicationShouldTerminate:sender]; - if ([self canQuit]) { - if (!startedQuit) { - startedQuit = true; - // Close open windows. This is done in order to deliver de-expose - // events while the event loop is still running. - const QWindowList topLevels = QGuiApplication::topLevelWindows(); - for (int i = 0; i < topLevels.size(); ++i) { - QWindow *topLevelWindow = topLevels.at(i); - // Already closed windows will not have a platform window, skip those - if (topLevelWindow->handle()) - QWindowSystemInterface::handleCloseEvent(topLevelWindow); - } - QWindowSystemInterface::flushWindowSystemEvents(); - - QGuiApplication::exit(0); - startedQuit = false; - } - } - if (QGuiApplicationPrivate::instance()->threadData->eventLoops.isEmpty()) { - // INVARIANT: No event loop is executing. This probably - // means that Qt is used as a plugin, or as a part of a native - // Cocoa application. In any case it should be fine to - // terminate now: + // No event loop is executing. This probably means that Qt is used as a plugin, + // or as a part of a native Cocoa application. In any case it should be fine to + // terminate now. return NSTerminateNow; } + QCloseEvent ev; + QGuiApplication::sendEvent(qGuiApp, &ev); + if (!ev.isAccepted()) + return NSTerminateCancel; + + if (!startedQuit) { + startedQuit = true; + // Close open windows. This is done in order to deliver de-expose + // events while the event loop is still running. + for (QWindow *topLevelWindow : QGuiApplication::topLevelWindows()) { + // Already closed windows will not have a platform window, skip those + if (!topLevelWindow->handle()) + continue; + + QWindowSystemInterface::handleCloseEvent(topLevelWindow); + } + + QGuiApplication::exit(0); + startedQuit = false; + } + return NSTerminateCancel; } -- cgit v1.2.3 From 1b6db1849477be30ef0ca52c288d358b911ea1e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Mon, 14 Oct 2019 15:58:50 +0200 Subject: Propagate application termination requests through QPA Instead of having each platform plugin deal with application termination in their own weird ways, we now have a QPA API to signal that the system requested the application to terminate. On the QGuiApplication side this results in a Quit event being sent to the application, which triggers the default behavior of closing all app windows, and then finally calling QCoreApplication::quit(). The quit event replaces the misuse of a close event being sent to the application. Close events are documented as being sent to windows. The close events that are sent to individual windows as part of the quit process can be ignored. This will skip the final quit() of the application, and also inform the platform that the quit was not accepted, in case that should be propagated further. In the future the logic for closing windows should be unified between the various approaches in closeAllWindows, shouldQuit, and friends. Change-Id: I0ed7f1c0d3f0bf1a755e1dd4066e1575fc3a28e1 Reviewed-by: Paul Olav Tvete --- .../platforms/cocoa/qcocoaapplicationdelegate.mm | 31 +++++++++------------- 1 file changed, 12 insertions(+), 19 deletions(-) (limited to 'src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm') diff --git a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm index 01abeb1a0e..9b0a6b1b86 100644 --- a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm +++ b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm @@ -88,10 +88,13 @@ #include #include +QT_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcQpaApplication, "qt.qpa.application"); +QT_END_NAMESPACE + QT_USE_NAMESPACE @implementation QCocoaApplicationDelegate { - bool startedQuit; NSObject *reflectionDelegate; bool inLaunch; } @@ -150,30 +153,20 @@ QT_USE_NAMESPACE // No event loop is executing. This probably means that Qt is used as a plugin, // or as a part of a native Cocoa application. In any case it should be fine to // terminate now. + qCDebug(lcQpaApplication) << "No running event loops, terminating now"; return NSTerminateNow; } - QCloseEvent ev; - QGuiApplication::sendEvent(qGuiApp, &ev); - if (!ev.isAccepted()) + if (!QWindowSystemInterface::handleApplicationTermination()) { + qCDebug(lcQpaApplication) << "Application termination canceled"; return NSTerminateCancel; - - if (!startedQuit) { - startedQuit = true; - // Close open windows. This is done in order to deliver de-expose - // events while the event loop is still running. - for (QWindow *topLevelWindow : QGuiApplication::topLevelWindows()) { - // Already closed windows will not have a platform window, skip those - if (!topLevelWindow->handle()) - continue; - - QWindowSystemInterface::handleCloseEvent(topLevelWindow); - } - - QGuiApplication::exit(0); - startedQuit = false; } + // Even if the application termination was accepted by the application we can't + // return NSTerminateNow, as that would trigger AppKit to ultimately call exit(). + // We need to ensure that the runloop continues spinning so that we can return + // from our own event loop back to main(), and exit from there. + qCDebug(lcQpaApplication) << "Termination accepted, but returning to runloop for exit through main()"; return NSTerminateCancel; } -- cgit v1.2.3