diff options
author | Tor Arne Vestbø <tor.arne.vestbo@qt.io> | 2021-09-03 15:06:59 +0200 |
---|---|---|
committer | Tor Arne Vestbø <tor.arne.vestbo@qt.io> | 2021-09-17 14:56:19 +0200 |
commit | 28b14b966fe8535d7a81914b70759546b694e31b (patch) | |
tree | 707f4b0702242cf4a7e01e5003d9772e8bb1a703 | |
parent | 1b0cb842129616d67ddf279e7e900fcdf433e390 (diff) |
Deduplicate maybeQuitOnLastWindowClosed handling
The functionality now lives in QGuiApplication, and is triggered
by QGuiApplication and QApplication after dispatching the close
event to the window.
The slight difference between how a Qt GUI and Qt Widget app
determines if a window should contribute to the close-on-quit
behavior has been abstracted into a QWindowPrivate helper.
The additional checks that were in place for skipping out of
the whole maybeQuitOnLastWindowClosed machinery have been kept.
Task-number: QTBUG-53286
Change-Id: I81bd474755f9adb3a2b082621e5ecaa1c4726808
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
-rw-r--r-- | src/gui/kernel/qguiapplication.cpp | 30 | ||||
-rw-r--r-- | src/gui/kernel/qguiapplication_p.h | 1 | ||||
-rw-r--r-- | src/gui/kernel/qwindow.cpp | 51 | ||||
-rw-r--r-- | src/gui/kernel/qwindow_p.h | 4 | ||||
-rw-r--r-- | src/widgets/kernel/qapplication.cpp | 4 | ||||
-rw-r--r-- | src/widgets/kernel/qwidget.cpp | 26 | ||||
-rw-r--r-- | src/widgets/kernel/qwidgetwindow.cpp | 34 |
7 files changed, 82 insertions, 68 deletions
diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp index 98b5ea03e6..294684584b 100644 --- a/src/gui/kernel/qguiapplication.cpp +++ b/src/gui/kernel/qguiapplication.cpp @@ -1918,6 +1918,8 @@ void QGuiApplicationPrivate::captureGlobalModifierState(QEvent *e) */ bool QGuiApplication::notify(QObject *object, QEvent *event) { + Q_D(QGuiApplication); + if (object->isWindowType()) { if (QGuiApplicationPrivate::sendQWindowEventToQPlatformWindow(static_cast<QWindow *>(object), event)) return true; // Platform plugin ate the event @@ -1925,7 +1927,12 @@ bool QGuiApplication::notify(QObject *object, QEvent *event) QGuiApplicationPrivate::captureGlobalModifierState(event); - return QCoreApplication::notify(object, event); + bool accepted = QCoreApplication::notify(object, event); + + if (event->type() == QEvent::Close && object->isWindowType() && accepted) + d->maybeQuitOnLastWindowClosed(static_cast<QWindow*>(object)); + + return accepted; } /*! \reimp @@ -3533,13 +3540,30 @@ void QGuiApplication::setQuitOnLastWindowClosed(bool quit) QCoreApplication::setQuitLockEnabled(quit); } - - bool QGuiApplication::quitOnLastWindowClosed() { return QCoreApplication::isQuitLockEnabled(); } +void QGuiApplicationPrivate::maybeQuitOnLastWindowClosed(QWindow *closedWindow) +{ + Q_ASSERT(closedWindow); + + if (!qt_window_private(closedWindow)->shouldTriggerQuitOnClose()) + return; + + // Check if there are any remaining windows that should prevent us from quitting + for (auto *topLevelWindow : QGuiApplication::topLevelWindows()) { + auto *windowPrivate = qt_window_private(topLevelWindow); + if (windowPrivate->shouldCancelQuitOnClose()) + return; + } + + emitLastWindowClosed(); + + if (QGuiApplication::quitOnLastWindowClosed()) + maybeQuit(); +} /*! \fn void QGuiApplication::lastWindowClosed() diff --git a/src/gui/kernel/qguiapplication_p.h b/src/gui/kernel/qguiapplication_p.h index 7b28c70993..3450c6e36b 100644 --- a/src/gui/kernel/qguiapplication_p.h +++ b/src/gui/kernel/qguiapplication_p.h @@ -109,6 +109,7 @@ public: void quit() override; bool shouldQuitInternal(const QWindowList &processedWindows); + void maybeQuitOnLastWindowClosed(QWindow *closedWindow); static void captureGlobalModifierState(QEvent *e); static Qt::KeyboardModifiers modifier_buttons; diff --git a/src/gui/kernel/qwindow.cpp b/src/gui/kernel/qwindow.cpp index e1733cbf46..cdb2534b2e 100644 --- a/src/gui/kernel/qwindow.cpp +++ b/src/gui/kernel/qwindow.cpp @@ -2268,6 +2268,18 @@ bool QWindow::close() return d->platformWindow->close(); } +bool QWindowPrivate::shouldTriggerQuitOnClose() const +{ + Q_Q(const QWindow); + return q->isTopLevel(); +} + +bool QWindowPrivate::shouldCancelQuitOnClose() const +{ + Q_Q(const QWindow); + return q->isVisible() && !q->transientParent() && q->type() != Qt::ToolTip; +} + /*! The expose event (\a ev) is sent by the window system when a window moves between the un-exposed and exposed states. @@ -2444,17 +2456,8 @@ bool QWindow::event(QEvent *ev) case QEvent::Close: closeEvent(static_cast<QCloseEvent*>(ev)); - if (ev->isAccepted()) { - Q_D(QWindow); - bool wasVisible = isVisible(); + if (ev->isAccepted()) destroy(); - if (wasVisible) { - // FIXME: This check for visibility is a workaround for both QWidgetWindow - // and QWindow having logic to emit lastWindowClosed, and possibly quit the - // application. We should find a better way to handle this. - d->maybeQuitOnLastWindowClosed(); - } - } break; case QEvent::Expose: @@ -2806,34 +2809,6 @@ Q_GUI_EXPORT QWindowPrivate *qt_window_private(QWindow *window) return window->d_func(); } -void QWindowPrivate::maybeQuitOnLastWindowClosed() -{ - if (!QCoreApplication::instance()) - return; - - Q_Q(QWindow); - if (!q->isTopLevel()) - return; - - QWindowList list = QGuiApplication::topLevelWindows(); - bool lastWindowClosed = true; - for (int i = 0; i < list.size(); ++i) { - QWindow *w = list.at(i); - if (!w->isVisible() || w->transientParent() || w->type() == Qt::ToolTip) - continue; - lastWindowClosed = false; - break; - } - if (lastWindowClosed) { - QGuiApplicationPrivate::emitLastWindowClosed(); - - if (QGuiApplication::quitOnLastWindowClosed()) { - QCoreApplicationPrivate *applicationPrivate = static_cast<QCoreApplicationPrivate*>(QObjectPrivate::get(QCoreApplication::instance())); - applicationPrivate->maybeQuit(); - } - } -} - QWindow *QWindowPrivate::topLevelWindow(QWindow::AncestorMode mode) const { Q_Q(const QWindow); diff --git a/src/gui/kernel/qwindow_p.h b/src/gui/kernel/qwindow_p.h index fef8b4ca72..3eeafeff86 100644 --- a/src/gui/kernel/qwindow_p.h +++ b/src/gui/kernel/qwindow_p.h @@ -81,7 +81,6 @@ public: void init(QScreen *targetScreen = nullptr); - void maybeQuitOnLastWindowClosed(); #ifndef QT_NO_CURSOR void setCursor(const QCursor *c = nullptr); bool applyCursor(); @@ -119,6 +118,9 @@ public: virtual void processSafeAreaMarginsChanged() {} + virtual bool shouldTriggerQuitOnClose() const; + virtual bool shouldCancelQuitOnClose() const; + bool isPopup() const { return (windowFlags & Qt::WindowType_Mask) == Qt::Popup; } void setAutomaticPositionAndResizeEnabled(bool a) { positionAutomatic = resizeAutomatic = a; } diff --git a/src/widgets/kernel/qapplication.cpp b/src/widgets/kernel/qapplication.cpp index 9069b9005d..74868077b3 100644 --- a/src/widgets/kernel/qapplication.cpp +++ b/src/widgets/kernel/qapplication.cpp @@ -3342,6 +3342,10 @@ bool QApplication::notify(QObject *receiver, QEvent *e) break; } + // We don't call QGuiApplication::notify here, so we need to duplicate the logic + if (e->type() == QEvent::Close && receiver->isWindowType() && res) + d->maybeQuitOnLastWindowClosed(static_cast<QWindow *>(receiver)); + return res; } diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index dcd3d1f1d9..a92454547a 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -8399,8 +8399,6 @@ bool QWidgetPrivate::handleClose(CloseMode mode) QPointer<QWidget> that = q; QPointer<QWidget> parentWidget = (q->parentWidget() && !QObjectPrivate::get(q->parentWidget())->wasDeleted) ? q->parentWidget() : nullptr; - bool quitOnClose = q->testAttribute(Qt::WA_QuitOnClose); - if (data.in_destructor) mode = CloseNoEvent; @@ -8420,30 +8418,6 @@ bool QWidgetPrivate::handleClose(CloseMode mode) if (!that.isNull() && !q->isHidden()) q->hide(); - // Attempt to close the application only if this has WA_QuitOnClose set and a non-visible parent - quitOnClose = quitOnClose && (parentWidget.isNull() || !parentWidget->isVisible()); - - if (quitOnClose) { - /* if there is no non-withdrawn primary window left (except - the ones without QuitOnClose), we emit the lastWindowClosed - signal */ - QWidgetList list = QApplication::topLevelWidgets(); - bool lastWindowClosed = true; - for (int i = 0; i < list.size(); ++i) { - QWidget *w = list.at(i); - if (!w->isVisible() || w->parentWidget() || !w->testAttribute(Qt::WA_QuitOnClose)) - continue; - lastWindowClosed = false; - break; - } - if (lastWindowClosed) { - QGuiApplicationPrivate::emitLastWindowClosed(); - QCoreApplicationPrivate *applicationPrivate = static_cast<QCoreApplicationPrivate*>(QObjectPrivate::get(QCoreApplication::instance())); - applicationPrivate->maybeQuit(); - } - } - - if (!that.isNull()) { data.is_closing = false; if (q->testAttribute(Qt::WA_DeleteOnClose)) { diff --git a/src/widgets/kernel/qwidgetwindow.cpp b/src/widgets/kernel/qwidgetwindow.cpp index 80e9b1ab5c..0d73e55719 100644 --- a/src/widgets/kernel/qwidgetwindow.cpp +++ b/src/widgets/kernel/qwidgetwindow.cpp @@ -115,6 +115,9 @@ public: if (QWidget *widget = q->widget()) QWidgetPrivate::get(widget)->updateContentsRect(); } + + bool shouldTriggerQuitOnClose() const override; + bool shouldCancelQuitOnClose() const override; }; QRectF QWidgetWindowPrivate::closestAcceptableGeometry(const QRectF &rect) const @@ -843,6 +846,37 @@ void QWidgetWindow::handleCloseEvent(QCloseEvent *event) event->setAccepted(accepted); } +bool QWidgetWindowPrivate::shouldTriggerQuitOnClose() const +{ + Q_Q(const QWidgetWindow); + QWidget *widget = q->widget(); + + // Closing a window without WA_QuitOnClose should stop us from even + // looking at the other windows. Otherwise we might find that all the + // other windows miss WA_QuitOnClose as well, and end up quitting, + // but that's not what the user intended. + if (!widget->testAttribute(Qt::WA_QuitOnClose)) + return false; + + // Qt::Tool windows do not have WA_QuitOnClose set by default, which + // means that if you open a dialog from one, and the tool window is + // the only remaining window, then closing the dialog would result + // in quitting the application. To prevent this we check if the + // closed widget has a visible parent (the Qt:Tool window in this + // case), and if so bail out. + if (widget->parentWidget() && widget->parentWidget()->isVisible()) + return false; + + return true; +} + +bool QWidgetWindowPrivate::shouldCancelQuitOnClose() const +{ + Q_Q(const QWidgetWindow); + QWidget *w = q->widget(); + return w->isVisible() && !w->parentWidget() & w->testAttribute(Qt::WA_QuitOnClose); +} + #if QT_CONFIG(wheelevent) void QWidgetWindow::handleWheelEvent(QWheelEvent *event) |