summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephen Kelly <stephen.kelly@kdab.com>2012-02-11 01:33:55 +0100
committerQt by Nokia <qt-info@nokia.com>2012-02-16 01:56:27 +0100
commit66603985f2de74ac5f3bd5d259f0e65f710f62d7 (patch)
tree65049a5d31f1e8a978f60f5fc12d1dd26be73284
parent90feedb6429df225b81bc279093ae1ca1278b391 (diff)
Fix ref counted window close handling.
Instead of refcounting QWindow visibility, we ask the Application subclass whether quitting is appropriate. Task-Id: QTBUG-24120 Change-Id: Idd19cc1a3e5742fddded89c7638aaaa5e47c568d Reviewed-by: Bradley T. Hughes <bradley.hughes@nokia.com> Reviewed-by: Robin Burchell <robin+qt@viroteck.net>
-rw-r--r--src/corelib/kernel/qcoreapplication.cpp10
-rw-r--r--src/corelib/kernel/qcoreapplication_p.h4
-rw-r--r--src/gui/kernel/qguiapplication.cpp11
-rw-r--r--src/gui/kernel/qguiapplication_p.h2
-rw-r--r--src/gui/kernel/qwindow.cpp24
-rw-r--r--src/widgets/kernel/qapplication.cpp15
-rw-r--r--src/widgets/kernel/qapplication_p.h2
-rw-r--r--src/widgets/kernel/qwidget.cpp20
-rw-r--r--tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp513
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