Deduplicate lastWindowClosed handling
Whenever a window was closed, we had logic to check if it was the last window, and if so emitted lastWindowClosed and then tried to quit the application. But the automatic quit process also checked if there were any remaining windows, as this process could be triggered as a result of a QEventLoopLocker going out of scope. The two paths now share the same logic for determining if there are any remaining windows. The docs have been updated to reflect the original intent of the logic, dealing only with primary windows (top levels without a transient parent). This was how both the original code paths implemented their logic. For historical reasons the Qt::WA_QuitOnClose attribute is closely tied to the lastWindowClosed signal, and isn't merely limited to controlling whether we try an automatic quit when the last window closes. For now this behavior has been kept, but the docs have been updated to clarify how the attribute is handled in practice. Change-Id: If3d06b065236aad2e59e9091cac3146bc4cf79f6 Reviewed-by: Doris Verria <doris.verria@qt.io> Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
This commit is contained in:
parent
30404b49d8
commit
1dfc74970c
@ -1126,10 +1126,10 @@
|
|||||||
e.g., when a hidden widget was resized. This flag is set or cleared by the
|
e.g., when a hidden widget was resized. This flag is set or cleared by the
|
||||||
Qt kernel.
|
Qt kernel.
|
||||||
|
|
||||||
\value WA_QuitOnClose Makes Qt quit the application when the last widget
|
\value WA_QuitOnClose Indicates that the widget should be taken into account
|
||||||
with the attribute set has accepted closeEvent(). This behavior can be
|
when deciding whether to quit the application when the last window is closed.
|
||||||
modified with the QGuiApplication::quitOnLastWindowClosed property. By default
|
This behavior can be modified with the QGuiApplication::quitOnLastWindowClosed
|
||||||
this attribute is set for all widgets of type Qt::Window.
|
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
|
\value WA_Resized Indicates that the widget has an explicit size. This flag
|
||||||
is set or cleared by QWidget::resize() and QWidget::setGeometry().
|
is set or cleared by QWidget::resize() and QWidget::setGeometry().
|
||||||
|
@ -1930,7 +1930,7 @@ bool QGuiApplication::notify(QObject *object, QEvent *event)
|
|||||||
bool accepted = QCoreApplication::notify(object, event);
|
bool accepted = QCoreApplication::notify(object, event);
|
||||||
|
|
||||||
if (event->type() == QEvent::Close && object->isWindowType() && accepted)
|
if (event->type() == QEvent::Close && object->isWindowType() && accepted)
|
||||||
d->maybeQuitOnLastWindowClosed(static_cast<QWindow*>(object));
|
d->maybeLastWindowClosed(static_cast<QWindow*>(object));
|
||||||
|
|
||||||
return accepted;
|
return accepted;
|
||||||
}
|
}
|
||||||
@ -3530,7 +3530,8 @@ void QGuiApplicationPrivate::notifyWindowIconChanged()
|
|||||||
The default is \c true.
|
The default is \c true.
|
||||||
|
|
||||||
If this property is \c true, the applications quits when the last visible
|
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()
|
\sa quit(), QWindow::close()
|
||||||
*/
|
*/
|
||||||
@ -3545,19 +3546,17 @@ bool QGuiApplication::quitOnLastWindowClosed()
|
|||||||
return QCoreApplication::isQuitLockEnabled();
|
return QCoreApplication::isQuitLockEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
void QGuiApplicationPrivate::maybeQuitOnLastWindowClosed(QWindow *closedWindow)
|
void QGuiApplicationPrivate::maybeLastWindowClosed(QWindow *closedWindow)
|
||||||
{
|
{
|
||||||
Q_ASSERT(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;
|
return;
|
||||||
|
|
||||||
// Check if there are any remaining windows that should prevent us from quitting
|
if (!lastWindowClosed())
|
||||||
for (auto *topLevelWindow : QGuiApplication::topLevelWindows()) {
|
return;
|
||||||
auto *windowPrivate = qt_window_private(topLevelWindow);
|
|
||||||
if (windowPrivate->shouldCancelQuitOnClose())
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (in_exec)
|
if (in_exec)
|
||||||
emit q_func()->lastWindowClosed();
|
emit q_func()->lastWindowClosed();
|
||||||
@ -3570,32 +3569,31 @@ void QGuiApplicationPrivate::maybeQuitOnLastWindowClosed(QWindow *closedWindow)
|
|||||||
\fn void QGuiApplication::lastWindowClosed()
|
\fn void QGuiApplication::lastWindowClosed()
|
||||||
|
|
||||||
This signal is emitted from exec() when the last visible
|
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
|
By default, QGuiApplication quits after this signal is emitted. This feature
|
||||||
can be turned off by setting \l quitOnLastWindowClosed to \c false.
|
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()
|
bool QGuiApplicationPrivate::shouldQuit()
|
||||||
{
|
{
|
||||||
const QWindowList processedWindows;
|
return lastWindowClosed();
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void QGuiApplicationPrivate::quit()
|
void QGuiApplicationPrivate::quit()
|
||||||
|
@ -108,8 +108,8 @@ public:
|
|||||||
virtual bool shouldQuit() override;
|
virtual bool shouldQuit() override;
|
||||||
void quit() override;
|
void quit() override;
|
||||||
|
|
||||||
bool shouldQuitInternal(const QWindowList &processedWindows);
|
void maybeLastWindowClosed(QWindow *closedWindow);
|
||||||
void maybeQuitOnLastWindowClosed(QWindow *closedWindow);
|
bool lastWindowClosed() const;
|
||||||
|
|
||||||
static void captureGlobalModifierState(QEvent *e);
|
static void captureGlobalModifierState(QEvent *e);
|
||||||
static Qt::KeyboardModifiers modifier_buttons;
|
static Qt::KeyboardModifiers modifier_buttons;
|
||||||
|
@ -2269,16 +2269,25 @@ bool QWindow::close()
|
|||||||
return d->platformWindow->close();
|
return d->platformWindow->close();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QWindowPrivate::shouldTriggerQuitOnClose() const
|
bool QWindowPrivate::participatesInLastWindowClosed() const
|
||||||
{
|
{
|
||||||
Q_Q(const QWindow);
|
Q_Q(const QWindow);
|
||||||
return q->isTopLevel();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QWindowPrivate::shouldCancelQuitOnClose() const
|
if (!q->isTopLevel())
|
||||||
{
|
return false;
|
||||||
Q_Q(const QWindow);
|
|
||||||
return q->isVisible() && !q->transientParent() && q->type() != Qt::ToolTip;
|
// 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -118,8 +118,7 @@ public:
|
|||||||
|
|
||||||
virtual void processSafeAreaMarginsChanged() {}
|
virtual void processSafeAreaMarginsChanged() {}
|
||||||
|
|
||||||
virtual bool shouldTriggerQuitOnClose() const;
|
virtual bool participatesInLastWindowClosed() const;
|
||||||
virtual bool shouldCancelQuitOnClose() const;
|
|
||||||
|
|
||||||
bool isPopup() const { return (windowFlags & Qt::WindowType_Mask) == Qt::Popup; }
|
bool isPopup() const { return (windowFlags & Qt::WindowType_Mask) == Qt::Popup; }
|
||||||
void setAutomaticPositionAndResizeEnabled(bool a)
|
void setAutomaticPositionAndResizeEnabled(bool a)
|
||||||
|
@ -2614,24 +2614,6 @@ int QApplication::exec()
|
|||||||
return QGuiApplication::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
|
/*! \reimp
|
||||||
*/
|
*/
|
||||||
bool QApplication::notify(QObject *receiver, QEvent *e)
|
bool QApplication::notify(QObject *receiver, QEvent *e)
|
||||||
@ -3301,7 +3283,7 @@ bool QApplication::notify(QObject *receiver, QEvent *e)
|
|||||||
res = d->notify_helper(receiver, e);
|
res = d->notify_helper(receiver, e);
|
||||||
// We don't call QGuiApplication::notify here, so we need to duplicate the logic
|
// We don't call QGuiApplication::notify here, so we need to duplicate the logic
|
||||||
if (res && e->type() == QEvent::Close)
|
if (res && e->type() == QEvent::Close)
|
||||||
d->maybeQuitOnLastWindowClosed(static_cast<QWindow *>(receiver));
|
d->maybeLastWindowClosed(static_cast<QWindow *>(receiver));
|
||||||
} else {
|
} else {
|
||||||
res = d->notify_helper(receiver, e);
|
res = d->notify_helper(receiver, e);
|
||||||
}
|
}
|
||||||
|
@ -106,8 +106,6 @@ public:
|
|||||||
virtual void notifyLayoutDirectionChange() override;
|
virtual void notifyLayoutDirectionChange() override;
|
||||||
virtual void notifyActiveWindowChange(QWindow *) override;
|
virtual void notifyActiveWindowChange(QWindow *) override;
|
||||||
|
|
||||||
virtual bool shouldQuit() override;
|
|
||||||
|
|
||||||
static bool autoSipEnabled;
|
static bool autoSipEnabled;
|
||||||
static QString desktopStyleKey();
|
static QString desktopStyleKey();
|
||||||
|
|
||||||
|
@ -119,8 +119,7 @@ public:
|
|||||||
QWidgetPrivate::get(widget)->updateContentsRect();
|
QWidgetPrivate::get(widget)->updateContentsRect();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool shouldTriggerQuitOnClose() const override;
|
bool participatesInLastWindowClosed() const override;
|
||||||
bool shouldCancelQuitOnClose() const override;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
QRectF QWidgetWindowPrivate::closestAcceptableGeometry(const QRectF &rect) const
|
QRectF QWidgetWindowPrivate::closestAcceptableGeometry(const QRectF &rect) const
|
||||||
@ -847,35 +846,18 @@ void QWidgetWindow::handleCloseEvent(QCloseEvent *event)
|
|||||||
event->setAccepted(accepted);
|
event->setAccepted(accepted);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QWidgetWindowPrivate::shouldTriggerQuitOnClose() const
|
bool QWidgetWindowPrivate::participatesInLastWindowClosed() const
|
||||||
{
|
{
|
||||||
Q_Q(const QWidgetWindow);
|
Q_Q(const QWidgetWindow);
|
||||||
QWidget *widget = q->widget();
|
|
||||||
|
|
||||||
// Closing a window without WA_QuitOnClose should stop us from even
|
// For historical reasons WA_QuitOnClose has been closely tied
|
||||||
// looking at the other windows. Otherwise we might find that all the
|
// to the lastWindowClosed signal, since the default behavior
|
||||||
// other windows miss WA_QuitOnClose as well, and end up quitting,
|
// is to quit the application after emitting lastWindowClosed.
|
||||||
// but that's not what the user intended.
|
// ### Qt 7: Rename this attribute, or decouple behavior.
|
||||||
if (!widget->testAttribute(Qt::WA_QuitOnClose))
|
if (!q->widget()->testAttribute(Qt::WA_QuitOnClose))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Qt::Tool windows do not have WA_QuitOnClose set by default, which
|
return QWindowPrivate::participatesInLastWindowClosed();
|
||||||
// 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)
|
#if QT_CONFIG(wheelevent)
|
||||||
|
Loading…
Reference in New Issue
Block a user