Deliver Quit event when calling QCoreApplication::quit()

Instead of QCoreApplication::quit() directly calling exit(0), which would
leave QGuiApplication and client code out of the loop, we now send the
Quit event, and let it pass through event delivery, before finally ending
up in QCoreApplication::event(), where we call exit(0).

This has the advantage that QGuiApplication can ensure all windows are
closed before quitting, and if any of those windows ignore the close
event the quit will be aborted. This aligns the behavior of synthetic
quits via QCoreApplication::quit() with spontaneous quits from the
platform via QGuiApplicationPrivate::processApplicationTermination.

Clients who wish to exit the application without any event delivery or
potential user interaction can call the lower level exit() function
directly.

[ChangeLog][QtGui] Application termination via qApp->quit() will now
deliver Quit events to the application, which in turn will result in
application windows being closed as part of the application quit,
with an option to cancel the application quit by ignoring the close
event. Clients who explicitly want to exit the application without
any user interaction should call QCoreApplication::exit() explicitly.

Task-number: QTBUG-45262
Task-number: QTBUG-33235
Task-number: QTBUG-72013
Task-number: QTBUG-59782
Change-Id: Id4b3907e329b9ecfd936fe9a5f8a70cb66b76bb7
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
This commit is contained in:
Tor Arne Vestbø 2019-10-15 14:18:40 +02:00
parent 0ae5b8af9c
commit 89f7a2759c
11 changed files with 103 additions and 25 deletions

View File

@ -83,7 +83,7 @@ MainWindow::MainWindow(QWidget *parent)
fileMenu->addAction(tr("&Detect File Type..."), this, &MainWindow::detectFile);
detectFileAction->setShortcuts(QKeySequence::Open);
QAction *exitAction = fileMenu->addAction(tr("E&xit"), qApp, &QApplication::closeAllWindows);
QAction *exitAction = fileMenu->addAction(tr("E&xit"), qApp, &QApplication::quit);
exitAction->setShortcuts(QKeySequence::Quit);
QMenu *findMenu = menuBar()->addMenu(tr("&Edit"));

View File

@ -369,7 +369,7 @@ void MainWindow::createActions()
//! [0]
const QIcon exitIcon = QIcon::fromTheme("application-exit");
QAction *exitAct = fileMenu->addAction(exitIcon, tr("E&xit"), qApp, &QApplication::closeAllWindows);
QAction *exitAct = fileMenu->addAction(exitIcon, tr("E&xit"), qApp, &QApplication::quit);
exitAct->setShortcuts(QKeySequence::Quit);
exitAct->setStatusTip(tr("Exit the application"));
fileMenu->addAction(exitAct);

View File

@ -227,7 +227,7 @@ void MainWindow::createActions()
closeAct->setStatusTip(tr("Close this window"));
const QIcon exitIcon = QIcon::fromTheme("application-exit");
QAction *exitAct = fileMenu->addAction(exitIcon, tr("E&xit"), qApp, &QApplication::closeAllWindows);
QAction *exitAct = fileMenu->addAction(exitIcon, tr("E&xit"), qApp, &QApplication::quit);
exitAct->setShortcuts(QKeySequence::Quit);
exitAct->setStatusTip(tr("Exit the application"));

View File

@ -1920,7 +1920,7 @@ void QCoreApplicationPrivate::removePostedEvent(QEvent * event)
bool QCoreApplication::event(QEvent *e)
{
if (e->type() == QEvent::Quit) {
quit();
exit(0);
return true;
}
return QObject::event(e);
@ -1944,12 +1944,18 @@ void QCoreApplicationPrivate::maybeQuit()
}
/*!
Tells the application to exit with return code 0 (success).
Equivalent to calling QCoreApplication::exit(0).
Asks the application to quit.
It's common to connect the QGuiApplication::lastWindowClosed() signal
to quit(), and you also often connect e.g. QAbstractButton::clicked() or
signals in QAction, QMenu, or QMenuBar to it.
The request may be ignored if the application prevents the quit,
for example if one of its windows can't be closed. The application
can affect this by handling the QEvent::Quit event on the application
level, or QEvent::Close events for the individual windows.
If the quit is not interrupted the application will exit with return
code 0 (success).
To exit the application without a chance of being interrupted, call
exit() directly.
It's good practice to always connect signals to this slot using a
\l{Qt::}{QueuedConnection}. If a signal connected (non-queued) to this slot
@ -1962,12 +1968,19 @@ void QCoreApplicationPrivate::maybeQuit()
\snippet code/src_corelib_kernel_qcoreapplication.cpp 1
\sa exit(), aboutToQuit(), QGuiApplication::lastWindowClosed()
\sa exit(), aboutToQuit()
*/
void QCoreApplication::quit()
{
exit(0);
if (!self)
return;
if (QThread::currentThread() == self->d_func()->mainThread()) {
QEvent quitEvent(QEvent::Quit);
QCoreApplication::sendEvent(self, &quitEvent);
} else {
QCoreApplication::postEvent(self, new QEvent(QEvent::Quit));
}
}
/*!

View File

@ -119,7 +119,7 @@ void MainWindow::setupMenus()
QAction *exitAct = new QAction(tr("E&xit"), this);
exitAct->setShortcut(tr("Ctrl+Q"));
exitAct->setStatusTip(tr("Exit the application"));
connect(exitAct, &QAction::triggered, qApp, &QApplication::closeAllWindows);
connect(exitAct, &QAction::triggered, qApp, &QApplication::quit);
QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
fileMenu->addAction(exitAct);

View File

@ -1605,19 +1605,17 @@ retry:
Closes all top-level windows.
This function is particularly useful for applications with many top-level
windows. It could, for example, be connected to a \uicontrol{Exit} entry in the
\uicontrol{File} menu:
\snippet mainwindows/mdi/mainwindow.cpp 0
windows.
The windows are closed in random order, until one window does not accept
the close event. The application quits when the last window was
successfully closed; this can be turned off by setting
\l quitOnLastWindowClosed to false.
the close event. The application quits when the last window was successfully
closed, unless \l quitOnLastWindowClosed is set to false. To trigger application
termination from e.g. a menu, use QCoreApplication::quit() instead of this
function.
\sa quitOnLastWindowClosed, lastWindowClosed(), QWidget::close(),
QWidget::closeEvent(), lastWindowClosed(), QCoreApplication::quit(), topLevelWidgets(),
QWidget::isWindow()
QWidget::closeEvent(), lastWindowClosed(), QCoreApplication::quit(),
topLevelWidgets(), QWidget::isWindow()
*/
void QApplication::closeAllWindows()
{

View File

@ -617,6 +617,72 @@ void tst_QCoreApplication::processEventsAlwaysSendsPostedEvents()
} while (t.elapsed() < 1000);
}
class QuitBlocker : public QObject
{
Q_OBJECT
public:
bool eventFilter(QObject *, QEvent *event)
{
if (event->type() == QEvent::Quit) {
event->ignore();
return true;
}
return false;
}
};
void tst_QCoreApplication::quit()
{
TestApplication::quit(); // Should not do anything
{
int argc = 1;
char *argv[] = { const_cast<char*>(QTest::currentAppName()) };
TestApplication app(argc, argv);
EventSpy spy;
app.installEventFilter(&spy);
{
QTimer::singleShot(0, &app, SLOT(quit()));
app.exec();
QVERIFY(spy.recordedEvents.contains(QEvent::Quit));
}
spy.recordedEvents.clear();
{
QTimer::singleShot(0, qApp, SLOT(quit()));
app.exec();
QVERIFY(spy.recordedEvents.contains(QEvent::Quit));
}
spy.recordedEvents.clear();
{
QTimer::singleShot(0, [&]{ TestApplication::quit(); });
app.exec();
QVERIFY(spy.recordedEvents.contains(QEvent::Quit));
}
spy.recordedEvents.clear();
{
QuitBlocker quitBlocker;
app.installEventFilter(&quitBlocker);
QTimer::singleShot(0, [&]{ TestApplication::quit(); });
QTimer::singleShot(200, [&]{ TestApplication::exit(); });
app.exec();
QVERIFY(!spy.recordedEvents.contains(QEvent::Quit));
}
}
TestApplication::quit(); // Should not do anything
}
void tst_QCoreApplication::reexec()
{
int argc = 1;

View File

@ -49,6 +49,7 @@ private slots:
void applicationPid();
void globalPostedEventsCount();
void processEventsAlwaysSendsPostedEvents();
void quit();
void reexec();
void execAfterExit();
void eventLoopExecAfterExit();

View File

@ -675,7 +675,7 @@ void tst_QApplication::quitOnLastWindowClosed()
bool quitApplicationTriggered = false;
auto quitSlot = [&quitApplicationTriggered] () {
quitApplicationTriggered = true;
QCoreApplication::quit();
QCoreApplication::exit();
};
{

View File

@ -462,7 +462,7 @@ ControllerWidget::ControllerWidget(QWidget *parent)
QMenu *fileMenu = menuBar()->addMenu(tr("File"));
QAction *exitAction = fileMenu->addAction(tr("Exit"));
exitAction->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_Q));
connect(exitAction, SIGNAL(triggered()), qApp, SLOT(closeAllWindows()));
connect(exitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
QString title = QLatin1String("Child Window Geometry test, (Qt ");
title += QLatin1String(QT_VERSION_STR);

View File

@ -400,7 +400,7 @@ ControllerWidget::ControllerWidget(QWidget *parent)
QMenu *fileMenu = menuBar()->addMenu(tr("File"));
QAction *exitAction = fileMenu->addAction(tr("Exit"));
exitAction->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_Q));
connect(exitAction, SIGNAL(triggered()), qApp, SLOT(closeAllWindows()));
connect(exitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
QString title = QLatin1String("Geometry test, (Qt ");
title += QLatin1String(QT_VERSION_STR);