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>
This commit is contained in:
parent
1b0cb84212
commit
28b14b966f
@ -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()
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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; }
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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)) {
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user