| 1 | /**************************************************************************** |
| 2 | ** |
| 3 | ** Copyright (C) 2016 The Qt Company Ltd. |
| 4 | ** Contact: https://www.qt.io/licensing/ |
| 5 | ** |
| 6 | ** This file is part of the test suite of the Qt Toolkit. |
| 7 | ** |
| 8 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
| 9 | ** Commercial License Usage |
| 10 | ** Licensees holding valid commercial Qt licenses may use this file in |
| 11 | ** accordance with the commercial license agreement provided with the |
| 12 | ** Software or, alternatively, in accordance with the terms contained in |
| 13 | ** a written agreement between you and The Qt Company. For licensing terms |
| 14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
| 15 | ** information use the contact form at https://www.qt.io/contact-us. |
| 16 | ** |
| 17 | ** GNU General Public License Usage |
| 18 | ** Alternatively, this file may be used under the terms of the GNU |
| 19 | ** General Public License version 3 as published by the Free Software |
| 20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
| 21 | ** included in the packaging of this file. Please review the following |
| 22 | ** information to ensure the GNU General Public License requirements will |
| 23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
| 24 | ** |
| 25 | ** $QT_END_LICENSE$ |
| 26 | ** |
| 27 | ****************************************************************************/ |
| 28 | |
| 29 | |
| 30 | #include <QtTest/QtTest> |
| 31 | #include <QtGui/QGuiApplication> |
| 32 | #include <QtGui/QWindow> |
| 33 | #include <QtGui/QScreen> |
| 34 | #include <QtGui/QCursor> |
| 35 | #include <QtGui/QFont> |
| 36 | #include <QtGui/QPalette> |
| 37 | #include <QtGui/QStyleHints> |
| 38 | #include <qpa/qplatformintegration.h> |
| 39 | #include <qpa/qwindowsysteminterface.h> |
| 40 | #include <qgenericplugin.h> |
| 41 | |
| 42 | #include <private/qguiapplication_p.h> |
| 43 | |
| 44 | #if defined(Q_OS_QNX) |
| 45 | #include <QOpenGLContext> |
| 46 | #endif |
| 47 | |
| 48 | #include <QtGui/private/qopenglcontext_p.h> |
| 49 | |
| 50 | #include <QDebug> |
| 51 | |
| 52 | #include "tst_qcoreapplication.h" |
| 53 | |
| 54 | enum { spacing = 50, windowSize = 200 }; |
| 55 | |
| 56 | class tst_QGuiApplication: public tst_QCoreApplication |
| 57 | { |
| 58 | Q_OBJECT |
| 59 | |
| 60 | private slots: |
| 61 | void initTestCase(); |
| 62 | void cleanup(); |
| 63 | void displayName(); |
| 64 | void desktopFileName(); |
| 65 | void firstWindowTitle(); |
| 66 | void windowIcon(); |
| 67 | void focusObject(); |
| 68 | void allWindows(); |
| 69 | void topLevelWindows(); |
| 70 | void abortQuitOnShow(); |
| 71 | void changeFocusWindow(); |
| 72 | void keyboardModifiers(); |
| 73 | void palette(); |
| 74 | void font(); |
| 75 | void modalWindow(); |
| 76 | void quitOnLastWindowClosed(); |
| 77 | void quitOnLastWindowClosedMulti(); |
| 78 | void dontQuitOnLastWindowClosed(); |
| 79 | void genericPluginsAndWindowSystemEvents(); |
| 80 | void layoutDirection(); |
| 81 | void globalShareContext(); |
| 82 | void testSetPaletteAttribute(); |
| 83 | |
| 84 | void staticFunctions(); |
| 85 | |
| 86 | void settableStyleHints_data(); |
| 87 | void settableStyleHints(); // Needs to run last as it changes style hints. |
| 88 | }; |
| 89 | |
| 90 | void tst_QGuiApplication::initTestCase() |
| 91 | { |
| 92 | #ifdef QT_QPA_DEFAULT_PLATFORM_NAME |
| 93 | if ((QString::compare(QStringLiteral(QT_QPA_DEFAULT_PLATFORM_NAME), |
| 94 | QStringLiteral("eglfs" ), cs: Qt::CaseInsensitive) == 0) || |
| 95 | (QString::compare(s1: QString::fromLatin1(str: qgetenv(varName: "QT_QPA_PLATFORM" )), |
| 96 | QStringLiteral("eglfs" ), cs: Qt::CaseInsensitive) == 0)) { |
| 97 | // Set env variables to disable input and cursor because eglfs is single fullscreen window |
| 98 | // and trying to initialize input and cursor will crash test. |
| 99 | qputenv(varName: "QT_QPA_EGLFS_DISABLE_INPUT" , value: "1" ); |
| 100 | qputenv(varName: "QT_QPA_EGLFS_HIDECURSOR" , value: "1" ); |
| 101 | } |
| 102 | #endif |
| 103 | } |
| 104 | |
| 105 | void tst_QGuiApplication::cleanup() |
| 106 | { |
| 107 | QVERIFY(QGuiApplication::allWindows().isEmpty()); |
| 108 | } |
| 109 | |
| 110 | void tst_QGuiApplication::displayName() |
| 111 | { |
| 112 | int argc = 1; |
| 113 | char *argv[] = { const_cast<char*>("tst_qguiapplication" ) }; |
| 114 | QGuiApplication app(argc, argv); |
| 115 | QSignalSpy spy(&app, &QGuiApplication::applicationDisplayNameChanged); |
| 116 | |
| 117 | QCOMPARE(::qAppName(), QString::fromLatin1("tst_qguiapplication" )); |
| 118 | QCOMPARE(QGuiApplication::applicationName(), QString::fromLatin1("tst_qguiapplication" )); |
| 119 | QCOMPARE(QGuiApplication::applicationDisplayName(), QString::fromLatin1("tst_qguiapplication" )); |
| 120 | |
| 121 | QGuiApplication::setApplicationName("The Core Application" ); |
| 122 | QCOMPARE(QGuiApplication::applicationName(), QString::fromLatin1("The Core Application" )); |
| 123 | QCOMPARE(QGuiApplication::applicationDisplayName(), QString::fromLatin1("The Core Application" )); |
| 124 | QCOMPARE(spy.count(), 1); |
| 125 | |
| 126 | QGuiApplication::setApplicationDisplayName("The GUI Application" ); |
| 127 | QCOMPARE(QGuiApplication::applicationDisplayName(), QString::fromLatin1("The GUI Application" )); |
| 128 | QCOMPARE(spy.count(), 2); |
| 129 | |
| 130 | QGuiApplication::setApplicationName("The Core Application 2" ); |
| 131 | QCOMPARE(QGuiApplication::applicationName(), QString::fromLatin1("The Core Application 2" )); |
| 132 | QCOMPARE(QGuiApplication::applicationDisplayName(), QString::fromLatin1("The GUI Application" )); |
| 133 | QCOMPARE(spy.count(), 2); |
| 134 | |
| 135 | QGuiApplication::setApplicationDisplayName("The GUI Application 2" ); |
| 136 | QCOMPARE(QGuiApplication::applicationDisplayName(), QString::fromLatin1("The GUI Application 2" )); |
| 137 | QCOMPARE(spy.count(), 3); |
| 138 | } |
| 139 | |
| 140 | void tst_QGuiApplication::desktopFileName() |
| 141 | { |
| 142 | int argc = 1; |
| 143 | char *argv[] = { const_cast<char*>("tst_qguiapplication" ) }; |
| 144 | QGuiApplication app(argc, argv); |
| 145 | |
| 146 | QCOMPARE(QGuiApplication::desktopFileName(), QString()); |
| 147 | |
| 148 | QGuiApplication::setDesktopFileName("io.qt.QGuiApplication.desktop" ); |
| 149 | QCOMPARE(QGuiApplication::desktopFileName(), QString::fromLatin1("io.qt.QGuiApplication.desktop" )); |
| 150 | |
| 151 | QGuiApplication::setDesktopFileName(QString()); |
| 152 | QCOMPARE(QGuiApplication::desktopFileName(), QString()); |
| 153 | } |
| 154 | |
| 155 | void tst_QGuiApplication::firstWindowTitle() |
| 156 | { |
| 157 | int argc = 3; |
| 158 | char *argv[] = { const_cast<char*>("tst_qguiapplication" ), const_cast<char*>("-qwindowtitle" ), const_cast<char*>("User Title" ) }; |
| 159 | QGuiApplication app(argc, argv); |
| 160 | QWindow window; |
| 161 | window.setTitle("Application Title" ); |
| 162 | window.show(); |
| 163 | QCOMPARE(window.title(), QString("User Title" )); |
| 164 | } |
| 165 | |
| 166 | void tst_QGuiApplication::windowIcon() |
| 167 | { |
| 168 | int argc = 3; |
| 169 | char *argv[] = { const_cast<char*>("tst_qguiapplication" ), const_cast<char*>("-qwindowicon" ), const_cast<char*>(":/icons/usericon.png" ) }; |
| 170 | QGuiApplication app(argc, argv); |
| 171 | QIcon appIcon(":/icons/appicon.png" ); |
| 172 | app.setWindowIcon(appIcon); |
| 173 | |
| 174 | QWindow window; |
| 175 | window.show(); |
| 176 | |
| 177 | QIcon userIcon(":/icons/usericon.png" ); |
| 178 | // Comparing icons is hard. cacheKey() differs because the icon was independently loaded. |
| 179 | // So we use availableSizes, after making sure that the app and user icons do have different sizes. |
| 180 | QVERIFY(userIcon.availableSizes() != appIcon.availableSizes()); |
| 181 | QCOMPARE(window.icon().availableSizes(), userIcon.availableSizes()); |
| 182 | } |
| 183 | |
| 184 | class DummyWindow : public QWindow |
| 185 | { |
| 186 | public: |
| 187 | DummyWindow() : m_focusObject(nullptr) {} |
| 188 | |
| 189 | virtual QObject *focusObject() const |
| 190 | { |
| 191 | return m_focusObject; |
| 192 | } |
| 193 | |
| 194 | void setFocusObject(QObject *object) |
| 195 | { |
| 196 | m_focusObject = object; |
| 197 | emit focusObjectChanged(object); |
| 198 | } |
| 199 | |
| 200 | QObject *m_focusObject; |
| 201 | }; |
| 202 | |
| 203 | |
| 204 | void tst_QGuiApplication::focusObject() |
| 205 | { |
| 206 | int argc = 0; |
| 207 | QGuiApplication app(argc, nullptr); |
| 208 | |
| 209 | if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::WindowActivation)) |
| 210 | QSKIP("QWindow::requestActivate() is not supported." ); |
| 211 | |
| 212 | QObject obj1, obj2, obj3; |
| 213 | const QRect screenGeometry = QGuiApplication::primaryScreen()->availableVirtualGeometry(); |
| 214 | |
| 215 | DummyWindow window1; |
| 216 | #if defined(Q_OS_QNX) |
| 217 | window1.setSurfaceType(QSurface::OpenGLSurface); |
| 218 | #endif |
| 219 | window1.resize(w: windowSize, h: windowSize); |
| 220 | window1.setTitle(QStringLiteral("focusObject:window1" )); |
| 221 | window1.setFramePosition(QPoint(screenGeometry.left() + spacing, screenGeometry.top() + spacing)); |
| 222 | DummyWindow window2; |
| 223 | window2.resize(w: windowSize, h: windowSize); |
| 224 | window2.setFramePosition(QPoint(screenGeometry.left() + 2 * spacing + windowSize, screenGeometry.top() + spacing)); |
| 225 | window2.setTitle(QStringLiteral("focusObject:window2" )); |
| 226 | |
| 227 | window1.show(); |
| 228 | |
| 229 | #if defined(Q_OS_QNX) // We either need to create a eglSurface or a create a backing store |
| 230 | // and then post the window in order for screen to show the window |
| 231 | QOpenGLContext context; |
| 232 | context.create(); |
| 233 | context.makeCurrent(&window1); |
| 234 | QVERIFY(QTest::qWaitForWindowExposed(&window1)); // Buffer swap only succeeds with exposed window |
| 235 | context.swapBuffers(&window1); |
| 236 | #endif |
| 237 | |
| 238 | QSignalSpy spy(&app, SIGNAL(focusObjectChanged(QObject*))); |
| 239 | |
| 240 | |
| 241 | // verify active window focus propagates to qguiapplication |
| 242 | window1.requestActivate(); |
| 243 | QVERIFY(QTest::qWaitForWindowActive(&window1)); |
| 244 | QCOMPARE(app.focusWindow(), &window1); |
| 245 | |
| 246 | window1.setFocusObject(&obj1); |
| 247 | QCOMPARE(app.focusObject(), &obj1); |
| 248 | QCOMPARE(spy.count(), 1); |
| 249 | |
| 250 | spy.clear(); |
| 251 | window1.setFocusObject(&obj2); |
| 252 | QCOMPARE(app.focusObject(), &obj2); |
| 253 | QCOMPARE(spy.count(), 1); |
| 254 | |
| 255 | spy.clear(); |
| 256 | window2.setFocusObject(&obj3); |
| 257 | QCOMPARE(app.focusObject(), &obj2); // not yet changed |
| 258 | window2.show(); |
| 259 | QVERIFY(QTest::qWaitForWindowExposed(&window2)); |
| 260 | QTRY_COMPARE(app.focusWindow(), &window2); |
| 261 | QCOMPARE(app.focusObject(), &obj3); |
| 262 | QCOMPARE(spy.count(), 1); |
| 263 | |
| 264 | // focus change on unfocused window does not show |
| 265 | spy.clear(); |
| 266 | window1.setFocusObject(&obj1); |
| 267 | QCOMPARE(spy.count(), 0); |
| 268 | QCOMPARE(app.focusObject(), &obj3); |
| 269 | } |
| 270 | |
| 271 | void tst_QGuiApplication::allWindows() |
| 272 | { |
| 273 | int argc = 0; |
| 274 | QGuiApplication app(argc, nullptr); |
| 275 | QWindow *window1 = new QWindow; |
| 276 | QWindow *window2 = new QWindow(window1); |
| 277 | QVERIFY(app.allWindows().contains(window1)); |
| 278 | QVERIFY(app.allWindows().contains(window2)); |
| 279 | QCOMPARE(app.allWindows().count(), 2); |
| 280 | delete window1; |
| 281 | window1 = nullptr; |
| 282 | window2 = nullptr; |
| 283 | QVERIFY(!app.allWindows().contains(window2)); |
| 284 | QVERIFY(!app.allWindows().contains(window1)); |
| 285 | QCOMPARE(app.allWindows().count(), 0); |
| 286 | } |
| 287 | |
| 288 | void tst_QGuiApplication::topLevelWindows() |
| 289 | { |
| 290 | int argc = 0; |
| 291 | QGuiApplication app(argc, nullptr); |
| 292 | QWindow *window1 = new QWindow; |
| 293 | QWindow *window2 = new QWindow(window1); |
| 294 | QVERIFY(app.topLevelWindows().contains(window1)); |
| 295 | QVERIFY(!app.topLevelWindows().contains(window2)); |
| 296 | QCOMPARE(app.topLevelWindows().count(), 1); |
| 297 | delete window1; |
| 298 | window1 = nullptr; |
| 299 | window2 = nullptr; |
| 300 | QVERIFY(!app.topLevelWindows().contains(window2)); |
| 301 | QVERIFY(!app.topLevelWindows().contains(window1)); |
| 302 | QCOMPARE(app.topLevelWindows().count(), 0); |
| 303 | } |
| 304 | |
| 305 | class ShowCloseShowWindow : public QWindow |
| 306 | { |
| 307 | Q_OBJECT |
| 308 | public: |
| 309 | ShowCloseShowWindow(bool showAgain, QWindow *parent = nullptr) |
| 310 | : QWindow(parent), showAgain(showAgain) |
| 311 | { |
| 312 | QTimer::singleShot(msec: 0, receiver: this, SLOT(doClose())); |
| 313 | QTimer::singleShot(msec: 500, receiver: this, SLOT(exitApp())); |
| 314 | } |
| 315 | |
| 316 | private slots: |
| 317 | void doClose() { |
| 318 | close(); |
| 319 | if (showAgain) |
| 320 | show(); |
| 321 | } |
| 322 | |
| 323 | void exitApp() { |
| 324 | qApp->exit(retcode: 1); |
| 325 | } |
| 326 | |
| 327 | private: |
| 328 | bool showAgain; |
| 329 | }; |
| 330 | |
| 331 | void tst_QGuiApplication::abortQuitOnShow() |
| 332 | { |
| 333 | int argc = 0; |
| 334 | QGuiApplication app(argc, nullptr); |
| 335 | const QRect screenGeometry = QGuiApplication::primaryScreen()->availableVirtualGeometry(); |
| 336 | |
| 337 | QScopedPointer<QWindow> window1(new ShowCloseShowWindow(false)); |
| 338 | window1->resize(w: windowSize, h: windowSize); |
| 339 | window1->setFramePosition(QPoint(screenGeometry.left() + spacing, screenGeometry.top() + spacing)); |
| 340 | window1->setTitle(QStringLiteral("abortQuitOnShow:window1" )); |
| 341 | window1->show(); |
| 342 | QCOMPARE(app.exec(), 0); |
| 343 | |
| 344 | QScopedPointer<QWindow> window2(new ShowCloseShowWindow(true)); |
| 345 | window2->setTitle(QStringLiteral("abortQuitOnShow:window2" )); |
| 346 | window2->resize(w: windowSize, h: windowSize); |
| 347 | window2->setFramePosition(QPoint(screenGeometry.left() + 2 * spacing + windowSize, screenGeometry.top() + spacing)); |
| 348 | window2->show(); |
| 349 | QCOMPARE(app.exec(), 1); |
| 350 | } |
| 351 | |
| 352 | |
| 353 | class FocusChangeWindow: public QWindow |
| 354 | { |
| 355 | protected: |
| 356 | virtual bool event(QEvent *ev) |
| 357 | { |
| 358 | if (ev->type() == QEvent::FocusAboutToChange) |
| 359 | windowDuringFocusAboutToChange = qGuiApp->focusWindow(); |
| 360 | return QWindow::event(ev); |
| 361 | } |
| 362 | |
| 363 | virtual void focusOutEvent(QFocusEvent *) |
| 364 | { |
| 365 | windowDuringFocusOut = qGuiApp->focusWindow(); |
| 366 | } |
| 367 | |
| 368 | public: |
| 369 | FocusChangeWindow() : QWindow(), windowDuringFocusAboutToChange(nullptr), windowDuringFocusOut(nullptr) {} |
| 370 | |
| 371 | QWindow *windowDuringFocusAboutToChange; |
| 372 | QWindow *windowDuringFocusOut; |
| 373 | }; |
| 374 | |
| 375 | void tst_QGuiApplication::changeFocusWindow() |
| 376 | { |
| 377 | #ifdef Q_OS_WINRT |
| 378 | QSKIP("WinRt does not support multiple native windows." ); |
| 379 | #endif |
| 380 | int argc = 0; |
| 381 | QGuiApplication app(argc, nullptr); |
| 382 | |
| 383 | if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::WindowActivation)) |
| 384 | QSKIP("QWindow::requestActivate() is not supported." ); |
| 385 | |
| 386 | const QRect screenGeometry = QGuiApplication::primaryScreen()->availableVirtualGeometry(); |
| 387 | |
| 388 | // focus is changed between FocusAboutToChange and FocusChanged |
| 389 | FocusChangeWindow window1; |
| 390 | #if defined(Q_OS_QNX) |
| 391 | window1.setSurfaceType(QSurface::OpenGLSurface); |
| 392 | #endif |
| 393 | window1.resize(w: windowSize, h: windowSize); |
| 394 | window1.setFramePosition(QPoint(screenGeometry.left() + spacing, screenGeometry.top() + spacing)); |
| 395 | window1.setTitle(QStringLiteral("changeFocusWindow:window1" )); |
| 396 | window1.show(); |
| 397 | #if defined(Q_OS_QNX) // We either need to create a eglSurface or a create a backing store |
| 398 | // and then post the window in order for screen to show the window |
| 399 | QOpenGLContext context; |
| 400 | context.create(); |
| 401 | context.makeCurrent(&window1); |
| 402 | QVERIFY(QTest::qWaitForWindowExposed(&window1)); // Buffer swap only succeeds with exposed window |
| 403 | context.swapBuffers(&window1); |
| 404 | #endif |
| 405 | FocusChangeWindow window2; |
| 406 | #if defined(Q_OS_QNX) |
| 407 | window2.setSurfaceType(QSurface::OpenGLSurface); |
| 408 | #endif |
| 409 | window2.resize(w: windowSize, h: windowSize); |
| 410 | window2.setFramePosition(QPoint(screenGeometry.left() + 2 * spacing + windowSize, screenGeometry.top() + spacing)); |
| 411 | window2.setTitle(QStringLiteral("changeFocusWindow:window2" )); |
| 412 | window2.show(); |
| 413 | #if defined(Q_OS_QNX) // We either need to create a eglSurface or a create a backing store |
| 414 | // and then post the window in order for screen to show the window |
| 415 | context.makeCurrent(&window2); |
| 416 | QVERIFY(QTest::qWaitForWindowExposed(&window2)); // Buffer swap only succeeds with exposed window |
| 417 | context.swapBuffers(&window2); |
| 418 | #endif |
| 419 | QVERIFY(QTest::qWaitForWindowExposed(&window1)); |
| 420 | QVERIFY(QTest::qWaitForWindowExposed(&window2)); |
| 421 | window1.requestActivate(); |
| 422 | QTRY_COMPARE(app.focusWindow(), &window1); |
| 423 | |
| 424 | window2.requestActivate(); |
| 425 | QTRY_COMPARE(app.focusWindow(), &window2); |
| 426 | QCOMPARE(window1.windowDuringFocusAboutToChange, &window1); |
| 427 | QCOMPARE(window1.windowDuringFocusOut, &window2); |
| 428 | } |
| 429 | |
| 430 | void tst_QGuiApplication::keyboardModifiers() |
| 431 | { |
| 432 | int argc = 0; |
| 433 | QGuiApplication app(argc, nullptr); |
| 434 | const QRect screenGeometry = QGuiApplication::primaryScreen()->availableVirtualGeometry(); |
| 435 | |
| 436 | QScopedPointer<QWindow> window(new QWindow); |
| 437 | window->resize(w: windowSize, h: windowSize); |
| 438 | window->setFramePosition(QPoint(screenGeometry.left() + spacing, screenGeometry.top() + spacing)); |
| 439 | window->setTitle(QStringLiteral("keyboardModifiers" )); |
| 440 | |
| 441 | window->show(); |
| 442 | QVERIFY(QTest::qWaitForWindowExposed(window.data())); |
| 443 | QCOMPARE(QGuiApplication::keyboardModifiers(), Qt::NoModifier); |
| 444 | |
| 445 | // mouse events |
| 446 | QPoint center = window->geometry().center(); |
| 447 | QTest::mouseEvent(action: QTest::MousePress, window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: center); |
| 448 | QCOMPARE(QGuiApplication::keyboardModifiers(), Qt::NoModifier); |
| 449 | QTest::mouseEvent(action: QTest::MouseRelease, window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: center); |
| 450 | QCOMPARE(QGuiApplication::keyboardModifiers(), Qt::NoModifier); |
| 451 | QTest::mouseEvent(action: QTest::MousePress, window: window.data(), button: Qt::RightButton, stateKey: Qt::ControlModifier, pos: center); |
| 452 | QCOMPARE(QGuiApplication::keyboardModifiers(), Qt::ControlModifier); |
| 453 | QTest::mouseEvent(action: QTest::MouseRelease, window: window.data(), button: Qt::RightButton, stateKey: Qt::ControlModifier, pos: center); |
| 454 | QCOMPARE(QGuiApplication::keyboardModifiers(), Qt::ControlModifier); |
| 455 | |
| 456 | // shortcut events |
| 457 | QTest::keyEvent(action: QTest::Shortcut, window: window.data(), key: Qt::Key_5, modifier: Qt::MetaModifier); |
| 458 | QCOMPARE(QGuiApplication::keyboardModifiers(), Qt::MetaModifier); |
| 459 | QTest::keyEvent(action: QTest::Shortcut, window: window.data(), key: Qt::Key_Period, modifier: Qt::NoModifier); |
| 460 | QCOMPARE(QGuiApplication::keyboardModifiers(), Qt::NoModifier); |
| 461 | QTest::keyEvent(action: QTest::Shortcut, window: window.data(), key: Qt::Key_0, modifier: Qt::ControlModifier); |
| 462 | QCOMPARE(QGuiApplication::keyboardModifiers(), Qt::ControlModifier); |
| 463 | |
| 464 | // key events |
| 465 | QTest::keyEvent(action: QTest::Press, window: window.data(), key: Qt::Key_C); |
| 466 | QCOMPARE(QGuiApplication::keyboardModifiers(), Qt::NoModifier); |
| 467 | QTest::keyEvent(action: QTest::Release, window: window.data(), key: Qt::Key_C); |
| 468 | QCOMPARE(QGuiApplication::keyboardModifiers(), Qt::NoModifier); |
| 469 | |
| 470 | QTest::keyEvent(action: QTest::Press, window: window.data(), key: Qt::Key_U, modifier: Qt::ControlModifier); |
| 471 | QCOMPARE(QGuiApplication::keyboardModifiers(), Qt::ControlModifier); |
| 472 | QTest::keyEvent(action: QTest::Release, window: window.data(), key: Qt::Key_U, modifier: Qt::ControlModifier); |
| 473 | QCOMPARE(QGuiApplication::keyboardModifiers(), Qt::ControlModifier); |
| 474 | |
| 475 | QTest::keyEvent(action: QTest::Press, window: window.data(), key: Qt::Key_T); |
| 476 | QCOMPARE(QGuiApplication::keyboardModifiers(), Qt::NoModifier); |
| 477 | QTest::keyEvent(action: QTest::Release, window: window.data(), key: Qt::Key_T); |
| 478 | QCOMPARE(QGuiApplication::keyboardModifiers(), Qt::NoModifier); |
| 479 | |
| 480 | QTest::keyEvent(action: QTest::Press, window: window.data(), key: Qt::Key_E, modifier: Qt::ControlModifier); |
| 481 | QCOMPARE(QGuiApplication::keyboardModifiers(), Qt::ControlModifier); |
| 482 | QTest::keyEvent(action: QTest::Release, window: window.data(), key: Qt::Key_E, modifier: Qt::ControlModifier); |
| 483 | QCOMPARE(QGuiApplication::keyboardModifiers(), Qt::ControlModifier); |
| 484 | |
| 485 | // wheel events |
| 486 | QPoint global = window->mapToGlobal(pos: center); |
| 487 | QPoint delta(0, 1); |
| 488 | QWindowSystemInterface::handleWheelEvent(window: window.data(), local: center, global, pixelDelta: delta, angleDelta: delta, mods: Qt::NoModifier); |
| 489 | QWindowSystemInterface::sendWindowSystemEvents(flags: QEventLoop::AllEvents); |
| 490 | QCOMPARE(QGuiApplication::keyboardModifiers(), Qt::NoModifier); |
| 491 | QWindowSystemInterface::handleWheelEvent(window: window.data(), local: center, global, pixelDelta: delta, angleDelta: delta, mods: Qt::AltModifier); |
| 492 | QWindowSystemInterface::sendWindowSystemEvents(flags: QEventLoop::AllEvents); |
| 493 | QCOMPARE(QGuiApplication::keyboardModifiers(), Qt::AltModifier); |
| 494 | QWindowSystemInterface::handleWheelEvent(window: window.data(), local: center, global, pixelDelta: delta, angleDelta: delta, mods: Qt::ControlModifier); |
| 495 | QWindowSystemInterface::sendWindowSystemEvents(flags: QEventLoop::AllEvents); |
| 496 | QCOMPARE(QGuiApplication::keyboardModifiers(), Qt::ControlModifier); |
| 497 | |
| 498 | // touch events |
| 499 | QList<const QTouchDevice *> touchDevices = QTouchDevice::devices(); |
| 500 | if (!touchDevices.isEmpty()) { |
| 501 | QTouchDevice *touchDevice = const_cast<QTouchDevice *>(touchDevices.first()); |
| 502 | QTest::touchEvent(window: window.data(), device: touchDevice).press(touchId: 1, pt: center).release(touchId: 1, pt: center); |
| 503 | QCOMPARE(QGuiApplication::keyboardModifiers(), Qt::NoModifier); |
| 504 | } |
| 505 | |
| 506 | window->close(); |
| 507 | } |
| 508 | |
| 509 | /* |
| 510 | Compare actual against expected but ignore unset roles. |
| 511 | |
| 512 | Comparing palettes via operator== will compare all roles. |
| 513 | */ |
| 514 | static bool palettesMatch(const QPalette &actual, const QPalette &expected) |
| 515 | { |
| 516 | if (actual.resolve() != expected.resolve()) |
| 517 | return false; |
| 518 | |
| 519 | for (int i = 0; i < QPalette::NColorGroups; i++) { |
| 520 | for (int j = 0; j < QPalette::NColorRoles; j++) { |
| 521 | const auto g = QPalette::ColorGroup(i); |
| 522 | const auto r = QPalette::ColorRole(j); |
| 523 | if (expected.isBrushSet(cg: g, cr: r)) { |
| 524 | if (actual.brush(cg: g, cr: r) != expected.brush(cg: g, cr: r)) |
| 525 | return false; |
| 526 | } |
| 527 | } |
| 528 | } |
| 529 | return true; |
| 530 | } |
| 531 | |
| 532 | void tst_QGuiApplication::palette() |
| 533 | { |
| 534 | // Getting the palette before application construction should work |
| 535 | QPalette paletteBeforeAppConstruction = QGuiApplication::palette(); |
| 536 | // And should be reflected in the default constructed palette |
| 537 | QCOMPARE(paletteBeforeAppConstruction, QPalette()); |
| 538 | |
| 539 | int argc = 1; |
| 540 | char *argv[] = { const_cast<char*>("tst_qguiapplication" ) }; |
| 541 | QGuiApplication app(argc, argv); |
| 542 | |
| 543 | // The same should be true after application construction |
| 544 | QCOMPARE(QGuiApplication::palette(), QPalette()); |
| 545 | |
| 546 | // The default application palette is not resolved |
| 547 | QVERIFY(!QGuiApplication::palette().resolve()); |
| 548 | |
| 549 | QSignalSpy signalSpy(&app, SIGNAL(paletteChanged(QPalette))); |
| 550 | |
| 551 | QPalette oldPalette = QGuiApplication::palette(); |
| 552 | QPalette newPalette = QPalette(Qt::red); |
| 553 | |
| 554 | QGuiApplication::setPalette(newPalette); |
| 555 | QVERIFY(palettesMatch(QGuiApplication::palette(), newPalette)); |
| 556 | QCOMPARE(signalSpy.count(), 1); |
| 557 | QVERIFY(palettesMatch(signalSpy.at(0).at(0).value<QPalette>(), newPalette)); |
| 558 | QCOMPARE(QGuiApplication::palette(), QPalette()); |
| 559 | |
| 560 | QGuiApplication::setPalette(oldPalette); |
| 561 | QVERIFY(palettesMatch(QGuiApplication::palette(), oldPalette)); |
| 562 | QCOMPARE(signalSpy.count(), 2); |
| 563 | QVERIFY(palettesMatch(signalSpy.at(1).at(0).value<QPalette>(), oldPalette)); |
| 564 | QCOMPARE(QGuiApplication::palette(), QPalette()); |
| 565 | |
| 566 | QGuiApplication::setPalette(oldPalette); |
| 567 | QVERIFY(palettesMatch(QGuiApplication::palette(), oldPalette)); |
| 568 | QCOMPARE(signalSpy.count(), 2); |
| 569 | QCOMPARE(QGuiApplication::palette(), QPalette()); |
| 570 | } |
| 571 | |
| 572 | void tst_QGuiApplication::font() |
| 573 | { |
| 574 | int argc = 1; |
| 575 | char *argv[] = { const_cast<char*>("tst_qguiapplication" ) }; |
| 576 | QGuiApplication app(argc, argv); |
| 577 | QSignalSpy signalSpy(&app, SIGNAL(fontChanged(QFont))); |
| 578 | |
| 579 | QFont oldFont = QGuiApplication::font(); |
| 580 | QFont newFont = QFont("BogusFont" , 33); |
| 581 | |
| 582 | QGuiApplication::setFont(newFont); |
| 583 | QCOMPARE(QGuiApplication::font(), newFont); |
| 584 | QCOMPARE(signalSpy.count(), 1); |
| 585 | QCOMPARE(signalSpy.at(0).at(0), QVariant(newFont)); |
| 586 | |
| 587 | QGuiApplication::setFont(oldFont); |
| 588 | QCOMPARE(QGuiApplication::font(), oldFont); |
| 589 | QCOMPARE(signalSpy.count(), 2); |
| 590 | QCOMPARE(signalSpy.at(1).at(0), QVariant(oldFont)); |
| 591 | |
| 592 | QGuiApplication::setFont(oldFont); |
| 593 | QCOMPARE(QGuiApplication::font(), oldFont); |
| 594 | QCOMPARE(signalSpy.count(), 2); |
| 595 | } |
| 596 | |
| 597 | class BlockableWindow : public QWindow |
| 598 | { |
| 599 | Q_OBJECT |
| 600 | public: |
| 601 | int blocked; |
| 602 | int leaves; |
| 603 | int enters; |
| 604 | |
| 605 | inline explicit BlockableWindow(QWindow *parent = nullptr) |
| 606 | : QWindow(parent), blocked(false), leaves(0), enters(0) {} |
| 607 | |
| 608 | bool event(QEvent *e) |
| 609 | { |
| 610 | switch (e->type()) { |
| 611 | case QEvent::WindowBlocked: |
| 612 | ++blocked; |
| 613 | break; |
| 614 | case QEvent::WindowUnblocked: |
| 615 | --blocked; |
| 616 | break; |
| 617 | case QEvent::Leave: |
| 618 | leaves++; |
| 619 | break; |
| 620 | case QEvent::Enter: |
| 621 | enters++; |
| 622 | break; |
| 623 | default: |
| 624 | break; |
| 625 | } |
| 626 | return QWindow::event(e); |
| 627 | } |
| 628 | |
| 629 | void resetCounts() |
| 630 | { |
| 631 | leaves = 0; |
| 632 | enters = 0; |
| 633 | } |
| 634 | }; |
| 635 | |
| 636 | void tst_QGuiApplication::modalWindow() |
| 637 | { |
| 638 | #ifdef Q_OS_WINRT |
| 639 | QSKIP("WinRt does not support multiple native windows." ); |
| 640 | #endif |
| 641 | int argc = 0; |
| 642 | QGuiApplication app(argc, nullptr); |
| 643 | const QRect screenGeometry = QGuiApplication::primaryScreen()->availableVirtualGeometry(); |
| 644 | |
| 645 | int x = screenGeometry.left() + spacing; |
| 646 | int y = screenGeometry.top() + spacing; |
| 647 | |
| 648 | QScopedPointer<BlockableWindow> window1(new BlockableWindow); |
| 649 | window1->setTitle(QStringLiteral("window1" )); |
| 650 | window1->resize(w: windowSize, h: windowSize); |
| 651 | window1->setFramePosition(QPoint(x, y)); |
| 652 | BlockableWindow *childWindow1 = new BlockableWindow(window1.data()); |
| 653 | childWindow1->resize(w: windowSize / 2, h: windowSize / 2); |
| 654 | x += spacing + windowSize; |
| 655 | |
| 656 | QScopedPointer<BlockableWindow> window2(new BlockableWindow); |
| 657 | window2->setTitle(QStringLiteral("window2" )); |
| 658 | window2->setFlags(window2->flags() & Qt::Tool); // QTBUG-32433, don't be fooled by unusual window flags. |
| 659 | window2->resize(w: windowSize, h: windowSize); |
| 660 | window2->setFramePosition(QPoint(x, y)); |
| 661 | x += spacing + windowSize; |
| 662 | |
| 663 | QScopedPointer<BlockableWindow> windowModalWindow1(new BlockableWindow); |
| 664 | windowModalWindow1->setTitle(QStringLiteral("windowModalWindow1" )); |
| 665 | windowModalWindow1->setTransientParent(window1.data()); |
| 666 | windowModalWindow1->setModality(Qt::WindowModal); |
| 667 | windowModalWindow1->resize(w: windowSize, h: windowSize); |
| 668 | windowModalWindow1->setFramePosition(QPoint(x, y)); |
| 669 | x += spacing + windowSize; |
| 670 | |
| 671 | QScopedPointer<BlockableWindow> windowModalWindow2(new BlockableWindow); |
| 672 | windowModalWindow2->setTitle(QStringLiteral("windowModalWindow2" )); |
| 673 | windowModalWindow2->setTransientParent(windowModalWindow1.data()); |
| 674 | windowModalWindow2->setModality(Qt::WindowModal); |
| 675 | windowModalWindow2->resize(w: windowSize, h: windowSize); |
| 676 | windowModalWindow2->setFramePosition(QPoint(x, y)); |
| 677 | x = screenGeometry.left() + spacing; |
| 678 | y += spacing + windowSize; |
| 679 | |
| 680 | QScopedPointer<BlockableWindow> applicationModalWindow1(new BlockableWindow); |
| 681 | applicationModalWindow1->setTitle(QStringLiteral("applicationModalWindow1" )); |
| 682 | applicationModalWindow1->setModality(Qt::ApplicationModal); |
| 683 | applicationModalWindow1->resize(w: windowSize, h: windowSize); |
| 684 | applicationModalWindow1->setFramePosition(QPoint(x, y)); |
| 685 | |
| 686 | #ifndef QT_NO_CURSOR // Get the mouse cursor out of the way since we are manually sending enter/leave. |
| 687 | QCursor::setPos(QPoint(x + 2 * spacing + windowSize, y)); |
| 688 | #endif |
| 689 | |
| 690 | // show the 2 windows, nothing is blocked |
| 691 | window1->show(); |
| 692 | window2->show(); |
| 693 | QVERIFY(QTest::qWaitForWindowExposed(window1.data())); |
| 694 | QVERIFY(QTest::qWaitForWindowExposed(window2.data())); |
| 695 | QCOMPARE(app.modalWindow(), static_cast<QWindow *>(nullptr)); |
| 696 | QCOMPARE(window1->blocked, 0); |
| 697 | QCOMPARE(childWindow1->blocked, 0); |
| 698 | QCOMPARE(window2->blocked, 0); |
| 699 | QCOMPARE(windowModalWindow1->blocked, 0); |
| 700 | QCOMPARE(windowModalWindow2->blocked, 0); |
| 701 | QCOMPARE(applicationModalWindow1->blocked, 0); |
| 702 | |
| 703 | // enter mouse in window1 |
| 704 | QWindowSystemInterface::handleEnterEvent(window: window1.data()); |
| 705 | QGuiApplication::processEvents(); |
| 706 | QCOMPARE(window1->enters, 1); |
| 707 | QCOMPARE(window1->leaves, 0); |
| 708 | |
| 709 | // show applicationModalWindow1, everything is blocked |
| 710 | applicationModalWindow1->show(); |
| 711 | QCOMPARE(app.modalWindow(), applicationModalWindow1.data()); |
| 712 | QCOMPARE(window1->blocked, 1); |
| 713 | QCOMPARE(childWindow1->blocked, 1); // QTBUG-32242, blocked status needs to be set on children as well. |
| 714 | QCOMPARE(window2->blocked, 1); |
| 715 | QCOMPARE(windowModalWindow1->blocked, 1); |
| 716 | QCOMPARE(windowModalWindow2->blocked, 1); |
| 717 | QCOMPARE(applicationModalWindow1->blocked, 0); |
| 718 | |
| 719 | // opening modal causes leave for previously entered window, but not others |
| 720 | QGuiApplication::processEvents(); |
| 721 | QCOMPARE(window1->enters, 1); |
| 722 | QCOMPARE(window1->leaves, 1); |
| 723 | QCOMPARE(window2->enters, 0); |
| 724 | QCOMPARE(window2->leaves, 0); |
| 725 | QCOMPARE(applicationModalWindow1->enters, 0); |
| 726 | QCOMPARE(applicationModalWindow1->leaves, 0); |
| 727 | window1->resetCounts(); |
| 728 | |
| 729 | // Try entering/leaving blocked window2 - no events should reach it |
| 730 | QWindowSystemInterface::handleEnterEvent(window: window2.data()); |
| 731 | QGuiApplication::processEvents(); |
| 732 | QWindowSystemInterface::handleLeaveEvent(window: window2.data()); |
| 733 | QGuiApplication::processEvents(); |
| 734 | QCOMPARE(window2->enters, 0); |
| 735 | QCOMPARE(window2->leaves, 0); |
| 736 | |
| 737 | // everything is unblocked when applicationModalWindow1 is hidden |
| 738 | applicationModalWindow1->hide(); |
| 739 | QCOMPARE(app.modalWindow(), static_cast<QWindow *>(nullptr)); |
| 740 | QCOMPARE(window1->blocked, 0); |
| 741 | QCOMPARE(childWindow1->blocked, 0); // QTBUG-32242, blocked status needs to be set on children as well. |
| 742 | QCOMPARE(window2->blocked, 0); |
| 743 | QCOMPARE(windowModalWindow1->blocked, 0); |
| 744 | QCOMPARE(windowModalWindow2->blocked, 0); |
| 745 | QCOMPARE(applicationModalWindow1->blocked, 0); |
| 746 | |
| 747 | // Enter window2 - should not be blocked |
| 748 | QWindowSystemInterface::handleEnterEvent(window: window2.data()); |
| 749 | QGuiApplication::processEvents(); |
| 750 | QCOMPARE(window2->enters, 1); |
| 751 | QCOMPARE(window2->leaves, 0); |
| 752 | |
| 753 | // show the windowModalWindow1, only window1 is blocked |
| 754 | windowModalWindow1->show(); |
| 755 | QCOMPARE(app.modalWindow(), windowModalWindow1.data()); |
| 756 | QCOMPARE(window1->blocked, 1); |
| 757 | QCOMPARE(window2->blocked, 0); |
| 758 | QCOMPARE(windowModalWindow1->blocked, 0); |
| 759 | QCOMPARE(windowModalWindow2->blocked, 0); |
| 760 | QCOMPARE(applicationModalWindow1->blocked, 0); |
| 761 | |
| 762 | // opening window modal window doesn't cause leave for unblocked window |
| 763 | QGuiApplication::processEvents(); |
| 764 | QCOMPARE(window1->enters, 0); |
| 765 | QCOMPARE(window1->leaves, 0); |
| 766 | QCOMPARE(window2->enters, 1); |
| 767 | QCOMPARE(window2->leaves, 0); |
| 768 | QCOMPARE(windowModalWindow1->enters, 0); |
| 769 | QCOMPARE(windowModalWindow1->leaves, 0); |
| 770 | |
| 771 | // show the windowModalWindow2, windowModalWindow1 is blocked as well |
| 772 | windowModalWindow2->show(); |
| 773 | QCOMPARE(app.modalWindow(), windowModalWindow2.data()); |
| 774 | QCOMPARE(window1->blocked, 1); |
| 775 | QCOMPARE(window2->blocked, 0); |
| 776 | QCOMPARE(windowModalWindow1->blocked, 1); |
| 777 | QCOMPARE(windowModalWindow2->blocked, 0); |
| 778 | QCOMPARE(applicationModalWindow1->blocked, 0); |
| 779 | |
| 780 | // hide windowModalWindow1, nothing is unblocked |
| 781 | windowModalWindow1->hide(); |
| 782 | QCOMPARE(app.modalWindow(), windowModalWindow2.data()); |
| 783 | QCOMPARE(window1->blocked, 1); |
| 784 | QCOMPARE(window2->blocked, 0); |
| 785 | QCOMPARE(windowModalWindow1->blocked, 1); |
| 786 | QCOMPARE(windowModalWindow2->blocked, 0); |
| 787 | QCOMPARE(applicationModalWindow1->blocked, 0); |
| 788 | |
| 789 | // hide windowModalWindow2, windowModalWindow1 and window1 are unblocked |
| 790 | windowModalWindow2->hide(); |
| 791 | QCOMPARE(app.modalWindow(), static_cast<QWindow *>(nullptr)); |
| 792 | QCOMPARE(window1->blocked, 0); |
| 793 | QCOMPARE(window2->blocked, 0); |
| 794 | QCOMPARE(windowModalWindow1->blocked, 0); |
| 795 | QCOMPARE(windowModalWindow2->blocked, 0); |
| 796 | QCOMPARE(applicationModalWindow1->blocked, 0); |
| 797 | |
| 798 | // show windowModalWindow1 again, window1 is blocked |
| 799 | windowModalWindow1->show(); |
| 800 | QCOMPARE(app.modalWindow(), windowModalWindow1.data()); |
| 801 | QCOMPARE(window1->blocked, 1); |
| 802 | QCOMPARE(window2->blocked, 0); |
| 803 | QCOMPARE(windowModalWindow1->blocked, 0); |
| 804 | QCOMPARE(windowModalWindow2->blocked, 0); |
| 805 | QCOMPARE(applicationModalWindow1->blocked, 0); |
| 806 | |
| 807 | // show windowModalWindow2 again, windowModalWindow1 is also blocked |
| 808 | windowModalWindow2->show(); |
| 809 | QCOMPARE(app.modalWindow(), windowModalWindow2.data()); |
| 810 | QCOMPARE(window1->blocked, 1); |
| 811 | QCOMPARE(window2->blocked, 0); |
| 812 | QCOMPARE(windowModalWindow1->blocked, 1); |
| 813 | QCOMPARE(windowModalWindow2->blocked, 0); |
| 814 | QCOMPARE(applicationModalWindow1->blocked, 0); |
| 815 | |
| 816 | // show applicationModalWindow1, everything is blocked |
| 817 | applicationModalWindow1->show(); |
| 818 | QCOMPARE(app.modalWindow(), applicationModalWindow1.data()); |
| 819 | QCOMPARE(window1->blocked, 1); |
| 820 | QCOMPARE(window2->blocked, 1); |
| 821 | QCOMPARE(windowModalWindow1->blocked, 1); |
| 822 | QCOMPARE(windowModalWindow2->blocked, 1); |
| 823 | QCOMPARE(applicationModalWindow1->blocked, 0); |
| 824 | |
| 825 | // window2 gets finally the leave |
| 826 | QGuiApplication::processEvents(); |
| 827 | QCOMPARE(window1->enters, 0); |
| 828 | QCOMPARE(window1->leaves, 0); |
| 829 | QCOMPARE(window2->enters, 1); |
| 830 | QCOMPARE(window2->leaves, 1); |
| 831 | QCOMPARE(windowModalWindow1->enters, 0); |
| 832 | QCOMPARE(windowModalWindow1->leaves, 0); |
| 833 | QCOMPARE(applicationModalWindow1->enters, 0); |
| 834 | QCOMPARE(applicationModalWindow1->leaves, 0); |
| 835 | |
| 836 | // hide applicationModalWindow1, windowModalWindow1 and window1 are blocked |
| 837 | applicationModalWindow1->hide(); |
| 838 | QCOMPARE(app.modalWindow(), windowModalWindow2.data()); |
| 839 | QCOMPARE(window1->blocked, 1); |
| 840 | QCOMPARE(window2->blocked, 0); |
| 841 | QCOMPARE(windowModalWindow1->blocked, 1); |
| 842 | QCOMPARE(windowModalWindow2->blocked, 0); |
| 843 | QCOMPARE(applicationModalWindow1->blocked, 0); |
| 844 | |
| 845 | // hide windowModalWindow2, window1 is blocked |
| 846 | windowModalWindow2->hide(); |
| 847 | QCOMPARE(app.modalWindow(), windowModalWindow1.data()); |
| 848 | QCOMPARE(window1->blocked, 1); |
| 849 | QCOMPARE(window2->blocked, 0); |
| 850 | QCOMPARE(windowModalWindow1->blocked, 0); |
| 851 | QCOMPARE(windowModalWindow2->blocked, 0); |
| 852 | QCOMPARE(applicationModalWindow1->blocked, 0); |
| 853 | |
| 854 | // hide windowModalWindow1, everything is unblocked |
| 855 | windowModalWindow1->hide(); |
| 856 | QCOMPARE(app.modalWindow(), static_cast<QWindow *>(nullptr)); |
| 857 | QCOMPARE(window1->blocked, 0); |
| 858 | QCOMPARE(window2->blocked, 0); |
| 859 | QCOMPARE(windowModalWindow1->blocked, 0); |
| 860 | QCOMPARE(windowModalWindow2->blocked, 0); |
| 861 | QCOMPARE(applicationModalWindow1->blocked, 0); |
| 862 | |
| 863 | window2->hide(); |
| 864 | window1->hide(); |
| 865 | } |
| 866 | |
| 867 | void tst_QGuiApplication::quitOnLastWindowClosed() |
| 868 | { |
| 869 | int argc = 0; |
| 870 | QGuiApplication app(argc, nullptr); |
| 871 | const QRect screenGeometry = QGuiApplication::primaryScreen()->availableVirtualGeometry(); |
| 872 | |
| 873 | QTimer timer; |
| 874 | timer.setInterval(100); |
| 875 | |
| 876 | QSignalSpy spyAboutToQuit(&app, &QCoreApplication::aboutToQuit); |
| 877 | QSignalSpy spyTimeout(&timer, &QTimer::timeout); |
| 878 | |
| 879 | QWindow mainWindow; |
| 880 | mainWindow.setTitle(QStringLiteral("quitOnLastWindowClosedMainWindow" )); |
| 881 | mainWindow.resize(w: windowSize, h: windowSize); |
| 882 | mainWindow.setFramePosition(QPoint(screenGeometry.left() + spacing, screenGeometry.top() + spacing)); |
| 883 | |
| 884 | QWindow dialog; |
| 885 | dialog.setTransientParent(&mainWindow); |
| 886 | dialog.setTitle(QStringLiteral("quitOnLastWindowClosedDialog" )); |
| 887 | dialog.resize(w: windowSize, h: windowSize); |
| 888 | dialog.setFramePosition(QPoint(screenGeometry.left() + 2 * spacing + windowSize, screenGeometry.top() + spacing)); |
| 889 | |
| 890 | QVERIFY(app.quitOnLastWindowClosed()); |
| 891 | |
| 892 | mainWindow.show(); |
| 893 | dialog.show(); |
| 894 | QVERIFY(QTest::qWaitForWindowExposed(&dialog)); |
| 895 | |
| 896 | timer.start(); |
| 897 | QTimer::singleShot(interval: 1000, receiver: &mainWindow, slot: &QWindow::close); // This should quit the application |
| 898 | QTimer::singleShot(interval: 2000, context: &app, slot: QCoreApplication::quit); // This makes sure we quit even if it didn't |
| 899 | |
| 900 | app.exec(); |
| 901 | |
| 902 | QCOMPARE(spyAboutToQuit.count(), 1); |
| 903 | // Should be around 10 if closing caused the quit |
| 904 | QVERIFY2(spyTimeout.count() < 15, QByteArray::number(spyTimeout.count()).constData()); |
| 905 | } |
| 906 | |
| 907 | void tst_QGuiApplication::quitOnLastWindowClosedMulti() |
| 908 | { |
| 909 | int argc = 0; |
| 910 | QGuiApplication app(argc, nullptr); |
| 911 | const QRect screenGeometry = QGuiApplication::primaryScreen()->availableVirtualGeometry(); |
| 912 | |
| 913 | QTimer timer; |
| 914 | timer.setInterval(100); |
| 915 | |
| 916 | QSignalSpy spyAboutToQuit(&app, &QCoreApplication::aboutToQuit); |
| 917 | QSignalSpy spyTimeout(&timer, &QTimer::timeout); |
| 918 | |
| 919 | QWindow mainWindow; |
| 920 | mainWindow.setTitle(QStringLiteral("quitOnLastWindowClosedMultiMainWindow" )); |
| 921 | mainWindow.resize(w: windowSize, h: windowSize); |
| 922 | mainWindow.setFramePosition(QPoint(screenGeometry.left() + spacing, screenGeometry.top() + spacing)); |
| 923 | |
| 924 | QWindow dialog; |
| 925 | dialog.setTitle(QStringLiteral("quitOnLastWindowClosedMultiDialog" )); |
| 926 | dialog.resize(w: windowSize, h: windowSize); |
| 927 | dialog.setFramePosition(QPoint(screenGeometry.left() + 2 * spacing + windowSize, screenGeometry.top() + spacing)); |
| 928 | |
| 929 | QVERIFY(!dialog.transientParent()); |
| 930 | QVERIFY(app.quitOnLastWindowClosed()); |
| 931 | |
| 932 | mainWindow.show(); |
| 933 | dialog.show(); |
| 934 | QVERIFY(QTest::qWaitForWindowExposed(&dialog)); |
| 935 | |
| 936 | timer.start(); |
| 937 | QTimer::singleShot(interval: 1000, receiver: &mainWindow, slot: &QWindow::close); // This should not quit the application |
| 938 | QTimer::singleShot(interval: 2000, context: &app, slot: &QCoreApplication::quit); |
| 939 | |
| 940 | app.exec(); |
| 941 | |
| 942 | QCOMPARE(spyAboutToQuit.count(), 1); |
| 943 | // Should be around 20 if closing did not cause the quit |
| 944 | QVERIFY2(spyTimeout.count() > 15, QByteArray::number(spyTimeout.count()).constData()); |
| 945 | } |
| 946 | |
| 947 | void tst_QGuiApplication::dontQuitOnLastWindowClosed() |
| 948 | { |
| 949 | int argc = 0; |
| 950 | QGuiApplication app(argc, nullptr); |
| 951 | app.setQuitOnLastWindowClosed(false); |
| 952 | |
| 953 | QTimer timer; |
| 954 | timer.setInterval(2000); |
| 955 | timer.setSingleShot(true); |
| 956 | QObject::connect(sender: &timer, signal: &QTimer::timeout, context: &app, slot: &QCoreApplication::quit); |
| 957 | |
| 958 | QSignalSpy spyLastWindowClosed(&app, &QGuiApplication::lastWindowClosed); |
| 959 | QSignalSpy spyTimeout(&timer, &QTimer::timeout); |
| 960 | |
| 961 | QScopedPointer<QWindow> mainWindow(new QWindow); |
| 962 | |
| 963 | mainWindow->show(); |
| 964 | |
| 965 | QTimer::singleShot(interval: 1000, receiver: mainWindow.data(), slot: &QWindow::close); // This should not quit the application |
| 966 | timer.start(); |
| 967 | |
| 968 | app.exec(); |
| 969 | |
| 970 | app.setQuitOnLastWindowClosed(true); // restore underlying static to default value |
| 971 | |
| 972 | QCOMPARE(spyTimeout.count(), 1); // quit timer fired |
| 973 | QCOMPARE(spyLastWindowClosed.count(), 1); // lastWindowClosed emitted |
| 974 | } |
| 975 | |
| 976 | static Qt::ScreenOrientation testOrientationToSend = Qt::PrimaryOrientation; |
| 977 | |
| 978 | class TestPlugin : public QObject |
| 979 | { |
| 980 | Q_OBJECT |
| 981 | public: |
| 982 | TestPlugin() |
| 983 | { |
| 984 | QScreen* screen = QGuiApplication::primaryScreen(); |
| 985 | // Make sure the orientation we want to send doesn't get filtered out. |
| 986 | screen->setOrientationUpdateMask(screen->orientationUpdateMask() | testOrientationToSend); |
| 987 | QWindowSystemInterface::handleScreenOrientationChange(screen, newOrientation: testOrientationToSend); |
| 988 | } |
| 989 | }; |
| 990 | |
| 991 | class TestPluginFactory : public QGenericPlugin |
| 992 | { |
| 993 | Q_OBJECT |
| 994 | Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QGenericPluginFactoryInterface" FILE "testplugin.json" ) |
| 995 | public: |
| 996 | QObject* create(const QString &key, const QString &) |
| 997 | { |
| 998 | if (key == "testplugin" ) |
| 999 | return new TestPlugin; |
| 1000 | return nullptr; |
| 1001 | } |
| 1002 | }; |
| 1003 | |
| 1004 | class TestEventReceiver : public QObject |
| 1005 | { |
| 1006 | Q_OBJECT |
| 1007 | public: |
| 1008 | int customEvents; |
| 1009 | |
| 1010 | TestEventReceiver() |
| 1011 | : customEvents(0) |
| 1012 | {} |
| 1013 | |
| 1014 | virtual void customEvent(QEvent *) |
| 1015 | { |
| 1016 | customEvents++; |
| 1017 | } |
| 1018 | }; |
| 1019 | |
| 1020 | #include "tst_qguiapplication.moc" |
| 1021 | |
| 1022 | void tst_QGuiApplication::genericPluginsAndWindowSystemEvents() |
| 1023 | { |
| 1024 | testOrientationToSend = Qt::InvertedLandscapeOrientation; |
| 1025 | |
| 1026 | TestEventReceiver testReceiver; |
| 1027 | QCoreApplication::postEvent(receiver: &testReceiver, event: new QEvent(QEvent::User)); |
| 1028 | QCOMPARE(testReceiver.customEvents, 0); |
| 1029 | |
| 1030 | #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) |
| 1031 | QStaticPlugin testPluginInfo(qt_plugin_instance, qt_plugin_query_metadata); |
| 1032 | #else |
| 1033 | QStaticPlugin testPluginInfo; |
| 1034 | testPluginInfo.instance = qt_plugin_instance; |
| 1035 | testPluginInfo.rawMetaData = qt_plugin_query_metadata; |
| 1036 | #endif |
| 1037 | qRegisterStaticPluginFunction(staticPlugin: testPluginInfo); |
| 1038 | int argc = 3; |
| 1039 | char *argv[] = { const_cast<char*>(QTest::currentAppName()), const_cast<char*>("-plugin" ), const_cast<char*>("testplugin" ) }; |
| 1040 | QGuiApplication app(argc, argv); |
| 1041 | |
| 1042 | QVERIFY(QGuiApplication::primaryScreen()); |
| 1043 | QCOMPARE(QGuiApplication::primaryScreen()->orientation(), testOrientationToSend); |
| 1044 | |
| 1045 | QCOMPARE(testReceiver.customEvents, 0); |
| 1046 | QCoreApplication::sendPostedEvents(receiver: &testReceiver); |
| 1047 | QCOMPARE(testReceiver.customEvents, 1); |
| 1048 | } |
| 1049 | |
| 1050 | Q_DECLARE_METATYPE(Qt::LayoutDirection) |
| 1051 | void tst_QGuiApplication::layoutDirection() |
| 1052 | { |
| 1053 | qRegisterMetaType<Qt::LayoutDirection>(); |
| 1054 | |
| 1055 | Qt::LayoutDirection oldDirection = QGuiApplication::layoutDirection(); |
| 1056 | Qt::LayoutDirection newDirection = oldDirection == Qt::LeftToRight ? Qt::RightToLeft : Qt::LeftToRight; |
| 1057 | |
| 1058 | QGuiApplication::setLayoutDirection(newDirection); |
| 1059 | QCOMPARE(QGuiApplication::layoutDirection(), newDirection); |
| 1060 | |
| 1061 | int argc = 1; |
| 1062 | char *argv[] = { const_cast<char*>("tst_qguiapplication" ) }; |
| 1063 | QGuiApplication app(argc, argv); |
| 1064 | QSignalSpy signalSpy(&app, SIGNAL(layoutDirectionChanged(Qt::LayoutDirection))); |
| 1065 | |
| 1066 | QGuiApplication::setLayoutDirection(oldDirection); |
| 1067 | QCOMPARE(QGuiApplication::layoutDirection(), oldDirection); |
| 1068 | QCOMPARE(signalSpy.count(), 1); |
| 1069 | QCOMPARE(signalSpy.at(0).at(0).toInt(), static_cast<int>(oldDirection)); |
| 1070 | |
| 1071 | QGuiApplication::setLayoutDirection(oldDirection); |
| 1072 | QCOMPARE(QGuiApplication::layoutDirection(), oldDirection); |
| 1073 | QCOMPARE(signalSpy.count(), 1); |
| 1074 | } |
| 1075 | |
| 1076 | void tst_QGuiApplication::globalShareContext() |
| 1077 | { |
| 1078 | #ifndef QT_NO_OPENGL |
| 1079 | // Test that there is a global share context when requested. |
| 1080 | QCoreApplication::setAttribute(attribute: Qt::AA_ShareOpenGLContexts); |
| 1081 | int argc = 1; |
| 1082 | char *argv[] = { const_cast<char*>("tst_qguiapplication" ) }; |
| 1083 | QScopedPointer<QGuiApplication> app(new QGuiApplication(argc, argv)); |
| 1084 | QOpenGLContext *ctx = qt_gl_global_share_context(); |
| 1085 | QVERIFY(ctx); |
| 1086 | app.reset(); |
| 1087 | ctx = qt_gl_global_share_context(); |
| 1088 | QVERIFY(!ctx); |
| 1089 | |
| 1090 | // Test that there is no global share context by default. |
| 1091 | QCoreApplication::setAttribute(attribute: Qt::AA_ShareOpenGLContexts, on: false); |
| 1092 | app.reset(other: new QGuiApplication(argc, argv)); |
| 1093 | ctx = qt_gl_global_share_context(); |
| 1094 | QVERIFY(!ctx); |
| 1095 | #else |
| 1096 | QSKIP("No OpenGL support" ); |
| 1097 | #endif |
| 1098 | } |
| 1099 | |
| 1100 | void tst_QGuiApplication::testSetPaletteAttribute() |
| 1101 | { |
| 1102 | QCoreApplication::setAttribute(attribute: Qt::AA_SetPalette, on: false); |
| 1103 | int argc = 1; |
| 1104 | char *argv[] = { const_cast<char*>("tst_qguiapplication" ) }; |
| 1105 | |
| 1106 | QGuiApplication app(argc, argv); |
| 1107 | |
| 1108 | QVERIFY(!QCoreApplication::testAttribute(Qt::AA_SetPalette)); |
| 1109 | QPalette palette; |
| 1110 | palette.setColor(acr: QPalette::WindowText, acolor: Qt::red); |
| 1111 | QGuiApplication::setPalette(palette); |
| 1112 | |
| 1113 | QVERIFY(QCoreApplication::testAttribute(Qt::AA_SetPalette)); |
| 1114 | |
| 1115 | QGuiApplication::setPalette(QPalette()); |
| 1116 | QVERIFY(!QCoreApplication::testAttribute(Qt::AA_SetPalette)); |
| 1117 | } |
| 1118 | |
| 1119 | // Test that static functions do not crash if there is no application instance. |
| 1120 | void tst_QGuiApplication::staticFunctions() |
| 1121 | { |
| 1122 | QGuiApplication::setApplicationDisplayName(QString()); |
| 1123 | QGuiApplication::applicationDisplayName(); |
| 1124 | QGuiApplication::allWindows(); |
| 1125 | QGuiApplication::topLevelWindows(); |
| 1126 | QGuiApplication::topLevelAt(pos: QPoint(0, 0)); |
| 1127 | QGuiApplication::setWindowIcon(QIcon()); |
| 1128 | QGuiApplication::windowIcon(); |
| 1129 | QGuiApplication::platformName(); |
| 1130 | QGuiApplication::modalWindow(); |
| 1131 | QGuiApplication::focusWindow(); |
| 1132 | QGuiApplication::focusObject(); |
| 1133 | QGuiApplication::primaryScreen(); |
| 1134 | QGuiApplication::screens(); |
| 1135 | QGuiApplication::overrideCursor(); |
| 1136 | QGuiApplication::setOverrideCursor(QCursor()); |
| 1137 | QGuiApplication::changeOverrideCursor(QCursor()); |
| 1138 | QGuiApplication::restoreOverrideCursor(); |
| 1139 | QGuiApplication::keyboardModifiers(); |
| 1140 | QGuiApplication::queryKeyboardModifiers(); |
| 1141 | QGuiApplication::mouseButtons(); |
| 1142 | QGuiApplication::setLayoutDirection(Qt::LeftToRight); |
| 1143 | QGuiApplication::layoutDirection(); |
| 1144 | QGuiApplication::styleHints(); |
| 1145 | QGuiApplication::setDesktopSettingsAware(true); |
| 1146 | QGuiApplication::desktopSettingsAware(); |
| 1147 | QGuiApplication::inputMethod(); |
| 1148 | QGuiApplication::platformNativeInterface(); |
| 1149 | QGuiApplication::platformFunction(QByteArrayLiteral("bla" )); |
| 1150 | QGuiApplication::setQuitOnLastWindowClosed(true); |
| 1151 | QGuiApplication::quitOnLastWindowClosed(); |
| 1152 | QGuiApplication::applicationState(); |
| 1153 | |
| 1154 | QPixmap::defaultDepth(); |
| 1155 | } |
| 1156 | |
| 1157 | void tst_QGuiApplication::settableStyleHints_data() |
| 1158 | { |
| 1159 | QTest::addColumn<bool>(name: "appInstance" ); |
| 1160 | QTest::newRow(dataTag: "app" ) << true; |
| 1161 | QTest::newRow(dataTag: "no-app" ) << false; |
| 1162 | } |
| 1163 | |
| 1164 | void tst_QGuiApplication::settableStyleHints() |
| 1165 | { |
| 1166 | QFETCH(bool, appInstance); |
| 1167 | int argc = 0; |
| 1168 | QScopedPointer<QGuiApplication> app; |
| 1169 | if (appInstance) |
| 1170 | app.reset(other: new QGuiApplication(argc, nullptr)); |
| 1171 | |
| 1172 | const int keyboardInputInterval = 555; |
| 1173 | QGuiApplication::styleHints()->setKeyboardInputInterval(keyboardInputInterval); |
| 1174 | QCOMPARE(QGuiApplication::styleHints()->keyboardInputInterval(), keyboardInputInterval); |
| 1175 | } |
| 1176 | |
| 1177 | QTEST_APPLESS_MAIN(tst_QGuiApplication) |
| 1178 | |