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#define QT_STATICPLUGIN
30#include <QtWidgets/qstyleplugin.h>
31
32#include <qdebug.h>
33
34#include <QtTest/QtTest>
35
36#include <QtCore/QAbstractEventDispatcher>
37#include <QtCore/QFileInfo>
38#include <QtCore/QDir>
39#if QT_CONFIG(process)
40# include <QtCore/QProcess>
41#endif
42#include <QtCore/private/qeventloop_p.h>
43
44#include <QtGui/QFontDatabase>
45#include <QtGui/QClipboard>
46
47#include <QtWidgets/QApplication>
48#include <QtWidgets/QMessageBox>
49#include <QtWidgets/QStyleFactory>
50#include <QtWidgets/QHBoxLayout>
51#include <QtWidgets/QPushButton>
52#include <QtWidgets/QLineEdit>
53#include <QtWidgets/QLabel>
54#include <QtWidgets/QMainWindow>
55#include <QtWidgets/QScrollArea>
56#include <QtWidgets/QScrollBar>
57#include <QtWidgets/QHeaderView>
58#include <QtWidgets/private/qapplication_p.h>
59#include <QtWidgets/QStyle>
60#include <QtWidgets/qproxystyle.h>
61#include <QtWidgets/QTextEdit>
62
63#include <qpa/qwindowsysteminterface.h>
64#include <qpa/qwindowsysteminterface_p.h>
65#include <private/qhighdpiscaling_p.h>
66
67#include <algorithm>
68
69Q_LOGGING_CATEGORY(lcTests, "qt.widgets.tests")
70
71QT_BEGIN_NAMESPACE
72
73extern bool Q_GUI_EXPORT qt_tab_all_widgets(); // from qapplication.cpp
74QT_END_NAMESPACE
75
76class tst_QApplication : public QObject
77{
78Q_OBJECT
79
80private slots:
81 void cleanup();
82 void sendEventsOnProcessEvents(); // this must be the first test
83 void staticSetup();
84
85 void alert();
86
87 void multiple_data();
88 void multiple();
89
90 void nonGui();
91
92 void setFont_data();
93 void setFont();
94 void setFontForClass();
95
96 void args_data();
97 void args();
98 void appName();
99
100 void lastWindowClosed();
101 void quitOnLastWindowClosed();
102 void closeAllWindows();
103 void testDeleteLater();
104 void testDeleteLaterProcessEvents1();
105 void testDeleteLaterProcessEvents2();
106 void testDeleteLaterProcessEvents3();
107 void testDeleteLaterProcessEvents4();
108 void testDeleteLaterProcessEvents5();
109
110#if QT_CONFIG(library)
111 void libraryPaths();
112 void libraryPaths_qt_plugin_path();
113 void libraryPaths_qt_plugin_path_2();
114#endif
115
116 void sendPostedEvents();
117
118 void thread();
119 void desktopSettingsAware();
120
121 void setActiveWindow();
122
123 void focusWidget();
124 void focusChanged();
125 void focusOut();
126 void focusMouseClick();
127
128 void execAfterExit();
129
130#if QT_CONFIG(wheelevent)
131 void wheelScrollLines();
132#endif
133
134 void task109149();
135
136 void style();
137 void applicationPalettePolish();
138
139 void allWidgets();
140 void topLevelWidgets();
141
142 void setAttribute();
143
144 void touchEventPropagation();
145 void wheelEventPropagation_data();
146 void wheelEventPropagation();
147
148 void qtbug_12673();
149 void noQuitOnHide();
150
151 void globalStaticObjectDestruction(); // run this last
152
153 void abortQuitOnShow();
154
155 void staticFunctions();
156
157 void settableStyleHints_data();
158 void settableStyleHints(); // Needs to run last as it changes style hints.
159};
160
161class EventSpy : public QObject
162{
163 Q_OBJECT
164
165public:
166 QList<int> recordedEvents;
167 bool eventFilter(QObject *, QEvent *event) override
168 {
169 recordedEvents.append(t: event->type());
170 return false;
171 }
172};
173
174void tst_QApplication::sendEventsOnProcessEvents()
175{
176 int argc = 0;
177 QApplication app(argc, nullptr);
178
179 EventSpy spy;
180 app.installEventFilter(filterObj: &spy);
181
182 QCoreApplication::postEvent(receiver: &app, event: new QEvent(QEvent::Type(QEvent::User + 1)));
183 QCoreApplication::processEvents();
184 QVERIFY(spy.recordedEvents.contains(QEvent::User + 1));
185}
186
187
188class CloseEventTestWindow : public QWidget
189{
190public:
191 void closeEvent(QCloseEvent *event) override
192 {
193 QWidget dialog;
194 dialog.setWindowTitle(QLatin1String("CloseEventTestWindow"));
195 dialog.show();
196 dialog.close();
197
198 event->ignore();
199 }
200};
201
202static char *argv0;
203
204void tst_QApplication::cleanup()
205{
206// TODO: Add cleanup code here.
207// This will be executed immediately after each test is run.
208 QVERIFY(QApplication::topLevelWidgets().isEmpty());
209}
210
211void tst_QApplication::staticSetup()
212{
213 QVERIFY(!qApp);
214
215 QStyle *style = QStyleFactory::create(QLatin1String("Windows"));
216 QVERIFY(style);
217 QApplication::setStyle(style);
218
219 bool palette_changed = false;
220 QPalette pal;
221 QApplication::setPalette(pal);
222
223 /*QFont font;
224 QApplication::setFont(font);*/
225
226 int argc = 0;
227 QApplication app(argc, nullptr);
228 QObject::connect(sender: &app, signal: &QApplication::paletteChanged, slot: [&palette_changed]{ palette_changed = true; });
229 QVERIFY(!palette_changed);
230 qApp->setPalette(QPalette(Qt::red));
231 QVERIFY(palette_changed);
232}
233
234
235// QApp subclass that exits the event loop after 150ms
236class TestApplication : public QApplication
237{
238public:
239 TestApplication(int &argc, char **argv) : QApplication( argc, argv)
240 {
241 startTimer(interval: 150);
242 }
243
244 void timerEvent(QTimerEvent *) override
245 {
246 quit();
247 }
248};
249
250void tst_QApplication::alert()
251{
252#ifdef Q_OS_WINRT
253 QSKIP("WinRT does not support more than 1 native widget at the same time");
254#endif
255 int argc = 0;
256 QApplication app(argc, nullptr);
257 QApplication::alert(widget: nullptr, duration: 0);
258
259 QWidget widget;
260 widget.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
261 QWidget widget2;
262 widget2.setWindowTitle(QLatin1String(QTest::currentTestFunction()) + QLatin1Char('2'));
263 QApplication::alert(widget: &widget, duration: 100);
264 widget.show();
265 widget2.show();
266 QVERIFY(QTest::qWaitForWindowExposed(&widget));
267 QVERIFY(QTest::qWaitForWindowExposed(&widget2));
268 QApplication::alert(widget: &widget, duration: -1);
269 QApplication::alert(widget: &widget, duration: 250);
270 widget2.activateWindow();
271 QApplication::setActiveWindow(&widget2);
272 QApplication::alert(widget: &widget, duration: 0);
273 widget.activateWindow();
274 QApplication::setActiveWindow(&widget);
275 QApplication::alert(widget: &widget, duration: 200);
276}
277
278void tst_QApplication::multiple_data()
279{
280 QTest::addColumn<QStringList>(name: "features");
281
282 // return a list of things to try
283 QTest::newRow( dataTag: "data0" ) << QStringList( "" );
284 QTest::newRow( dataTag: "data1" ) << QStringList( "QFont" );
285 QTest::newRow( dataTag: "data2" ) << QStringList( "QPixmap" );
286 QTest::newRow( dataTag: "data3" ) << QStringList( "QWidget" );
287}
288
289void tst_QApplication::multiple()
290{
291 QFETCH(QStringList,features);
292
293 int i = 0;
294 int argc = 0;
295 while (i++ < 5) {
296 TestApplication app(argc, nullptr);
297
298 if (features.contains(str: "QFont")) {
299 // create font and force loading
300 QFont font("Arial", 12);
301 QFontInfo finfo(font);
302 finfo.exactMatch();
303 }
304 if (features.contains(str: "QPixmap")) {
305 QPixmap pix(100, 100);
306 pix.fill(fillColor: Qt::black);
307 }
308 if (features.contains(str: "QWidget")) {
309 QWidget widget;
310 }
311
312 QVERIFY(!QCoreApplication::exec());
313 }
314}
315
316void tst_QApplication::nonGui()
317{
318#ifdef Q_OS_HPUX
319 // ### This is only to allow us to generate a test report for now.
320 QSKIP("This test shuts down the window manager on HP-UX.");
321#endif
322
323 int argc = 0;
324 QApplication app(argc, nullptr, false);
325 QCOMPARE(qApp, &app);
326}
327
328void tst_QApplication::setFont_data()
329{
330 QTest::addColumn<QString>(name: "family");
331 QTest::addColumn<int>(name: "pointsize");
332 QTest::addColumn<bool>(name: "beforeAppConstructor");
333
334 int argc = 0;
335 QApplication app(argc, nullptr); // Needed for QFontDatabase
336
337 QFontDatabase fdb;
338 const QStringList &families = fdb.families();
339 for (int i = 0, count = qMin(a: 3, b: families.size()); i < count; ++i) {
340 const auto &family = families.at(i);
341 const QStringList &styles = fdb.styles(family);
342 if (!styles.isEmpty()) {
343 QList<int> sizes = fdb.pointSizes(family, style: styles.constFirst());
344 if (sizes.isEmpty())
345 sizes = QFontDatabase::standardSizes();
346 if (!sizes.isEmpty()) {
347 const QByteArray name = QByteArrayLiteral("data") + QByteArray::number(i);
348 QTest::newRow(dataTag: (name + 'a').constData())
349 << family
350 << sizes.constFirst()
351 << false;
352 QTest::newRow(dataTag: (name + 'b').constData())
353 << family
354 << sizes.constFirst()
355 << true;
356 }
357 }
358 }
359
360 QTest::newRow(dataTag: "nonexistingfont after") << "nosuchfont_probably_quiteunlikely"
361 << 0 << false;
362 QTest::newRow(dataTag: "nonexistingfont before") << "nosuchfont_probably_quiteunlikely"
363 << 0 << true;
364
365 QTest::newRow(dataTag: "largescaleable after") << "smoothtimes" << 100 << false;
366 QTest::newRow(dataTag: "largescaleable before") << "smoothtimes" << 100 << true;
367
368 QTest::newRow(dataTag: "largeunscaleale after") << "helvetica" << 100 << false;
369 QTest::newRow(dataTag: "largeunscaleale before") << "helvetica" << 100 << true;
370}
371
372void tst_QApplication::setFont()
373{
374 QFETCH( QString, family );
375 QFETCH( int, pointsize );
376 QFETCH( bool, beforeAppConstructor );
377
378 QFont font( family, pointsize );
379 if (beforeAppConstructor) {
380 QApplication::setFont( font );
381 QCOMPARE(QApplication::font(), font);
382 }
383
384 int argc = 0;
385 QApplication app(argc, nullptr);
386 if (!beforeAppConstructor)
387 QApplication::setFont( font );
388
389 QCOMPARE( app.font(), font );
390}
391
392class tstHeaderView : public QHeaderView
393{
394 Q_OBJECT
395public:
396 explicit tstHeaderView(Qt::Orientation orientation, QWidget *parent = nullptr)
397 : QHeaderView(orientation, parent)
398 {}
399};
400class tstFrame : public QFrame { Q_OBJECT };
401class tstWidget : public QWidget { Q_OBJECT };
402
403void tst_QApplication::setFontForClass()
404{
405 // QTBUG-89910
406 // If a default font was not registered for the widget's class,
407 // it returns the default font of its nearest registered superclass.
408 int argc = 0;
409 QApplication app(argc, nullptr);
410
411 QFont font;
412 int pointSize = 10;
413 const QByteArrayList classNames{"QHeaderView", "QAbstractItemView", "QAbstractScrollView", "QFrame", "QWidget", "QObject"};
414 for (auto className : classNames) {
415 font.setPointSizeF(pointSize++);
416 app.setFont(font, className: className.constData());
417 }
418
419 tstHeaderView headView(Qt::Horizontal);
420 tstFrame frame;
421 tstWidget widget;
422
423 QFont headViewFont = QApplication::font(&headView);
424 QFont frameFont = QApplication::font(&frame);
425 QFont widgetFont = QApplication::font(&widget);
426
427 QCOMPARE(headViewFont.pointSize(), QApplication::font("QHeaderView").pointSize());
428 QCOMPARE(frameFont.pointSize(), QApplication::font("QFrame").pointSize());
429 QCOMPARE(widgetFont.pointSize(), QApplication::font("QWidget").pointSize());
430}
431
432void tst_QApplication::args_data()
433{
434 QTest::addColumn<int>(name: "argc_in");
435 QTest::addColumn<QString>(name: "args_in");
436 QTest::addColumn<int>(name: "argc_out");
437 QTest::addColumn<QString>(name: "args_out");
438
439 QTest::newRow( dataTag: "App name" ) << 1 << "/usr/bin/appname" << 1 << "/usr/bin/appname";
440 QTest::newRow( dataTag: "No arguments" ) << 0 << QString() << 0 << QString();
441 QTest::newRow( dataTag: "App name, style" ) << 3 << "/usr/bin/appname -style windows" << 1 << "/usr/bin/appname";
442 QTest::newRow( dataTag: "App name, style, arbitrary, reverse" ) << 5 << "/usr/bin/appname -style windows -arbitrary -reverse"
443 << 2 << "/usr/bin/appname -arbitrary";
444}
445
446void tst_QApplication::task109149()
447{
448 int argc = 0;
449 QApplication app(argc, nullptr);
450 QApplication::setFont(QFont("helvetica", 100));
451
452 QWidget w;
453 w.setWindowTitle("hello");
454 w.show();
455
456 QCoreApplication::processEvents();
457}
458
459static char **QString2cstrings(const QString &args)
460{
461 static QByteArrayList cache;
462
463 const auto &list = args.splitRef(sep: ' ');
464 auto argarray = new char*[list.count() + 1];
465
466 int i = 0;
467 for (; i < list.size(); ++i ) {
468 QByteArray l1 = list[i].toLatin1();
469 argarray[i] = l1.data();
470 cache.append(t: l1);
471 }
472 argarray[i] = nullptr;
473
474 return argarray;
475}
476
477static QString cstrings2QString( char **args )
478{
479 QString string;
480 if ( !args )
481 return string;
482
483 int i = 0;
484 while ( args[i] ) {
485 string += args[i];
486 if ( args[i+1] )
487 string += QLatin1Char(' ');
488 ++i;
489 }
490 return string;
491}
492
493void tst_QApplication::args()
494{
495 QFETCH( int, argc_in );
496 QFETCH( QString, args_in );
497 QFETCH( int, argc_out );
498 QFETCH( QString, args_out );
499
500 char **argv = QString2cstrings( args: args_in );
501
502 QApplication app( argc_in, argv);
503 QString argv_out = cstrings2QString(args: argv);
504
505 QCOMPARE( argc_in, argc_out );
506 QCOMPARE( argv_out, args_out );
507
508 delete [] argv;
509 // Make sure we switch back to native style.
510 QApplicationPrivate::styleOverride.clear();
511}
512
513void tst_QApplication::appName()
514{
515 char argv0[] = "tst_qapplication";
516 char *argv[] = { argv0, nullptr };
517 int argc = 1;
518 QApplication app(argc, argv);
519 QCOMPARE(::qAppName(), QString::fromLatin1("tst_qapplication"));
520 QCOMPARE(QCoreApplication::applicationName(), QString::fromLatin1("tst_qapplication"));
521}
522
523class CloseWidget : public QWidget
524{
525 Q_OBJECT
526public:
527 CloseWidget()
528 {
529 startTimer(interval: 500);
530 }
531
532protected:
533 void timerEvent(QTimerEvent *) override
534 {
535 close();
536 }
537
538};
539
540void tst_QApplication::lastWindowClosed()
541{
542 int argc = 0;
543 QApplication app(argc, nullptr);
544
545 QSignalSpy spy(&app, &QGuiApplication::lastWindowClosed);
546
547 QPointer<QDialog> dialog = new QDialog;
548 dialog->setWindowTitle(QLatin1String(QTest::currentTestFunction()) + QLatin1String("Dialog"));
549 QVERIFY(dialog->testAttribute(Qt::WA_QuitOnClose));
550 QTimer::singleShot(interval: 1000, receiver: dialog.data(), slot: &QDialog::accept);
551 dialog->exec();
552 QVERIFY(dialog);
553 QCOMPARE(spy.count(), 0);
554
555 QPointer<CloseWidget>widget = new CloseWidget;
556 widget->setWindowTitle(QLatin1String(QTest::currentTestFunction()) + QLatin1String("CloseWidget"));
557 QVERIFY(widget->testAttribute(Qt::WA_QuitOnClose));
558 widget->show();
559 QObject::connect(sender: &app, signal: &QGuiApplication::lastWindowClosed, receiver: widget.data(), slot: &QObject::deleteLater);
560 QCoreApplication::exec();
561 QVERIFY(!widget);
562 QCOMPARE(spy.count(), 1);
563 spy.clear();
564
565 delete dialog;
566
567 // show 3 windows, close them, should only get lastWindowClosed once
568 QWidget w1;
569 w1.setWindowTitle(QLatin1String(QTest::currentTestFunction()) + QLatin1Char('1'));
570 QWidget w2;
571 w1.setWindowTitle(QLatin1String(QTest::currentTestFunction()) + QLatin1Char('2'));
572 QWidget w3;
573 w1.setWindowTitle(QLatin1String(QTest::currentTestFunction()) + QLatin1Char('3'));
574 w1.show();
575 w2.show();
576 w3.show();
577
578 QTimer::singleShot(interval: 1000, context: &app, slot: &QApplication::closeAllWindows);
579 QCoreApplication::exec();
580 QCOMPARE(spy.count(), 1);
581}
582
583class QuitOnLastWindowClosedDialog : public QDialog
584{
585 Q_OBJECT
586public:
587 QuitOnLastWindowClosedDialog()
588 {
589 QHBoxLayout *hbox = new QHBoxLayout(this);
590 m_okButton = new QPushButton("&ok", this);
591
592 hbox->addWidget(m_okButton);
593 connect(sender: m_okButton, signal: &QAbstractButton::clicked, receiver: this, slot: &QDialog::accept);
594 connect(sender: m_okButton, signal: &QAbstractButton::clicked, receiver: this, slot: &QuitOnLastWindowClosedDialog::ok_clicked);
595 }
596
597public slots:
598 void animateOkClick() { m_okButton->animateClick(); }
599
600 void ok_clicked()
601 {
602 QDialog other;
603
604 QTimer timer;
605 connect(sender: &timer, signal: &QTimer::timeout, receiver: &other, slot: &QDialog::accept);
606 QSignalSpy spy(&timer, &QTimer::timeout);
607 QSignalSpy appSpy(qApp, &QGuiApplication::lastWindowClosed);
608
609 timer.start(msec: 1000);
610 other.exec();
611
612 // verify that the eventloop ran and let the timer fire
613 QCOMPARE(spy.count(), 1);
614 QCOMPARE(appSpy.count(), 1);
615 }
616
617private:
618 QPushButton *m_okButton;
619};
620
621class QuitOnLastWindowClosedWindow : public QWidget
622{
623 Q_OBJECT
624
625public:
626 QuitOnLastWindowClosedWindow() = default;
627
628public slots:
629 void execDialogThenShow()
630 {
631 QDialog dialog;
632 dialog.setWindowTitle(QLatin1String("QuitOnLastWindowClosedWindow Dialog"));
633 QTimer timer1;
634 connect(sender: &timer1, signal: &QTimer::timeout, receiver: &dialog, slot: &QDialog::accept);
635 QSignalSpy spy1(&timer1, &QTimer::timeout);
636 timer1.setSingleShot(true);
637 timer1.start(msec: 1000);
638 dialog.exec();
639 QCOMPARE(spy1.count(), 1);
640
641 show();
642 }
643};
644
645void tst_QApplication::quitOnLastWindowClosed()
646{
647 {
648 int argc = 0;
649 QApplication app(argc, nullptr);
650
651 QuitOnLastWindowClosedDialog d;
652 d.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
653 d.show();
654 QTimer::singleShot(interval: 1000, receiver: &d, slot: &QuitOnLastWindowClosedDialog::animateOkClick);
655
656 QSignalSpy appSpy(&app, &QGuiApplication::lastWindowClosed);
657 QCoreApplication::exec();
658
659 // lastWindowClosed() signal should only be sent after the last dialog is closed
660 QCOMPARE(appSpy.count(), 2);
661 }
662 {
663 int argc = 0;
664 QApplication app(argc, nullptr);
665 QSignalSpy appSpy(&app, &QGuiApplication::lastWindowClosed);
666
667 QDialog dialog;
668 dialog.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
669 QTimer timer1;
670 connect(sender: &timer1, signal: &QTimer::timeout, receiver: &dialog, slot: &QDialog::accept);
671 QSignalSpy spy1(&timer1, &QTimer::timeout);
672 timer1.setSingleShot(true);
673 timer1.start(msec: 1000);
674 dialog.exec();
675 QCOMPARE(spy1.count(), 1);
676 QCOMPARE(appSpy.count(), 0);
677
678 QTimer timer2;
679 connect(sender: &timer2, signal: &QTimer::timeout, context: &app, slot: &QCoreApplication::quit);
680 QSignalSpy spy2(&timer2, &QTimer::timeout);
681 timer2.setSingleShot(true);
682 timer2.start(msec: 1000);
683 int returnValue = QCoreApplication::exec();
684 QCOMPARE(returnValue, 0);
685 QCOMPARE(spy2.count(), 1);
686 QCOMPARE(appSpy.count(), 0);
687 }
688 {
689 int argc = 0;
690 QApplication app(argc, nullptr);
691 QTimer timer;
692 timer.setInterval(100);
693
694 QSignalSpy spy(&app, &QCoreApplication::aboutToQuit);
695 QSignalSpy spy2(&timer, &QTimer::timeout);
696
697 QMainWindow mainWindow;
698 mainWindow.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
699 QDialog *dialog = new QDialog(&mainWindow);
700 dialog->setWindowTitle(QLatin1String(QTest::currentTestFunction()) + QLatin1String("Dialog"));
701
702 QVERIFY(app.quitOnLastWindowClosed());
703 QVERIFY(mainWindow.testAttribute(Qt::WA_QuitOnClose));
704 QVERIFY(dialog->testAttribute(Qt::WA_QuitOnClose));
705
706 mainWindow.show();
707 QVERIFY(QTest::qWaitForWindowExposed(&mainWindow));
708 dialog->show();
709 QVERIFY(QTest::qWaitForWindowExposed(dialog));
710
711 timer.start();
712 QTimer::singleShot(interval: 1000, receiver: &mainWindow, slot: &QWidget::close); // This should quit the application
713 QTimer::singleShot(interval: 2000, context: &app, slot: &QCoreApplication::quit); // This makes sure we quit even if it didn't
714
715 QCoreApplication::exec();
716
717 QCOMPARE(spy.count(), 1);
718 QVERIFY(spy2.count() < 15); // Should be around 10 if closing caused the quit
719 }
720
721 bool quitApplicationTriggered = false;
722 auto quitSlot = [&quitApplicationTriggered] () {
723 quitApplicationTriggered = true;
724 QCoreApplication::quit();
725 };
726
727 {
728 int argc = 0;
729 QApplication app(argc, nullptr);
730
731 QSignalSpy spy(&app, &QCoreApplication::aboutToQuit);
732
733 CloseEventTestWindow mainWindow;
734 mainWindow.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
735
736 QVERIFY(app.quitOnLastWindowClosed());
737 QVERIFY(mainWindow.testAttribute(Qt::WA_QuitOnClose));
738
739 mainWindow.show();
740 QVERIFY(QTest::qWaitForWindowExposed(&mainWindow));
741
742 QTimer::singleShot(interval: 1000, receiver: &mainWindow, slot: &QWidget::close); // This should NOT quit the application (see CloseEventTestWindow)
743 quitApplicationTriggered = false;
744 QTimer::singleShot(interval: 2000, context: this, slot: quitSlot); // This actually quits the application.
745
746 QCoreApplication::exec();
747
748 QCOMPARE(spy.count(), 1);
749 QVERIFY(quitApplicationTriggered);
750 }
751 {
752 int argc = 0;
753 QApplication app(argc, nullptr);
754 QSignalSpy appSpy(&app, &QApplication::lastWindowClosed);
755
756 // exec a dialog for 1 second, then show the window
757 QuitOnLastWindowClosedWindow window;
758 window.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
759 QTimer::singleShot(interval: 0, receiver: &window, slot: &QuitOnLastWindowClosedWindow::execDialogThenShow);
760
761 QTimer timer;
762 QSignalSpy timerSpy(&timer, &QTimer::timeout);
763 connect(sender: &timer, signal: &QTimer::timeout, receiver: &window, slot: &QWidget::close);
764 timer.setSingleShot(true);
765 timer.start(msec: 2000);
766 int returnValue = QCoreApplication::exec();
767 QCOMPARE(returnValue, 0);
768 // failure here means the timer above didn't fire, and the
769 // quit was caused the dialog being closed (not the window)
770 QCOMPARE(timerSpy.count(), 1);
771 QCOMPARE(appSpy.count(), 2);
772 }
773 {
774 int argc = 0;
775 QApplication app(argc, nullptr);
776 QVERIFY(app.quitOnLastWindowClosed());
777
778 QWindow w;
779 w.setTitle(QLatin1String(QTest::currentTestFunction()) + QLatin1String("Window"));
780 w.show();
781
782 QWidget wid;
783 wid.setWindowTitle(QLatin1String(QTest::currentTestFunction()) + QLatin1String("Widget"));
784 wid.show();
785
786 QTimer::singleShot(interval: 1000, receiver: &wid, slot: &QWidget::close); // This should NOT quit the application because the
787 // QWindow is still there.
788 quitApplicationTriggered = false;
789 QTimer::singleShot(interval: 2000, context: this, slot: quitSlot); // This causes the quit.
790
791 QCoreApplication::exec();
792
793 QVERIFY(quitApplicationTriggered); // Should be around 20 if closing did not caused the quit
794 }
795 { // QTBUG-31569: If the last widget with Qt::WA_QuitOnClose set is closed, other
796 // widgets that don't have the attribute set should be closed automatically.
797 int argc = 0;
798 QApplication app(argc, nullptr);
799 QVERIFY(app.quitOnLastWindowClosed());
800
801 QWidget w1;
802 w1.setWindowTitle(QLatin1String(QTest::currentTestFunction()) + QLatin1Char('1'));
803 w1.show();
804
805 QWidget w2;
806 w1.setWindowTitle(QLatin1String(QTest::currentTestFunction()) + QLatin1Char('2'));
807 w2.setAttribute(Qt::WA_QuitOnClose, on: false);
808 w2.show();
809
810 QVERIFY(QTest::qWaitForWindowExposed(&w2));
811
812 QTimer timer;
813 timer.setInterval(100);
814 timer.start();
815 QSignalSpy timerSpy(&timer, &QTimer::timeout);
816
817 QTimer::singleShot(interval: 100, receiver: &w1, slot: &QWidget::close);
818 QCoreApplication::exec();
819
820 QVERIFY(timerSpy.count() < 10);
821 }
822}
823
824static inline bool isVisible(const QWidget *w)
825{
826 return w->isVisible();
827}
828
829class PromptOnCloseWidget : public QWidget
830{
831public:
832 void closeEvent(QCloseEvent *event) override
833 {
834 QMessageBox *messageBox = new QMessageBox(this);
835 messageBox->setWindowTitle("Unsaved data");
836 messageBox->setText("Would you like to save or discard your current data?");
837 messageBox->setStandardButtons(QMessageBox::Save|QMessageBox::Discard|QMessageBox::Cancel);
838 messageBox->setDefaultButton(QMessageBox::Save);
839
840 messageBox->show();
841 QVERIFY(QTest::qWaitForWindowExposed(messageBox));
842
843 // verify that all windows are visible
844 const auto &topLevels = QApplication::topLevelWidgets();
845 QVERIFY(std::all_of(topLevels.cbegin(), topLevels.cend(), ::isVisible));
846 // flush event queue
847 QCoreApplication::processEvents();
848 // close all windows
849 QApplication::closeAllWindows();
850
851 if (messageBox->standardButton(button: messageBox->clickedButton()) == QMessageBox::Cancel)
852 event->ignore();
853 else
854 event->accept();
855
856 delete messageBox;
857 }
858};
859
860void tst_QApplication::closeAllWindows()
861{
862#ifdef Q_OS_WINRT
863 QSKIP("PromptOnCloseWidget does not work on WinRT - QTBUG-68297");
864#endif
865 int argc = 0;
866 QApplication app(argc, nullptr);
867
868 // create some windows
869 new QWidget;
870 new QWidget;
871 new QWidget;
872
873 // show all windows
874 auto topLevels = QApplication::topLevelWidgets();
875 for (QWidget *w : qAsConst(t&: topLevels)) {
876 w->show();
877 QVERIFY(QTest::qWaitForWindowExposed(w));
878 }
879 // verify that they are visible
880 QVERIFY(std::all_of(topLevels.cbegin(), topLevels.cend(), isVisible));
881 // empty event queue
882 QCoreApplication::processEvents();
883 // close all windows
884 QApplication::closeAllWindows();
885 // all windows should no longer be visible
886 QVERIFY(std::all_of(topLevels.cbegin(), topLevels.cend(), [] (const QWidget *w) { return !w->isVisible(); }));
887
888 // add a window that prompts the user when closed
889 PromptOnCloseWidget *promptOnCloseWidget = new PromptOnCloseWidget;
890 // show all windows
891 topLevels = QApplication::topLevelWidgets();
892 for (QWidget *w : qAsConst(t&: topLevels)) {
893 w->show();
894 QVERIFY(QTest::qWaitForWindowExposed(w));
895 }
896 // close the last window to open the prompt (eventloop recurses)
897 promptOnCloseWidget->close();
898 // all windows should not be visible, except the one that opened the prompt
899 for (QWidget *w : qAsConst(t&: topLevels)) {
900 if (w == promptOnCloseWidget)
901 QVERIFY(w->isVisible());
902 else
903 QVERIFY(!w->isVisible());
904 }
905
906 qDeleteAll(c: QApplication::topLevelWidgets());
907}
908
909bool isPathListIncluded(const QStringList &l, const QStringList &r)
910{
911 int size = r.count();
912 if (size > l.count())
913 return false;
914#if defined (Q_OS_WIN)
915 Qt::CaseSensitivity cs = Qt::CaseInsensitive;
916#else
917 Qt::CaseSensitivity cs = Qt::CaseSensitive;
918#endif
919 int i = 0, j = 0;
920 for ( ; i < l.count() && j < r.count(); ++i) {
921 if (QDir::toNativeSeparators(pathName: l[i]).compare(s: QDir::toNativeSeparators(pathName: r[j]), cs) == 0) {
922 ++j;
923 i = -1;
924 }
925 }
926 return j == r.count();
927}
928
929#if QT_CONFIG(library)
930void tst_QApplication::libraryPaths()
931{
932#ifndef BUILTIN_TESTDATA
933 const QString testDir = QFileInfo(QFINDTESTDATA("test/test.pro")).absolutePath();
934#else
935 const QString testDir = QFileInfo(QFINDTESTDATA("test.pro")).absolutePath();
936#endif
937 QVERIFY(!testDir.isEmpty());
938 {
939 QApplication::setLibraryPaths(QStringList() << testDir);
940 QCOMPARE(QApplication::libraryPaths(), (QStringList() << testDir));
941
942 // creating QApplication adds the applicationDirPath to the libraryPath
943 int argc = 1;
944 QApplication app(argc, &argv0);
945 QString appDirPath = QDir(QCoreApplication::applicationDirPath()).canonicalPath();
946
947 QStringList actual = QApplication::libraryPaths();
948 actual.sort();
949 QStringList expected;
950 expected << testDir << appDirPath;
951 expected = QSet<QString>(expected.constBegin(), expected.constEnd()).values();
952 expected.sort();
953
954 QVERIFY2(isPathListIncluded(actual, expected),
955 qPrintable("actual:\n - " + actual.join("\n - ") +
956 "\nexpected:\n - " + expected.join("\n - ")));
957 }
958 {
959 // creating QApplication adds the applicationDirPath and plugin install path to the libraryPath
960 int argc = 1;
961 QApplication app(argc, &argv0);
962 QString appDirPath = QCoreApplication::applicationDirPath();
963 QString installPathPlugins = QLibraryInfo::location(QLibraryInfo::PluginsPath);
964
965 QStringList actual = QApplication::libraryPaths();
966 actual.sort();
967
968 QStringList expected;
969 expected << installPathPlugins << appDirPath;
970 expected = QSet<QString>(expected.constBegin(), expected.constEnd()).values();
971 expected.sort();
972
973#ifdef Q_OS_WINRT
974 QEXPECT_FAIL("", "On WinRT PluginsPath is outside of sandbox. QTBUG-68297", Abort);
975#endif
976 QVERIFY2(isPathListIncluded(actual, expected),
977 qPrintable("actual:\n - " + actual.join("\n - ") +
978 "\nexpected:\n - " + expected.join("\n - ")));
979
980 // setting the library paths overrides everything
981 QApplication::setLibraryPaths(QStringList() << testDir);
982 QVERIFY2(isPathListIncluded(QApplication::libraryPaths(), (QStringList() << testDir)),
983 qPrintable("actual:\n - " + QApplication::libraryPaths().join("\n - ") +
984 "\nexpected:\n - " + testDir));
985 }
986 {
987 qCDebug(lcTests) << "Initial library path:" << QApplication::libraryPaths();
988
989 int count = QApplication::libraryPaths().count();
990#if 0
991 // this test doesn't work if KDE 4 is installed
992 QCOMPARE(count, 1); // before creating QApplication, only the PluginsPath is in the libraryPaths()
993#endif
994 QString installPathPlugins = QLibraryInfo::location(QLibraryInfo::PluginsPath);
995 QApplication::addLibraryPath(installPathPlugins);
996 qCDebug(lcTests) << "installPathPlugins" << installPathPlugins;
997 qCDebug(lcTests) << "After adding plugins path:" << QApplication::libraryPaths();
998 QCOMPARE(QApplication::libraryPaths().count(), count);
999 QApplication::addLibraryPath(testDir);
1000 QCOMPARE(QApplication::libraryPaths().count(), count + 1);
1001
1002 // creating QApplication adds the applicationDirPath to the libraryPath
1003 int argc = 1;
1004 QApplication app(argc, &argv0);
1005 QString appDirPath = QCoreApplication::applicationDirPath();
1006 qCDebug(lcTests) << QApplication::libraryPaths();
1007 // On Windows CE these are identical and might also be the case for other
1008 // systems too
1009 if (appDirPath != installPathPlugins)
1010 QCOMPARE(QApplication::libraryPaths().count(), count + 2);
1011 }
1012 {
1013 int argc = 1;
1014 QApplication app(argc, &argv0);
1015
1016 qCDebug(lcTests) << "Initial library path:" << QCoreApplication::libraryPaths();
1017 int count = QCoreApplication::libraryPaths().count();
1018 QString installPathPlugins = QLibraryInfo::location(QLibraryInfo::PluginsPath);
1019 QCoreApplication::addLibraryPath(installPathPlugins);
1020 qCDebug(lcTests) << "installPathPlugins" << installPathPlugins;
1021 qCDebug(lcTests) << "After adding plugins path:" << QCoreApplication::libraryPaths();
1022 QCOMPARE(QCoreApplication::libraryPaths().count(), count);
1023
1024 QString appDirPath = QCoreApplication::applicationDirPath();
1025
1026 QCoreApplication::addLibraryPath(appDirPath);
1027 QCoreApplication::addLibraryPath(appDirPath + "/..");
1028 qCDebug(lcTests) << "appDirPath" << appDirPath;
1029 qCDebug(lcTests) << "After adding appDirPath && appDirPath + /..:" << QCoreApplication::libraryPaths();
1030 QCOMPARE(QCoreApplication::libraryPaths().count(), count + 1);
1031#ifdef Q_OS_MACOS
1032 QCoreApplication::addLibraryPath(appDirPath + "/../MacOS");
1033#else
1034 QCoreApplication::addLibraryPath(appDirPath + "/tmp/..");
1035#endif
1036 qCDebug(lcTests) << "After adding appDirPath + /tmp/..:" << QCoreApplication::libraryPaths();
1037 QCOMPARE(QCoreApplication::libraryPaths().count(), count + 1);
1038 }
1039}
1040
1041void tst_QApplication::libraryPaths_qt_plugin_path()
1042{
1043 int argc = 1;
1044
1045 QApplication app(argc, &argv0);
1046 QString appDirPath = QCoreApplication::applicationDirPath();
1047
1048 // Our hook into libraryPaths() initialization: Set the QT_PLUGIN_PATH environment variable
1049 QString installPathPluginsDeCanon = appDirPath + QString::fromLatin1(str: "/tmp/..");
1050 QByteArray ascii = QFile::encodeName(fileName: installPathPluginsDeCanon);
1051 qputenv(varName: "QT_PLUGIN_PATH", value: ascii);
1052
1053 QVERIFY(!QCoreApplication::libraryPaths().contains(appDirPath + QString::fromLatin1("/tmp/..")));
1054}
1055
1056void tst_QApplication::libraryPaths_qt_plugin_path_2()
1057{
1058#ifdef Q_OS_UNIX
1059 QByteArray validPath = QDir("/tmp").canonicalPath().toLatin1();
1060 QByteArray nonExistentPath = "/nonexistent";
1061 QByteArray pluginPath = validPath + ':' + nonExistentPath;
1062#elif defined(Q_OS_WIN)
1063 QByteArray validPath = "C:\\windows";
1064 QByteArray nonExistentPath = "Z:\\nonexistent";
1065 QByteArray pluginPath = validPath + ';' + nonExistentPath;
1066#endif
1067
1068 {
1069 // Our hook into libraryPaths() initialization: Set the QT_PLUGIN_PATH environment variable
1070 qputenv(varName: "QT_PLUGIN_PATH", value: pluginPath);
1071
1072 int argc = 1;
1073
1074 QApplication app(argc, &argv0);
1075
1076 // library path list should contain the default plus the one valid path
1077 QStringList expected =
1078 QStringList()
1079 << QLibraryInfo::location(QLibraryInfo::PluginsPath)
1080 << QDir(QCoreApplication::applicationDirPath()).canonicalPath()
1081 << QDir(QDir::fromNativeSeparators(pathName: QString::fromLatin1(str: validPath))).canonicalPath();
1082
1083#ifdef Q_OS_WINRT
1084 QEXPECT_FAIL("", "On WinRT PluginsPath is outside of sandbox. QTBUG-68297", Abort);
1085#endif
1086 QVERIFY2(isPathListIncluded(QCoreApplication::libraryPaths(), expected),
1087 qPrintable("actual:\n - " + QCoreApplication::libraryPaths().join("\n - ") +
1088 "\nexpected:\n - " + expected.join("\n - ")));
1089 }
1090
1091 {
1092 int argc = 1;
1093
1094 QApplication app(argc, &argv0);
1095
1096 // library paths are initialized by the QApplication, setting
1097 // the environment variable here doesn't work
1098 qputenv(varName: "QT_PLUGIN_PATH", value: pluginPath);
1099
1100 // library path list should contain the default
1101 QStringList expected =
1102 QStringList()
1103 << QLibraryInfo::location(QLibraryInfo::PluginsPath)
1104 << QCoreApplication::applicationDirPath();
1105 QVERIFY(isPathListIncluded(QCoreApplication::libraryPaths(), expected));
1106
1107 qputenv(varName: "QT_PLUGIN_PATH", value: QByteArray());
1108 }
1109}
1110#endif
1111
1112class SendPostedEventsTester : public QObject
1113{
1114 Q_OBJECT
1115public:
1116 QList<int> eventSpy;
1117 bool event(QEvent *e) override;
1118private slots:
1119 void doTest();
1120};
1121
1122bool SendPostedEventsTester::event(QEvent *e)
1123{
1124 eventSpy.append(t: e->type());
1125 return QObject::event(event: e);
1126}
1127
1128void SendPostedEventsTester::doTest()
1129{
1130 QPointer<SendPostedEventsTester> p = this;
1131 QApplication::postEvent(receiver: this, event: new QEvent(QEvent::User));
1132 // DeferredDelete should not be delivered until returning from this function
1133 QApplication::postEvent(receiver: this, event: new QDeferredDeleteEvent());
1134
1135 QEventLoop eventLoop;
1136 QMetaObject::invokeMethod(obj: &eventLoop, member: "quit", type: Qt::QueuedConnection);
1137 eventLoop.exec();
1138 QVERIFY(p != nullptr);
1139
1140 QCOMPARE(eventSpy.count(), 2);
1141 QCOMPARE(eventSpy.at(0), int(QEvent::MetaCall));
1142 QCOMPARE(eventSpy.at(1), int(QEvent::User));
1143 eventSpy.clear();
1144}
1145
1146void tst_QApplication::sendPostedEvents()
1147{
1148 int argc = 0;
1149 QApplication app(argc, nullptr);
1150 SendPostedEventsTester *tester = new SendPostedEventsTester;
1151 QMetaObject::invokeMethod(obj: tester, member: "doTest", type: Qt::QueuedConnection);
1152 QMetaObject::invokeMethod(obj: &app, member: "quit", type: Qt::QueuedConnection);
1153 QPointer<SendPostedEventsTester> p = tester;
1154 (void) QCoreApplication::exec();
1155 QVERIFY(p.isNull());
1156}
1157
1158void tst_QApplication::thread()
1159{
1160 QThread *currentThread = QThread::currentThread();
1161 // no app, but still have a valid thread
1162 QVERIFY(currentThread != nullptr);
1163
1164 // the thread should be running and not finished
1165 QVERIFY(currentThread->isRunning());
1166 QVERIFY(!currentThread->isFinished());
1167
1168 // this should probably be in the tst_QObject::thread() test, but
1169 // we put it here since we want to make sure that objects created
1170 // *before* the QApplication has a thread
1171 QObject object;
1172 QObject child(&object);
1173 QCOMPARE(object.thread(), currentThread);
1174 QCOMPARE(child.thread(), currentThread);
1175
1176 {
1177 int argc = 0;
1178 QApplication app(argc, nullptr);
1179
1180 // current thread still valid
1181 QVERIFY(QThread::currentThread() != nullptr);
1182 // thread should be the same as before
1183 QCOMPARE(QThread::currentThread(), currentThread);
1184
1185 // app's thread should be the current thread
1186 QCOMPARE(app.thread(), currentThread);
1187
1188 // the thread should still be running and not finished
1189 QVERIFY(currentThread->isRunning());
1190 QVERIFY(!currentThread->isFinished());
1191
1192 QTestEventLoop::instance().enterLoop(secs: 1);
1193 }
1194
1195 // app dead, current thread still valid
1196 QVERIFY(QThread::currentThread() != nullptr);
1197 QCOMPARE(QThread::currentThread(), currentThread);
1198
1199 // the thread should still be running and not finished
1200 QVERIFY(currentThread->isRunning());
1201 QVERIFY(!currentThread->isFinished());
1202
1203 // should still have a thread
1204 QCOMPARE(object.thread(), currentThread);
1205 QCOMPARE(child.thread(), currentThread);
1206
1207 // do the test again, making sure that the thread is the same as
1208 // before
1209 {
1210 int argc = 0;
1211 QApplication app(argc, nullptr);
1212
1213 // current thread still valid
1214 QVERIFY(QThread::currentThread() != nullptr);
1215 // thread should be the same as before
1216 QCOMPARE(QThread::currentThread(), currentThread);
1217
1218 // app's thread should be the current thread
1219 QCOMPARE(app.thread(), currentThread);
1220
1221 // the thread should be running and not finished
1222 QVERIFY(currentThread->isRunning());
1223 QVERIFY(!currentThread->isFinished());
1224
1225 // should still have a thread
1226 QCOMPARE(object.thread(), currentThread);
1227 QCOMPARE(child.thread(), currentThread);
1228
1229 QTestEventLoop::instance().enterLoop(secs: 1);
1230 }
1231
1232 // app dead, current thread still valid
1233 QVERIFY(QThread::currentThread() != nullptr);
1234 QCOMPARE(QThread::currentThread(), currentThread);
1235
1236 // the thread should still be running and not finished
1237 QVERIFY(currentThread->isRunning());
1238 QVERIFY(!currentThread->isFinished());
1239
1240 // should still have a thread
1241 QCOMPARE(object.thread(), currentThread);
1242 QCOMPARE(child.thread(), currentThread);
1243}
1244
1245class DeleteLaterWidget : public QWidget
1246{
1247 Q_OBJECT
1248public:
1249 explicit DeleteLaterWidget(QApplication *_app, QWidget *parent = nullptr)
1250 : QWidget(parent), app(_app) {}
1251
1252 bool child_deleted = false;
1253 QApplication *app;
1254
1255public slots:
1256 void runTest();
1257 void checkDeleteLater();
1258 void childDeleted() { child_deleted = true; }
1259};
1260
1261
1262void DeleteLaterWidget::runTest()
1263{
1264 QObject *stillAlive = this->findChild<QObject*>(aName: "deleteLater");
1265
1266 QWidget *w = new QWidget(this);
1267 connect(sender: w, signal: &QObject::destroyed, receiver: this, slot: &DeleteLaterWidget::childDeleted);
1268
1269 w->deleteLater();
1270 QVERIFY(!child_deleted);
1271
1272 QDialog dlg;
1273 QTimer::singleShot(interval: 500, receiver: &dlg, slot: &QDialog::reject);
1274 dlg.exec();
1275
1276 QVERIFY(!child_deleted);
1277 QCoreApplication::processEvents();
1278 QVERIFY(!child_deleted);
1279
1280 QTimer::singleShot(interval: 500, receiver: this, slot: &DeleteLaterWidget::checkDeleteLater);
1281
1282 QCoreApplication::processEvents();
1283
1284 // At this point, the event queue is empty. As we want a deferred
1285 // deletion to occur before the timer event, we should provoke the
1286 // event dispatcher for the next spin.
1287 QCoreApplication::eventDispatcher()->interrupt();
1288
1289 QVERIFY(!stillAlive); // verify at the end to make test terminate
1290}
1291
1292void DeleteLaterWidget::checkDeleteLater()
1293{
1294 QVERIFY(child_deleted);
1295
1296 close();
1297}
1298
1299void tst_QApplication::testDeleteLater()
1300{
1301#ifdef Q_OS_MAC
1302 QSKIP("This test fails and then hangs on OS X, see QTBUG-24318");
1303#endif
1304 int argc = 0;
1305 QApplication app(argc, nullptr);
1306 connect(sender: &app, signal: &QApplication::lastWindowClosed, context: &app, slot: &QCoreApplication::quit);
1307
1308 DeleteLaterWidget *wgt = new DeleteLaterWidget(&app);
1309 QTimer::singleShot(interval: 500, receiver: wgt, slot: &DeleteLaterWidget::runTest);
1310
1311 QObject *object = new QObject(wgt);
1312 object->setObjectName("deleteLater");
1313 object->deleteLater();
1314
1315 QObject *stillAlive = wgt->findChild<QObject*>(aName: "deleteLater");
1316 QVERIFY(stillAlive);
1317
1318 wgt->show();
1319 QCoreApplication::exec();
1320
1321 QVERIFY(wgt->isHidden());
1322 delete wgt;
1323
1324}
1325
1326class EventLoopNester : public QObject
1327{
1328 Q_OBJECT
1329public slots:
1330 void deleteLaterAndEnterLoop()
1331 {
1332 QEventLoop eventLoop;
1333 QPointer<QObject> p(this);
1334 deleteLater();
1335 /*
1336 DeferredDelete events are compressed, meaning this second
1337 deleteLater() will *not* delete the object in the nested
1338 event loop
1339 */
1340 QMetaObject::invokeMethod(obj: this, member: "deleteLater", type: Qt::QueuedConnection);
1341 QTimer::singleShot(interval: 1000, receiver: &eventLoop, slot: &QEventLoop::quit);
1342 eventLoop.exec();
1343 QVERIFY(p);
1344 }
1345 void deleteLaterAndExitLoop()
1346 {
1347 // Check that 'p' is not deleted before exec returns, since the call
1348 // to QEventLoop::quit() should stop 'eventLoop' from processing
1349 // any more events (that is, delete later) until we return to the
1350 // _current_ event loop:
1351 QEventLoop eventLoop;
1352 QPointer<QObject> p(this);
1353 QMetaObject::invokeMethod(obj: this, member: "deleteLater", type: Qt::QueuedConnection);
1354 QMetaObject::invokeMethod(obj: &eventLoop, member: "quit", type: Qt::QueuedConnection);
1355 eventLoop.exec();
1356 QVERIFY(p); // not dead yet
1357 }
1358
1359 void processEventsOnly()
1360 {
1361 QApplication::processEvents();
1362 }
1363 void sendPostedEventsWithDeferredDelete()
1364 {
1365 QApplication::sendPostedEvents(receiver: nullptr, event_type: QEvent::DeferredDelete);
1366 }
1367
1368 void deleteLaterAndProcessEvents()
1369 {
1370 QEventLoop eventLoop;
1371
1372 QPointer<QObject> p = this;
1373 deleteLater();
1374
1375 // trying to delete this object in a deeper eventloop just won't work
1376 QMetaObject::invokeMethod(obj: this,
1377 member: "processEventsOnly",
1378 type: Qt::QueuedConnection);
1379 QMetaObject::invokeMethod(obj: &eventLoop, member: "quit", type: Qt::QueuedConnection);
1380 eventLoop.exec();
1381 QVERIFY(p);
1382 QMetaObject::invokeMethod(obj: this,
1383 member: "sendPostedEventsWithDeferredDelete",
1384 type: Qt::QueuedConnection);
1385 QMetaObject::invokeMethod(obj: &eventLoop, member: "quit", type: Qt::QueuedConnection);
1386 eventLoop.exec();
1387 QVERIFY(p);
1388
1389 // trying to delete it from this eventloop still doesn't work
1390 QApplication::processEvents();
1391 QVERIFY(p);
1392
1393 // however, it *will* work with this magic incantation
1394 QApplication::sendPostedEvents(receiver: nullptr, event_type: QEvent::DeferredDelete);
1395 QVERIFY(!p);
1396 }
1397};
1398
1399void tst_QApplication::testDeleteLaterProcessEvents1()
1400{
1401 // Calling processEvents() with no event dispatcher does nothing.
1402 QObject *object = new QObject;
1403 QPointer<QObject> p(object);
1404 object->deleteLater();
1405 QApplication::processEvents();
1406 QVERIFY(p);
1407 delete object;
1408}
1409
1410void tst_QApplication::testDeleteLaterProcessEvents2()
1411{
1412 int argc = 0;
1413 QApplication app(argc, nullptr);
1414 // If you call processEvents() with an event dispatcher present, but
1415 // outside any event loops, deferred deletes are not processed unless
1416 // sendPostedEvents(0, DeferredDelete) is called.
1417 auto object = new QObject;
1418 QPointer<QObject> p(object);
1419 object->deleteLater();
1420 QCoreApplication::processEvents();
1421 QVERIFY(p);
1422 QCoreApplication::sendPostedEvents(receiver: nullptr, event_type: QEvent::DeferredDelete);
1423 QVERIFY(!p);
1424
1425 // If you call deleteLater() on an object when there is no parent
1426 // event loop, and then enter an event loop, the object will get
1427 // deleted.
1428 QEventLoop loop;
1429 object = new QObject;
1430 connect(sender: object, signal: &QObject::destroyed, receiver: &loop, slot: &QEventLoop::quit);
1431 p = object;
1432 object->deleteLater();
1433 QTimer::singleShot(interval: 1000, receiver: &loop, slot: &QEventLoop::quit);
1434 loop.exec();
1435 QVERIFY(!p);
1436}
1437
1438void tst_QApplication::testDeleteLaterProcessEvents3()
1439{
1440 int argc = 0;
1441 // When an object is in an event loop, then calls deleteLater() and enters
1442 // an event loop recursively, it should not die until the parent event
1443 // loop continues.
1444 QApplication app(argc, nullptr);
1445 QEventLoop loop;
1446 EventLoopNester *nester = new EventLoopNester;
1447 QPointer<QObject> p(nester);
1448 QTimer::singleShot(interval: 3000, receiver: &loop, slot: &QEventLoop::quit);
1449 QTimer::singleShot(interval: 0, receiver: nester, slot: &EventLoopNester::deleteLaterAndEnterLoop);
1450
1451 loop.exec();
1452 QVERIFY(!p);
1453}
1454
1455void tst_QApplication::testDeleteLaterProcessEvents4()
1456{
1457 int argc = 0;
1458 // When the event loop that calls deleteLater() is exited
1459 // immediately, the object should die when returning to the
1460 // parent event loop
1461 QApplication app(argc, nullptr);
1462 QEventLoop loop;
1463 EventLoopNester *nester = new EventLoopNester;
1464 QPointer<QObject> p(nester);
1465 QTimer::singleShot(interval: 3000, receiver: &loop, slot: &QEventLoop::quit);
1466 QTimer::singleShot(interval: 0, receiver: nester, slot: &EventLoopNester::deleteLaterAndExitLoop);
1467
1468 loop.exec();
1469 QVERIFY(!p);
1470}
1471
1472void tst_QApplication::testDeleteLaterProcessEvents5()
1473{
1474 // when the event loop that calls deleteLater() also calls
1475 // processEvents() immediately afterwards, the object should
1476 // not die until the parent loop continues
1477 int argc = 0;
1478 QApplication app(argc, nullptr);
1479 QEventLoop loop;
1480 EventLoopNester *nester = new EventLoopNester();
1481 QPointer<QObject> p(nester);
1482 QTimer::singleShot(interval: 3000, receiver: &loop, slot: &QEventLoop::quit);
1483 QTimer::singleShot(interval: 0, receiver: nester, slot: &EventLoopNester::deleteLaterAndProcessEvents);
1484
1485 loop.exec();
1486 QVERIFY(!p);
1487}
1488
1489/*
1490 Test for crash with QApplication::setDesktopSettingsAware(false).
1491*/
1492void tst_QApplication::desktopSettingsAware()
1493{
1494#if QT_CONFIG(process)
1495 QProcess testProcess;
1496#ifdef Q_OS_MACOS
1497 QStringList environment = QProcess::systemEnvironment();
1498 environment += QLatin1String("QT_MAC_DISABLE_FOREGROUND_APPLICATION_TRANSFORM=1");
1499 testProcess.setEnvironment(environment);
1500#endif
1501 // Add the executable's directory to path so that we can find the test helper next to it
1502 // in a cross-platform way. We must do this because the CWD is not pointing to this directory
1503 // in debug-and-release builds.
1504 int argc = 0;
1505 QApplication app(argc, nullptr);
1506 QByteArray path = qgetenv(varName: "PATH");
1507 qputenv(varName: "PATH",
1508 value: path + QDir::listSeparator().toLatin1()
1509 + QCoreApplication::applicationDirPath().toLocal8Bit());
1510 auto restore = qScopeGuard(f: [&] { qputenv(varName: "PATH", value: path); });
1511
1512 testProcess.start(command: "desktopsettingsaware_helper");
1513 QVERIFY2(testProcess.waitForStarted(),
1514 qPrintable(QString::fromLatin1("Cannot start 'desktopsettingsaware_helper': %1").arg(testProcess.errorString())));
1515 QVERIFY(testProcess.waitForFinished(10000));
1516 QCOMPARE(int(testProcess.state()), int(QProcess::NotRunning));
1517 QVERIFY(int(testProcess.error()) != int(QProcess::Crashed));
1518#endif
1519}
1520
1521void tst_QApplication::setActiveWindow()
1522{
1523 int argc = 0;
1524 QApplication MyApp(argc, nullptr);
1525
1526 QWidget* w = new QWidget;
1527 QVBoxLayout* layout = new QVBoxLayout(w);
1528
1529 QLineEdit* pb1 = new QLineEdit("Testbutton1", w);
1530 QLineEdit* pb2 = new QLineEdit("Test Line Edit", w);
1531
1532 layout->addWidget(pb1);
1533 layout->addWidget(pb2);
1534
1535 pb2->setFocus();
1536 pb2->setParent(nullptr);
1537 delete pb2;
1538
1539 w->show();
1540 QApplication::setActiveWindow(w); // needs this on twm (focus follows mouse)
1541 QVERIFY(pb1->hasFocus());
1542 delete w;
1543}
1544
1545void tst_QApplication::focusWidget()
1546{
1547 int argc = 0;
1548 QApplication app(argc, nullptr);
1549
1550 // The focus widget is the active window itself
1551 {
1552 QTextEdit te;
1553 te.show();
1554
1555 QApplication::setActiveWindow(&te);
1556 QVERIFY(QTest::qWaitForWindowActive(&te));
1557
1558 const auto focusWidget = QApplication::focusWidget();
1559 QVERIFY(focusWidget);
1560 QVERIFY(focusWidget->hasFocus());
1561 QVERIFY(te.hasFocus());
1562 QCOMPARE(focusWidget, te.focusWidget());
1563 }
1564
1565 // The focus widget is a child of the active window
1566 {
1567 QWidget w;
1568 QTextEdit te(&w);
1569 w.show();
1570
1571 QApplication::setActiveWindow(&w);
1572 QVERIFY(QTest::qWaitForWindowActive(&w));
1573
1574 const auto focusWidget = QApplication::focusWidget();
1575 QVERIFY(focusWidget);
1576 QVERIFY(focusWidget->hasFocus());
1577 QVERIFY(!w.hasFocus());
1578 QVERIFY(te.hasFocus());
1579 QCOMPARE(te.focusWidget(), w.focusWidget());
1580 QCOMPARE(focusWidget, w.focusWidget());
1581 }
1582}
1583
1584/* This might fail on some X11 window managers? */
1585void tst_QApplication::focusChanged()
1586{
1587 int argc = 0;
1588 QApplication app(argc, nullptr);
1589
1590 QSignalSpy spy(&app, QOverload<QWidget*,QWidget*>::of(ptr: &QApplication::focusChanged));
1591 QWidget *now = nullptr;
1592 QWidget *old = nullptr;
1593
1594 QWidget parent1;
1595 parent1.setWindowTitle(QLatin1String(QTest::currentTestFunction()) + QLatin1Char('1'));
1596 QHBoxLayout hbox1(&parent1);
1597 QLabel lb1(&parent1);
1598 QLineEdit le1(&parent1);
1599 QPushButton pb1(&parent1);
1600 hbox1.addWidget(&lb1);
1601 hbox1.addWidget(&le1);
1602 hbox1.addWidget(&pb1);
1603
1604 QCOMPARE(spy.count(), 0);
1605
1606 parent1.show();
1607 QApplication::setActiveWindow(&parent1); // needs this on twm (focus follows mouse)
1608 QCOMPARE(spy.count(), 1);
1609 QCOMPARE(spy.at(0).count(), 2);
1610 old = qvariant_cast<QWidget*>(v: spy.at(i: 0).at(i: 0));
1611 now = qvariant_cast<QWidget*>(v: spy.at(i: 0).at(i: 1));
1612 QCOMPARE(now, &le1);
1613 QCOMPARE(now, QApplication::focusWidget());
1614 QVERIFY(!old);
1615 spy.clear();
1616 QCOMPARE(spy.count(), 0);
1617
1618 pb1.setFocus();
1619 QCOMPARE(spy.count(), 1);
1620 old = qvariant_cast<QWidget*>(v: spy.at(i: 0).at(i: 0));
1621 now = qvariant_cast<QWidget*>(v: spy.at(i: 0).at(i: 1));
1622 QCOMPARE(now, &pb1);
1623 QCOMPARE(now, QApplication::focusWidget());
1624 QCOMPARE(old, &le1);
1625 spy.clear();
1626
1627 lb1.setFocus();
1628 QCOMPARE(spy.count(), 1);
1629 old = qvariant_cast<QWidget*>(v: spy.at(i: 0).at(i: 0));
1630 now = qvariant_cast<QWidget*>(v: spy.at(i: 0).at(i: 1));
1631 QCOMPARE(now, &lb1);
1632 QCOMPARE(now, QApplication::focusWidget());
1633 QCOMPARE(old, &pb1);
1634 spy.clear();
1635
1636 lb1.clearFocus();
1637 QCOMPARE(spy.count(), 1);
1638 old = qvariant_cast<QWidget*>(v: spy.at(i: 0).at(i: 0));
1639 now = qvariant_cast<QWidget*>(v: spy.at(i: 0).at(i: 1));
1640 QVERIFY(!now);
1641 QCOMPARE(now, QApplication::focusWidget());
1642 QCOMPARE(old, &lb1);
1643 spy.clear();
1644
1645 QWidget parent2;
1646 parent2.setWindowTitle(QLatin1String(QTest::currentTestFunction()) + QLatin1Char('1'));
1647 QHBoxLayout hbox2(&parent2);
1648 QLabel lb2(&parent2);
1649 QLineEdit le2(&parent2);
1650 QPushButton pb2(&parent2);
1651 hbox2.addWidget(&lb2);
1652 hbox2.addWidget(&le2);
1653 hbox2.addWidget(&pb2);
1654
1655 parent2.show();
1656 QApplication::setActiveWindow(&parent2); // needs this on twm (focus follows mouse)
1657 QVERIFY(spy.count() > 0); // one for deactivation, one for activation on Windows
1658 old = qvariant_cast<QWidget*>(v: spy.at(i: spy.count()-1).at(i: 0));
1659 now = qvariant_cast<QWidget*>(v: spy.at(i: spy.count()-1).at(i: 1));
1660 QCOMPARE(now, &le2);
1661 QCOMPARE(now, QApplication::focusWidget());
1662 QVERIFY(!old);
1663 spy.clear();
1664
1665 QTestKeyEvent tab(QTest::Press, Qt::Key_Tab, Qt::KeyboardModifiers(), 0);
1666 QTestKeyEvent backtab(QTest::Press, Qt::Key_Backtab, Qt::KeyboardModifiers(), 0);
1667 QTestMouseEvent click(QTest::MouseClick, Qt::LeftButton, Qt::KeyboardModifiers(), QPoint(5, 5), 0);
1668
1669 bool tabAllControls = true;
1670#ifdef Q_OS_MAC
1671 // Mac has two modes, one where you tab to everything, one where you can
1672 // only tab to input controls, here's what we get. Determine which ones we
1673 // should get.
1674 QSettings appleSettings(QLatin1String("apple.com"));
1675 QVariant appleValue = appleSettings.value(QLatin1String("AppleKeyboardUIMode"), 0);
1676 tabAllControls = (appleValue.toInt() & 0x2);
1677#endif
1678
1679 // make sure Qt's idea of tabbing between widgets matches what we think it should
1680 QCOMPARE(qt_tab_all_widgets(), tabAllControls);
1681
1682 tab.simulate(w: now);
1683 if (!tabAllControls) {
1684 QCOMPARE(spy.count(), 0);
1685 QCOMPARE(now, QApplication::focusWidget());
1686 } else {
1687 QVERIFY(spy.count() > 0);
1688 old = qvariant_cast<QWidget*>(v: spy.at(i: 0).at(i: 0));
1689 now = qvariant_cast<QWidget*>(v: spy.at(i: 0).at(i: 1));
1690 QCOMPARE(now, &pb2);
1691 QCOMPARE(now, QApplication::focusWidget());
1692 QCOMPARE(old, &le2);
1693 spy.clear();
1694 }
1695
1696 if (!tabAllControls) {
1697 QCOMPARE(spy.count(), 0);
1698 QCOMPARE(now, QApplication::focusWidget());
1699 } else {
1700 tab.simulate(w: now);
1701 QVERIFY(spy.count() > 0);
1702 old = qvariant_cast<QWidget*>(v: spy.at(i: 0).at(i: 0));
1703 now = qvariant_cast<QWidget*>(v: spy.at(i: 0).at(i: 1));
1704 QCOMPARE(now, &le2);
1705 QCOMPARE(now, QApplication::focusWidget());
1706 QCOMPARE(old, &pb2);
1707 spy.clear();
1708 }
1709
1710 if (!tabAllControls) {
1711 QCOMPARE(spy.count(), 0);
1712 QCOMPARE(now, QApplication::focusWidget());
1713 } else {
1714 backtab.simulate(w: now);
1715 QVERIFY(spy.count() > 0);
1716 old = qvariant_cast<QWidget*>(v: spy.at(i: 0).at(i: 0));
1717 now = qvariant_cast<QWidget*>(v: spy.at(i: 0).at(i: 1));
1718 QCOMPARE(now, &pb2);
1719 QCOMPARE(now, QApplication::focusWidget());
1720 QCOMPARE(old, &le2);
1721 spy.clear();
1722 }
1723
1724
1725 if (!tabAllControls) {
1726 QCOMPARE(spy.count(), 0);
1727 QCOMPARE(now, QApplication::focusWidget());
1728 old = &pb2;
1729 } else {
1730 backtab.simulate(w: now);
1731 QVERIFY(spy.count() > 0);
1732 old = qvariant_cast<QWidget*>(v: spy.at(i: 0).at(i: 0));
1733 now = qvariant_cast<QWidget*>(v: spy.at(i: 0).at(i: 1));
1734 QCOMPARE(now, &le2);
1735 QCOMPARE(now, QApplication::focusWidget());
1736 QCOMPARE(old, &pb2);
1737 spy.clear();
1738 }
1739
1740 click.simulate(w: old);
1741 if (!(pb2.focusPolicy() & Qt::ClickFocus)) {
1742 QCOMPARE(spy.count(), 0);
1743 QCOMPARE(now, QApplication::focusWidget());
1744 } else {
1745 QVERIFY(spy.count() > 0);
1746 old = qvariant_cast<QWidget*>(v: spy.at(i: 0).at(i: 0));
1747 now = qvariant_cast<QWidget*>(v: spy.at(i: 0).at(i: 1));
1748 QCOMPARE(now, &pb2);
1749 QCOMPARE(now, QApplication::focusWidget());
1750 QCOMPARE(old, &le2);
1751 spy.clear();
1752
1753 click.simulate(w: old);
1754 QVERIFY(spy.count() > 0);
1755 old = qvariant_cast<QWidget*>(v: spy.at(i: 0).at(i: 0));
1756 now = qvariant_cast<QWidget*>(v: spy.at(i: 0).at(i: 1));
1757 QCOMPARE(now, &le2);
1758 QCOMPARE(now, QApplication::focusWidget());
1759 QCOMPARE(old, &pb2);
1760 spy.clear();
1761 }
1762
1763 parent1.activateWindow();
1764 QApplication::setActiveWindow(&parent1); // needs this on twm (focus follows mouse)
1765 QVERIFY(spy.count() == 1 || spy.count() == 2); // one for deactivation, one for activation on Windows
1766
1767 //on windows, the change of focus is made in 2 steps
1768 //(the focusChanged SIGNAL is emitted twice)
1769 if (spy.count()==1)
1770 old = qvariant_cast<QWidget*>(v: spy.at(i: spy.count()-1).at(i: 0));
1771 else
1772 old = qvariant_cast<QWidget*>(v: spy.at(i: spy.count()-2).at(i: 0));
1773 now = qvariant_cast<QWidget*>(v: spy.at(i: spy.count()-1).at(i: 1));
1774 QCOMPARE(now, &le1);
1775 QCOMPARE(now, QApplication::focusWidget());
1776 QCOMPARE(old, &le2);
1777 spy.clear();
1778}
1779
1780class LineEdit : public QLineEdit
1781{
1782public:
1783 using QLineEdit::QLineEdit;
1784
1785protected:
1786 void focusOutEvent(QFocusEvent *e) override
1787 {
1788 QLineEdit::focusOutEvent(e);
1789 if (objectName() == "le1")
1790 setStyleSheet("");
1791 }
1792
1793 void focusInEvent(QFocusEvent *e) override
1794 {
1795 QLineEdit::focusInEvent(e);
1796 if (objectName() == "le2")
1797 setStyleSheet("");
1798 }
1799};
1800
1801void tst_QApplication::focusOut()
1802{
1803 int argc = 1;
1804 QApplication app(argc, &argv0);
1805
1806 // Tests the case where the style pointer changes when on focus in/out
1807 // (the above is the case when the stylesheet changes)
1808 QWidget w;
1809 w.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
1810 QLineEdit *le1 = new LineEdit(&w);
1811 le1->setObjectName("le1");
1812 le1->setStyleSheet("background: #fee");
1813 le1->setFocus();
1814
1815 QLineEdit *le2 = new LineEdit(&w);
1816 le2->setObjectName("le2");
1817 le2->setStyleSheet("background: #fee");
1818 le2->move(ax: 100, ay: 100);
1819 w.show();
1820 QVERIFY(QTest::qWaitForWindowExposed(&w));
1821
1822 QTest::qWait(ms: 2000);
1823 le2->setFocus();
1824 QTest::qWait(ms: 2000);
1825}
1826
1827void tst_QApplication::focusMouseClick()
1828{
1829 int argc = 1;
1830 QApplication app(argc, &argv0);
1831
1832 if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::WindowActivation))
1833 QSKIP("Window activation is not supported");
1834
1835 QWidget w;
1836 w.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
1837 w.setFocusPolicy(Qt::StrongFocus);
1838 QWidget w2(&w);
1839 w2.setFocusPolicy(Qt::TabFocus);
1840 w.show();
1841 w.setFocus();
1842 QTRY_COMPARE(QApplication::focusWidget(), &w);
1843
1844 // front most widget has Qt::TabFocus, parent widget accepts clicks as well
1845 // now send a mouse button press event and check what happens with the focus
1846 // it should be given to the parent widget
1847 QMouseEvent ev(QEvent::MouseButtonPress, QPointF(), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
1848 QSpontaneKeyEvent::setSpontaneous(&ev);
1849 QVERIFY(ev.spontaneous());
1850 qApp->notify(&w2, &ev);
1851 QCOMPARE(QApplication::focusWidget(), &w);
1852
1853 // then we give the inner widget strong focus -> it should get focus
1854 w2.setFocusPolicy(Qt::StrongFocus);
1855 QSpontaneKeyEvent::setSpontaneous(&ev);
1856 QVERIFY(ev.spontaneous());
1857 qApp->notify(&w2, &ev);
1858#ifdef Q_OS_WINRT
1859 QEXPECT_FAIL("", "Fails on WinRT - QTBUG-68297", Abort);
1860#endif
1861 QTRY_COMPARE(QApplication::focusWidget(), &w2);
1862
1863 // now back to tab focus and click again (it already had focus) -> focus should stay
1864 // (focus was revoked as of QTBUG-34042)
1865 w2.setFocusPolicy(Qt::TabFocus);
1866 QSpontaneKeyEvent::setSpontaneous(&ev);
1867 QVERIFY(ev.spontaneous());
1868 qApp->notify(&w2, &ev);
1869 QCOMPARE(QApplication::focusWidget(), &w2);
1870}
1871
1872void tst_QApplication::execAfterExit()
1873{
1874 int argc = 1;
1875 QApplication app(argc, &argv0);
1876 QMetaObject::invokeMethod(obj: &app, member: "quit", type: Qt::QueuedConnection);
1877 // this should be ignored, as exec() will reset the exitCode
1878 QApplication::exit(retcode: 1);
1879 int exitCode = QCoreApplication::exec();
1880 QCOMPARE(exitCode, 0);
1881
1882 // the quitNow flag should have been reset, so we can spin an
1883 // eventloop after QApplication::exec() returns
1884 QEventLoop eventLoop;
1885 QMetaObject::invokeMethod(obj: &eventLoop, member: "quit", type: Qt::QueuedConnection);
1886 exitCode = eventLoop.exec();
1887 QCOMPARE(exitCode, 0);
1888}
1889
1890#if QT_CONFIG(wheelevent)
1891void tst_QApplication::wheelScrollLines()
1892{
1893 int argc = 1;
1894 QApplication app(argc, &argv0);
1895 // If wheelScrollLines returns 0, the mose wheel will be disabled.
1896 QVERIFY(app.wheelScrollLines() > 0);
1897}
1898#endif // QT_CONFIG(wheelevent)
1899
1900void tst_QApplication::style()
1901{
1902 int argc = 1;
1903
1904 {
1905 QApplication app(argc, &argv0);
1906 QPointer<QStyle> style = QApplication::style();
1907 QApplication::setStyle(QStyleFactory::create(QLatin1String("Windows")));
1908 QVERIFY(style.isNull());
1909 }
1910
1911 QApplication app(argc, &argv0);
1912
1913 // qApp style can never be 0
1914 QVERIFY(QApplication::style() != nullptr);
1915}
1916
1917class CustomStyle : public QProxyStyle
1918{
1919public:
1920 CustomStyle() : QProxyStyle("Windows") { Q_ASSERT(!polished); }
1921 ~CustomStyle() { polished = 0; }
1922 void polish(QPalette &palette)
1923 {
1924 polished++;
1925 palette.setColor(acg: QPalette::Active, acr: QPalette::Link, acolor: Qt::red);
1926 }
1927 static int polished;
1928};
1929
1930int CustomStyle::polished = 0;
1931
1932class CustomStylePlugin : public QStylePlugin
1933{
1934 Q_OBJECT
1935 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QStyleFactoryInterface" FILE "customstyle.json")
1936public:
1937 QStyle *create(const QString &) { return new CustomStyle; }
1938};
1939
1940Q_IMPORT_PLUGIN(CustomStylePlugin)
1941
1942void tst_QApplication::applicationPalettePolish()
1943{
1944 int argc = 1;
1945
1946#if defined(QT_BUILD_INTERNAL)
1947 {
1948 qputenv(varName: "QT_DESKTOP_STYLE_KEY", value: "customstyle");
1949 QApplication app(argc, &argv0);
1950 QVERIFY(CustomStyle::polished);
1951 QVERIFY(!app.palette().resolve());
1952 QCOMPARE(app.palette().color(QPalette::Link), Qt::red);
1953 qunsetenv(varName: "QT_DESKTOP_STYLE_KEY");
1954 }
1955#endif
1956
1957 {
1958 QApplication::setStyle(new CustomStyle);
1959 QApplication app(argc, &argv0);
1960 QVERIFY(CustomStyle::polished);
1961 QVERIFY(!app.palette().resolve());
1962 QCOMPARE(app.palette().color(QPalette::Link), Qt::red);
1963 }
1964
1965 {
1966 QApplication app(argc, &argv0);
1967 app.setStyle(new CustomStyle);
1968 QVERIFY(CustomStyle::polished);
1969 QVERIFY(!app.palette().resolve());
1970 QCOMPARE(app.palette().color(QPalette::Link), Qt::red);
1971
1972 CustomStyle::polished = 0;
1973 app.setPalette(QPalette());
1974 QVERIFY(CustomStyle::polished);
1975 QVERIFY(!app.palette().resolve());
1976 QCOMPARE(app.palette().color(QPalette::Link), Qt::red);
1977
1978 CustomStyle::polished = 0;
1979 QPalette palette;
1980 palette.setColor(acg: QPalette::Active, acr: QPalette::Highlight, acolor: Qt::green);
1981 app.setPalette(palette);
1982 QVERIFY(CustomStyle::polished);
1983 QVERIFY(app.palette().resolve());
1984 QCOMPARE(app.palette().color(QPalette::Link), Qt::red);
1985 QCOMPARE(app.palette().color(QPalette::Highlight), Qt::green);
1986 }
1987}
1988
1989void tst_QApplication::allWidgets()
1990{
1991 int argc = 1;
1992 QApplication app(argc, &argv0);
1993 QWidget *w = new QWidget;
1994 QVERIFY(QApplication::allWidgets().contains(w)); // uncreate widget test
1995 delete w;
1996 QVERIFY(!QApplication::allWidgets().contains(w)); // removal test
1997}
1998
1999void tst_QApplication::topLevelWidgets()
2000{
2001 int argc = 1;
2002 QApplication app(argc, &argv0);
2003 QWidget *w = new QWidget;
2004 w->show();
2005#ifndef QT_NO_CLIPBOARD
2006 QClipboard *clipboard = QGuiApplication::clipboard();
2007 clipboard->setText(QLatin1String("newText"));
2008#endif
2009 QCoreApplication::processEvents();
2010 QVERIFY(QApplication::topLevelWidgets().contains(w));
2011 QCOMPARE(QApplication::topLevelWidgets().count(), 1);
2012 delete w;
2013 w = nullptr;
2014 QCoreApplication::processEvents();
2015 QCOMPARE(QApplication::topLevelWidgets().count(), 0);
2016}
2017
2018
2019
2020void tst_QApplication::setAttribute()
2021{
2022 int argc = 1;
2023 QApplication app(argc, &argv0);
2024 QVERIFY(!QApplication::testAttribute(Qt::AA_ImmediateWidgetCreation));
2025 QWidget *w = new QWidget;
2026 QVERIFY(!w->testAttribute(Qt::WA_WState_Created));
2027 delete w;
2028
2029 QApplication::setAttribute(attribute: Qt::AA_ImmediateWidgetCreation);
2030 QVERIFY(QApplication::testAttribute(Qt::AA_ImmediateWidgetCreation));
2031 w = new QWidget;
2032 QVERIFY(w->testAttribute(Qt::WA_WState_Created));
2033 QWidget *w2 = new QWidget(w);
2034 w2->setParent(nullptr);
2035 QVERIFY(w2->testAttribute(Qt::WA_WState_Created));
2036 delete w;
2037 delete w2;
2038
2039 QApplication::setAttribute(attribute: Qt::AA_ImmediateWidgetCreation, on: false);
2040 QVERIFY(!QApplication::testAttribute(Qt::AA_ImmediateWidgetCreation));
2041 w = new QWidget;
2042 QVERIFY(!w->testAttribute(Qt::WA_WState_Created));
2043 delete w;
2044}
2045
2046class TouchEventPropagationTestWidget : public QWidget
2047{
2048 Q_OBJECT
2049
2050public:
2051 bool seenTouchEvent = false, acceptTouchEvent = false, seenMouseEvent = false, acceptMouseEvent = false;
2052
2053
2054 explicit TouchEventPropagationTestWidget(QWidget *parent = nullptr) : QWidget(parent)
2055 {
2056 setAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents);
2057 }
2058
2059 void reset()
2060 {
2061 seenTouchEvent = acceptTouchEvent = seenMouseEvent = acceptMouseEvent = false;
2062 }
2063
2064 bool event(QEvent *event) override
2065 {
2066 switch (event->type()) {
2067 case QEvent::MouseButtonPress:
2068 case QEvent::MouseMove:
2069 case QEvent::MouseButtonRelease:
2070 // qDebug() << objectName() << "seenMouseEvent = true";
2071 seenMouseEvent = true;
2072 event->setAccepted(acceptMouseEvent);
2073 break;
2074 case QEvent::TouchBegin:
2075 case QEvent::TouchUpdate:
2076 case QEvent::TouchEnd:
2077 // qDebug() << objectName() << "seenTouchEvent = true";
2078 seenTouchEvent = true;
2079 event->setAccepted(acceptTouchEvent);
2080 break;
2081 default:
2082 return QWidget::event(event);
2083 }
2084 return true;
2085 }
2086};
2087
2088void tst_QApplication::touchEventPropagation()
2089{
2090 int argc = 1;
2091 QApplication app(argc, &argv0);
2092
2093 QList<QTouchEvent::TouchPoint> pressedTouchPoints;
2094 QTouchEvent::TouchPoint press(0);
2095 press.setState(Qt::TouchPointPressed);
2096 pressedTouchPoints << press;
2097
2098 QList<QTouchEvent::TouchPoint> releasedTouchPoints;
2099 QTouchEvent::TouchPoint release(0);
2100 release.setState(Qt::TouchPointReleased);
2101 releasedTouchPoints << release;
2102
2103 QTouchDevice *device = QTest::createTouchDevice();
2104
2105 {
2106 // touch event behavior on a window
2107 TouchEventPropagationTestWidget window;
2108 window.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
2109 window.resize(w: 200, h: 200);
2110 window.setObjectName("1. window");
2111 window.show(); // Must have an explicitly specified QWindow for handleTouchEvent,
2112 // passing 0 would result in using topLevelAt() which is not ok in this case
2113 // as the screen position in the point is bogus.
2114 auto handle = window.windowHandle();
2115 QVERIFY(QTest::qWaitForWindowExposed(&window));
2116 // QPA always takes screen positions and since we map the TouchPoint back to QPA's structure first,
2117 // we must ensure there is a screen position in the TouchPoint that maps to a local 0, 0.
2118 const QPoint deviceGlobalPos =
2119 QHighDpi::toNativePixels(value: window.mapToGlobal(QPoint(0, 0)), context: window.windowHandle()->screen());
2120 pressedTouchPoints[0].setScreenPos(deviceGlobalPos);
2121 releasedTouchPoints[0].setScreenPos(deviceGlobalPos);
2122
2123 QWindowSystemInterface::handleTouchEvent(window: handle,
2124 timestamp: 0,
2125 device,
2126 points: QWindowSystemInterfacePrivate::toNativeTouchPoints(pointList: pressedTouchPoints, window: handle));
2127 QWindowSystemInterface::handleTouchEvent(window: handle,
2128 timestamp: 0,
2129 device,
2130 points: QWindowSystemInterfacePrivate::toNativeTouchPoints(pointList: releasedTouchPoints, window: handle));
2131 QCoreApplication::processEvents();
2132 QVERIFY(!window.seenTouchEvent);
2133 QVERIFY(window.seenMouseEvent); // QApplication may transform ignored touch events in mouse events
2134
2135 window.reset();
2136 window.setAttribute(Qt::WA_AcceptTouchEvents);
2137 QWindowSystemInterface::handleTouchEvent(window: handle,
2138 timestamp: 0,
2139 device,
2140 points: QWindowSystemInterfacePrivate::toNativeTouchPoints(pointList: pressedTouchPoints, window: handle));
2141 QWindowSystemInterface::handleTouchEvent(window: handle,
2142 timestamp: 0,
2143 device,
2144 points: QWindowSystemInterfacePrivate::toNativeTouchPoints(pointList: releasedTouchPoints, window: handle));
2145 QCoreApplication::processEvents();
2146 QVERIFY(window.seenTouchEvent);
2147 QVERIFY(window.seenMouseEvent);
2148
2149 window.reset();
2150 window.acceptTouchEvent = true;
2151 QWindowSystemInterface::handleTouchEvent(window: handle,
2152 timestamp: 0,
2153 device,
2154 points: QWindowSystemInterfacePrivate::toNativeTouchPoints(pointList: pressedTouchPoints, window: handle));
2155 QWindowSystemInterface::handleTouchEvent(window: handle,
2156 timestamp: 0,
2157 device,
2158 points: QWindowSystemInterfacePrivate::toNativeTouchPoints(pointList: releasedTouchPoints, window: handle));
2159 QCoreApplication::processEvents();
2160 QVERIFY(window.seenTouchEvent);
2161 QVERIFY(!window.seenMouseEvent);
2162 }
2163
2164 {
2165 // touch event behavior on a window with a child widget
2166 TouchEventPropagationTestWidget window;
2167 window.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
2168 window.resize(w: 200, h: 200);
2169 window.setObjectName("2. window");
2170 TouchEventPropagationTestWidget widget(&window);
2171 widget.resize(w: 200, h: 200);
2172 widget.setObjectName("2. widget");
2173 window.show();
2174 auto handle = window.windowHandle();
2175 QVERIFY(QTest::qWaitForWindowExposed(&window));
2176 const QPoint deviceGlobalPos =
2177 QHighDpi::toNativePixels(value: window.mapToGlobal(QPoint(50, 150)), context: window.windowHandle()->screen());
2178 pressedTouchPoints[0].setScreenPos(deviceGlobalPos);
2179 releasedTouchPoints[0].setScreenPos(deviceGlobalPos);
2180
2181 QWindowSystemInterface::handleTouchEvent(window: handle,
2182 timestamp: 0,
2183 device,
2184 points: QWindowSystemInterfacePrivate::toNativeTouchPoints(pointList: pressedTouchPoints, window: handle));
2185 QWindowSystemInterface::handleTouchEvent(window: handle,
2186 timestamp: 0,
2187 device,
2188 points: QWindowSystemInterfacePrivate::toNativeTouchPoints(pointList: releasedTouchPoints, window: handle));
2189 QTRY_VERIFY(widget.seenMouseEvent);
2190 QVERIFY(!widget.seenTouchEvent);
2191 QVERIFY(!window.seenTouchEvent);
2192 QVERIFY(window.seenMouseEvent);
2193
2194 window.reset();
2195 widget.reset();
2196 widget.setAttribute(Qt::WA_AcceptTouchEvents);
2197 QWindowSystemInterface::handleTouchEvent(window: handle,
2198 timestamp: 0,
2199 device,
2200 points: QWindowSystemInterfacePrivate::toNativeTouchPoints(pointList: pressedTouchPoints, window: handle));
2201 QWindowSystemInterface::handleTouchEvent(window: handle,
2202 timestamp: 0,
2203 device,
2204 points: QWindowSystemInterfacePrivate::toNativeTouchPoints(pointList: releasedTouchPoints, window: handle));
2205 QCoreApplication::processEvents();
2206 QVERIFY(widget.seenTouchEvent);
2207 QVERIFY(widget.seenMouseEvent);
2208 QVERIFY(!window.seenTouchEvent);
2209 QVERIFY(window.seenMouseEvent);
2210
2211 window.reset();
2212 widget.reset();
2213 widget.acceptMouseEvent = true;
2214 QWindowSystemInterface::handleTouchEvent(window: handle,
2215 timestamp: 0,
2216 device,
2217 points: QWindowSystemInterfacePrivate::toNativeTouchPoints(pointList: pressedTouchPoints, window: handle));
2218 QWindowSystemInterface::handleTouchEvent(window: handle,
2219 timestamp: 0,
2220 device,
2221 points: QWindowSystemInterfacePrivate::toNativeTouchPoints(pointList: releasedTouchPoints, window: handle));
2222 QCoreApplication::processEvents();
2223 QVERIFY(widget.seenTouchEvent);
2224 QVERIFY(widget.seenMouseEvent);
2225 QVERIFY(!window.seenTouchEvent);
2226 QVERIFY(!window.seenMouseEvent);
2227
2228 window.reset();
2229 widget.reset();
2230 widget.acceptTouchEvent = true;
2231 QWindowSystemInterface::handleTouchEvent(window: handle,
2232 timestamp: 0,
2233 device,
2234 points: QWindowSystemInterfacePrivate::toNativeTouchPoints(pointList: pressedTouchPoints, window: handle));
2235 QWindowSystemInterface::handleTouchEvent(window: handle,
2236 timestamp: 0,
2237 device,
2238 points: QWindowSystemInterfacePrivate::toNativeTouchPoints(pointList: releasedTouchPoints, window: handle));
2239 QCoreApplication::processEvents();
2240 QVERIFY(widget.seenTouchEvent);
2241 QVERIFY(!widget.seenMouseEvent);
2242 QVERIFY(!window.seenTouchEvent);
2243 QVERIFY(!window.seenMouseEvent);
2244
2245 window.reset();
2246 widget.reset();
2247 widget.setAttribute(Qt::WA_AcceptTouchEvents, on: false);
2248 window.setAttribute(Qt::WA_AcceptTouchEvents);
2249 QWindowSystemInterface::handleTouchEvent(window: handle,
2250 timestamp: 0,
2251 device,
2252 points: QWindowSystemInterfacePrivate::toNativeTouchPoints(pointList: pressedTouchPoints, window: handle));
2253 QWindowSystemInterface::handleTouchEvent(window: handle,
2254 timestamp: 0,
2255 device,
2256 points: QWindowSystemInterfacePrivate::toNativeTouchPoints(pointList: releasedTouchPoints, window: handle));
2257 QCoreApplication::processEvents();
2258 QVERIFY(!widget.seenTouchEvent);
2259 QVERIFY(widget.seenMouseEvent);
2260 QVERIFY(window.seenTouchEvent);
2261 QVERIFY(window.seenMouseEvent);
2262
2263 window.reset();
2264 widget.reset();
2265 window.acceptTouchEvent = true;
2266 QWindowSystemInterface::handleTouchEvent(window: handle,
2267 timestamp: 0,
2268 device,
2269 points: QWindowSystemInterfacePrivate::toNativeTouchPoints(pointList: pressedTouchPoints, window: handle));
2270 QWindowSystemInterface::handleTouchEvent(window: handle,
2271 timestamp: 0,
2272 device,
2273 points: QWindowSystemInterfacePrivate::toNativeTouchPoints(pointList: releasedTouchPoints, window: handle));
2274 QCoreApplication::processEvents();
2275 QVERIFY(!widget.seenTouchEvent);
2276 QVERIFY(!widget.seenMouseEvent);
2277 QVERIFY(window.seenTouchEvent);
2278 QVERIFY(!window.seenMouseEvent);
2279
2280 window.reset();
2281 widget.reset();
2282 widget.acceptMouseEvent = true; // doesn't matter, touch events are propagated first
2283 window.acceptTouchEvent = true;
2284 QWindowSystemInterface::handleTouchEvent(window: handle,
2285 timestamp: 0,
2286 device,
2287 points: QWindowSystemInterfacePrivate::toNativeTouchPoints(pointList: pressedTouchPoints, window: handle));
2288 QWindowSystemInterface::handleTouchEvent(window: handle,
2289 timestamp: 0,
2290 device,
2291 points: QWindowSystemInterfacePrivate::toNativeTouchPoints(pointList: releasedTouchPoints, window: handle));
2292 QCoreApplication::processEvents();
2293 QVERIFY(!widget.seenTouchEvent);
2294 QVERIFY(!widget.seenMouseEvent);
2295 QVERIFY(window.seenTouchEvent);
2296 QVERIFY(!window.seenMouseEvent);
2297 }
2298}
2299
2300/*!
2301 Test that wheel events are propagated correctly.
2302
2303 The event propagation of wheel events is complex: generally, they are propagated
2304 up the parent tree like other input events, until a widget accepts the event. However,
2305 wheel events are ignored by default (unlike mouse events, which are accepted by default,
2306 and ignored in the default implementation of the event handler of QWidget).
2307
2308 And Qt tries to make sure that wheel events that "belong together" are going to the same
2309 widget. However, for low-precision events as generated by an old-fashioned
2310 mouse wheel, each event is a distinct event, so Qt has no choice than to deliver the event
2311 to the widget under the mouse.
2312 High-precision events, as generated by track pads or other kinetic scrolling devices, come
2313 in a continuous stream, with different phases. Qt tries to make sure that all events in the
2314 same stream go to the widget that accepted the first event.
2315
2316 Also, QAbstractScrollArea forwards wheel events from the viewport to the relevant scrollbar,
2317 which adds more complexity to the handling.
2318
2319 This tests two scenarios:
2320 1) a large widget inside a scrollarea that scrolls, inside a scrollarea that also scrolls
2321 2) a large widget inside a scrollarea that doesn't scroll, within a scrollarea that does
2322
2323 For scenario 1 "inner", the expectation is that the inner scrollarea handles all wheel
2324 events.
2325 For scenario 2 "outer", the expectation is that the outer scrollarea handles all wheel
2326 events.
2327*/
2328struct WheelEvent
2329{
2330 WheelEvent(Qt::ScrollPhase p = Qt::NoScrollPhase, Qt::Orientation o = Qt::Vertical)
2331 : phase(p), orientation(o)
2332 {}
2333 Qt::ScrollPhase phase = Qt::NoScrollPhase;
2334 Qt::Orientation orientation = Qt::Vertical;
2335};
2336using WheelEventList = QList<WheelEvent>;
2337Q_DECLARE_METATYPE(WheelEvent);
2338
2339void tst_QApplication::wheelEventPropagation_data()
2340{
2341 qRegisterMetaType<WheelEventList>();
2342
2343 QTest::addColumn<bool>(name: "innerScrolls");
2344 QTest::addColumn<WheelEventList>(name: "events");
2345
2346 QTest::addRow(format: "inner, classic")
2347 << true
2348 << WheelEventList{{}, {}, {}};
2349 QTest::addRow(format: "outer, classic")
2350 << false
2351 << WheelEventList{{}, {}, {}};
2352 QTest::addRow(format: "inner, kinetic")
2353 << true
2354 << WheelEventList{Qt::ScrollBegin, Qt::ScrollUpdate, Qt::ScrollMomentum, Qt::ScrollEnd};
2355 QTest::addRow(format: "outer, kinetic")
2356 << false
2357 << WheelEventList{Qt::ScrollBegin, Qt::ScrollUpdate, Qt::ScrollMomentum, Qt::ScrollEnd};
2358 QTest::addRow(format: "inner, partial kinetic")
2359 << true
2360 << WheelEventList{Qt::ScrollUpdate, Qt::ScrollMomentum, Qt::ScrollEnd};
2361 QTest::addRow(format: "outer, partial kinetic")
2362 << false
2363 << WheelEventList{Qt::ScrollUpdate, Qt::ScrollMomentum, Qt::ScrollEnd};
2364 QTest::addRow(format: "inner, changing direction")
2365 << true
2366 << WheelEventList{Qt::ScrollUpdate, {Qt::ScrollUpdate, Qt::Horizontal}, Qt::ScrollMomentum, Qt::ScrollEnd};
2367 QTest::addRow(format: "outer, changing direction")
2368 << false
2369 << WheelEventList{Qt::ScrollUpdate, {Qt::ScrollUpdate, Qt::Horizontal}, Qt::ScrollMomentum, Qt::ScrollEnd};
2370}
2371
2372void tst_QApplication::wheelEventPropagation()
2373{
2374#ifdef Q_OS_WINRT
2375 QSKIP("Not enough available screen space on WinRT for this test");
2376#endif
2377
2378 QFETCH(bool, innerScrolls);
2379 QFETCH(WheelEventList, events);
2380
2381 const QSize baseSize(500, 500);
2382 const QPointF center(baseSize.width() / 2, baseSize.height() / 2);
2383 int scrollStep = 50;
2384
2385 int argc = 1;
2386 QApplication app(argc, &argv0);
2387
2388 QScrollArea outerArea;
2389 outerArea.setObjectName("outerArea");
2390 outerArea.viewport()->setObjectName("outerArea_viewport");
2391 QScrollArea innerArea;
2392 innerArea.setObjectName("innerArea");
2393 innerArea.viewport()->setObjectName("innerArea_viewport");
2394 QWidget largeWidget;
2395 largeWidget.setObjectName("largeWidget");
2396 QScrollBar trap(Qt::Vertical, &largeWidget);
2397 trap.setObjectName("It's a trap!");
2398
2399 largeWidget.setFixedSize(baseSize * 8);
2400
2401 // classic wheel events will be grabbed by the widget under the mouse, so don't place a trap
2402 if (events.at(i: 0).phase == Qt::NoScrollPhase)
2403 trap.hide();
2404 // kinetic wheel events should all go to the first widget; place a trap
2405 else
2406 trap.setGeometry(ax: center.x() - 50, ay: center.y() + scrollStep, aw: 100, ah: baseSize.height());
2407
2408 // if the inner area is large enough to host the widget, then it won't scroll
2409 innerArea.setWidget(&largeWidget);
2410 innerArea.setFixedSize(innerScrolls ? baseSize * 4
2411 : largeWidget.minimumSize() + QSize(100, 100));
2412 // the outer area always scrolls
2413 outerArea.setFixedSize(baseSize);
2414 outerArea.setWidget(&innerArea);
2415 outerArea.show();
2416
2417 if (!QTest::qWaitForWindowExposed(widget: &outerArea))
2418 QSKIP("Window failed to show, can't run test");
2419
2420 auto innerVBar = innerArea.verticalScrollBar();
2421 innerVBar->setObjectName("innerArea_vbar");
2422 QCOMPARE(innerVBar->isVisible(), innerScrolls);
2423 auto innerHBar = innerArea.horizontalScrollBar();
2424 innerHBar->setObjectName("innerArea_hbar");
2425 QCOMPARE(innerHBar->isVisible(), innerScrolls);
2426 auto outerVBar = outerArea.verticalScrollBar();
2427 outerVBar->setObjectName("outerArea_vbar");
2428 QVERIFY(outerVBar->isVisible());
2429 auto outerHBar = outerArea.horizontalScrollBar();
2430 outerHBar->setObjectName("outerArea_hbar");
2431 QVERIFY(outerHBar->isVisible());
2432
2433 const QPointF global(outerArea.mapToGlobal(center.toPoint()));
2434
2435 QSignalSpy innerVSpy(innerVBar, &QAbstractSlider::valueChanged);
2436 QSignalSpy innerHSpy(innerHBar, &QAbstractSlider::valueChanged);
2437 QSignalSpy outerVSpy(outerVBar, &QAbstractSlider::valueChanged);
2438 QSignalSpy outerHSpy(outerHBar, &QAbstractSlider::valueChanged);
2439
2440 int vcount = 0;
2441 int hcount = 0;
2442
2443 for (const auto &event : qAsConst(t&: events)) {
2444 const QPoint pixelDelta = event.orientation == Qt::Vertical ? QPoint(0, -scrollStep) : QPoint(-scrollStep, 0);
2445 const QPoint angleDelta = event.orientation == Qt::Vertical ? QPoint(0, -120) : QPoint(-120, 0);
2446 QWindowSystemInterface::handleWheelEvent(window: outerArea.windowHandle(), local: center, global,
2447 pixelDelta, angleDelta, mods: Qt::NoModifier,
2448 phase: event.phase);
2449 if (event.orientation == Qt::Vertical)
2450 ++vcount;
2451 else
2452 ++hcount;
2453 QCoreApplication::processEvents();
2454 QCOMPARE(innerVSpy.count(), innerScrolls ? vcount : 0);
2455 QCOMPARE(innerHSpy.count(), innerScrolls ? hcount : 0);
2456 QCOMPARE(outerVSpy.count(), innerScrolls ? 0 : vcount);
2457 QCOMPARE(outerHSpy.count(), innerScrolls ? 0 : hcount);
2458 }
2459}
2460
2461void tst_QApplication::qtbug_12673()
2462{
2463#if QT_CONFIG(process)
2464 QProcess testProcess;
2465 QStringList arguments;
2466 // Add the executable's directory to path so that we can find the test helper next to it
2467 // in a cross-platform way. We must do this because the CWD is not pointing to this directory
2468 // in debug-and-release builds.
2469 int argc = 0;
2470 QApplication app(argc, nullptr);
2471 QByteArray path = qgetenv(varName: "PATH");
2472 qputenv(varName: "PATH",
2473 value: path + QDir::listSeparator().toLatin1()
2474 + QCoreApplication::applicationDirPath().toLocal8Bit());
2475 auto restore = qScopeGuard(f: [&] { qputenv(varName: "PATH", value: path); });
2476
2477 testProcess.start(program: "modal_helper", arguments);
2478 QVERIFY2(testProcess.waitForStarted(),
2479 qPrintable(QString::fromLatin1("Cannot start 'modal_helper': %1").arg(testProcess.errorString())));
2480 QVERIFY(testProcess.waitForFinished(20000));
2481 QCOMPARE(testProcess.exitStatus(), QProcess::NormalExit);
2482#else
2483 QSKIP( "No QProcess support", SkipAll);
2484#endif
2485}
2486
2487class NoQuitOnHideWidget : public QWidget
2488{
2489 Q_OBJECT
2490public:
2491 explicit NoQuitOnHideWidget(QWidget *parent = nullptr)
2492 : QWidget(parent)
2493 {
2494 QTimer::singleShot(interval: 0, receiver: this, slot: &QWidget::hide);
2495 QTimer::singleShot(interval: 500, context: this, slot: [] () { QCoreApplication::exit(retcode: 1); });
2496 }
2497};
2498
2499void tst_QApplication::noQuitOnHide()
2500{
2501 int argc = 0;
2502 QApplication app(argc, nullptr);
2503 NoQuitOnHideWidget window1;
2504 window1.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
2505 window1.show();
2506 QCOMPARE(QCoreApplication::exec(), 1);
2507}
2508
2509class ShowCloseShowWidget : public QWidget
2510{
2511 Q_OBJECT
2512public:
2513 explicit ShowCloseShowWidget(bool showAgain, QWidget *parent = nullptr)
2514 : QWidget(parent), showAgain(showAgain)
2515 {
2516 QTimer::singleShot(interval: 0, receiver: this, slot: &ShowCloseShowWidget::doClose);
2517 QTimer::singleShot(interval: 500, context: this, slot: [] () { QCoreApplication::exit(retcode: 1); });
2518 }
2519
2520private slots:
2521 void doClose() {
2522 close();
2523 if (showAgain)
2524 show();
2525 }
2526
2527private:
2528 const bool showAgain;
2529};
2530
2531void tst_QApplication::abortQuitOnShow()
2532{
2533 int argc = 0;
2534 QApplication app(argc, nullptr);
2535 ShowCloseShowWidget window1(false);
2536 window1.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
2537 window1.show();
2538 QCOMPARE(QCoreApplication::exec(), 0);
2539
2540 ShowCloseShowWidget window2(true);
2541 window2.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
2542 window2.show();
2543 QCOMPARE(QCoreApplication::exec(), 1);
2544}
2545
2546// Test that static functions do not crash if there is no application instance.
2547void tst_QApplication::staticFunctions()
2548{
2549 QApplication::setStyle(QStringLiteral("blub"));
2550 QApplication::allWidgets();
2551 QApplication::topLevelWidgets();
2552 QApplication::desktop();
2553 QApplication::activePopupWidget();
2554 QApplication::activeModalWidget();
2555 QApplication::focusWidget();
2556 QApplication::activeWindow();
2557 QApplication::setActiveWindow(nullptr);
2558 QApplication::widgetAt(p: QPoint(0, 0));
2559 QApplication::topLevelAt(p: QPoint(0, 0));
2560 QApplication::setGlobalStrut(QSize(0, 0));
2561 QApplication::globalStrut();
2562 QApplication::isEffectEnabled(Qt::UI_General);
2563 QApplication::setEffectEnabled(Qt::UI_General, enable: false);
2564}
2565
2566void tst_QApplication::settableStyleHints_data()
2567{
2568 QTest::addColumn<bool>(name: "appInstance");
2569 QTest::newRow(dataTag: "app") << true;
2570 QTest::newRow(dataTag: "no-app") << false;
2571}
2572
2573void tst_QApplication::settableStyleHints()
2574{
2575 QFETCH(bool, appInstance);
2576 int argc = 0;
2577 QScopedPointer<QApplication> app;
2578 if (appInstance)
2579 app.reset(other: new QApplication(argc, nullptr));
2580
2581 QApplication::setCursorFlashTime(437);
2582 QCOMPARE(QApplication::cursorFlashTime(), 437);
2583 QApplication::setDoubleClickInterval(128);
2584 QCOMPARE(QApplication::doubleClickInterval(), 128);
2585 QApplication::setStartDragDistance(122000);
2586 QCOMPARE(QApplication::startDragDistance(), 122000);
2587 QApplication::setStartDragTime(834);
2588 QCOMPARE(QApplication::startDragTime(), 834);
2589 QApplication::setKeyboardInputInterval(309);
2590 QCOMPARE(QApplication::keyboardInputInterval(), 309);
2591}
2592
2593/*
2594 This test is meant to ensure that certain objects (public & commonly used)
2595 can safely be used in a Q_GLOBAL_STATIC such that their destructors are
2596 executed *after* the destruction of QApplication.
2597 */
2598Q_GLOBAL_STATIC(QLocale, tst_qapp_locale);
2599#if QT_CONFIG(process)
2600Q_GLOBAL_STATIC(QProcess, tst_qapp_process);
2601#endif
2602#if QT_CONFIG(filesystemwatcher)
2603Q_GLOBAL_STATIC(QFileSystemWatcher, tst_qapp_fileSystemWatcher);
2604#endif
2605#ifndef QT_NO_SHAREDMEMORY
2606Q_GLOBAL_STATIC(QSharedMemory, tst_qapp_sharedMemory);
2607#endif
2608Q_GLOBAL_STATIC(QElapsedTimer, tst_qapp_elapsedTimer);
2609Q_GLOBAL_STATIC(QMutex, tst_qapp_mutex);
2610Q_GLOBAL_STATIC(QWidget, tst_qapp_widget);
2611Q_GLOBAL_STATIC(QPixmap, tst_qapp_pixmap);
2612Q_GLOBAL_STATIC(QFont, tst_qapp_font);
2613Q_GLOBAL_STATIC(QRegion, tst_qapp_region);
2614Q_GLOBAL_STATIC(QFontDatabase, tst_qapp_fontDatabase);
2615#ifndef QT_NO_CURSOR
2616Q_GLOBAL_STATIC(QCursor, tst_qapp_cursor);
2617#endif
2618
2619void tst_QApplication::globalStaticObjectDestruction()
2620{
2621 int argc = 1;
2622 QApplication app(argc, &argv0);
2623 QVERIFY(tst_qapp_locale());
2624#if QT_CONFIG(process)
2625 QVERIFY(tst_qapp_process());
2626#endif
2627#if QT_CONFIG(filesystemwatcher)
2628 QVERIFY(tst_qapp_fileSystemWatcher());
2629#endif
2630#ifndef QT_NO_SHAREDMEMORY
2631 QVERIFY(tst_qapp_sharedMemory());
2632#endif
2633 QVERIFY(tst_qapp_elapsedTimer());
2634 QVERIFY(tst_qapp_mutex());
2635 QVERIFY(tst_qapp_widget());
2636 QVERIFY(tst_qapp_pixmap());
2637 QVERIFY(tst_qapp_font());
2638 QVERIFY(tst_qapp_region());
2639 QVERIFY(tst_qapp_fontDatabase());
2640#ifndef QT_NO_CURSOR
2641 QVERIFY(tst_qapp_cursor());
2642#endif
2643}
2644
2645//QTEST_APPLESS_MAIN(tst_QApplication)
2646int main(int argc, char *argv[])
2647{
2648 tst_QApplication tc;
2649 argv0 = argv[0];
2650 return QTest::qExec(testObject: &tc, argc, argv);
2651}
2652
2653#include "tst_qapplication.moc"
2654

source code of qtbase/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp