summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTor Arne Vestbø <tor.arne.vestbo@qt.io>2021-09-03 15:06:59 +0200
committerTor Arne Vestbø <tor.arne.vestbo@qt.io>2021-09-17 14:56:19 +0200
commit28b14b966fe8535d7a81914b70759546b694e31b (patch)
tree707f4b0702242cf4a7e01e5003d9772e8bb1a703
parent1b0cb842129616d67ddf279e7e900fcdf433e390 (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.cpp30
-rw-r--r--src/gui/kernel/qguiapplication_p.h1
-rw-r--r--src/gui/kernel/qwindow.cpp51
-rw-r--r--src/gui/kernel/qwindow_p.h4
-rw-r--r--src/widgets/kernel/qapplication.cpp4
-rw-r--r--src/widgets/kernel/qwidget.cpp26
-rw-r--r--src/widgets/kernel/qwidgetwindow.cpp34
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)