diff options
-rw-r--r-- | src/corelib/kernel/qcoreapplication.cpp | 10 | ||||
-rw-r--r-- | src/corelib/kernel/qcoreapplication_p.h | 4 | ||||
-rw-r--r-- | src/gui/kernel/qguiapplication.cpp | 11 | ||||
-rw-r--r-- | src/gui/kernel/qguiapplication_p.h | 2 | ||||
-rw-r--r-- | src/gui/kernel/qwindow.cpp | 24 | ||||
-rw-r--r-- | src/widgets/kernel/qapplication.cpp | 15 | ||||
-rw-r--r-- | src/widgets/kernel/qapplication_p.h | 2 | ||||
-rw-r--r-- | src/widgets/kernel/qwidget.cpp | 20 | ||||
-rw-r--r-- | tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp | 513 |
9 files changed, 79 insertions, 522 deletions
diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp index cf3ae1b7c5..80e42336d6 100644 --- a/src/corelib/kernel/qcoreapplication.cpp +++ b/src/corelib/kernel/qcoreapplication.cpp @@ -1506,8 +1506,14 @@ void QCoreApplicationPrivate::ref() void QCoreApplicationPrivate::deref() { - if (!quitLockRef.deref() && in_exec && quitLockRefEnabled) - QCoreApplication::postEvent(qApp, new QEvent(QEvent::Quit)); + if (!quitLockRef.deref()) + maybeQuit(); +} + +void QCoreApplicationPrivate::maybeQuit() +{ + if (quitLockRef.load() == 0 && in_exec && quitLockRefEnabled && shouldQuit()) + QCoreApplication::postEvent(QCoreApplication::instance(), new QEvent(QEvent::Quit)); } /*! diff --git a/src/corelib/kernel/qcoreapplication_p.h b/src/corelib/kernel/qcoreapplication_p.h index 861a046f16..112b313129 100644 --- a/src/corelib/kernel/qcoreapplication_p.h +++ b/src/corelib/kernel/qcoreapplication_p.h @@ -92,6 +92,10 @@ public: QAtomicInt quitLockRef; void ref(); void deref(); + virtual bool shouldQuit() { + return true; + } + void maybeQuit(); static QThread *theMainThread; static QThread *mainThread(); diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp index 4d80aafea5..5f50fe343a 100644 --- a/src/gui/kernel/qguiapplication.cpp +++ b/src/gui/kernel/qguiapplication.cpp @@ -1477,6 +1477,17 @@ void QGuiApplicationPrivate::emitLastWindowClosed() } } +bool QGuiApplicationPrivate::shouldQuit() +{ + /* 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 (w->visible()) + return false; + } + return true; +} /*! \property QGuiApplication::layoutDirection diff --git a/src/gui/kernel/qguiapplication_p.h b/src/gui/kernel/qguiapplication_p.h index 7fafe0336d..f86d8264fa 100644 --- a/src/gui/kernel/qguiapplication_p.h +++ b/src/gui/kernel/qguiapplication_p.h @@ -74,6 +74,8 @@ public: virtual void notifyLayoutDirectionChange(); virtual void notifyActiveWindowChange(QWindow *previous); + virtual bool shouldQuit(); + static Qt::KeyboardModifiers modifier_buttons; static Qt::MouseButtons mouse_buttons; diff --git a/src/gui/kernel/qwindow.cpp b/src/gui/kernel/qwindow.cpp index 61ae2a2438..d15198c505 100644 --- a/src/gui/kernel/qwindow.cpp +++ b/src/gui/kernel/qwindow.cpp @@ -165,14 +165,6 @@ void QWindow::setVisible(bool visible) return; d->visible = visible; emit visibleChanged(visible); - if (QCoreApplication::instance() && !transientParent()) { - QCoreApplicationPrivate *applicationPrivate = static_cast<QCoreApplicationPrivate*>(QObjectPrivate::get(QCoreApplication::instance())); - if (visible) { - applicationPrivate->ref(); - } else { - applicationPrivate->deref(); - } - } if (!d->platformWindow) create(); @@ -514,15 +506,6 @@ void QWindow::setTransientParent(QWindow *parent) QWindow *previousParent = d->transientParent; d->transientParent = parent; - - if (QCoreApplication::instance() && d->visible) { - QCoreApplicationPrivate *applicationPrivate = static_cast<QCoreApplicationPrivate*>(QObjectPrivate::get(QCoreApplication::instance())); - if (parent && !previousParent) { - applicationPrivate->deref(); - } else if (!parent && previousParent) { - applicationPrivate->ref(); - } - } } QWindow *QWindow::transientParent() const @@ -1116,13 +1099,16 @@ void QWindowPrivate::maybeQuitOnLastWindowClosed() bool lastWindowClosed = true; for (int i = 0; i < list.size(); ++i) { QWindow *w = list.at(i); - if (!w->visible() || w->parent()) + if (!w->visible()) continue; lastWindowClosed = false; break; } - if (lastWindowClosed) + if (lastWindowClosed) { QGuiApplicationPrivate::emitLastWindowClosed(); + QCoreApplicationPrivate *applicationPrivate = static_cast<QCoreApplicationPrivate*>(QObjectPrivate::get(QCoreApplication::instance())); + applicationPrivate->maybeQuit(); + } } } diff --git a/src/widgets/kernel/qapplication.cpp b/src/widgets/kernel/qapplication.cpp index 4387d2a287..b04925d85d 100644 --- a/src/widgets/kernel/qapplication.cpp +++ b/src/widgets/kernel/qapplication.cpp @@ -67,6 +67,7 @@ #include "private/qstylesheetstyle_p.h" #include "private/qstyle_p.h" #include "qmessagebox.h" +#include "qwidgetwindow_qpa_p.h" #include <QtWidgets/qgraphicsproxywidget.h> #include <QtGui/qstylehints.h> #include <QtGui/qinputmethod.h> @@ -3289,6 +3290,20 @@ 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(); + for (int i = 0; i < list.size(); ++i) { + QWidget *w = list.at(i); + if (w->isVisible() && !w->parentWidget() && w->testAttribute(Qt::WA_QuitOnClose)) + return false; + } + return true; +} + /*! \reimp */ bool QApplication::notify(QObject *receiver, QEvent *e) diff --git a/src/widgets/kernel/qapplication_p.h b/src/widgets/kernel/qapplication_p.h index 790176afe3..e728868182 100644 --- a/src/widgets/kernel/qapplication_p.h +++ b/src/widgets/kernel/qapplication_p.h @@ -178,6 +178,8 @@ public: virtual void notifyLayoutDirectionChange(); virtual void notifyActiveWindowChange(QWindow *); + virtual bool shouldQuit(); + #if defined(Q_WS_X11) #ifndef QT_NO_SETTINGS static bool x11_apply_settings(); diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index f98f7fbe66..f095e475af 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -7410,8 +7410,24 @@ bool QWidgetPrivate::close_helper(CloseMode mode) // 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 && q->windowHandle()) { - static_cast<QWindowPrivate*>(QObjectPrivate::get(q->windowHandle()))->maybeQuitOnLastWindowClosed(); + 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(); + } } diff --git a/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp b/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp index c600956912..fcb6b93e99 100644 --- a/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp +++ b/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp @@ -137,16 +137,7 @@ private slots: void touchEventPropagation(); void qtbug_12673(); - - void testQuitLockRef(); - void testQuitLock1(); - void testQuitLock2(); - void testQuitLock3(); - void testQuitLock4(); - void testQuitLock5(); - void testQuitLock6(); - void testQuitLock7(); - void testQuitLock8(); + void noQuitOnHide(); void globalStaticObjectDestruction(); // run this last @@ -2060,506 +2051,30 @@ void tst_QApplication::qtbug_12673() QCOMPARE(testProcess.exitStatus(), QProcess::NormalExit); } -class JobObject : public QObject -{ - Q_OBJECT -public: - JobObject(int milliseconds, QObject *parent = 0) - : QObject(parent) - { - QTimer::singleShot(milliseconds, this, SLOT(timeout())); - } - - JobObject(QObject *parent = 0) - : QObject(parent) - { - QTimer::singleShot(1000, this, SLOT(timeout())); - } - -private slots: - void timeout() - { - emit done(); - deleteLater(); - } - -signals: - void done(); - -private: - QEventLoopLocker locker; -}; - -class QuitLockRefTester : public QObject -{ - Q_OBJECT -public: - QuitLockRefTester(QObject *parent = 0) - : QObject(parent) - { - QTimer::singleShot(0, this, SLOT(doTest())); - } - -private slots: - void doTest() - { - QApplicationPrivate *privateClass = static_cast<QApplicationPrivate*>(QObjectPrivate::get(qApp)); - - { - QDialog *win1 = new QDialog; - - // Test with a lock active so that the refcount doesn't drop to zero during these tests, causing a quit. - // (until we exit the scope) - QEventLoopLocker locker; - - QCOMPARE(privateClass->quitLockRef.load(), 1); - - win1->show(); - - QCOMPARE(privateClass->quitLockRef.load(), 2); - - QDialog *win2 = new QDialog; - - win2->show(); - - QCOMPARE(privateClass->quitLockRef.load(), 3); - - delete win1; - - QCOMPARE(privateClass->quitLockRef.load(), 2); - - delete win2; - - QCOMPARE(privateClass->quitLockRef.load(), 1); - - win1 = new QDialog; - - win1->show(); - - QCOMPARE(privateClass->quitLockRef.load(), 2); - - JobObject *job1 = new JobObject(this); - - QCOMPARE(privateClass->quitLockRef.load(), 3); - - delete win1; - - QCOMPARE(privateClass->quitLockRef.load(), 2); - - delete job1; - - QCOMPARE(privateClass->quitLockRef.load(), 1); - - QWidget *w1 = new QWidget; - - w1->show(); - - QCOMPARE(privateClass->quitLockRef.load(), 2); - - QWidget *w2 = new QMainWindow; - - w2->show(); - - QCOMPARE(privateClass->quitLockRef.load(), 3); - - QWidget *w3 = new QWidget(0, Qt::Dialog); - - w3->show(); - - QCOMPARE(privateClass->quitLockRef.load(), 4); - - delete w3; - - QCOMPARE(privateClass->quitLockRef.load(), 3); - - delete w2; - - QCOMPARE(privateClass->quitLockRef.load(), 2); - - QWidget *subWidget1 = new QWidget(w1, Qt::Window); - - // Even though We create a new widget and show it, - // the ref count does not go up because it is a child of - // w1, which is the top-level, and what we are actually - // refcounting. - subWidget1->show(); - - QCOMPARE(privateClass->quitLockRef.load(), 2); - - // When we use setParent(0) and re-show, the - // ref count does increase: - QCOMPARE(subWidget1->isVisible(), true); - subWidget1->setParent(0); - QCOMPARE(subWidget1->isVisible(), false); - - QCOMPARE(privateClass->quitLockRef.load(), 2); - - subWidget1->show(); - QCOMPARE(subWidget1->isVisible(), true); - QCOMPARE(privateClass->quitLockRef.load(), 3); - - subWidget1->setParent(w1); - QCOMPARE(privateClass->quitLockRef.load(), 2); - - QWidget *subWidget2 = new QWidget(w1); - - QCOMPARE(privateClass->quitLockRef.load(), 2); - - subWidget2->show(); - - QCOMPARE(privateClass->quitLockRef.load(), 2); - - delete subWidget2; - - QCOMPARE(privateClass->quitLockRef.load(), 2); - - QWidget *subWidget3 = new QWidget(w1); - - QCOMPARE(privateClass->quitLockRef.load(), 2); - - subWidget3->show(); - - QCOMPARE(privateClass->quitLockRef.load(), 2); - - subWidget3->hide(); - - QCOMPARE(privateClass->quitLockRef.load(), 2); - - delete subWidget3; - - QCOMPARE(privateClass->quitLockRef.load(), 2); - - QWidget *subWidget4 = new QWidget(subWidget1); - QWidget *subWidget5 = new QWidget(subWidget1); - - QCOMPARE(privateClass->quitLockRef.load(), 2); - - QWidget *subWidget6 = new QWidget(subWidget4, Qt::Window); - - subWidget6->show(); - - QCOMPARE(privateClass->quitLockRef.load(), 2); - - delete w1; - - QCOMPARE(privateClass->quitLockRef.load(), 1); - - w1 = new QWidget; - w2 = new QWidget; - w3 = new QWidget; - - QHBoxLayout *layout = new QHBoxLayout(w1); - - layout->addWidget(w2); - layout->addWidget(w3); - - QCOMPARE(privateClass->quitLockRef.load(), 1); - - w1->show(); - - QCOMPARE(privateClass->quitLockRef.load(), 2); - - w1->hide(); - QCOMPARE(privateClass->quitLockRef.load(), 1); - - delete w1; - - } - QCOMPARE(privateClass->quitLockRef.load(), 0); - } -}; - -void tst_QApplication::testQuitLockRef() -{ - int argc = 1; - char *argv[] = { "tst_qapplication" }; - QApplication app(argc, argv); - - QuitLockRefTester tester; - - app.exec(); -} - -void tst_QApplication::testQuitLock1() -{ - int argc = 1; - char *argv[] = { "tst_qcoreapplication" }; - QApplication app(argc, argv); - - QWidget *w = new QWidget; - - w->show(); - - QMetaObject::invokeMethod(w, "close", Qt::QueuedConnection); - - app.exec(); - - // No hang = pass. -} - -void tst_QApplication::testQuitLock2() -{ - int argc = 1; - char *argv[] = { "tst_qcoreapplication" }; - QApplication app(argc, argv); - - QWidget *w1 = new QWidget; - - w1->show(); - - QWidget *w2 = new QWidget; - - w2->show(); - - QMetaObject::invokeMethod(w1, "deleteLater", Qt::QueuedConnection); - QMetaObject::invokeMethod(w2, "hide", Qt::QueuedConnection); - - app.exec(); - - // No hang = pass. -} - -class Result : public QObject +class NoQuitOnHideWidget : public QWidget { Q_OBJECT public: - Result(QObject *parent = 0) - : QObject(parent), m_passes(false) + NoQuitOnHideWidget(QWidget *parent = 0) + : QWidget(parent) { - - } - - bool result() const - { - return m_passes; - } - -public slots: - - void setPasses() - { - setResult(true); - } - - void setFails() - { - setResult(false); - } - - void setResult(bool result) - { - m_passes = result; - } - -private: - bool m_passes; -}; - -void tst_QApplication::testQuitLock3() -{ - int argc = 1; - char *argv[] = { "tst_qcoreapplication" }; - QApplication app(argc, argv); - - Result *result = new Result(&app); - - JobObject *job = new JobObject(&app); - - QObject::connect(job, SIGNAL(done()), result, SLOT(setPasses())); - - app.exec(); - - QVERIFY(result->result()); -} - -void tst_QApplication::testQuitLock4() -{ - int argc = 1; - char *argv[] = { "tst_qcoreapplication" }; - QApplication app(argc, argv); - - QWidget *w = new QWidget; - - w->show(); - - Result *result = new Result(&app); - JobObject *job = new JobObject(1000, &app); - - QTimer::singleShot(500, w, SLOT(deleteLater())); - - QObject::connect(w, SIGNAL(destroyed()), result, SLOT(setFails())); - QObject::connect(job, SIGNAL(done()), result, SLOT(setPasses())); - - app.exec(); - - QVERIFY(result->result()); -} - -class JobBeforeWindowRunner : public QObject -{ - Q_OBJECT -public: - JobBeforeWindowRunner(QObject *parent = 0) - : QObject(parent), m_result(new Result(this)) - { - - } - - void start() - { - JobObject *job = new JobObject(this); - connect(job, SIGNAL(done()), m_result, SLOT(setFails())); - connect(job, SIGNAL(destroyed()), SLOT(showWindowDelayed()), Qt::QueuedConnection); + QTimer::singleShot(0, this, SLOT(hide())); + QTimer::singleShot(500, this, SLOT(exitApp())); } - bool result() const { return m_result->result(); } - private slots: - void showWindowDelayed() - { - qApp->setQuitLockEnabled(true); - QTimer::singleShot(500, this, SLOT(showWindow())); - } - - void showWindow() - { - QWidget *w = new QWidget; - w->show(); - w->deleteLater(); - connect(w, SIGNAL(destroyed()), m_result, SLOT(setPasses())); - } - -private: - Result * const m_result; -}; - -void tst_QApplication::testQuitLock5() -{ - int argc = 1; - char *argv[] = { "tst_qcoreapplication" }; - QApplication app(argc, argv); - app.setQuitLockEnabled(false); - // Run a job before showing a window, and only enable the refcounting - // after doing so. - // Although the job brings the refcount to zero, the app does not exit - // until setQuitLockEnabled is called and the feature re-enabled. - - JobBeforeWindowRunner *eventRunner = new JobBeforeWindowRunner(&app); - - eventRunner->start(); - - app.exec(); - - QVERIFY(eventRunner->result()); -} - -class JobDuringWindowRunner : public QObject -{ - Q_OBJECT -public: - JobDuringWindowRunner(QObject *parent = 0) - : QObject(parent), m_result(new Result(this)) - { - - } - - void start() - { - JobObject *job = new JobObject(this); - - QWidget *w = new QWidget; - w->show(); - w->deleteLater(); - - QObject::connect(w, SIGNAL(destroyed()), m_result, SLOT(setFails())); - QObject::connect(job, SIGNAL(done()), m_result, SLOT(setPasses())); - } - - bool result() const { return m_result->result(); } - -private: - Result * const m_result; -}; - -void tst_QApplication::testQuitLock6() -{ - int argc = 1; - char *argv[] = { "tst_qcoreapplication" }; - QApplication app(argc, argv); - - // A job runs, and while it is running, a window is shown and closed, - // then the job ends, which causes the quit. - - JobDuringWindowRunner *eventRunner = new JobDuringWindowRunner(&app); - - eventRunner->start(); - - app.exec(); - - QVERIFY(eventRunner->result()); -} -class JobWindowJobWindowRunner : public QObject -{ - Q_OBJECT -public: - JobWindowJobWindowRunner(QObject *parent = 0) - : QObject(parent), m_result(new Result(this)) - { - - } - - void start() - { - JobObject *job = new JobObject(500, this); - - QWidget *w = new QWidget; - w->show(); - QTimer::singleShot(1000, w, SLOT(deleteLater())); - - QObject::connect(w, SIGNAL(destroyed()), m_result, SLOT(setPasses())); - QObject::connect(job, SIGNAL(done()), m_result, SLOT(setFails())); + void exitApp() { + qApp->exit(1); } - - bool result() const { return m_result->result(); } -private: - Result * const m_result; }; -void tst_QApplication::testQuitLock7() +void tst_QApplication::noQuitOnHide() { - int argc = 1; - char *argv[] = { "tst_qcoreapplication" }; - QApplication app(argc, argv); - - // A job runs, and while it is running, a window is shown - // then the job ends, then the window is closed, which causes the quit. - - JobWindowJobWindowRunner *eventRunner = new JobWindowJobWindowRunner(&app); - - eventRunner->start(); - - app.exec(); - - QVERIFY(eventRunner->result()); -} - -void tst_QApplication::testQuitLock8() -{ - int argc = 1; - char *argv[] = { "tst_qcoreapplication" }; - QApplication app(argc, argv); - - QMainWindow *mw1 = new QMainWindow; - mw1->show(); - QMainWindow *mw2 = new QMainWindow; - mw2->show(); - - QMetaObject::invokeMethod(mw1, "close", Qt::QueuedConnection); - QMetaObject::invokeMethod(mw2, "close", Qt::QueuedConnection); - - app.exec(); - - // No hang = pass + int argc = 0; + QApplication app(argc, 0); + QWidget *window1 = new NoQuitOnHideWidget(false); + window1->show(); + QCOMPARE(app.exec(), 1); } class ShowCloseShowWidget : public QWidget |