diff --git a/src/corelib/global/qnamespace.qdoc b/src/corelib/global/qnamespace.qdoc index 873a4c91ee..59a479d3d4 100644 --- a/src/corelib/global/qnamespace.qdoc +++ b/src/corelib/global/qnamespace.qdoc @@ -1126,10 +1126,10 @@ e.g., when a hidden widget was resized. This flag is set or cleared by the Qt kernel. - \value WA_QuitOnClose Makes Qt quit the application when the last widget - with the attribute set has accepted closeEvent(). This behavior can be - modified with the QGuiApplication::quitOnLastWindowClosed property. By default - this attribute is set for all widgets of type Qt::Window. + \value WA_QuitOnClose Indicates that the widget should be taken into account + when deciding whether to quit the application when the last window is closed. + This behavior can be modified with the QGuiApplication::quitOnLastWindowClosed + property. By default this attribute is set for all widgets of type Qt::Window. \value WA_Resized Indicates that the widget has an explicit size. This flag is set or cleared by QWidget::resize() and QWidget::setGeometry(). diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp index 86ec5ad7b5..2ca1ff0a44 100644 --- a/src/gui/kernel/qguiapplication.cpp +++ b/src/gui/kernel/qguiapplication.cpp @@ -1930,7 +1930,7 @@ bool QGuiApplication::notify(QObject *object, QEvent *event) bool accepted = QCoreApplication::notify(object, event); if (event->type() == QEvent::Close && object->isWindowType() && accepted) - d->maybeQuitOnLastWindowClosed(static_cast(object)); + d->maybeLastWindowClosed(static_cast(object)); return accepted; } @@ -3530,7 +3530,8 @@ void QGuiApplicationPrivate::notifyWindowIconChanged() The default is \c true. If this property is \c true, the applications quits when the last visible - primary window (i.e. window with no parent) is closed. + \l{Primary and Secondary Windows}{primary window} (i.e. top level window + with no transient parent) is closed. \sa quit(), QWindow::close() */ @@ -3545,19 +3546,17 @@ bool QGuiApplication::quitOnLastWindowClosed() return QCoreApplication::isQuitLockEnabled(); } -void QGuiApplicationPrivate::maybeQuitOnLastWindowClosed(QWindow *closedWindow) +void QGuiApplicationPrivate::maybeLastWindowClosed(QWindow *closedWindow) { Q_ASSERT(closedWindow); - if (!qt_window_private(closedWindow)->shouldTriggerQuitOnClose()) + // Only windows that themselves participates in last-window-closed + // should be allowed to trigger lastWindowClosed. + if (!qt_window_private(closedWindow)->participatesInLastWindowClosed()) 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; - } + if (!lastWindowClosed()) + return; if (in_exec) emit q_func()->lastWindowClosed(); @@ -3570,32 +3569,31 @@ void QGuiApplicationPrivate::maybeQuitOnLastWindowClosed(QWindow *closedWindow) \fn void QGuiApplication::lastWindowClosed() This signal is emitted from exec() when the last visible - primary window (i.e. window with no parent) is closed. + \l{Primary and Secondary Windows}{primary window} (i.e. + top level window with no transient parent) is closed. By default, QGuiApplication quits after this signal is emitted. This feature can be turned off by setting \l quitOnLastWindowClosed to \c false. - \sa QWindow::close(), QWindow::isTopLevel() + \sa QWindow::close(), QWindow::isTopLevel(), QWindow::transientParent() */ +bool QGuiApplicationPrivate::lastWindowClosed() const +{ + for (auto *window : QGuiApplication::topLevelWindows()) { + if (!qt_window_private(window)->participatesInLastWindowClosed()) + continue; + + if (window->isVisible()) + return false; + } + + return true; +} + bool QGuiApplicationPrivate::shouldQuit() { - const QWindowList processedWindows; - return shouldQuitInternal(processedWindows); -} - -bool QGuiApplicationPrivate::shouldQuitInternal(const QWindowList &processedWindows) -{ - /* if there is no visible top-level window left, we allow the quit */ - QWindowList list = QGuiApplication::topLevelWindows(); - for (int i = 0; i < list.size(); ++i) { - QWindow *w = list.at(i); - if (processedWindows.contains(w)) - continue; - if (w->isVisible() && !w->transientParent()) - return false; - } - return true; + return lastWindowClosed(); } void QGuiApplicationPrivate::quit() diff --git a/src/gui/kernel/qguiapplication_p.h b/src/gui/kernel/qguiapplication_p.h index 1c66e007db..d43bfd755e 100644 --- a/src/gui/kernel/qguiapplication_p.h +++ b/src/gui/kernel/qguiapplication_p.h @@ -108,8 +108,8 @@ public: virtual bool shouldQuit() override; void quit() override; - bool shouldQuitInternal(const QWindowList &processedWindows); - void maybeQuitOnLastWindowClosed(QWindow *closedWindow); + void maybeLastWindowClosed(QWindow *closedWindow); + bool lastWindowClosed() const; 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 275d190a5a..26ad97bc86 100644 --- a/src/gui/kernel/qwindow.cpp +++ b/src/gui/kernel/qwindow.cpp @@ -2269,16 +2269,25 @@ bool QWindow::close() return d->platformWindow->close(); } -bool QWindowPrivate::shouldTriggerQuitOnClose() const +bool QWindowPrivate::participatesInLastWindowClosed() 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; + if (!q->isTopLevel()) + return false; + + // Tool-tip widgets do not normally have Qt::WA_QuitOnClose, + // but since we do not have a similar flag for non-widget + // windows we need an explicit exclusion here as well. + if (q->type() == Qt::ToolTip) + return false; + + // A window with a transient parent is not a primary window, + // it's a secondary window. + if (q->transientParent()) + return false; + + return true; } /*! diff --git a/src/gui/kernel/qwindow_p.h b/src/gui/kernel/qwindow_p.h index 3eeafeff86..843375cc7e 100644 --- a/src/gui/kernel/qwindow_p.h +++ b/src/gui/kernel/qwindow_p.h @@ -118,8 +118,7 @@ public: virtual void processSafeAreaMarginsChanged() {} - virtual bool shouldTriggerQuitOnClose() const; - virtual bool shouldCancelQuitOnClose() const; + virtual bool participatesInLastWindowClosed() const; bool isPopup() const { return (windowFlags & Qt::WindowType_Mask) == Qt::Popup; } void setAutomaticPositionAndResizeEnabled(bool a) diff --git a/src/widgets/kernel/qapplication.cpp b/src/widgets/kernel/qapplication.cpp index 651a44c990..6ad6004030 100644 --- a/src/widgets/kernel/qapplication.cpp +++ b/src/widgets/kernel/qapplication.cpp @@ -2614,24 +2614,6 @@ int QApplication::exec() return QGuiApplication::exec(); } -bool QApplicationPrivate::shouldQuit() -{ - /* if there is no non-withdrawn primary window left (except - the ones without QuitOnClose), we emit the lastWindowClosed - signal */ - QWidgetList list = QApplication::topLevelWidgets(); - QWindowList processedWindows; - for (int i = 0; i < list.size(); ++i) { - QWidget *w = list.at(i); - if (QWindow *window = w->windowHandle()) { // Menus, popup widgets may not have a QWindow - processedWindows.push_back(window); - if (w->isVisible() && !w->parentWidget() && w->testAttribute(Qt::WA_QuitOnClose)) - return false; - } - } - return QGuiApplicationPrivate::shouldQuitInternal(processedWindows); -} - /*! \reimp */ bool QApplication::notify(QObject *receiver, QEvent *e) @@ -3301,7 +3283,7 @@ bool QApplication::notify(QObject *receiver, QEvent *e) res = d->notify_helper(receiver, e); // We don't call QGuiApplication::notify here, so we need to duplicate the logic if (res && e->type() == QEvent::Close) - d->maybeQuitOnLastWindowClosed(static_cast(receiver)); + d->maybeLastWindowClosed(static_cast(receiver)); } else { res = d->notify_helper(receiver, e); } diff --git a/src/widgets/kernel/qapplication_p.h b/src/widgets/kernel/qapplication_p.h index 59c13f06af..6bb1f35d1b 100644 --- a/src/widgets/kernel/qapplication_p.h +++ b/src/widgets/kernel/qapplication_p.h @@ -106,8 +106,6 @@ public: virtual void notifyLayoutDirectionChange() override; virtual void notifyActiveWindowChange(QWindow *) override; - virtual bool shouldQuit() override; - static bool autoSipEnabled; static QString desktopStyleKey(); diff --git a/src/widgets/kernel/qwidgetwindow.cpp b/src/widgets/kernel/qwidgetwindow.cpp index 846300100b..ba1230301e 100644 --- a/src/widgets/kernel/qwidgetwindow.cpp +++ b/src/widgets/kernel/qwidgetwindow.cpp @@ -119,8 +119,7 @@ public: QWidgetPrivate::get(widget)->updateContentsRect(); } - bool shouldTriggerQuitOnClose() const override; - bool shouldCancelQuitOnClose() const override; + bool participatesInLastWindowClosed() const override; }; QRectF QWidgetWindowPrivate::closestAcceptableGeometry(const QRectF &rect) const @@ -847,35 +846,18 @@ void QWidgetWindow::handleCloseEvent(QCloseEvent *event) event->setAccepted(accepted); } -bool QWidgetWindowPrivate::shouldTriggerQuitOnClose() const +bool QWidgetWindowPrivate::participatesInLastWindowClosed() 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)) + // For historical reasons WA_QuitOnClose has been closely tied + // to the lastWindowClosed signal, since the default behavior + // is to quit the application after emitting lastWindowClosed. + // ### Qt 7: Rename this attribute, or decouple behavior. + if (!q->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); + return QWindowPrivate::participatesInLastWindowClosed(); } #if QT_CONFIG(wheelevent)