summaryrefslogtreecommitdiffstats
path: root/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp')
-rw-r--r--tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp1063
1 files changed, 819 insertions, 244 deletions
diff --git a/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp b/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp
index 5b4a5d30a5..2eb2d84504 100644
--- a/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp
+++ b/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp
@@ -1,35 +1,17 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+#define QT_STATICPLUGIN
+#include <QtWidgets/qstyleplugin.h>
#include <qdebug.h>
-#include <QtTest/QtTest>
+#include <QTest>
+#include <QTimer>
+#include <QLibraryInfo>
+#include <QSignalSpy>
+#include <QFileSystemWatcher>
+#include <QSharedMemory>
#include <QtCore/QAbstractEventDispatcher>
#include <QtCore/QFileInfo>
@@ -41,6 +23,7 @@
#include <QtGui/QFontDatabase>
#include <QtGui/QClipboard>
+#include <QtGui/QStyleHints>
#include <QtWidgets/QApplication>
#include <QtWidgets/QMessageBox>
@@ -50,11 +33,17 @@
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QLabel>
#include <QtWidgets/QMainWindow>
+#include <QtWidgets/QScrollArea>
+#include <QtWidgets/QScrollBar>
+#include <QtWidgets/QHeaderView>
#include <QtWidgets/private/qapplication_p.h>
#include <QtWidgets/QStyle>
+#include <QtWidgets/qproxystyle.h>
+#include <QtWidgets/QTextEdit>
#include <qpa/qwindowsysteminterface.h>
#include <qpa/qwindowsysteminterface_p.h>
+#include <private/qevent_p.h>
#include <private/qhighdpiscaling_p.h>
#include <algorithm>
@@ -84,6 +73,7 @@ private slots:
void setFont_data();
void setFont();
+ void setFontForClass();
void args_data();
void args();
@@ -93,7 +83,11 @@ private slots:
void quitOnLastWindowClosed();
void closeAllWindows();
void testDeleteLater();
- void testDeleteLaterProcessEvents();
+ void testDeleteLaterProcessEvents1();
+ void testDeleteLaterProcessEvents2();
+ void testDeleteLaterProcessEvents3();
+ void testDeleteLaterProcessEvents4();
+ void testDeleteLaterProcessEvents5();
#if QT_CONFIG(library)
void libraryPaths();
@@ -101,13 +95,17 @@ private slots:
void libraryPaths_qt_plugin_path_2();
#endif
+#ifdef QT_BUILD_INTERNAL
void sendPostedEvents();
+#endif // ifdef QT_BUILD_INTERNAL
void thread();
void desktopSettingsAware();
void setActiveWindow();
+ void activateDeactivateEvent();
+ void focusWidget();
void focusChanged();
void focusOut();
void focusMouseClick();
@@ -121,6 +119,8 @@ private slots:
void task109149();
void style();
+ void applicationPalettePolish();
+ void setColorScheme();
void allWidgets();
void topLevelWidgets();
@@ -128,8 +128,11 @@ private slots:
void setAttribute();
void touchEventPropagation();
+ void wheelEventPropagation_data();
+ void wheelEventPropagation();
void qtbug_12673();
+ void qtbug_103611();
void noQuitOnHide();
void globalStaticObjectDestruction(); // run this last
@@ -165,6 +168,21 @@ void tst_QApplication::sendEventsOnProcessEvents()
QCoreApplication::postEvent(&app, new QEvent(QEvent::Type(QEvent::User + 1)));
QCoreApplication::processEvents();
+
+#ifdef Q_OS_LINUX
+ if ((QSysInfo::productType() == "rhel" && QSysInfo::productVersion().startsWith(u'9'))
+ || (QSysInfo::productType() == "ubuntu" && QSysInfo::productVersion().startsWith(u'2')))
+ {
+ QFile f("/proc/self/maps");
+ QVERIFY(f.open(QIODevice::ReadOnly));
+
+ QByteArray libs = f.readAll();
+ if (libs.contains("libqgtk3.") || libs.contains("libqgtk3TestInfix.")) {
+ QEXPECT_FAIL("", "Fails if qgtk3 (Glib) is loaded, see QTBUG-87137", Abort);
+ }
+ }
+#endif
+
QVERIFY(spy.recordedEvents.contains(QEvent::User + 1));
}
@@ -202,12 +220,63 @@ void tst_QApplication::staticSetup()
QPalette pal;
QApplication::setPalette(pal);
-
- /*QFont font;
- QApplication::setFont(font);*/
+ QFont font;
+ QApplication::setFont(font);
int argc = 0;
QApplication app(argc, nullptr);
+
+ class EventWatcher : public QObject
+ {
+ public:
+ int palette_changed = 0;
+ int font_changed = 0;
+
+ EventWatcher()
+ {
+ qApp->installEventFilter(this);
+#if QT_DEPRECATED_SINCE(6, 0)
+QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED
+ QObject::connect(qApp, &QApplication::paletteChanged, [&]{ ++palette_changed; });
+ QObject::connect(qApp, &QApplication::fontChanged, [&]{ ++font_changed; });
+QT_WARNING_POP
+#endif
+ }
+
+ protected:
+ bool eventFilter(QObject *, QEvent *event) override
+ {
+ switch (event->type()) {
+ case QEvent::ApplicationPaletteChange:
+ ++palette_changed;
+ break;
+ case QEvent::ApplicationFontChange:
+ ++font_changed;
+ break;
+ default:
+ break;
+ }
+
+ return false;
+ }
+ };
+
+ EventWatcher watcher;
+
+ QCOMPARE(watcher.palette_changed, 0);
+ QCOMPARE(watcher.font_changed, 0);
+ qApp->setPalette(QPalette(Qt::red));
+
+ font.setBold(!font.bold());
+ qApp->setFont(font);
+ QApplication::processEvents();
+#if QT_DEPRECATED_SINCE(6, 0)
+ QCOMPARE(watcher.palette_changed, 2);
+ QCOMPARE(watcher.font_changed, 2);
+#else
+ QCOMPARE(watcher.palette_changed, 1);
+ QCOMPARE(watcher.font_changed, 1);
+#endif
}
@@ -228,9 +297,6 @@ public:
void tst_QApplication::alert()
{
-#ifdef Q_OS_WINRT
- QSKIP("WinRT does not support more than 1 native widget at the same time");
-#endif
int argc = 0;
QApplication app(argc, nullptr);
QApplication::alert(nullptr, 0);
@@ -247,10 +313,8 @@ void tst_QApplication::alert()
QApplication::alert(&widget, -1);
QApplication::alert(&widget, 250);
widget2.activateWindow();
- QApplication::setActiveWindow(&widget2);
QApplication::alert(&widget, 0);
widget.activateWindow();
- QApplication::setActiveWindow(&widget);
QApplication::alert(&widget, 200);
}
@@ -313,13 +377,12 @@ void tst_QApplication::setFont_data()
int argc = 0;
QApplication app(argc, nullptr); // Needed for QFontDatabase
- QFontDatabase fdb;
- const QStringList &families = fdb.families();
+ const QStringList &families = QFontDatabase::families();
for (int i = 0, count = qMin(3, families.size()); i < count; ++i) {
const auto &family = families.at(i);
- const QStringList &styles = fdb.styles(family);
+ const QStringList &styles = QFontDatabase::styles(family);
if (!styles.isEmpty()) {
- QList<int> sizes = fdb.pointSizes(family, styles.constFirst());
+ QList<int> sizes = QFontDatabase::pointSizes(family, styles.constFirst());
if (sizes.isEmpty())
sizes = QFontDatabase::standardSizes();
if (!sizes.isEmpty()) {
@@ -368,6 +431,46 @@ void tst_QApplication::setFont()
QCOMPARE( app.font(), font );
}
+class tstHeaderView : public QHeaderView
+{
+ Q_OBJECT
+public:
+ explicit tstHeaderView(Qt::Orientation orientation, QWidget *parent = nullptr)
+ : QHeaderView(orientation, parent)
+ {}
+};
+class tstFrame : public QFrame { Q_OBJECT };
+class tstWidget : public QWidget { Q_OBJECT };
+
+void tst_QApplication::setFontForClass()
+{
+ // QTBUG-89910
+ // If a default font was not registered for the widget's class,
+ // it returns the default font of its nearest registered superclass.
+ int argc = 0;
+ QApplication app(argc, nullptr);
+
+ QFont font;
+ int pointSize = 10;
+ const QByteArrayList classNames{"QHeaderView", "QAbstractItemView", "QAbstractScrollView", "QFrame", "QWidget", "QObject"};
+ for (auto className : classNames) {
+ font.setPointSizeF(pointSize++);
+ app.setFont(font, className.constData());
+ }
+
+ tstHeaderView headView(Qt::Horizontal);
+ tstFrame frame;
+ tstWidget widget;
+
+ QFont headViewFont = QApplication::font(&headView);
+ QFont frameFont = QApplication::font(&frame);
+ QFont widgetFont = QApplication::font(&widget);
+
+ QCOMPARE(headViewFont.pointSize(), QApplication::font("QHeaderView").pointSize());
+ QCOMPARE(frameFont.pointSize(), QApplication::font("QFrame").pointSize());
+ QCOMPARE(widgetFont.pointSize(), QApplication::font("QWidget").pointSize());
+}
+
void tst_QApplication::args_data()
{
QTest::addColumn<int>("argc_in");
@@ -399,8 +502,8 @@ static char **QString2cstrings(const QString &args)
{
static QByteArrayList cache;
- const auto &list = args.splitRef(' ');
- auto argarray = new char*[list.count() + 1];
+ const auto &list = QStringView{ args }.split(' ');
+ auto argarray = new char*[list.size() + 1];
int i = 0;
for (; i < list.size(); ++i ) {
@@ -486,10 +589,10 @@ void tst_QApplication::lastWindowClosed()
QPointer<QDialog> dialog = new QDialog;
dialog->setWindowTitle(QLatin1String(QTest::currentTestFunction()) + QLatin1String("Dialog"));
QVERIFY(dialog->testAttribute(Qt::WA_QuitOnClose));
- QTimer::singleShot(1000, dialog, &QDialog::accept);
+ QTimer::singleShot(1000, dialog.data(), &QDialog::accept);
dialog->exec();
QVERIFY(dialog);
- QCOMPARE(spy.count(), 0);
+ QCOMPARE(spy.size(), 0);
QPointer<CloseWidget>widget = new CloseWidget;
widget->setWindowTitle(QLatin1String(QTest::currentTestFunction()) + QLatin1String("CloseWidget"));
@@ -498,7 +601,7 @@ void tst_QApplication::lastWindowClosed()
QObject::connect(&app, &QGuiApplication::lastWindowClosed, widget.data(), &QObject::deleteLater);
QCoreApplication::exec();
QVERIFY(!widget);
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
spy.clear();
delete dialog;
@@ -516,7 +619,7 @@ void tst_QApplication::lastWindowClosed()
QTimer::singleShot(1000, &app, &QApplication::closeAllWindows);
QCoreApplication::exec();
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
}
class QuitOnLastWindowClosedDialog : public QDialog
@@ -549,8 +652,8 @@ public slots:
other.exec();
// verify that the eventloop ran and let the timer fire
- QCOMPARE(spy.count(), 1);
- QCOMPARE(appSpy.count(), 1);
+ QCOMPARE(spy.size(), 1);
+ QCOMPARE(appSpy.size(), 1);
}
private:
@@ -575,7 +678,7 @@ public slots:
timer1.setSingleShot(true);
timer1.start(1000);
dialog.exec();
- QCOMPARE(spy1.count(), 1);
+ QCOMPARE(spy1.size(), 1);
show();
}
@@ -596,7 +699,7 @@ void tst_QApplication::quitOnLastWindowClosed()
QCoreApplication::exec();
// lastWindowClosed() signal should only be sent after the last dialog is closed
- QCOMPARE(appSpy.count(), 2);
+ QCOMPARE(appSpy.size(), 2);
}
{
int argc = 0;
@@ -611,8 +714,8 @@ void tst_QApplication::quitOnLastWindowClosed()
timer1.setSingleShot(true);
timer1.start(1000);
dialog.exec();
- QCOMPARE(spy1.count(), 1);
- QCOMPARE(appSpy.count(), 0);
+ QCOMPARE(spy1.size(), 1);
+ QCOMPARE(appSpy.size(), 0);
QTimer timer2;
connect(&timer2, &QTimer::timeout, &app, &QCoreApplication::quit);
@@ -621,8 +724,8 @@ void tst_QApplication::quitOnLastWindowClosed()
timer2.start(1000);
int returnValue = QCoreApplication::exec();
QCOMPARE(returnValue, 0);
- QCOMPARE(spy2.count(), 1);
- QCOMPARE(appSpy.count(), 0);
+ QCOMPARE(spy2.size(), 1);
+ QCOMPARE(appSpy.size(), 0);
}
{
int argc = 0;
@@ -653,14 +756,14 @@ void tst_QApplication::quitOnLastWindowClosed()
QCoreApplication::exec();
- QCOMPARE(spy.count(), 1);
- QVERIFY(spy2.count() < 15); // Should be around 10 if closing caused the quit
+ QCOMPARE(spy.size(), 1);
+ QVERIFY(spy2.size() < 15); // Should be around 10 if closing caused the quit
}
bool quitApplicationTriggered = false;
auto quitSlot = [&quitApplicationTriggered] () {
quitApplicationTriggered = true;
- QCoreApplication::quit();
+ QCoreApplication::exit();
};
{
@@ -684,13 +787,13 @@ void tst_QApplication::quitOnLastWindowClosed()
QCoreApplication::exec();
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
QVERIFY(quitApplicationTriggered);
}
{
int argc = 0;
QApplication app(argc, nullptr);
- QSignalSpy appSpy(&app, &QApplication::lastWindowClosed);
+ QSignalSpy appSpy(&app, &QGuiApplication::lastWindowClosed);
// exec a dialog for 1 second, then show the window
QuitOnLastWindowClosedWindow window;
@@ -706,8 +809,8 @@ void tst_QApplication::quitOnLastWindowClosed()
QCOMPARE(returnValue, 0);
// failure here means the timer above didn't fire, and the
// quit was caused the dialog being closed (not the window)
- QCOMPARE(timerSpy.count(), 1);
- QCOMPARE(appSpy.count(), 2);
+ QCOMPARE(timerSpy.size(), 1);
+ QCOMPARE(appSpy.size(), 2);
}
{
int argc = 0;
@@ -756,7 +859,7 @@ void tst_QApplication::quitOnLastWindowClosed()
QTimer::singleShot(100, &w1, &QWidget::close);
QCoreApplication::exec();
- QVERIFY(timerSpy.count() < 10);
+ QVERIFY(timerSpy.size() < 10);
}
}
@@ -798,11 +901,9 @@ public:
void tst_QApplication::closeAllWindows()
{
-#ifdef Q_OS_WINRT
- QSKIP("PromptOnCloseWidget does not work on WinRT - QTBUG-68297");
-#endif
int argc = 0;
QApplication app(argc, nullptr);
+ app.setAttribute(Qt::AA_DontUseNativeDialogs, true);
// create some windows
new QWidget;
@@ -811,7 +912,7 @@ void tst_QApplication::closeAllWindows()
// show all windows
auto topLevels = QApplication::topLevelWidgets();
- for (QWidget *w : qAsConst(topLevels)) {
+ for (QWidget *w : std::as_const(topLevels)) {
w->show();
QVERIFY(QTest::qWaitForWindowExposed(w));
}
@@ -828,14 +929,14 @@ void tst_QApplication::closeAllWindows()
PromptOnCloseWidget *promptOnCloseWidget = new PromptOnCloseWidget;
// show all windows
topLevels = QApplication::topLevelWidgets();
- for (QWidget *w : qAsConst(topLevels)) {
+ for (QWidget *w : std::as_const(topLevels)) {
w->show();
QVERIFY(QTest::qWaitForWindowExposed(w));
}
// close the last window to open the prompt (eventloop recurses)
promptOnCloseWidget->close();
// all windows should not be visible, except the one that opened the prompt
- for (QWidget *w : qAsConst(topLevels)) {
+ for (QWidget *w : std::as_const(topLevels)) {
if (w == promptOnCloseWidget)
QVERIFY(w->isVisible());
else
@@ -847,8 +948,8 @@ void tst_QApplication::closeAllWindows()
bool isPathListIncluded(const QStringList &l, const QStringList &r)
{
- int size = r.count();
- if (size > l.count())
+ int size = r.size();
+ if (size > l.size())
return false;
#if defined (Q_OS_WIN)
Qt::CaseSensitivity cs = Qt::CaseInsensitive;
@@ -856,22 +957,22 @@ bool isPathListIncluded(const QStringList &l, const QStringList &r)
Qt::CaseSensitivity cs = Qt::CaseSensitive;
#endif
int i = 0, j = 0;
- for ( ; i < l.count() && j < r.count(); ++i) {
+ for ( ; i < l.size() && j < r.size(); ++i) {
if (QDir::toNativeSeparators(l[i]).compare(QDir::toNativeSeparators(r[j]), cs) == 0) {
++j;
i = -1;
}
}
- return j == r.count();
+ return j == r.size();
}
#if QT_CONFIG(library)
void tst_QApplication::libraryPaths()
{
#ifndef BUILTIN_TESTDATA
- const QString testDir = QFileInfo(QFINDTESTDATA("test/test.pro")).absolutePath();
+ const QString testDir = QFileInfo(QFINDTESTDATA("test/CMakeLists.txt")).absolutePath();
#else
- const QString testDir = QFileInfo(QFINDTESTDATA("test.pro")).absolutePath();
+ const QString testDir = QFileInfo(QFINDTESTDATA("CMakeLists.txt")).absolutePath();
#endif
QVERIFY(!testDir.isEmpty());
{
@@ -885,7 +986,9 @@ void tst_QApplication::libraryPaths()
QStringList actual = QApplication::libraryPaths();
actual.sort();
- QStringList expected = QSet<QString>::fromList((QStringList() << testDir << appDirPath)).toList();
+ QStringList expected;
+ expected << testDir << appDirPath;
+ expected = QSet<QString>(expected.constBegin(), expected.constEnd()).values();
expected.sort();
QVERIFY2(isPathListIncluded(actual, expected),
@@ -897,17 +1000,16 @@ void tst_QApplication::libraryPaths()
int argc = 1;
QApplication app(argc, &argv0);
QString appDirPath = QCoreApplication::applicationDirPath();
- QString installPathPlugins = QLibraryInfo::location(QLibraryInfo::PluginsPath);
+ QString installPathPlugins = QLibraryInfo::path(QLibraryInfo::PluginsPath);
QStringList actual = QApplication::libraryPaths();
actual.sort();
- QStringList expected = QSet<QString>::fromList((QStringList() << installPathPlugins << appDirPath)).toList();
+ QStringList expected;
+ expected << installPathPlugins << appDirPath;
+ expected = QSet<QString>(expected.constBegin(), expected.constEnd()).values();
expected.sort();
-#ifdef Q_OS_WINRT
- QEXPECT_FAIL("", "On WinRT PluginsPath is outside of sandbox. QTBUG-68297", Abort);
-#endif
QVERIFY2(isPathListIncluded(actual, expected),
qPrintable("actual:\n - " + actual.join("\n - ") +
"\nexpected:\n - " + expected.join("\n - ")));
@@ -921,18 +1023,18 @@ void tst_QApplication::libraryPaths()
{
qCDebug(lcTests) << "Initial library path:" << QApplication::libraryPaths();
- int count = QApplication::libraryPaths().count();
+ int count = QApplication::libraryPaths().size();
#if 0
// this test doesn't work if KDE 4 is installed
QCOMPARE(count, 1); // before creating QApplication, only the PluginsPath is in the libraryPaths()
#endif
- QString installPathPlugins = QLibraryInfo::location(QLibraryInfo::PluginsPath);
+ QString installPathPlugins = QLibraryInfo::path(QLibraryInfo::PluginsPath);
QApplication::addLibraryPath(installPathPlugins);
qCDebug(lcTests) << "installPathPlugins" << installPathPlugins;
qCDebug(lcTests) << "After adding plugins path:" << QApplication::libraryPaths();
- QCOMPARE(QApplication::libraryPaths().count(), count);
+ QCOMPARE(QApplication::libraryPaths().size(), count);
QApplication::addLibraryPath(testDir);
- QCOMPARE(QApplication::libraryPaths().count(), count + 1);
+ QCOMPARE(QApplication::libraryPaths().size(), count + 1);
// creating QApplication adds the applicationDirPath to the libraryPath
int argc = 1;
@@ -942,19 +1044,19 @@ void tst_QApplication::libraryPaths()
// On Windows CE these are identical and might also be the case for other
// systems too
if (appDirPath != installPathPlugins)
- QCOMPARE(QApplication::libraryPaths().count(), count + 2);
+ QCOMPARE(QApplication::libraryPaths().size(), count + 2);
}
{
int argc = 1;
QApplication app(argc, &argv0);
qCDebug(lcTests) << "Initial library path:" << QCoreApplication::libraryPaths();
- int count = QCoreApplication::libraryPaths().count();
- QString installPathPlugins = QLibraryInfo::location(QLibraryInfo::PluginsPath);
+ int count = QCoreApplication::libraryPaths().size();
+ QString installPathPlugins = QLibraryInfo::path(QLibraryInfo::PluginsPath);
QCoreApplication::addLibraryPath(installPathPlugins);
qCDebug(lcTests) << "installPathPlugins" << installPathPlugins;
qCDebug(lcTests) << "After adding plugins path:" << QCoreApplication::libraryPaths();
- QCOMPARE(QCoreApplication::libraryPaths().count(), count);
+ QCOMPARE(QCoreApplication::libraryPaths().size(), count);
QString appDirPath = QCoreApplication::applicationDirPath();
@@ -962,14 +1064,14 @@ void tst_QApplication::libraryPaths()
QCoreApplication::addLibraryPath(appDirPath + "/..");
qCDebug(lcTests) << "appDirPath" << appDirPath;
qCDebug(lcTests) << "After adding appDirPath && appDirPath + /..:" << QCoreApplication::libraryPaths();
- QCOMPARE(QCoreApplication::libraryPaths().count(), count + 1);
+ QCOMPARE(QCoreApplication::libraryPaths().size(), count + 1);
#ifdef Q_OS_MACOS
QCoreApplication::addLibraryPath(appDirPath + "/../MacOS");
#else
QCoreApplication::addLibraryPath(appDirPath + "/tmp/..");
#endif
qCDebug(lcTests) << "After adding appDirPath + /tmp/..:" << QCoreApplication::libraryPaths();
- QCOMPARE(QCoreApplication::libraryPaths().count(), count + 1);
+ QCOMPARE(QCoreApplication::libraryPaths().size(), count + 1);
}
}
@@ -1011,13 +1113,10 @@ void tst_QApplication::libraryPaths_qt_plugin_path_2()
// library path list should contain the default plus the one valid path
QStringList expected =
QStringList()
- << QLibraryInfo::location(QLibraryInfo::PluginsPath)
+ << QLibraryInfo::path(QLibraryInfo::PluginsPath)
<< QDir(QCoreApplication::applicationDirPath()).canonicalPath()
<< QDir(QDir::fromNativeSeparators(QString::fromLatin1(validPath))).canonicalPath();
-#ifdef Q_OS_WINRT
- QEXPECT_FAIL("", "On WinRT PluginsPath is outside of sandbox. QTBUG-68297", Abort);
-#endif
QVERIFY2(isPathListIncluded(QCoreApplication::libraryPaths(), expected),
qPrintable("actual:\n - " + QCoreApplication::libraryPaths().join("\n - ") +
"\nexpected:\n - " + expected.join("\n - ")));
@@ -1035,15 +1134,16 @@ void tst_QApplication::libraryPaths_qt_plugin_path_2()
// library path list should contain the default
QStringList expected =
QStringList()
- << QLibraryInfo::location(QLibraryInfo::PluginsPath)
+ << QLibraryInfo::path(QLibraryInfo::PluginsPath)
<< QCoreApplication::applicationDirPath();
QVERIFY(isPathListIncluded(QCoreApplication::libraryPaths(), expected));
- qputenv("QT_PLUGIN_PATH", QByteArray());
+ qputenv("QT_PLUGIN_PATH", nullptr);
}
}
#endif
+#ifdef QT_BUILD_INTERNAL
class SendPostedEventsTester : public QObject
{
Q_OBJECT
@@ -1065,14 +1165,14 @@ void SendPostedEventsTester::doTest()
QPointer<SendPostedEventsTester> p = this;
QApplication::postEvent(this, new QEvent(QEvent::User));
// DeferredDelete should not be delivered until returning from this function
- QApplication::postEvent(this, new QDeferredDeleteEvent());
+ deleteLater();
QEventLoop eventLoop;
QMetaObject::invokeMethod(&eventLoop, "quit", Qt::QueuedConnection);
eventLoop.exec();
QVERIFY(p != nullptr);
- QCOMPARE(eventSpy.count(), 2);
+ QCOMPARE(eventSpy.size(), 2);
QCOMPARE(eventSpy.at(0), int(QEvent::MetaCall));
QCOMPARE(eventSpy.at(1), int(QEvent::User));
eventSpy.clear();
@@ -1089,6 +1189,7 @@ void tst_QApplication::sendPostedEvents()
(void) QCoreApplication::exec();
QVERIFY(p.isNull());
}
+#endif
void tst_QApplication::thread()
{
@@ -1216,6 +1317,11 @@ void DeleteLaterWidget::runTest()
QCoreApplication::processEvents();
+ // At this point, the event queue is empty. As we want a deferred
+ // deletion to occur before the timer event, we should provoke the
+ // event dispatcher for the next spin.
+ QCoreApplication::eventDispatcher()->interrupt();
+
QVERIFY(!stillAlive); // verify at the end to make test terminate
}
@@ -1228,12 +1334,9 @@ void DeleteLaterWidget::checkDeleteLater()
void tst_QApplication::testDeleteLater()
{
-#ifdef Q_OS_MAC
- QSKIP("This test fails and then hangs on OS X, see QTBUG-24318");
-#endif
int argc = 0;
QApplication app(argc, nullptr);
- connect(&app, &QApplication::lastWindowClosed, &app, &QCoreApplication::quit);
+ connect(&app, &QGuiApplication::lastWindowClosed, &app, &QCoreApplication::quit);
DeleteLaterWidget *wgt = new DeleteLaterWidget(&app);
QTimer::singleShot(500, wgt, &DeleteLaterWidget::runTest);
@@ -1245,8 +1348,10 @@ void tst_QApplication::testDeleteLater()
QObject *stillAlive = wgt->findChild<QObject*>("deleteLater");
QVERIFY(stillAlive);
+ wgt->show();
QCoreApplication::exec();
+ QVERIFY(wgt->isHidden());
delete wgt;
}
@@ -1324,10 +1429,8 @@ public slots:
}
};
-void tst_QApplication::testDeleteLaterProcessEvents()
+void tst_QApplication::testDeleteLaterProcessEvents1()
{
- int argc = 0;
-
// Calling processEvents() with no event dispatcher does nothing.
QObject *object = new QObject;
QPointer<QObject> p(object);
@@ -1335,75 +1438,85 @@ void tst_QApplication::testDeleteLaterProcessEvents()
QApplication::processEvents();
QVERIFY(p);
delete object;
+}
- {
- QApplication app(argc, nullptr);
- // If you call processEvents() with an event dispatcher present, but
- // outside any event loops, deferred deletes are not processed unless
- // sendPostedEvents(0, DeferredDelete) is called.
- object = new QObject;
- p = object;
- object->deleteLater();
- QCoreApplication::processEvents();
- QVERIFY(p);
- QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
- QVERIFY(!p);
-
- // If you call deleteLater() on an object when there is no parent
- // event loop, and then enter an event loop, the object will get
- // deleted.
- object = new QObject;
- p = object;
- object->deleteLater();
- QEventLoop loop;
- QTimer::singleShot(1000, &loop, &QEventLoop::quit);
- loop.exec();
- QVERIFY(!p);
- }
- {
- // When an object is in an event loop, then calls deleteLater() and enters
- // an event loop recursively, it should not die until the parent event
- // loop continues.
- QApplication app(argc, nullptr);
- QEventLoop loop;
- EventLoopNester *nester = new EventLoopNester;
- p = nester;
- QTimer::singleShot(3000, &loop, &QEventLoop::quit);
- QTimer::singleShot(0, nester, &EventLoopNester::deleteLaterAndEnterLoop);
-
- loop.exec();
- QVERIFY(!p);
- }
-
- {
- // When the event loop that calls deleteLater() is exited
- // immediately, the object should die when returning to the
- // parent event loop
- QApplication app(argc, nullptr);
- QEventLoop loop;
- EventLoopNester *nester = new EventLoopNester;
- p = nester;
- QTimer::singleShot(3000, &loop, &QEventLoop::quit);
- QTimer::singleShot(0, nester, &EventLoopNester::deleteLaterAndExitLoop);
+void tst_QApplication::testDeleteLaterProcessEvents2()
+{
+ int argc = 0;
+ QApplication app(argc, nullptr);
+ // If you call processEvents() with an event dispatcher present, but
+ // outside any event loops, deferred deletes are not processed unless
+ // sendPostedEvents(0, DeferredDelete) is called.
+ auto object = new QObject;
+ QPointer<QObject> p(object);
+ object->deleteLater();
+ QCoreApplication::processEvents();
+ QVERIFY(p);
+ QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
+ QVERIFY(!p);
+
+ // If you call deleteLater() on an object when there is no parent
+ // event loop, and then enter an event loop, the object will get
+ // deleted.
+ QEventLoop loop;
+ object = new QObject;
+ connect(object, &QObject::destroyed, &loop, &QEventLoop::quit);
+ p = object;
+ object->deleteLater();
+ QTimer::singleShot(1000, &loop, &QEventLoop::quit);
+ loop.exec();
+ QVERIFY(!p);
+}
- loop.exec();
- QVERIFY(!p);
- }
+void tst_QApplication::testDeleteLaterProcessEvents3()
+{
+ int argc = 0;
+ // When an object is in an event loop, then calls deleteLater() and enters
+ // an event loop recursively, it should not die until the parent event
+ // loop continues.
+ QApplication app(argc, nullptr);
+ QEventLoop loop;
+ EventLoopNester *nester = new EventLoopNester;
+ QPointer<QObject> p(nester);
+ QTimer::singleShot(3000, &loop, &QEventLoop::quit);
+ QTimer::singleShot(0, nester, &EventLoopNester::deleteLaterAndEnterLoop);
+
+ loop.exec();
+ QVERIFY(!p);
+}
- {
- // when the event loop that calls deleteLater() also calls
- // processEvents() immediately afterwards, the object should
- // not die until the parent loop continues
- QApplication app(argc, nullptr);
- QEventLoop loop;
- EventLoopNester *nester = new EventLoopNester();
- p = nester;
- QTimer::singleShot(3000, &loop, &QEventLoop::quit);
- QTimer::singleShot(0, nester, &EventLoopNester::deleteLaterAndProcessEvents);
+void tst_QApplication::testDeleteLaterProcessEvents4()
+{
+ int argc = 0;
+ // When the event loop that calls deleteLater() is exited
+ // immediately, the object should die when returning to the
+ // parent event loop
+ QApplication app(argc, nullptr);
+ QEventLoop loop;
+ EventLoopNester *nester = new EventLoopNester;
+ QPointer<QObject> p(nester);
+ QTimer::singleShot(3000, &loop, &QEventLoop::quit);
+ QTimer::singleShot(0, nester, &EventLoopNester::deleteLaterAndExitLoop);
+
+ loop.exec();
+ QVERIFY(!p);
+}
- loop.exec();
- QVERIFY(!p);
- }
+void tst_QApplication::testDeleteLaterProcessEvents5()
+{
+ // when the event loop that calls deleteLater() also calls
+ // processEvents() immediately afterwards, the object should
+ // not die until the parent loop continues
+ int argc = 0;
+ QApplication app(argc, nullptr);
+ QEventLoop loop;
+ EventLoopNester *nester = new EventLoopNester();
+ QPointer<QObject> p(nester);
+ QTimer::singleShot(3000, &loop, &QEventLoop::quit);
+ QTimer::singleShot(0, nester, &EventLoopNester::deleteLaterAndProcessEvents);
+
+ loop.exec();
+ QVERIFY(!p);
}
/*
@@ -1413,7 +1526,12 @@ void tst_QApplication::desktopSettingsAware()
{
#if QT_CONFIG(process)
QProcess testProcess;
- testProcess.start("desktopsettingsaware_helper");
+#ifdef Q_OS_MACOS
+ QStringList environment = QProcess::systemEnvironment();
+ environment += QLatin1String("QT_MAC_DISABLE_FOREGROUND_APPLICATION_TRANSFORM=1");
+ testProcess.setEnvironment(environment);
+#endif
+ testProcess.start("./desktopsettingsaware_helper");
QVERIFY2(testProcess.waitForStarted(),
qPrintable(QString::fromLatin1("Cannot start 'desktopsettingsaware_helper': %1").arg(testProcess.errorString())));
QVERIFY(testProcess.waitForFinished(10000));
@@ -1441,11 +1559,97 @@ void tst_QApplication::setActiveWindow()
delete pb2;
w->show();
- QApplication::setActiveWindow(w); // needs this on twm (focus follows mouse)
+ QApplicationPrivate::setActiveWindow(w); // needs this on twm (focus follows mouse)
QVERIFY(pb1->hasFocus());
delete w;
}
+void tst_QApplication::activateDeactivateEvent()
+{
+ // Ensure that QWindows (other than QWidgetWindow)
+ // are activated / deactivated.
+ class Window : public QWindow
+ {
+ public:
+ using QWindow::QWindow;
+
+ int activateCount = 0;
+ int deactivateCount = 0;
+ protected:
+ bool event(QEvent *e) override
+ {
+ switch (e->type()) {
+ case QEvent::WindowActivate:
+ ++activateCount;
+ break;
+ case QEvent::WindowDeactivate:
+ ++deactivateCount;
+ break;
+ default:
+ break;
+ }
+ return QWindow::event(e);
+ }
+ };
+
+ int argc = 0;
+ QApplication app(argc, nullptr);
+
+ if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation))
+ QSKIP("QWindow::requestActivate() is not supported.");
+
+ Window w1;
+ Window w2;
+
+ w1.show();
+ w1.requestActivate();
+ QVERIFY(QTest::qWaitForWindowActive(&w1));
+ QCOMPARE(w1.activateCount, 1);
+ QCOMPARE(w1.deactivateCount, 0);
+
+ w2.show();
+ w2.requestActivate();
+ QVERIFY(QTest::qWaitForWindowActive(&w2));
+ QCOMPARE(w1.deactivateCount, 1);
+ QCOMPARE(w2.activateCount, 1);
+}
+
+void tst_QApplication::focusWidget()
+{
+ int argc = 0;
+ QApplication app(argc, nullptr);
+
+ // The focus widget is the active window itself
+ {
+ QTextEdit te;
+ te.show();
+
+ QVERIFY(QTest::qWaitForWindowActive(&te));
+
+ const auto focusWidget = QApplication::focusWidget();
+ QVERIFY(focusWidget);
+ QVERIFY(focusWidget->hasFocus());
+ QVERIFY(te.hasFocus());
+ QCOMPARE(focusWidget, te.focusWidget());
+ }
+
+ // The focus widget is a child of the active window
+ {
+ QWidget w;
+ QTextEdit te(&w);
+ w.show();
+
+ QVERIFY(QTest::qWaitForWindowActive(&w));
+
+ const auto focusWidget = QApplication::focusWidget();
+ QVERIFY(focusWidget);
+ QVERIFY(focusWidget->hasFocus());
+ QVERIFY(!w.hasFocus());
+ QVERIFY(te.hasFocus());
+ QCOMPARE(te.focusWidget(), w.focusWidget());
+ QCOMPARE(focusWidget, w.focusWidget());
+ }
+}
/* This might fail on some X11 window managers? */
void tst_QApplication::focusChanged()
@@ -1467,22 +1671,22 @@ void tst_QApplication::focusChanged()
hbox1.addWidget(&le1);
hbox1.addWidget(&pb1);
- QCOMPARE(spy.count(), 0);
+ QCOMPARE(spy.size(), 0);
parent1.show();
- QApplication::setActiveWindow(&parent1); // needs this on twm (focus follows mouse)
- QCOMPARE(spy.count(), 1);
- QCOMPARE(spy.at(0).count(), 2);
+ QApplicationPrivate::setActiveWindow(&parent1); // needs this on twm (focus follows mouse)
+ QCOMPARE(spy.size(), 1);
+ QCOMPARE(spy.at(0).size(), 2);
old = qvariant_cast<QWidget*>(spy.at(0).at(0));
now = qvariant_cast<QWidget*>(spy.at(0).at(1));
QCOMPARE(now, &le1);
QCOMPARE(now, QApplication::focusWidget());
QVERIFY(!old);
spy.clear();
- QCOMPARE(spy.count(), 0);
+ QCOMPARE(spy.size(), 0);
pb1.setFocus();
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
old = qvariant_cast<QWidget*>(spy.at(0).at(0));
now = qvariant_cast<QWidget*>(spy.at(0).at(1));
QCOMPARE(now, &pb1);
@@ -1491,7 +1695,7 @@ void tst_QApplication::focusChanged()
spy.clear();
lb1.setFocus();
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
old = qvariant_cast<QWidget*>(spy.at(0).at(0));
now = qvariant_cast<QWidget*>(spy.at(0).at(1));
QCOMPARE(now, &lb1);
@@ -1500,7 +1704,7 @@ void tst_QApplication::focusChanged()
spy.clear();
lb1.clearFocus();
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
old = qvariant_cast<QWidget*>(spy.at(0).at(0));
now = qvariant_cast<QWidget*>(spy.at(0).at(1));
QVERIFY(!now);
@@ -1519,10 +1723,10 @@ void tst_QApplication::focusChanged()
hbox2.addWidget(&pb2);
parent2.show();
- QApplication::setActiveWindow(&parent2); // needs this on twm (focus follows mouse)
- QVERIFY(spy.count() > 0); // one for deactivation, one for activation on Windows
- old = qvariant_cast<QWidget*>(spy.at(spy.count()-1).at(0));
- now = qvariant_cast<QWidget*>(spy.at(spy.count()-1).at(1));
+ QApplicationPrivate::setActiveWindow(&parent2); // needs this on twm (focus follows mouse)
+ QVERIFY(spy.size() > 0); // one for deactivation, one for activation on Windows
+ old = qvariant_cast<QWidget*>(spy.at(spy.size()-1).at(0));
+ now = qvariant_cast<QWidget*>(spy.at(spy.size()-1).at(1));
QCOMPARE(now, &le2);
QCOMPARE(now, QApplication::focusWidget());
QVERIFY(!old);
@@ -1547,10 +1751,10 @@ void tst_QApplication::focusChanged()
tab.simulate(now);
if (!tabAllControls) {
- QCOMPARE(spy.count(), 0);
+ QCOMPARE(spy.size(), 0);
QCOMPARE(now, QApplication::focusWidget());
} else {
- QVERIFY(spy.count() > 0);
+ QVERIFY(spy.size() > 0);
old = qvariant_cast<QWidget*>(spy.at(0).at(0));
now = qvariant_cast<QWidget*>(spy.at(0).at(1));
QCOMPARE(now, &pb2);
@@ -1560,11 +1764,11 @@ void tst_QApplication::focusChanged()
}
if (!tabAllControls) {
- QCOMPARE(spy.count(), 0);
+ QCOMPARE(spy.size(), 0);
QCOMPARE(now, QApplication::focusWidget());
} else {
tab.simulate(now);
- QVERIFY(spy.count() > 0);
+ QVERIFY(spy.size() > 0);
old = qvariant_cast<QWidget*>(spy.at(0).at(0));
now = qvariant_cast<QWidget*>(spy.at(0).at(1));
QCOMPARE(now, &le2);
@@ -1574,11 +1778,11 @@ void tst_QApplication::focusChanged()
}
if (!tabAllControls) {
- QCOMPARE(spy.count(), 0);
+ QCOMPARE(spy.size(), 0);
QCOMPARE(now, QApplication::focusWidget());
} else {
backtab.simulate(now);
- QVERIFY(spy.count() > 0);
+ QVERIFY(spy.size() > 0);
old = qvariant_cast<QWidget*>(spy.at(0).at(0));
now = qvariant_cast<QWidget*>(spy.at(0).at(1));
QCOMPARE(now, &pb2);
@@ -1589,12 +1793,12 @@ void tst_QApplication::focusChanged()
if (!tabAllControls) {
- QCOMPARE(spy.count(), 0);
+ QCOMPARE(spy.size(), 0);
QCOMPARE(now, QApplication::focusWidget());
old = &pb2;
} else {
backtab.simulate(now);
- QVERIFY(spy.count() > 0);
+ QVERIFY(spy.size() > 0);
old = qvariant_cast<QWidget*>(spy.at(0).at(0));
now = qvariant_cast<QWidget*>(spy.at(0).at(1));
QCOMPARE(now, &le2);
@@ -1605,10 +1809,10 @@ void tst_QApplication::focusChanged()
click.simulate(old);
if (!(pb2.focusPolicy() & Qt::ClickFocus)) {
- QCOMPARE(spy.count(), 0);
+ QCOMPARE(spy.size(), 0);
QCOMPARE(now, QApplication::focusWidget());
} else {
- QVERIFY(spy.count() > 0);
+ QVERIFY(spy.size() > 0);
old = qvariant_cast<QWidget*>(spy.at(0).at(0));
now = qvariant_cast<QWidget*>(spy.at(0).at(1));
QCOMPARE(now, &pb2);
@@ -1617,7 +1821,7 @@ void tst_QApplication::focusChanged()
spy.clear();
click.simulate(old);
- QVERIFY(spy.count() > 0);
+ QVERIFY(spy.size() > 0);
old = qvariant_cast<QWidget*>(spy.at(0).at(0));
now = qvariant_cast<QWidget*>(spy.at(0).at(1));
QCOMPARE(now, &le2);
@@ -1627,16 +1831,16 @@ void tst_QApplication::focusChanged()
}
parent1.activateWindow();
- QApplication::setActiveWindow(&parent1); // needs this on twm (focus follows mouse)
- QVERIFY(spy.count() == 1 || spy.count() == 2); // one for deactivation, one for activation on Windows
+ QApplicationPrivate::setActiveWindow(&parent1); // needs this on twm (focus follows mouse)
+ QVERIFY(spy.size() == 1 || spy.size() == 2); // one for deactivation, one for activation on Windows
//on windows, the change of focus is made in 2 steps
//(the focusChanged SIGNAL is emitted twice)
- if (spy.count()==1)
- old = qvariant_cast<QWidget*>(spy.at(spy.count()-1).at(0));
+ if (spy.size()==1)
+ old = qvariant_cast<QWidget*>(spy.at(spy.size()-1).at(0));
else
- old = qvariant_cast<QWidget*>(spy.at(spy.count()-2).at(0));
- now = qvariant_cast<QWidget*>(spy.at(spy.count()-1).at(1));
+ old = qvariant_cast<QWidget*>(spy.at(spy.size()-2).at(0));
+ now = qvariant_cast<QWidget*>(spy.at(spy.size()-1).at(1));
QCOMPARE(now, &le1);
QCOMPARE(now, QApplication::focusWidget());
QCOMPARE(old, &le2);
@@ -1695,6 +1899,9 @@ void tst_QApplication::focusMouseClick()
int argc = 1;
QApplication app(argc, &argv0);
+ if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation))
+ QSKIP("Window activation is not supported");
+
QWidget w;
w.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
w.setFocusPolicy(Qt::StrongFocus);
@@ -1707,7 +1914,7 @@ void tst_QApplication::focusMouseClick()
// front most widget has Qt::TabFocus, parent widget accepts clicks as well
// now send a mouse button press event and check what happens with the focus
// it should be given to the parent widget
- QMouseEvent ev(QEvent::MouseButtonPress, QPointF(), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
+ QMouseEvent ev(QEvent::MouseButtonPress, QPointF(), w.mapToGlobal(QPointF()), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
QSpontaneKeyEvent::setSpontaneous(&ev);
QVERIFY(ev.spontaneous());
qApp->notify(&w2, &ev);
@@ -1718,9 +1925,6 @@ void tst_QApplication::focusMouseClick()
QSpontaneKeyEvent::setSpontaneous(&ev);
QVERIFY(ev.spontaneous());
qApp->notify(&w2, &ev);
-#ifdef Q_OS_WINRT
- QEXPECT_FAIL("", "Fails on WinRT - QTBUG-68297", Abort);
-#endif
QTRY_COMPARE(QApplication::focusWidget(), &w2);
// now back to tab focus and click again (it already had focus) -> focus should stay
@@ -1777,6 +1981,194 @@ void tst_QApplication::style()
QVERIFY(QApplication::style() != nullptr);
}
+class CustomStyle : public QProxyStyle
+{
+public:
+ CustomStyle() : QProxyStyle("Windows") { Q_ASSERT(!polished); }
+ ~CustomStyle() { polished = 0; }
+ void polish(QPalette &palette) override
+ {
+ polished++;
+ palette.setColor(QPalette::Active, QPalette::Link, Qt::red);
+ }
+ static int polished;
+};
+
+int CustomStyle::polished = 0;
+
+class CustomStylePlugin : public QStylePlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QStyleFactoryInterface" FILE "customstyle.json")
+public:
+ QStyle *create(const QString &) override { return new CustomStyle; }
+};
+
+Q_IMPORT_PLUGIN(CustomStylePlugin)
+
+void tst_QApplication::applicationPalettePolish()
+{
+ int argc = 1;
+
+#if defined(QT_BUILD_INTERNAL)
+ {
+ qputenv("QT_DESKTOP_STYLE_KEY", "customstyle");
+ QApplication app(argc, &argv0);
+ QVERIFY(CustomStyle::polished);
+ QVERIFY(!app.palette().resolveMask());
+ QCOMPARE(app.palette().color(QPalette::Link), Qt::red);
+ qunsetenv("QT_DESKTOP_STYLE_KEY");
+ }
+#endif
+
+ {
+ QApplication::setStyle(new CustomStyle);
+ QApplication app(argc, &argv0);
+ QVERIFY(CustomStyle::polished);
+ QVERIFY(!app.palette().resolveMask());
+ QCOMPARE(app.palette().color(QPalette::Link), Qt::red);
+ }
+
+ {
+ QApplication app(argc, &argv0);
+ app.setStyle(new CustomStyle);
+ QVERIFY(CustomStyle::polished);
+ QVERIFY(!app.palette().resolveMask());
+ QCOMPARE(app.palette().color(QPalette::Link), Qt::red);
+
+ CustomStyle::polished = 0;
+ app.setPalette(QPalette());
+ QVERIFY(CustomStyle::polished);
+ QVERIFY(!app.palette().resolveMask());
+ QCOMPARE(app.palette().color(QPalette::Link), Qt::red);
+
+ CustomStyle::polished = 0;
+ QPalette palette;
+ palette.setColor(QPalette::Active, QPalette::Highlight, Qt::green);
+ app.setPalette(palette);
+ QVERIFY(CustomStyle::polished);
+ QVERIFY(app.palette().resolveMask());
+ QCOMPARE(app.palette().color(QPalette::Link), Qt::red);
+ QCOMPARE(app.palette().color(QPalette::Highlight), Qt::green);
+ }
+}
+
+void tst_QApplication::setColorScheme()
+{
+ int argc = 1;
+ QApplication app(argc, &argv0);
+
+ if (QStringList{"minimal", "offscreen", "wayland", "xcb", "wasm", "webassembly"}
+ .contains(QGuiApplication::platformName(), Qt::CaseInsensitive)) {
+ QSKIP("Setting the colorScheme is not implemented on this platform.");
+ }
+ qDebug() << "Testing setColorScheme on platform" << QGuiApplication::platformName();
+
+ if (QByteArrayView(app.style()->metaObject()->className()) == "QWindowsVistaStyle")
+ QSKIP("Setting the colorScheme is not supported with the Windows Vista style.");
+
+ const Qt::ColorScheme defaultColorScheme = QApplication::styleHints()->colorScheme();
+ // if we implement setColorScheme, then we must be able to read it
+ QVERIFY(defaultColorScheme != Qt::ColorScheme::Unknown);
+ const Qt::ColorScheme newColorScheme = defaultColorScheme == Qt::ColorScheme::Light
+ ? Qt::ColorScheme::Dark : Qt::ColorScheme::Light;
+
+ class TopLevelWidget : public QWidget
+ {
+ QList<QEvent::Type> events;
+ public:
+ TopLevelWidget()
+ {
+ setObjectName("colorScheme TopLevelWidget");
+ }
+
+ void clearEvents()
+ {
+ events.clear();
+ }
+ qsizetype eventCount(QEvent::Type type) const
+ {
+ return events.count(type);
+ }
+ protected:
+ bool event(QEvent *event) override
+ {
+ switch (event->type()) {
+ case QEvent::ApplicationPaletteChange:
+ case QEvent::PaletteChange:
+ case QEvent::ThemeChange:
+ events << event->type();
+ break;
+ default:
+ break;
+ }
+
+ return QWidget::event(event);
+ }
+ } topLevelWidget;
+ topLevelWidget.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&topLevelWidget));
+
+ QSignalSpy colorSchemeChangedSpy(app.styleHints(), &QStyleHints::colorSchemeChanged);
+
+ // always start with a clean list
+ topLevelWidget.clearEvents();
+ const QPalette defaultPalette = topLevelWidget.palette();
+
+ bool oldPaletteWhenSchemeChanged = false;
+ connect(app.styleHints(), &QStyleHints::colorSchemeChanged, this,
+ [defaultPalette, &topLevelWidget, &oldPaletteWhenSchemeChanged]{
+ oldPaletteWhenSchemeChanged = defaultPalette == topLevelWidget.palette();
+ });
+
+ app.styleHints()->setColorScheme(newColorScheme);
+ QTRY_COMPARE(colorSchemeChangedSpy.count(), 1);
+ // We have not yet updated the palette when we emit the colorSchemeChanged
+ // signal, so the toplevel widget should still use the previous palette
+ QVERIFY(oldPaletteWhenSchemeChanged);
+ QCOMPARE(topLevelWidget.eventCount(QEvent::ThemeChange), 1);
+ // We can't guarantee that there is only one ApplicationPaletteChange,
+ // and they might arrive asynchronously in response to ThemeChange
+ QTRY_COMPARE_GE(topLevelWidget.eventCount(QEvent::ApplicationPaletteChange), 1);
+ // But we can guarantee a single PaletteChange event for the widget
+ QCOMPARE(topLevelWidget.eventCount(QEvent::PaletteChange), 1);
+ // The palette should have changed
+ QCOMPARE_NE(topLevelWidget.palette(), defaultPalette);
+
+ topLevelWidget.clearEvents();
+ colorSchemeChangedSpy.clear();
+
+ // verify that a widget shown with a color scheme override in place respect that
+ QWidget newWidget;
+ newWidget.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&newWidget));
+ QCOMPARE(newWidget.palette(), topLevelWidget.palette());
+
+ // Setting to Unknown should follow the system preference again
+ app.styleHints()->setColorScheme(Qt::ColorScheme::Unknown);
+ QTRY_COMPARE(colorSchemeChangedSpy.count(), 1);
+ QCOMPARE(app.styleHints()->colorScheme(), defaultColorScheme);
+ QTRY_COMPARE(topLevelWidget.eventCount(QEvent::PaletteChange), 1);
+
+ auto debugPalette = qScopeGuard([defaultPalette, &topLevelWidget]{
+ qDebug() << "Inspecting palettes for differences";
+ const QPalette palette = topLevelWidget.palette();
+ for (int g = 0; g < QPalette::NColorGroups; ++g) {
+ for (int r = 0; r < QPalette::NColorRoles; ++r) {
+ const auto group = static_cast<QPalette::ColorGroup>(g);
+ const auto role = static_cast<QPalette::ColorRole>(r);
+ qDebug() << "...Checking" << group << role;
+ const auto actualBrush = palette.brush(group, role);
+ const auto expectedBrush = defaultPalette.brush(group, role);
+ if (palette.brush(group, role) != defaultPalette.brush(group, role))
+ qWarning() << "...Difference in" << group << role << actualBrush << expectedBrush;
+ }
+ }
+ });
+ QCOMPARE(topLevelWidget.palette(), defaultPalette);
+ debugPalette.dismiss();
+}
+
void tst_QApplication::allWidgets()
{
int argc = 1;
@@ -1799,11 +2191,11 @@ void tst_QApplication::topLevelWidgets()
#endif
QCoreApplication::processEvents();
QVERIFY(QApplication::topLevelWidgets().contains(w));
- QCOMPARE(QApplication::topLevelWidgets().count(), 1);
+ QCOMPARE(QApplication::topLevelWidgets().size(), 1);
delete w;
w = nullptr;
QCoreApplication::processEvents();
- QCOMPARE(QApplication::topLevelWidgets().count(), 0);
+ QCOMPARE(QApplication::topLevelWidgets().size(), 0);
}
@@ -1812,25 +2204,24 @@ void tst_QApplication::setAttribute()
{
int argc = 1;
QApplication app(argc, &argv0);
- QVERIFY(!QApplication::testAttribute(Qt::AA_ImmediateWidgetCreation));
+ QVERIFY(!QApplication::testAttribute(Qt::AA_NativeWindows));
QWidget *w = new QWidget;
- QVERIFY(!w->testAttribute(Qt::WA_WState_Created));
+ w->show(); // trigger creation;
+ QVERIFY(!w->testAttribute(Qt::WA_NativeWindow));
delete w;
- QApplication::setAttribute(Qt::AA_ImmediateWidgetCreation);
- QVERIFY(QApplication::testAttribute(Qt::AA_ImmediateWidgetCreation));
+ QApplication::setAttribute(Qt::AA_NativeWindows);
+ QVERIFY(QApplication::testAttribute(Qt::AA_NativeWindows));
w = new QWidget;
- QVERIFY(w->testAttribute(Qt::WA_WState_Created));
- QWidget *w2 = new QWidget(w);
- w2->setParent(nullptr);
- QVERIFY(w2->testAttribute(Qt::WA_WState_Created));
+ w->show(); // trigger creation
+ QVERIFY(w->testAttribute(Qt::WA_NativeWindow));
delete w;
- delete w2;
- QApplication::setAttribute(Qt::AA_ImmediateWidgetCreation, false);
- QVERIFY(!QApplication::testAttribute(Qt::AA_ImmediateWidgetCreation));
+ QApplication::setAttribute(Qt::AA_NativeWindows, false);
+ QVERIFY(!QApplication::testAttribute(Qt::AA_NativeWindows));
w = new QWidget;
- QVERIFY(!w->testAttribute(Qt::WA_WState_Created));
+ w->show(); // trigger creation;
+ QVERIFY(!w->testAttribute(Qt::WA_NativeWindow));
delete w;
}
@@ -1881,17 +2272,8 @@ void tst_QApplication::touchEventPropagation()
int argc = 1;
QApplication app(argc, &argv0);
- QList<QTouchEvent::TouchPoint> pressedTouchPoints;
- QTouchEvent::TouchPoint press(0);
- press.setState(Qt::TouchPointPressed);
- pressedTouchPoints << press;
-
- QList<QTouchEvent::TouchPoint> releasedTouchPoints;
- QTouchEvent::TouchPoint release(0);
- release.setState(Qt::TouchPointReleased);
- releasedTouchPoints << release;
- QTouchDevice *device = QTest::createTouchDevice();
+ QPointingDevice *device = QTest::createTouchDevice();
{
// touch event behavior on a window
@@ -1908,8 +2290,10 @@ void tst_QApplication::touchEventPropagation()
// we must ensure there is a screen position in the TouchPoint that maps to a local 0, 0.
const QPoint deviceGlobalPos =
QHighDpi::toNativePixels(window.mapToGlobal(QPoint(0, 0)), window.windowHandle()->screen());
- pressedTouchPoints[0].setScreenPos(deviceGlobalPos);
- releasedTouchPoints[0].setScreenPos(deviceGlobalPos);
+ auto pressedTouchPoints = QList<QEventPoint>() <<
+ QEventPoint(0, QEventPoint::State::Pressed, QPointF(), deviceGlobalPos);
+ auto releasedTouchPoints = QList<QEventPoint>() <<
+ QEventPoint(0, QEventPoint::State::Released, QPointF(), deviceGlobalPos);
QWindowSystemInterface::handleTouchEvent(handle,
0,
@@ -1966,8 +2350,10 @@ void tst_QApplication::touchEventPropagation()
QVERIFY(QTest::qWaitForWindowExposed(&window));
const QPoint deviceGlobalPos =
QHighDpi::toNativePixels(window.mapToGlobal(QPoint(50, 150)), window.windowHandle()->screen());
- pressedTouchPoints[0].setScreenPos(deviceGlobalPos);
- releasedTouchPoints[0].setScreenPos(deviceGlobalPos);
+ auto pressedTouchPoints = QList<QEventPoint>() <<
+ QEventPoint(0, QEventPoint::State::Pressed, QPointF(), deviceGlobalPos);
+ auto releasedTouchPoints = QList<QEventPoint>() <<
+ QEventPoint(0, QEventPoint::State::Released, QPointF(), deviceGlobalPos);
QWindowSystemInterface::handleTouchEvent(handle,
0,
@@ -2088,12 +2474,168 @@ void tst_QApplication::touchEventPropagation()
}
}
+/*!
+ Test that wheel events are propagated correctly.
+
+ The event propagation of wheel events is complex: generally, they are propagated
+ up the parent tree like other input events, until a widget accepts the event. However,
+ wheel events are ignored by default (unlike mouse events, which are accepted by default,
+ and ignored in the default implementation of the event handler of QWidget).
+
+ And Qt tries to make sure that wheel events that "belong together" are going to the same
+ widget. However, for low-precision events as generated by an old-fashioned
+ mouse wheel, each event is a distinct event, so Qt has no choice than to deliver the event
+ to the widget under the mouse.
+ High-precision events, as generated by track pads or other kinetic scrolling devices, come
+ in a continuous stream, with different phases. Qt tries to make sure that all events in the
+ same stream go to the widget that accepted the first event.
+
+ Also, QAbstractScrollArea forwards wheel events from the viewport to the relevant scrollbar,
+ which adds more complexity to the handling.
+
+ This tests two scenarios:
+ 1) a large widget inside a scrollarea that scrolls, inside a scrollarea that also scrolls
+ 2) a large widget inside a scrollarea that doesn't scroll, within a scrollarea that does
+
+ For scenario 1 "inner", the expectation is that the inner scrollarea handles all wheel
+ events.
+ For scenario 2 "outer", the expectation is that the outer scrollarea handles all wheel
+ events.
+*/
+struct WheelEvent
+{
+ WheelEvent(Qt::ScrollPhase p = Qt::NoScrollPhase, Qt::Orientation o = Qt::Vertical)
+ : phase(p), orientation(o)
+ {}
+ Qt::ScrollPhase phase = Qt::NoScrollPhase;
+ Qt::Orientation orientation = Qt::Vertical;
+};
+using WheelEventList = QList<WheelEvent>;
+
+void tst_QApplication::wheelEventPropagation_data()
+{
+ qRegisterMetaType<WheelEventList>();
+
+ QTest::addColumn<bool>("innerScrolls");
+ QTest::addColumn<WheelEventList>("events");
+
+ QTest::addRow("inner, classic")
+ << true
+ << WheelEventList{{}, {}, {}};
+ QTest::addRow("outer, classic")
+ << false
+ << WheelEventList{{}, {}, {}};
+ QTest::addRow("inner, kinetic")
+ << true
+ << WheelEventList{Qt::ScrollBegin, Qt::ScrollUpdate, Qt::ScrollMomentum, Qt::ScrollEnd};
+ QTest::addRow("outer, kinetic")
+ << false
+ << WheelEventList{Qt::ScrollBegin, Qt::ScrollUpdate, Qt::ScrollMomentum, Qt::ScrollEnd};
+ QTest::addRow("inner, partial kinetic")
+ << true
+ << WheelEventList{Qt::ScrollUpdate, Qt::ScrollMomentum, Qt::ScrollEnd};
+ QTest::addRow("outer, partial kinetic")
+ << false
+ << WheelEventList{Qt::ScrollUpdate, Qt::ScrollMomentum, Qt::ScrollEnd};
+ QTest::addRow("inner, changing direction")
+ << true
+ << WheelEventList{Qt::ScrollUpdate, {Qt::ScrollUpdate, Qt::Horizontal}, Qt::ScrollMomentum, Qt::ScrollEnd};
+ QTest::addRow("outer, changing direction")
+ << false
+ << WheelEventList{Qt::ScrollUpdate, {Qt::ScrollUpdate, Qt::Horizontal}, Qt::ScrollMomentum, Qt::ScrollEnd};
+}
+
+void tst_QApplication::wheelEventPropagation()
+{
+ QFETCH(bool, innerScrolls);
+ QFETCH(WheelEventList, events);
+
+ const QSize baseSize(500, 500);
+ const QPointF center(baseSize.width() / 2, baseSize.height() / 2);
+ int scrollStep = 50;
+
+ int argc = 1;
+ QApplication app(argc, &argv0);
+
+ QScrollArea outerArea;
+ outerArea.setObjectName("outerArea");
+ outerArea.viewport()->setObjectName("outerArea_viewport");
+ QScrollArea innerArea;
+ innerArea.setObjectName("innerArea");
+ innerArea.viewport()->setObjectName("innerArea_viewport");
+ QWidget largeWidget;
+ largeWidget.setObjectName("largeWidget");
+ QScrollBar trap(Qt::Vertical, &largeWidget);
+ trap.setObjectName("It's a trap!");
+
+ largeWidget.setFixedSize(baseSize * 8);
+
+ // classic wheel events will be grabbed by the widget under the mouse, so don't place a trap
+ if (events.at(0).phase == Qt::NoScrollPhase)
+ trap.hide();
+ // kinetic wheel events should all go to the first widget; place a trap
+ else
+ trap.setGeometry(center.x() - 50, center.y() + scrollStep, 100, baseSize.height());
+
+ // if the inner area is large enough to host the widget, then it won't scroll
+ innerArea.setWidget(&largeWidget);
+ innerArea.setFixedSize(innerScrolls ? baseSize * 4
+ : largeWidget.minimumSize() + QSize(100, 100));
+ // the outer area always scrolls
+ outerArea.setFixedSize(baseSize);
+ outerArea.setWidget(&innerArea);
+ outerArea.show();
+
+ if (!QTest::qWaitForWindowExposed(&outerArea))
+ QSKIP("Window failed to show, can't run test");
+
+ auto innerVBar = innerArea.verticalScrollBar();
+ innerVBar->setObjectName("innerArea_vbar");
+ QCOMPARE(innerVBar->isVisible(), innerScrolls);
+ auto innerHBar = innerArea.horizontalScrollBar();
+ innerHBar->setObjectName("innerArea_hbar");
+ QCOMPARE(innerHBar->isVisible(), innerScrolls);
+ auto outerVBar = outerArea.verticalScrollBar();
+ outerVBar->setObjectName("outerArea_vbar");
+ QVERIFY(outerVBar->isVisible());
+ auto outerHBar = outerArea.horizontalScrollBar();
+ outerHBar->setObjectName("outerArea_hbar");
+ QVERIFY(outerHBar->isVisible());
+
+ const QPointF global(outerArea.mapToGlobal(center.toPoint()));
+
+ QSignalSpy innerVSpy(innerVBar, &QAbstractSlider::valueChanged);
+ QSignalSpy innerHSpy(innerHBar, &QAbstractSlider::valueChanged);
+ QSignalSpy outerVSpy(outerVBar, &QAbstractSlider::valueChanged);
+ QSignalSpy outerHSpy(outerHBar, &QAbstractSlider::valueChanged);
+
+ int vcount = 0;
+ int hcount = 0;
+
+ for (const auto &event : std::as_const(events)) {
+ const QPoint pixelDelta = event.orientation == Qt::Vertical ? QPoint(0, -scrollStep) : QPoint(-scrollStep, 0);
+ const QPoint angleDelta = event.orientation == Qt::Vertical ? QPoint(0, -120) : QPoint(-120, 0);
+ QWindowSystemInterface::handleWheelEvent(outerArea.windowHandle(), center, global,
+ pixelDelta, angleDelta, Qt::NoModifier,
+ event.phase);
+ if (event.orientation == Qt::Vertical)
+ ++vcount;
+ else
+ ++hcount;
+ QCoreApplication::processEvents();
+ QCOMPARE(innerVSpy.size(), innerScrolls ? vcount : 0);
+ QCOMPARE(innerHSpy.size(), innerScrolls ? hcount : 0);
+ QCOMPARE(outerVSpy.size(), innerScrolls ? 0 : vcount);
+ QCOMPARE(outerHSpy.size(), innerScrolls ? 0 : hcount);
+ }
+}
+
void tst_QApplication::qtbug_12673()
{
#if QT_CONFIG(process)
QProcess testProcess;
QStringList arguments;
- testProcess.start("modal_helper", arguments);
+ testProcess.start("./modal_helper", arguments);
QVERIFY2(testProcess.waitForStarted(),
qPrintable(QString::fromLatin1("Cannot start 'modal_helper': %1").arg(testProcess.errorString())));
QVERIFY(testProcess.waitForFinished(20000));
@@ -2103,6 +2645,20 @@ void tst_QApplication::qtbug_12673()
#endif
}
+void tst_QApplication::qtbug_103611()
+{
+ {
+ int argc = 0;
+ QApplication app(argc, nullptr);
+ auto ll = QLocale().uiLanguages();
+ }
+ {
+ int argc = 0;
+ QApplication app(argc, nullptr);
+ auto ll = QLocale().uiLanguages();
+ }
+}
+
class NoQuitOnHideWidget : public QWidget
{
Q_OBJECT
@@ -2132,8 +2688,26 @@ public:
explicit ShowCloseShowWidget(bool showAgain, QWidget *parent = nullptr)
: QWidget(parent), showAgain(showAgain)
{
+ int timeout = 500;
+#ifdef Q_OS_ANDROID
+ // On Android, CI Android emulator is not running HW accelerated graphics and can be slow,
+ // use a longer timeout to avoid flaky failures
+ timeout = 1000;
+#endif
+ QTimer::singleShot(timeout, this, [] () { QCoreApplication::exit(1); });
+ }
+
+ bool shown = false;
+
+protected:
+ void showEvent(QShowEvent *) override
+ {
QTimer::singleShot(0, this, &ShowCloseShowWidget::doClose);
- QTimer::singleShot(500, this, [] () { QCoreApplication::exit(1); });
+ shown = true;
+ }
+ void hideEvent(QHideEvent *) override
+ {
+ shown = false;
}
private slots:
@@ -2149,16 +2723,21 @@ private:
void tst_QApplication::abortQuitOnShow()
{
+ if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive))
+ QSKIP("Wayland: This crash, see QTBUG-123172.");
+
int argc = 0;
QApplication app(argc, nullptr);
ShowCloseShowWidget window1(false);
window1.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
window1.show();
+ QVERIFY(QTest::qWaitFor([&window1](){ return window1.shown; }));
QCOMPARE(QCoreApplication::exec(), 0);
ShowCloseShowWidget window2(true);
window2.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
window2.show();
+ QVERIFY(QTest::qWaitFor([&window2](){ return window2.shown; }));
QCOMPARE(QCoreApplication::exec(), 1);
}
@@ -2166,20 +2745,17 @@ void tst_QApplication::abortQuitOnShow()
void tst_QApplication::staticFunctions()
{
QApplication::setStyle(QStringLiteral("blub"));
- QApplication::colorSpec();
- QApplication::setColorSpec(42);
QApplication::allWidgets();
QApplication::topLevelWidgets();
- QApplication::desktop();
QApplication::activePopupWidget();
+ QTest::ignoreMessage(QtWarningMsg, "Must construct a QGuiApplication first.");
QApplication::activeModalWidget();
QApplication::focusWidget();
QApplication::activeWindow();
- QApplication::setActiveWindow(nullptr);
+ QApplicationPrivate::setActiveWindow(nullptr);
QApplication::widgetAt(QPoint(0, 0));
QApplication::topLevelAt(QPoint(0, 0));
- QApplication::setGlobalStrut(QSize(0, 0));
- QApplication::globalStrut();
+ QTest::ignoreMessage(QtWarningMsg, "Must construct a QApplication first.");
QApplication::isEffectEnabled(Qt::UI_General);
QApplication::setEffectEnabled(Qt::UI_General, false);
}
@@ -2232,7 +2808,6 @@ Q_GLOBAL_STATIC(QWidget, tst_qapp_widget);
Q_GLOBAL_STATIC(QPixmap, tst_qapp_pixmap);
Q_GLOBAL_STATIC(QFont, tst_qapp_font);
Q_GLOBAL_STATIC(QRegion, tst_qapp_region);
-Q_GLOBAL_STATIC(QFontDatabase, tst_qapp_fontDatabase);
#ifndef QT_NO_CURSOR
Q_GLOBAL_STATIC(QCursor, tst_qapp_cursor);
#endif
@@ -2257,7 +2832,6 @@ void tst_QApplication::globalStaticObjectDestruction()
QVERIFY(tst_qapp_pixmap());
QVERIFY(tst_qapp_font());
QVERIFY(tst_qapp_region());
- QVERIFY(tst_qapp_fontDatabase());
#ifndef QT_NO_CURSOR
QVERIFY(tst_qapp_cursor());
#endif
@@ -2268,6 +2842,7 @@ int main(int argc, char *argv[])
{
tst_QApplication tc;
argv0 = argv[0];
+ QTEST_SET_MAIN_SOURCE_PATH
return QTest::qExec(&tc, argc, argv);
}