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#include <qrasterwindow.h>
30#include <qpa/qwindowsysteminterface.h>
31#include <qpa/qplatformintegration.h>
32#include <qpa/qplatformwindow.h>
33#include <private/qguiapplication_p.h>
34#include <private/qhighdpiscaling_p.h>
35#include <QtGui/QPainter>
36
37#include <QtTest/QtTest>
38
39#include <QEvent>
40#include <QStyleHints>
41
42#if defined(Q_OS_QNX)
43#include <QOpenGLContext>
44#elif defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
45# include <QtCore/qt_windows.h>
46#endif
47
48static bool isPlatformWinRT()
49{
50 static const bool isWinRT = !QGuiApplication::platformName().compare(other: QLatin1String("winrt"), cs: Qt::CaseInsensitive);
51 return isWinRT;
52}
53
54class tst_QWindow: public QObject
55{
56 Q_OBJECT
57
58private slots:
59 void create();
60 void setParent();
61 void setVisible();
62 void setVisibleFalseDoesNotCreateWindow();
63 void eventOrderOnShow();
64 void resizeEventAfterResize();
65 void exposeEventOnShrink_QTBUG54040();
66 void mapGlobal();
67 void positioning_data();
68 void positioning();
69 void positioningDuringMinimized();
70 void childWindowPositioning_data();
71 void childWindowPositioning();
72 void childWindowLevel();
73 void platformSurface();
74 void isExposed();
75 void isActive();
76 void testInputEvents();
77 void touchToMouseTranslation();
78 void touchToMouseTranslationForDevices();
79 void mouseToTouchTranslation();
80 void mouseToTouchLoop();
81 void touchCancel();
82 void touchCancelWithTouchToMouse();
83 void touchInterruptedByPopup();
84 void orientation();
85 void sizes();
86 void close();
87 void activateAndClose();
88 void mouseEventSequence();
89 void windowModality();
90 void inputReentrancy();
91 void tabletEvents();
92 void windowModality_QTBUG27039();
93 void visibility();
94 void mask();
95 void initialSize();
96 void modalDialog();
97 void modalDialogClosingOneOfTwoModal();
98 void modalWithChildWindow();
99 void modalWindowModallity();
100 void modalWindowPosition();
101#ifndef QT_NO_CURSOR
102 void modalWindowEnterEventOnHide_QTBUG35109();
103 void spuriousMouseMove();
104#endif
105 void windowsTransientChildren();
106 void requestUpdate();
107 void initTestCase();
108 void stateChange_data();
109 void stateChange();
110 void flags();
111 void cleanup();
112 void testBlockingWindowShownAfterModalDialog();
113 void generatedMouseMove();
114 void keepPendingUpdateRequests();
115
116private:
117 QPoint m_availableTopLeft;
118 QSize m_testWindowSize;
119 QTouchDevice *touchDevice = QTest::createTouchDevice();
120};
121
122void tst_QWindow::initTestCase()
123{
124 // Size of reference window, 200 for < 2000, scale up for larger screens
125 // to avoid Windows warnings about minimum size for decorated windows.
126 int width = 200;
127 const QScreen *screen = QGuiApplication::primaryScreen();
128 m_availableTopLeft = screen->availableGeometry().topLeft();
129 const int screenWidth = screen->geometry().width();
130 if (screenWidth > 2000)
131 width = 100 * ((screenWidth + 500) / 1000);
132 m_testWindowSize = QSize(width, width);
133}
134
135void tst_QWindow::cleanup()
136{
137 QVERIFY(QGuiApplication::allWindows().isEmpty());
138}
139
140void tst_QWindow::create()
141{
142 QWindow a;
143 QVERIFY2(!a.handle(), "QWindow should lazy init the platform window");
144
145 a.create();
146 QVERIFY2(a.handle(), "Explicitly creating a platform window should never fail");
147
148 QWindow b;
149 QWindow c(&b);
150 b.create();
151 QVERIFY(b.handle());
152 QVERIFY2(!c.handle(), "Creating a parent window should not automatically create children");
153
154 QWindow d;
155 QWindow e(&d);
156 e.create();
157 QVERIFY(e.handle());
158 QVERIFY2(d.handle(), "Creating a child window should automatically create parents");
159
160 QWindow f;
161 QWindow g(&f);
162 f.create();
163 QVERIFY(f.handle());
164 QPlatformWindow *platformWindow = f.handle();
165 g.create();
166 QVERIFY(g.handle());
167 QVERIFY2(f.handle() == platformWindow, "Creating a child window should not affect parent if already created");
168}
169
170void tst_QWindow::setParent()
171{
172 QWindow a;
173 QWindow b(&a);
174 QVERIFY2(b.parent() == &a, "Setting parent at construction time should work");
175 QVERIFY2(a.children().contains(&b), "Parent should have child in list of children");
176
177 QWindow c;
178 QWindow d;
179 d.setParent(&c);
180 QVERIFY2(d.parent() == &c, "Setting parent after construction should work");
181 QVERIFY2(c.children().contains(&d), "Parent should have child in list of children");
182
183 a.create();
184 b.setParent(nullptr);
185 QVERIFY2(!b.handle(), "Making window top level shouild not automatically create it");
186
187 QWindow e;
188 c.create();
189 e.setParent(&c);
190 QVERIFY2(!e.handle(), "Making window a child of a created window should not automatically create it");
191
192 QWindow f;
193 QWindow g;
194 g.create();
195 QVERIFY(g.handle());
196 g.setParent(&f);
197 QVERIFY2(f.handle(), "Making a created window a child of a non-created window should automatically create it");
198}
199
200void tst_QWindow::setVisible()
201{
202 QWindow a;
203 QWindow b(&a);
204 a.setVisible(true);
205 QVERIFY2(!b.handle(), "Making a top level window visible doesn't create its children");
206 QVERIFY2(!b.isVisible(), "Making a top level window visible doesn't make its children visible");
207 QVERIFY(QTest::qWaitForWindowExposed(&a));
208
209 QWindow c;
210 QWindow d(&c);
211 d.setVisible(true);
212 QVERIFY2(!c.handle(), "Making a child window visible doesn't create parent window if parent is hidden");
213 QVERIFY2(!c.isVisible(), "Making a child window visible doesn't make its parent visible");
214
215 QVERIFY2(!d.handle(), "Making a child window visible doesn't create platform window if parent is hidden");
216
217 c.create();
218 QVERIFY(c.handle());
219 QVERIFY2(d.handle(), "Creating a parent window should automatically create children if they are visible");
220 QVERIFY2(!c.isVisible(), "Creating a parent window should not make it visible just because it has visible children");
221
222 QWindow e;
223 QWindow f(&e);
224 f.setVisible(true);
225 QVERIFY(!f.handle());
226 QVERIFY(!e.handle());
227 f.setParent(nullptr);
228 QVERIFY2(f.handle(), "Making a visible but not created child window top level should create it");
229 QVERIFY(QTest::qWaitForWindowExposed(&f));
230
231 QWindow g;
232 QWindow h;
233 QWindow i(&g);
234 i.setVisible(true);
235 h.setVisible(true);
236 QVERIFY(QTest::qWaitForWindowExposed(&h));
237 QVERIFY(!i.handle());
238 QVERIFY(!g.handle());
239 QVERIFY(h.handle());
240 i.setParent(&h);
241 QVERIFY2(i.handle(), "Making a visible but not created child window child of a created window should create it");
242 if (isPlatformWinRT())
243 QEXPECT_FAIL("", "Child windows are unsupported on winrt", Continue);
244 QVERIFY(QTest::qWaitForWindowExposed(&i));
245}
246
247void tst_QWindow::setVisibleFalseDoesNotCreateWindow()
248{
249 QWindow w;
250 QVERIFY(!w.handle());
251 w.setVisible(false);
252 QVERIFY2(!w.handle(), "Hiding a non-created window doesn't create it");
253 w.setVisible(true);
254 QVERIFY2(w.handle(), "Showing a non-created window creates it");
255}
256
257void tst_QWindow::mapGlobal()
258{
259 QWindow a;
260 QWindow b(&a);
261 QWindow c(&b);
262
263 a.setGeometry(posx: 10, posy: 10, w: 300, h: 300);
264 b.setGeometry(posx: 20, posy: 20, w: 200, h: 200);
265 c.setGeometry(posx: 40, posy: 40, w: 100, h: 100);
266
267 QCOMPARE(a.mapToGlobal(QPoint(100, 100)), QPoint(110, 110));
268 QCOMPARE(b.mapToGlobal(QPoint(100, 100)), QPoint(130, 130));
269 QCOMPARE(c.mapToGlobal(QPoint(100, 100)), QPoint(170, 170));
270
271 QCOMPARE(a.mapFromGlobal(QPoint(100, 100)), QPoint(90, 90));
272 QCOMPARE(b.mapFromGlobal(QPoint(100, 100)), QPoint(70, 70));
273 QCOMPARE(c.mapFromGlobal(QPoint(100, 100)), QPoint(30, 30));
274}
275
276class Window : public QWindow
277{
278public:
279 Window(const Qt::WindowFlags flags = Qt::Window | Qt::WindowTitleHint | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint)
280 : QWindow(), lastReceivedWindowState(windowState())
281 {
282 reset();
283 setFlags(flags);
284#if defined(Q_OS_QNX)
285 setSurfaceType(QSurface::OpenGLSurface);
286#endif
287
288#if !defined(Q_OS_MACOS)
289 // FIXME: All platforms should send window-state change events, regardless
290 // of the sync/async nature of the the underlying platform, but they don't.
291 connect(sender: this, signal: &QWindow::windowStateChanged, slot: [=]() {
292 lastReceivedWindowState = windowState();
293 });
294#endif
295 }
296
297 void reset()
298 {
299 m_received.clear();
300 m_framePositionsOnMove.clear();
301 }
302
303 bool event(QEvent *event) override
304 {
305 m_received[event->type()]++;
306 m_order << event->type();
307 switch (event->type()) {
308 case QEvent::Expose:
309 m_exposeRegion = static_cast<QExposeEvent *>(event)->region();
310 break;
311
312 case QEvent::PlatformSurface:
313 m_surfaceventType = static_cast<QPlatformSurfaceEvent *>(event)->surfaceEventType();
314 break;
315
316 case QEvent::Move:
317 m_framePositionsOnMove << framePosition();
318 break;
319
320 case QEvent::WindowStateChange:
321 lastReceivedWindowState = windowState();
322 break;
323
324 default:
325 break;
326 }
327
328 return QWindow::event(event);
329 }
330
331 int received(QEvent::Type type)
332 {
333 return m_received.value(akey: type, adefaultValue: 0);
334 }
335
336 int eventIndex(QEvent::Type type)
337 {
338 return m_order.indexOf(t: type);
339 }
340
341 QRegion exposeRegion() const
342 {
343 return m_exposeRegion;
344 }
345
346 QPlatformSurfaceEvent::SurfaceEventType surfaceEventType() const
347 {
348 return m_surfaceventType;
349 }
350
351 QVector<QPoint> m_framePositionsOnMove;
352 Qt::WindowStates lastReceivedWindowState;
353
354private:
355 QHash<QEvent::Type, int> m_received;
356 QVector<QEvent::Type> m_order;
357 QRegion m_exposeRegion;
358 QPlatformSurfaceEvent::SurfaceEventType m_surfaceventType;
359};
360
361class ColoredWindow : public QRasterWindow {
362public:
363 explicit ColoredWindow(const QColor &color, QWindow *parent = nullptr) : QRasterWindow(parent), m_color(color) {}
364 void paintEvent(QPaintEvent *) override
365 {
366 QPainter p(this);
367 p.fillRect(QRect(QPoint(0, 0), size()), color: m_color);
368 }
369
370private:
371 const QColor m_color;
372};
373
374void tst_QWindow::eventOrderOnShow()
375{
376 // Some platforms enforce minimum widths for windows, which can cause extra resize
377 // events, so set the width to suitably large value to avoid those.
378 QRect geometry(m_availableTopLeft + QPoint(80, 80), m_testWindowSize);
379
380 Window window;
381 window.setTitle(QLatin1String(QTest::currentTestFunction()));
382 window.setGeometry(geometry);
383 window.show();
384 QCoreApplication::processEvents();
385
386 QTRY_COMPARE(window.received(QEvent::Show), 1);
387 QTRY_COMPARE(window.received(QEvent::Resize), 1);
388 QTRY_VERIFY(window.isExposed());
389
390 QVERIFY(window.eventIndex(QEvent::Show) < window.eventIndex(QEvent::Resize));
391 QVERIFY(window.eventIndex(QEvent::Resize) < window.eventIndex(QEvent::Expose));
392}
393
394void tst_QWindow::resizeEventAfterResize()
395{
396 // Some platforms enforce minimum widths for windows, which can cause extra resize
397 // events, so set the width to suitably large value to avoid those.
398 QRect geometry(m_availableTopLeft + QPoint(80, 80), m_testWindowSize * 2);
399
400 Window window;
401 window.setGeometry(geometry);
402 window.showNormal();
403
404 QTRY_COMPARE(window.received(QEvent::Resize), 1);
405
406 // QTBUG-32706
407 // Make sure we get a resizeEvent after calling resize
408 window.resize(newSize: m_testWindowSize);
409
410 if (isPlatformWinRT())
411 QEXPECT_FAIL("", "Winrt windows are fullscreen by default.", Continue);
412 QTRY_COMPARE(window.received(QEvent::Resize), 2);
413}
414
415void tst_QWindow::exposeEventOnShrink_QTBUG54040()
416{
417 if (isPlatformWinRT())
418 QSKIP("", "WinRT does not support non-maximized/non-fullscreen top level windows. QTBUG-54528", Continue);
419 Window window;
420 window.setGeometry(QRect(m_availableTopLeft + QPoint(80, 80), m_testWindowSize));
421 window.setTitle(QTest::currentTestFunction());
422 window.showNormal();
423
424 QVERIFY(QTest::qWaitForWindowExposed(&window));
425
426 int exposeCount = window.received(type: QEvent::Expose);
427 window.resize(w: window.width(), h: window.height() - 5);
428 QTRY_VERIFY(window.received(QEvent::Expose) > exposeCount);
429
430 exposeCount = window.received(type: QEvent::Expose);
431 window.resize(w: window.width() - 5, h: window.height());
432 QTRY_VERIFY(window.received(QEvent::Expose) > exposeCount);
433
434 exposeCount = window.received(type: QEvent::Expose);
435 window.resize(w: window.width() - 5, h: window.height() - 5);
436 QTRY_VERIFY(window.received(QEvent::Expose) > exposeCount);
437}
438
439void tst_QWindow::positioning_data()
440{
441 QTest::addColumn<Qt::WindowFlags>(name: "windowflags");
442
443 QTest::newRow(dataTag: "default") << (Qt::Window | Qt::WindowTitleHint | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint | Qt::WindowFullscreenButtonHint);
444
445#ifdef Q_OS_MACOS
446 QTest::newRow("fake") << (Qt::Window | Qt::WindowTitleHint | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint);
447#endif
448}
449
450// Compare a window position that may go through scaling in the platform plugin with fuzz.
451static inline bool qFuzzyCompareWindowPosition(const QPoint &p1, const QPoint p2, int fuzz)
452{
453 return (p1 - p2).manhattanLength() <= fuzz;
454}
455
456static inline bool qFuzzyCompareWindowSize(const QSize &s1, const QSize &s2, int fuzz)
457{
458 const int manhattanLength = qAbs(t: s1.width() - s2.width()) + qAbs(t: s1.height() - s2.height());
459 return manhattanLength <= fuzz;
460}
461
462static inline bool qFuzzyCompareWindowGeometry(const QRect &r1, const QRect &r2, int fuzz)
463{
464 return qFuzzyCompareWindowPosition(p1: r1.topLeft(), p2: r2.topLeft(), fuzz)
465 && qFuzzyCompareWindowSize(s1: r1.size(), s2: r2.size(), fuzz);
466}
467
468static QString msgPointMismatch(const QPoint &p1, const QPoint p2)
469{
470 QString result;
471 QDebug(&result) << p1 << "!=" << p2 << ", manhattanLength=" << (p1 - p2).manhattanLength();
472 return result;
473}
474
475static QString msgRectMismatch(const QRect &r1, const QRect &r2)
476{
477 QString result;
478 QDebug(&result) << r1 << "!=" << r2;
479 return result;
480}
481
482static bool isPlatformWayland()
483{
484 return !QGuiApplication::platformName().compare(other: QLatin1String("wayland"), cs: Qt::CaseInsensitive);
485}
486
487void tst_QWindow::positioning()
488{
489 if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(
490 cap: QPlatformIntegration::NonFullScreenWindows)) {
491 QSKIP("This platform does not support non-fullscreen windows");
492 }
493
494 if (isPlatformWayland())
495 QSKIP("Wayland: This fails. See QTBUG-68660.");
496
497 // Some platforms enforce minimum widths for windows, which can cause extra resize
498 // events, so set the width to suitably large value to avoid those.
499 const QRect geometry(m_availableTopLeft + QPoint(80, 80), m_testWindowSize);
500
501 QFETCH(Qt::WindowFlags, windowflags);
502 Window window(windowflags);
503 window.setGeometry(QRect(m_availableTopLeft + QPoint(20, 20), m_testWindowSize));
504 window.setFramePosition(m_availableTopLeft + QPoint(40, 40)); // Move window around before show, size must not change.
505 QCOMPARE(window.geometry().size(), m_testWindowSize);
506 window.setGeometry(geometry);
507 QCOMPARE(window.geometry(), geometry);
508 // explicitly use non-fullscreen show. show() can be fullscreen on some platforms
509 window.showNormal();
510 QCoreApplication::processEvents();
511
512 QVERIFY(QTest::qWaitForWindowExposed(&window));
513
514 QMargins originalMargins = window.frameMargins();
515
516 QCOMPARE(window.position(), window.framePosition() + QPoint(originalMargins.left(), originalMargins.top()));
517 QVERIFY(window.frameGeometry().contains(window.geometry()));
518
519 QPoint originalPos = window.position();
520 QPoint originalFramePos = window.framePosition();
521
522 window.reset();
523 window.setWindowState(Qt::WindowFullScreen);
524 QTRY_COMPARE(window.lastReceivedWindowState, Qt::WindowFullScreen);
525
526 QTRY_VERIFY(window.received(QEvent::Resize) > 0);
527
528 window.reset();
529 window.setWindowState(Qt::WindowNoState);
530 QTRY_COMPARE(window.lastReceivedWindowState, Qt::WindowNoState);
531
532 QTRY_VERIFY(window.received(QEvent::Resize) > 0);
533
534 QTRY_COMPARE(originalPos, window.position());
535 QTRY_COMPARE(originalFramePos, window.framePosition());
536 QTRY_COMPARE(originalMargins, window.frameMargins());
537
538 // if our positioning is actually fully respected by the window manager
539 // test whether it correctly handles frame positioning as well
540 if (originalPos == geometry.topLeft() && (originalMargins.top() != 0 || originalMargins.left() != 0)) {
541 const QScreen *screen = window.screen();
542 const QRect availableGeometry = screen->availableGeometry();
543 const QPoint framePos = availableGeometry.center();
544
545 window.reset();
546 const QPoint oldFramePos = window.framePosition();
547 window.setFramePosition(framePos);
548
549 QTRY_VERIFY(window.received(QEvent::Move));
550 const int fuzz = int(QHighDpiScaling::factor(context: &window));
551 if (!qFuzzyCompareWindowPosition(p1: window.framePosition(), p2: framePos, fuzz)) {
552 qDebug() << "About to fail auto-test. Here is some additional information:";
553 qDebug() << "window.framePosition() == " << window.framePosition();
554 qDebug() << "old frame position == " << oldFramePos;
555 qDebug() << "We received " << window.received(type: QEvent::Move) << " move events";
556 qDebug() << "frame positions after each move event:" << window.m_framePositionsOnMove;
557 }
558 QTRY_VERIFY2(qFuzzyCompareWindowPosition(window.framePosition(), framePos, fuzz),
559 qPrintable(msgPointMismatch(window.framePosition(), framePos)));
560 QTRY_COMPARE(originalMargins, window.frameMargins());
561 QCOMPARE(window.position(), window.framePosition() + QPoint(originalMargins.left(), originalMargins.top()));
562
563 // and back to regular positioning
564
565 window.reset();
566 window.setPosition(originalPos);
567 QTRY_VERIFY(window.received(QEvent::Move));
568 QTRY_COMPARE(originalPos, window.position());
569 }
570}
571
572void tst_QWindow::positioningDuringMinimized()
573{
574 // QTBUG-39544, setting a geometry in minimized state should work as well.
575 if (QGuiApplication::platformName().compare(s: "windows", cs: Qt::CaseInsensitive) != 0
576 && QGuiApplication::platformName().compare(s: "cocoa", cs: Qt::CaseInsensitive) != 0)
577 QSKIP("Not supported on this platform");
578 Window window;
579 window.setTitle(QStringLiteral("positioningDuringMinimized"));
580 const QRect initialGeometry(m_availableTopLeft + QPoint(100, 100), m_testWindowSize);
581 window.setGeometry(initialGeometry);
582 window.showNormal();
583 QVERIFY(QTest::qWaitForWindowExposed(&window));
584 QCOMPARE(window.geometry(), initialGeometry);
585 window.setWindowState(Qt::WindowMinimized);
586 QCOMPARE(window.geometry(), initialGeometry);
587 const QRect newGeometry(initialGeometry.topLeft() + QPoint(50, 50), initialGeometry.size() + QSize(50, 50));
588 window.setGeometry(newGeometry);
589 QTRY_COMPARE(window.geometry(), newGeometry);
590 window.setWindowState(Qt::WindowNoState);
591 QTRY_COMPARE(window.geometry(), newGeometry);
592}
593
594void tst_QWindow::childWindowPositioning_data()
595{
596 QTest::addColumn<bool>(name: "showInsteadOfCreate");
597
598 QTest::newRow(dataTag: "create") << false;
599 QTest::newRow(dataTag: "show") << true;
600}
601
602void tst_QWindow::childWindowPositioning()
603{
604 if (isPlatformWayland())
605 QSKIP("Wayland: This is flaky (protocol errors for xdg-shell v6). See QTBUG-67648.");
606 else if (isPlatformWinRT())
607 QSKIP("WinRT does not support child windows.");
608
609 const QPoint topLeftOrigin(0, 0);
610
611 ColoredWindow topLevelWindowFirst(Qt::green);
612 topLevelWindowFirst.setObjectName("topLevelWindowFirst");
613 ColoredWindow childWindowAfter(Qt::yellow, &topLevelWindowFirst);
614 childWindowAfter.setObjectName("childWindowAfter");
615
616 topLevelWindowFirst.setFramePosition(m_availableTopLeft);
617 childWindowAfter.setFramePosition(topLeftOrigin);
618
619 ColoredWindow topLevelWindowAfter(Qt::green);
620 topLevelWindowAfter.setObjectName("topLevelWindowAfter");
621 ColoredWindow childWindowFirst(Qt::yellow, &topLevelWindowAfter);
622 childWindowFirst.setObjectName("childWindowFirst");
623
624 topLevelWindowAfter.setFramePosition(m_availableTopLeft);
625 childWindowFirst.setFramePosition(topLeftOrigin);
626
627 QFETCH(bool, showInsteadOfCreate);
628
629 QWindow *windows[] = {&topLevelWindowFirst, &childWindowAfter, &childWindowFirst, &topLevelWindowAfter};
630 for (QWindow *window : windows) {
631 if (showInsteadOfCreate)
632 window->showNormal();
633 else
634 window->create();
635 }
636
637 if (showInsteadOfCreate) {
638 QVERIFY(QTest::qWaitForWindowExposed(&topLevelWindowFirst));
639 QVERIFY(QTest::qWaitForWindowExposed(&topLevelWindowAfter));
640 }
641
642 // Creation order shouldn't affect the geometry
643 // Use try compare since on X11 the window manager may still re-position the window after expose
644 QTRY_COMPARE(topLevelWindowFirst.geometry(), topLevelWindowAfter.geometry());
645 QTRY_COMPARE(childWindowAfter.geometry(), childWindowFirst.geometry());
646
647 // Creation order shouldn't affect the child ending up at 0,0
648 QCOMPARE(childWindowFirst.framePosition(), topLeftOrigin);
649 QCOMPARE(childWindowAfter.framePosition(), topLeftOrigin);
650}
651
652void tst_QWindow::childWindowLevel()
653{
654 ColoredWindow topLevel(Qt::green);
655 topLevel.setObjectName("topLevel");
656 ColoredWindow yellowChild(Qt::yellow, &topLevel);
657 yellowChild.setObjectName("yellowChild");
658 ColoredWindow redChild(Qt::red, &topLevel);
659 redChild.setObjectName("redChild");
660 ColoredWindow blueChild(Qt::blue, &topLevel);
661 blueChild.setObjectName("blueChild");
662
663 const QObjectList &siblings = topLevel.children();
664
665 QCOMPARE(siblings.constFirst(), &yellowChild);
666 QCOMPARE(siblings.constLast(), &blueChild);
667
668 yellowChild.raise();
669 QCOMPARE(siblings.constLast(), &yellowChild);
670
671 blueChild.lower();
672 QCOMPARE(siblings.constFirst(), &blueChild);
673}
674
675// QTBUG-49709: Verify that the normal geometry is correctly restored
676// when executing a sequence of window state changes. So far, Windows
677// only where state changes have immediate effect.
678
679typedef QList<Qt::WindowState> WindowStateList;
680
681Q_DECLARE_METATYPE(WindowStateList)
682
683void tst_QWindow::stateChange_data()
684{
685 QTest::addColumn<WindowStateList>(name: "stateSequence");
686
687 QTest::newRow(dataTag: "normal->min->normal") <<
688 (WindowStateList() << Qt::WindowMinimized << Qt::WindowNoState);
689 QTest::newRow(dataTag: "normal->maximized->normal") <<
690 (WindowStateList() << Qt::WindowMaximized << Qt::WindowNoState);
691 QTest::newRow(dataTag: "normal->fullscreen->normal") <<
692 (WindowStateList() << Qt::WindowFullScreen << Qt::WindowNoState);
693 QTest::newRow(dataTag: "normal->maximized->fullscreen->normal") <<
694 (WindowStateList() << Qt::WindowMaximized << Qt::WindowFullScreen << Qt::WindowNoState);
695}
696
697void tst_QWindow::stateChange()
698{
699 QFETCH(WindowStateList, stateSequence);
700
701 if (QGuiApplication::platformName().compare(other: QLatin1String("windows"), cs: Qt::CaseInsensitive))
702 QSKIP("Windows-only test");
703
704 Window window;
705 window.setTitle(QLatin1String(QTest::currentTestFunction()) + QLatin1Char(' ') + QLatin1String(QTest::currentDataTag()));
706 const QRect normalGeometry(m_availableTopLeft + QPoint(40, 40), m_testWindowSize);
707 window.setGeometry(normalGeometry);
708 // explicitly use non-fullscreen show. show() can be fullscreen on some platforms
709 window.showNormal();
710 QVERIFY(QTest::qWaitForWindowExposed(&window));
711 for (Qt::WindowState state : qAsConst(t&: stateSequence)) {
712 window.setWindowState(state);
713 QCoreApplication::processEvents();
714 }
715 const QRect geometry = window.geometry();
716 const int fuzz = int(QHighDpiScaling::factor(context: &window));
717 QVERIFY2(qFuzzyCompareWindowGeometry(geometry, normalGeometry, fuzz),
718 qPrintable(msgRectMismatch(geometry, normalGeometry)));
719}
720
721class PlatformWindowFilter : public QObject
722{
723 Q_OBJECT
724public:
725 explicit PlatformWindowFilter(Window *window) : m_window(window) {}
726
727 bool eventFilter(QObject *o, QEvent *e) override
728 {
729 // Check that the platform surface events are delivered synchronously.
730 // If they are, the native platform surface should always exist when we
731 // receive a QPlatformSurfaceEvent
732 if (e->type() == QEvent::PlatformSurface && o == m_window) {
733 m_alwaysExisted &= (m_window->handle() != nullptr);
734 }
735 return false;
736 }
737
738 bool surfaceExisted() const { return m_alwaysExisted; }
739
740private:
741 Window *m_window;
742 bool m_alwaysExisted = true;
743};
744
745void tst_QWindow::platformSurface()
746{
747 QRect geometry(m_availableTopLeft + QPoint(80, 80), m_testWindowSize);
748
749 Window window;
750 PlatformWindowFilter filter(&window);
751 window.installEventFilter(filterObj: &filter);
752
753 window.setGeometry(geometry);
754 QCOMPARE(window.geometry(), geometry);
755 window.create();
756
757 QTRY_COMPARE(window.received(QEvent::PlatformSurface), 1);
758 QTRY_COMPARE(window.surfaceEventType(), QPlatformSurfaceEvent::SurfaceCreated);
759 QTRY_VERIFY(window.handle() != nullptr);
760
761 window.destroy();
762 QTRY_COMPARE(window.received(QEvent::PlatformSurface), 2);
763 QTRY_COMPARE(window.surfaceEventType(), QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed);
764 QTRY_VERIFY(!window.handle());
765
766 // Check for synchronous delivery of platform surface events and that the platform
767 // surface always existed upon event delivery
768 QTRY_VERIFY(filter.surfaceExisted());
769}
770
771void tst_QWindow::isExposed()
772{
773 QRect geometry(m_availableTopLeft + QPoint(80, 80), m_testWindowSize);
774
775 Window window;
776 window.setTitle(QLatin1String(QTest::currentTestFunction()));
777 window.setGeometry(geometry);
778 QCOMPARE(window.geometry(), geometry);
779 window.show();
780 QCoreApplication::processEvents();
781
782 QTRY_VERIFY(window.received(QEvent::Expose) > 0);
783 QTRY_VERIFY(window.isExposed());
784
785#ifndef Q_OS_WIN
786 // This is a top-level window so assuming it is completely exposed, the
787 // expose region must be (0, 0), (width, height). If this is not the case,
788 // the platform plugin is sending expose events with a region in an
789 // incorrect coordinate system.
790 QRect r = window.exposeRegion().boundingRect();
791 r = QRect(window.mapToGlobal(pos: r.topLeft()), r.size());
792 QCOMPARE(r, window.geometry());
793#endif
794
795 window.hide();
796
797 QCoreApplication::processEvents();
798 QTRY_VERIFY(window.received(QEvent::Expose) > 1);
799 if (isPlatformWinRT())
800 QEXPECT_FAIL("", "WinRT does not destroy the window. Figure out why. QTBUG-68297", Continue);
801 QTRY_VERIFY(!window.isExposed());
802}
803
804
805void tst_QWindow::isActive()
806{
807 if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::WindowActivation))
808 QSKIP("QWindow::requestActivate() is not supported.");
809
810 Window window;
811 window.setTitle(QLatin1String(QTest::currentTestFunction()));
812 // Some platforms enforce minimum widths for windows, which can cause extra resize
813 // events, so set the width to suitably large value to avoid those.
814 window.setGeometry(QRect(m_availableTopLeft + QPoint(80, 80), m_testWindowSize));
815 window.show();
816 QCoreApplication::processEvents();
817
818 QTRY_VERIFY(window.isExposed());
819#if defined(Q_OS_QNX) // We either need to create a eglSurface or a create a backing store
820 // and then post the window in order for screen to show the window
821 QOpenGLContext context;
822 context.create();
823 context.makeCurrent(&window);
824 context.swapBuffers(&window);
825#endif
826 QTRY_COMPARE(window.received(QEvent::Resize), 1);
827 QTRY_COMPARE(QGuiApplication::focusWindow(), &window);
828 QVERIFY(window.isActive());
829
830 Window child;
831 child.setParent(&window);
832 child.setGeometry(posx: 10, posy: 10, w: 20, h: 20);
833 child.show();
834
835 if (isPlatformWinRT())
836 QEXPECT_FAIL("", "WinRT does not support native child windows.", Abort);
837 QTRY_VERIFY(child.isExposed());
838
839 child.requestActivate();
840
841 QTRY_COMPARE(QGuiApplication::focusWindow(), &child);
842 QVERIFY(child.isActive());
843
844 // parent shouldn't receive new resize events from child being shown
845 QCoreApplication::processEvents();
846 QTRY_COMPARE(window.received(QEvent::Resize), 1);
847 QTRY_COMPARE(window.received(QEvent::FocusIn), 1);
848 QTRY_COMPARE(window.received(QEvent::FocusOut), 1);
849 QTRY_COMPARE(child.received(QEvent::FocusIn), 1);
850
851 // child has focus
852 QVERIFY(window.isActive());
853
854 // test focus back to parent and then back to child (QTBUG-39362)
855 // also verify the cumulative FocusOut and FocusIn counts
856 // activate parent
857 window.requestActivate();
858 QTRY_COMPARE(QGuiApplication::focusWindow(), &window);
859 QVERIFY(window.isActive());
860 QCoreApplication::processEvents();
861 QTRY_COMPARE(child.received(QEvent::FocusOut), 1);
862 QTRY_COMPARE(window.received(QEvent::FocusIn), 2);
863
864 // activate child again
865 child.requestActivate();
866 QTRY_COMPARE(QGuiApplication::focusWindow(), &child);
867 QVERIFY(child.isActive());
868 QCoreApplication::processEvents();
869 QTRY_COMPARE(window.received(QEvent::FocusOut), 2);
870 QTRY_COMPARE(child.received(QEvent::FocusIn), 2);
871
872 Window dialog;
873 dialog.setTransientParent(&window);
874 dialog.setGeometry(QRect(m_availableTopLeft + QPoint(110, 100), m_testWindowSize));
875 dialog.show();
876
877 dialog.requestActivate();
878
879 QTRY_VERIFY(dialog.isExposed());
880 QCoreApplication::processEvents();
881 QTRY_COMPARE(dialog.received(QEvent::Resize), 1);
882 QTRY_COMPARE(QGuiApplication::focusWindow(), &dialog);
883 QVERIFY(dialog.isActive());
884
885 // transient child has focus
886 QVERIFY(window.isActive());
887
888 // parent is active
889 QVERIFY(child.isActive());
890
891 window.requestActivate();
892
893 QTRY_COMPARE(QGuiApplication::focusWindow(), &window);
894 QCoreApplication::processEvents();
895 QTRY_COMPARE(dialog.received(QEvent::FocusOut), 1);
896 // We should be checking for exactly three, but since this is a try-compare _loop_, we might
897 // loose and regain focus multiple times in the event of a system popup. This has been observed
898 // to fail on Windows, see QTBUG-77769.
899 QTRY_VERIFY2(window.received(QEvent::FocusIn) >= 3,
900 qPrintable(
901 QStringLiteral("Expected more than three focus in events, received: %1")
902 .arg(window.received(QEvent::FocusIn))));
903
904 QVERIFY(window.isActive());
905
906 // transient parent has focus
907 QVERIFY(dialog.isActive());
908
909 // parent has focus
910 QVERIFY(child.isActive());
911}
912
913class InputTestWindow : public ColoredWindow
914{
915public:
916 void keyPressEvent(QKeyEvent *event) override
917 {
918 keyPressCode = event->key();
919 }
920 void keyReleaseEvent(QKeyEvent *event) override
921 {
922 keyReleaseCode = event->key();
923 }
924 void mousePressEvent(QMouseEvent *event) override
925 {
926 if (ignoreMouse) {
927 event->ignore();
928 } else {
929 ++mousePressedCount;
930 mouseSequenceSignature += 'p';
931 mousePressButton = event->button();
932 mousePressScreenPos = event->screenPos();
933 mousePressLocalPos = event->localPos();
934 if (spinLoopWhenPressed)
935 QCoreApplication::processEvents();
936 }
937 }
938 void mouseReleaseEvent(QMouseEvent *event) override
939 {
940 if (ignoreMouse) {
941 event->ignore();
942 } else {
943 ++mouseReleasedCount;
944 mouseSequenceSignature += 'r';
945 mouseReleaseButton = event->button();
946 }
947 }
948 void mouseMoveEvent(QMouseEvent *event) override
949 {
950 buttonStateInGeneratedMove = event->buttons();
951 if (ignoreMouse) {
952 event->ignore();
953 } else {
954 ++mouseMovedCount;
955 mouseMoveButton = event->button();
956 mouseMoveScreenPos = event->screenPos();
957 }
958 }
959 void mouseDoubleClickEvent(QMouseEvent *event) override
960 {
961 if (ignoreMouse) {
962 event->ignore();
963 } else {
964 ++mouseDoubleClickedCount;
965 mouseSequenceSignature += 'd';
966 }
967 }
968 void touchEvent(QTouchEvent *event) override
969 {
970 if (ignoreTouch) {
971 event->ignore();
972 return;
973 }
974 touchEventType = event->type();
975 QList<QTouchEvent::TouchPoint> points = event->touchPoints();
976 for (int i = 0; i < points.count(); ++i) {
977 switch (points.at(i).state()) {
978 case Qt::TouchPointPressed:
979 ++touchPressedCount;
980 if (spinLoopWhenPressed)
981 QCoreApplication::processEvents();
982 break;
983 case Qt::TouchPointReleased:
984 ++touchReleasedCount;
985 break;
986 case Qt::TouchPointMoved:
987 ++touchMovedCount;
988 break;
989 default:
990 break;
991 }
992 }
993 }
994 bool event(QEvent *e) override
995 {
996 switch (e->type()) {
997 case QEvent::Enter:
998 ++enterEventCount;
999 break;
1000 case QEvent::Leave:
1001 ++leaveEventCount;
1002 break;
1003 default:
1004 break;
1005 }
1006 return ColoredWindow::event(event: e);
1007 }
1008 void resetCounters()
1009 {
1010 mousePressedCount = mouseReleasedCount = mouseMovedCount = mouseDoubleClickedCount = 0;
1011 mouseSequenceSignature.clear();
1012 touchPressedCount = touchReleasedCount = touchMovedCount = 0;
1013 enterEventCount = leaveEventCount = 0;
1014 }
1015
1016 explicit InputTestWindow(const QColor &color = Qt::white, QWindow *parent = nullptr)
1017 : ColoredWindow(color, parent) {}
1018
1019 int keyPressCode = 0, keyReleaseCode = 0;
1020 int mousePressButton = 0, mouseReleaseButton = 0, mouseMoveButton = 0;
1021 int mousePressedCount = 0, mouseReleasedCount = 0, mouseMovedCount = 0, mouseDoubleClickedCount = 0;
1022 QString mouseSequenceSignature;
1023 QPointF mousePressScreenPos, mouseMoveScreenPos, mousePressLocalPos;
1024 int touchPressedCount = 0, touchReleasedCount = 0, touchMovedCount = 0;
1025 QEvent::Type touchEventType = QEvent::None;
1026 int enterEventCount = 0, leaveEventCount = 0;
1027
1028 bool ignoreMouse = false, ignoreTouch = false;
1029
1030 bool spinLoopWhenPressed = false;
1031 Qt::MouseButtons buttonStateInGeneratedMove;
1032};
1033
1034static void simulateMouseClick(QWindow *target, const QPointF &local, const QPointF &global)
1035{
1036 QWindowSystemInterface::handleMouseEvent(window: target, local, global,
1037 state: {}, button: Qt::LeftButton, type: QEvent::MouseButtonPress);
1038 QWindowSystemInterface::handleMouseEvent(window: target, local, global,
1039 state: Qt::LeftButton, button: Qt::LeftButton, type: QEvent::MouseButtonRelease);
1040}
1041
1042static void simulateMouseClick(QWindow *target, ulong &timeStamp,
1043 const QPointF &local, const QPointF &global)
1044{
1045 QWindowSystemInterface::handleMouseEvent(window: target, timestamp: timeStamp++, local, global,
1046 state: {}, button: Qt::LeftButton, type: QEvent::MouseButtonPress);
1047 QWindowSystemInterface::handleMouseEvent(window: target, timestamp: timeStamp++, local, global,
1048 state: Qt::LeftButton, button: Qt::LeftButton, type: QEvent::MouseButtonRelease);
1049}
1050
1051void tst_QWindow::testInputEvents()
1052{
1053 InputTestWindow window;
1054 window.setGeometry(QRect(m_availableTopLeft + QPoint(80, 80), m_testWindowSize));
1055 window.showNormal();
1056 QVERIFY(QTest::qWaitForWindowExposed(&window));
1057
1058 QTest::keyClick(window: &window, key: Qt::Key_A, modifier: Qt::NoModifier);
1059 QCoreApplication::processEvents();
1060 QCOMPARE(window.keyPressCode, int(Qt::Key_A));
1061 QCOMPARE(window.keyReleaseCode, int(Qt::Key_A));
1062
1063 QPointF local(12, 34);
1064 QTest::mouseClick(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: local.toPoint());
1065 QCoreApplication::processEvents();
1066 QCOMPARE(window.mousePressButton, int(Qt::LeftButton));
1067 QCOMPARE(window.mouseReleaseButton, int(Qt::LeftButton));
1068 QCOMPARE(window.mousePressLocalPos, local);
1069
1070 QList<QWindowSystemInterface::TouchPoint> points;
1071 QWindowSystemInterface::TouchPoint tp1, tp2;
1072 tp1.id = 1;
1073 tp1.state = Qt::TouchPointPressed;
1074 tp1.area = QRect(10, 10, 4, 4);
1075 tp2.id = 2;
1076 tp2.state = Qt::TouchPointPressed;
1077 tp2.area = QRect(20, 20, 4, 4);
1078 points << tp1 << tp2;
1079 QWindowSystemInterface::handleTouchEvent(window: &window, device: touchDevice, points);
1080 points[0].state = Qt::TouchPointReleased;
1081 points[1].state = Qt::TouchPointReleased;
1082 QWindowSystemInterface::handleTouchEvent(window: &window, device: touchDevice, points);
1083 QCoreApplication::processEvents();
1084 QTRY_COMPARE(window.touchPressedCount, 2);
1085 QTRY_COMPARE(window.touchReleasedCount, 2);
1086
1087 // Now with null pointer as window. local param should not be utilized:
1088 // handleMouseEvent() with tlw == 0 means the event is in global coords only.
1089 window.mousePressButton = window.mouseReleaseButton = 0;
1090 const QPointF nonWindowGlobal(window.geometry().topRight() + QPoint(200, 50)); // not inside the window
1091 const QPointF deviceNonWindowGlobal = QHighDpi::toNativePixels(value: nonWindowGlobal, context: window.screen());
1092 simulateMouseClick(target: nullptr, local: deviceNonWindowGlobal, global: deviceNonWindowGlobal);
1093 QCoreApplication::processEvents();
1094 QCOMPARE(window.mousePressButton, 0);
1095 QCOMPARE(window.mouseReleaseButton, 0);
1096 const QPointF windowGlobal = window.mapToGlobal(pos: local.toPoint());
1097 const QPointF deviceWindowGlobal = QHighDpi::toNativePixels(value: windowGlobal, context: window.screen());
1098 simulateMouseClick(target: nullptr, local: deviceWindowGlobal, global: deviceWindowGlobal);
1099 QCoreApplication::processEvents();
1100 QCOMPARE(window.mousePressButton, int(Qt::LeftButton));
1101 QCOMPARE(window.mouseReleaseButton, int(Qt::LeftButton));
1102 QCOMPARE(window.mousePressScreenPos, windowGlobal);
1103 QCOMPARE(window.mousePressLocalPos, local); // the local we passed was bogus, verify that qGuiApp calculated the proper one
1104}
1105
1106void tst_QWindow::touchToMouseTranslation()
1107{
1108 InputTestWindow window;
1109 window.setTitle(QLatin1String(QTest::currentTestFunction()));
1110 window.ignoreTouch = true;
1111 window.setGeometry(QRect(m_availableTopLeft + QPoint(80, 80), m_testWindowSize));
1112 window.show();
1113 QVERIFY(QTest::qWaitForWindowExposed(&window));
1114
1115 QList<QWindowSystemInterface::TouchPoint> points;
1116 QWindowSystemInterface::TouchPoint tp1, tp2;
1117 const QRectF pressArea(101, 102, 4, 4);
1118 const QRectF moveArea(105, 108, 4, 4);
1119 tp1.id = 1;
1120 tp1.state = Qt::TouchPointPressed;
1121 tp1.area = QHighDpi::toNativePixels(value: pressArea, context: &window);
1122 tp2.id = 2;
1123 tp2.state = Qt::TouchPointPressed;
1124 points << tp1 << tp2;
1125 QWindowSystemInterface::handleTouchEvent(window: &window, device: touchDevice, points);
1126 // Now an update but with changed list order. The mouse event should still
1127 // be generated from the point with id 1.
1128 tp1.id = 2;
1129 tp1.state = Qt::TouchPointStationary;
1130 tp2.id = 1;
1131 tp2.state = Qt::TouchPointMoved;
1132 tp2.area = QHighDpi::toNativePixels(value: moveArea, context: &window);
1133 points.clear();
1134 points << tp1 << tp2;
1135 QWindowSystemInterface::handleTouchEvent(window: &window, device: touchDevice, points);
1136 points[0].state = Qt::TouchPointReleased;
1137 points[1].state = Qt::TouchPointReleased;
1138 QWindowSystemInterface::handleTouchEvent(window: &window, device: touchDevice, points);
1139 QCoreApplication::processEvents();
1140
1141 QTRY_COMPARE(window.mousePressButton, int(Qt::LeftButton));
1142 QTRY_COMPARE(window.mouseReleaseButton, int(Qt::LeftButton));
1143 QTRY_COMPARE(window.mousePressScreenPos, pressArea.center());
1144 QTRY_COMPARE(window.mouseMoveScreenPos, moveArea.center());
1145
1146 window.mousePressButton = 0;
1147 window.mouseReleaseButton = 0;
1148
1149 window.ignoreTouch = false;
1150
1151 points[0].state = Qt::TouchPointPressed;
1152 points[1].state = Qt::TouchPointPressed;
1153 QWindowSystemInterface::handleTouchEvent(window: &window, device: touchDevice, points);
1154 points[0].state = Qt::TouchPointReleased;
1155 points[1].state = Qt::TouchPointReleased;
1156 QWindowSystemInterface::handleTouchEvent(window: &window, device: touchDevice, points);
1157 QCoreApplication::processEvents();
1158
1159 // no new mouse events should be generated since the input window handles the touch events
1160 QTRY_COMPARE(window.mousePressButton, 0);
1161 QTRY_COMPARE(window.mouseReleaseButton, 0);
1162
1163 QCoreApplication::setAttribute(attribute: Qt::AA_SynthesizeMouseForUnhandledTouchEvents, on: false);
1164
1165 window.ignoreTouch = true;
1166 points[0].state = Qt::TouchPointPressed;
1167 points[1].state = Qt::TouchPointPressed;
1168 QWindowSystemInterface::handleTouchEvent(window: &window, device: touchDevice, points);
1169 points[0].state = Qt::TouchPointReleased;
1170 points[1].state = Qt::TouchPointReleased;
1171 QWindowSystemInterface::handleTouchEvent(window: &window, device: touchDevice, points);
1172 QCoreApplication::processEvents();
1173
1174 QCoreApplication::setAttribute(attribute: Qt::AA_SynthesizeMouseForUnhandledTouchEvents, on: true);
1175
1176 // mouse event synthesizing disabled
1177 QTRY_COMPARE(window.mousePressButton, 0);
1178 QTRY_COMPARE(window.mouseReleaseButton, 0);
1179
1180 points.clear();
1181 points.append(t: tp2);
1182 points[0].state = Qt::TouchPointPressed;
1183 QWindowSystemInterface::handleTouchEvent(window: &window, device: touchDevice, points);
1184 QCoreApplication::processEvents();
1185 points.clear();
1186 points.append(t: tp1);
1187 points[0].state = Qt::TouchPointPressed;
1188 QWindowSystemInterface::handleTouchEvent(window: &window, device: touchDevice, points);
1189 QCoreApplication::processEvents();
1190 QTRY_COMPARE(window.mousePressButton, 1);
1191
1192 points.clear();
1193 points.append(t: tp2);
1194 points[0].state = Qt::TouchPointReleased;
1195 QWindowSystemInterface::handleTouchEvent(window: &window, device: touchDevice, points);
1196 QCoreApplication::processEvents();
1197 points.clear();
1198 points.append(t: tp1);
1199 points[0].state = Qt::TouchPointReleased;
1200 QWindowSystemInterface::handleTouchEvent(window: &window, device: touchDevice, points);
1201 QCoreApplication::processEvents();
1202 QTRY_COMPARE(window.mouseReleaseButton, 1);
1203}
1204
1205void tst_QWindow::touchToMouseTranslationForDevices()
1206{
1207 InputTestWindow window;
1208 window.setTitle(QLatin1String(QTest::currentTestFunction()));
1209 window.ignoreTouch = true;
1210 window.setGeometry(QRect(m_availableTopLeft + QPoint(80, 80), m_testWindowSize));
1211 window.show();
1212 QVERIFY(QTest::qWaitForWindowExposed(&window));
1213
1214 QPoint touchPoint(10, 10);
1215
1216 QTest::touchEvent(window: &window, device: touchDevice).press(touchId: 0, pt: touchPoint, window: &window);
1217 QTest::touchEvent(window: &window, device: touchDevice).release(touchId: 0, pt: touchPoint, window: &window);
1218 QCoreApplication::processEvents();
1219
1220 QCOMPARE(window.mousePressedCount, 1);
1221 QCOMPARE(window.mouseReleasedCount, 1);
1222
1223 window.resetCounters();
1224
1225 touchDevice->setCapabilities(touchDevice->capabilities() | QTouchDevice::MouseEmulation);
1226 QTest::touchEvent(window: &window, device: touchDevice).press(touchId: 0, pt: touchPoint, window: &window);
1227 QTest::touchEvent(window: &window, device: touchDevice).release(touchId: 0, pt: touchPoint, window: &window);
1228 QCoreApplication::processEvents();
1229 touchDevice->setCapabilities(touchDevice->capabilities() & ~QTouchDevice::MouseEmulation);
1230
1231 QCOMPARE(window.mousePressedCount, 0);
1232 QCOMPARE(window.mouseReleasedCount, 0);
1233}
1234
1235void tst_QWindow::mouseToTouchTranslation()
1236{
1237 QCoreApplication::setAttribute(attribute: Qt::AA_SynthesizeTouchForUnhandledMouseEvents, on: true);
1238
1239 InputTestWindow window;
1240 window.setTitle(QLatin1String(QTest::currentTestFunction()));
1241 window.ignoreMouse = true;
1242 window.setGeometry(QRect(m_availableTopLeft + QPoint(80, 80), m_testWindowSize));
1243 window.show();
1244 QVERIFY(QTest::qWaitForWindowExposed(&window));
1245
1246 QTest::mouseClick(window: &window, button: Qt::LeftButton, stateKey: {}, pos: QPoint(10, 10));
1247 QCoreApplication::processEvents();
1248
1249 QCoreApplication::setAttribute(attribute: Qt::AA_SynthesizeTouchForUnhandledMouseEvents, on: false);
1250
1251 QTRY_COMPARE(window.touchPressedCount, 1);
1252 QTRY_COMPARE(window.touchReleasedCount, 1);
1253
1254 QCoreApplication::setAttribute(attribute: Qt::AA_SynthesizeTouchForUnhandledMouseEvents, on: true);
1255
1256 window.ignoreMouse = false;
1257
1258 QTest::mouseClick(window: &window, button: Qt::LeftButton, stateKey: {}, pos: QPoint(10, 10));
1259 QCoreApplication::processEvents();
1260
1261 QCoreApplication::setAttribute(attribute: Qt::AA_SynthesizeTouchForUnhandledMouseEvents, on: false);
1262
1263 // no new touch events should be generated since the input window handles the mouse events
1264 QTRY_COMPARE(window.touchPressedCount, 1);
1265 QTRY_COMPARE(window.touchReleasedCount, 1);
1266
1267 window.ignoreMouse = true;
1268
1269 QTest::mouseClick(window: &window, button: Qt::LeftButton, stateKey: {}, pos: QPoint(10, 10));
1270 QCoreApplication::processEvents();
1271
1272 // touch event synthesis disabled
1273 QTRY_COMPARE(window.touchPressedCount, 1);
1274 QTRY_COMPARE(window.touchReleasedCount, 1);
1275
1276
1277}
1278
1279void tst_QWindow::mouseToTouchLoop()
1280{
1281 // make sure there's no infinite loop when synthesizing both ways
1282 QCoreApplication::setAttribute(attribute: Qt::AA_SynthesizeTouchForUnhandledMouseEvents, on: true);
1283 QCoreApplication::setAttribute(attribute: Qt::AA_SynthesizeMouseForUnhandledTouchEvents, on: true);
1284
1285 InputTestWindow window;
1286 window.setTitle(QLatin1String(QTest::currentTestFunction()));
1287
1288 window.ignoreMouse = true;
1289 window.ignoreTouch = true;
1290 window.setGeometry(QRect(m_availableTopLeft + QPoint(80, 80), m_testWindowSize));
1291 window.show();
1292 QVERIFY(QTest::qWaitForWindowExposed(&window));
1293
1294 QTest::mouseClick(window: &window, button: Qt::LeftButton, stateKey: {}, pos: QPoint(10, 10));
1295 QCoreApplication::processEvents();
1296
1297 QCoreApplication::setAttribute(attribute: Qt::AA_SynthesizeTouchForUnhandledMouseEvents, on: false);
1298 QCoreApplication::setAttribute(attribute: Qt::AA_SynthesizeMouseForUnhandledTouchEvents, on: true);
1299}
1300
1301void tst_QWindow::touchCancel()
1302{
1303 InputTestWindow window;
1304 window.setTitle(QLatin1String(QTest::currentTestFunction()));
1305 window.setGeometry(QRect(m_availableTopLeft + QPoint(80, 80), m_testWindowSize));
1306 window.show();
1307 QVERIFY(QTest::qWaitForWindowExposed(&window));
1308
1309 QList<QWindowSystemInterface::TouchPoint> points;
1310 QWindowSystemInterface::TouchPoint tp1;
1311 tp1.id = 1;
1312
1313 // Start a touch.
1314 tp1.state = Qt::TouchPointPressed;
1315 tp1.area = QRect(10, 10, 4, 4);
1316 points << tp1;
1317 QWindowSystemInterface::handleTouchEvent(window: &window, device: touchDevice, points);
1318 QCoreApplication::processEvents();
1319 QTRY_COMPARE(window.touchEventType, QEvent::TouchBegin);
1320 QTRY_COMPARE(window.touchPressedCount, 1);
1321
1322 // Cancel the active touch sequence.
1323 QWindowSystemInterface::handleTouchCancelEvent(window: &window, device: touchDevice);
1324 QCoreApplication::processEvents();
1325 QTRY_COMPARE(window.touchEventType, QEvent::TouchCancel);
1326
1327 // Send a move -> will not be delivered due to the cancellation.
1328 QTRY_COMPARE(window.touchMovedCount, 0);
1329 points[0].state = Qt::TouchPointMoved;
1330 tp1.area.adjust(xp1: 2, yp1: 2, xp2: 2, yp2: 2);
1331 QWindowSystemInterface::handleTouchEvent(window: &window, device: touchDevice, points);
1332 QCoreApplication::processEvents();
1333 QTRY_COMPARE(window.touchMovedCount, 0);
1334
1335 // Likewise. The only allowed transition is TouchCancel -> TouchBegin.
1336 QTRY_COMPARE(window.touchReleasedCount, 0);
1337 points[0].state = Qt::TouchPointReleased;
1338 QWindowSystemInterface::handleTouchEvent(window: &window, device: touchDevice, points);
1339 QCoreApplication::processEvents();
1340 QTRY_COMPARE(window.touchReleasedCount, 0);
1341
1342 // Start a new sequence -> from this point on everything should go through normally.
1343 points[0].state = Qt::TouchPointPressed;
1344 QWindowSystemInterface::handleTouchEvent(window: &window, device: touchDevice, points);
1345 QCoreApplication::processEvents();
1346 QTRY_COMPARE(window.touchEventType, QEvent::TouchBegin);
1347 QTRY_COMPARE(window.touchPressedCount, 2);
1348
1349 points[0].state = Qt::TouchPointMoved;
1350 tp1.area.adjust(xp1: 2, yp1: 2, xp2: 2, yp2: 2);
1351 QWindowSystemInterface::handleTouchEvent(window: &window, device: touchDevice, points);
1352 QCoreApplication::processEvents();
1353 QTRY_COMPARE(window.touchMovedCount, 1);
1354
1355 points[0].state = Qt::TouchPointReleased;
1356 QWindowSystemInterface::handleTouchEvent(window: &window, device: touchDevice, points);
1357 QCoreApplication::processEvents();
1358 QTRY_COMPARE(window.touchReleasedCount, 1);
1359}
1360
1361void tst_QWindow::touchCancelWithTouchToMouse()
1362{
1363 InputTestWindow window;
1364 window.setTitle(QLatin1String(QTest::currentTestFunction()));
1365 window.ignoreTouch = true;
1366 window.setGeometry(QRect(m_availableTopLeft + QPoint(80, 80), m_testWindowSize));
1367 window.show();
1368 QVERIFY(QTest::qWaitForWindowExposed(&window));
1369
1370 QList<QWindowSystemInterface::TouchPoint> points;
1371 QWindowSystemInterface::TouchPoint tp1;
1372 tp1.id = 1;
1373
1374 tp1.state = Qt::TouchPointPressed;
1375 const QRect area(100, 100, 4, 4);
1376 tp1.area = QHighDpi::toNativePixels(value: area, context: &window);
1377 points << tp1;
1378 QWindowSystemInterface::handleTouchEvent(window: &window, device: touchDevice, points);
1379 QCoreApplication::processEvents();
1380 QTRY_COMPARE(window.mousePressButton, int(Qt::LeftButton));
1381 const int fuzz = 2 * int(QHighDpiScaling::factor(context: &window));
1382 QTRY_VERIFY2(qFuzzyCompareWindowPosition(window.mousePressScreenPos.toPoint(), area.center(), fuzz),
1383 qPrintable(msgPointMismatch(window.mousePressScreenPos.toPoint(), area.center())));
1384
1385 // Cancel the touch. Should result in a mouse release for windows that have
1386 // have an active touch-to-mouse sequence.
1387 QWindowSystemInterface::handleTouchCancelEvent(window: nullptr, device: touchDevice);
1388 QCoreApplication::processEvents();
1389
1390 QTRY_COMPARE(window.mouseReleaseButton, int(Qt::LeftButton));
1391
1392 // Now change the window to accept touches.
1393 window.mousePressButton = window.mouseReleaseButton = 0;
1394 window.ignoreTouch = false;
1395
1396 // Send a touch, there will be no mouse event generated.
1397 QWindowSystemInterface::handleTouchEvent(window: &window, device: touchDevice, points);
1398 QCoreApplication::processEvents();
1399 QTRY_COMPARE(window.mousePressButton, 0);
1400
1401 // Cancel the touch. It should not result in a mouse release with this window.
1402 QWindowSystemInterface::handleTouchCancelEvent(window: nullptr, device: touchDevice);
1403 QCoreApplication::processEvents();
1404 QTRY_COMPARE(window.mouseReleaseButton, 0);
1405}
1406
1407void tst_QWindow::touchInterruptedByPopup()
1408{
1409 if (isPlatformWayland())
1410 QSKIP("Wayland: This test crashes with xdg-shell unstable v6");
1411
1412 InputTestWindow window;
1413 window.setTitle(QLatin1String(QTest::currentTestFunction()));
1414 window.setGeometry(QRect(m_availableTopLeft + QPoint(80, 80), m_testWindowSize));
1415 window.show();
1416 QVERIFY(QTest::qWaitForWindowExposed(&window));
1417
1418 QList<QWindowSystemInterface::TouchPoint> points;
1419 QWindowSystemInterface::TouchPoint tp1;
1420 tp1.id = 1;
1421
1422 // Start a touch.
1423 tp1.state = Qt::TouchPointPressed;
1424 tp1.area = QRect(10, 10, 4, 4);
1425 points << tp1;
1426 QWindowSystemInterface::handleTouchEvent(window: &window, device: touchDevice, points);
1427 QCoreApplication::processEvents();
1428 QTRY_COMPARE(window.touchEventType, QEvent::TouchBegin);
1429 QTRY_COMPARE(window.touchPressedCount, 1);
1430
1431 // Launch a popup window
1432 InputTestWindow popup;
1433 popup.setFlags(Qt::Popup);
1434 popup.setModality(Qt::WindowModal);
1435 popup.resize(newSize: m_testWindowSize / 2);
1436 popup.setTransientParent(&window);
1437 popup.show();
1438 QVERIFY(QTest::qWaitForWindowExposed(&popup));
1439
1440 // Send a move -> will not be delivered to the original window
1441 // (TODO verify where it is forwarded, after we've defined that)
1442 QTRY_COMPARE(window.touchMovedCount, 0);
1443 points[0].state = Qt::TouchPointMoved;
1444 tp1.area.adjust(xp1: 2, yp1: 2, xp2: 2, yp2: 2);
1445 QWindowSystemInterface::handleTouchEvent(window: &window, device: touchDevice, points);
1446 QCoreApplication::processEvents();
1447 QTRY_COMPARE(window.touchMovedCount, 0);
1448
1449 // Send a touch end -> will not be delivered to the original window
1450 QTRY_COMPARE(window.touchReleasedCount, 0);
1451 points[0].state = Qt::TouchPointReleased;
1452 QWindowSystemInterface::handleTouchEvent(window: &window, device: touchDevice, points);
1453 QCoreApplication::processEvents();
1454 QTRY_COMPARE(window.touchReleasedCount, 0);
1455
1456 // Due to temporary fix for QTBUG-37371: the original window should receive a TouchCancel
1457 QTRY_COMPARE(window.touchEventType, QEvent::TouchCancel);
1458}
1459
1460void tst_QWindow::orientation()
1461{
1462 qRegisterMetaType<Qt::ScreenOrientation>(typeName: "Qt::ScreenOrientation");
1463
1464 QWindow window;
1465 window.setGeometry(QRect(m_availableTopLeft + QPoint(80, 80), m_testWindowSize));
1466 window.create();
1467
1468 window.reportContentOrientationChange(orientation: Qt::PortraitOrientation);
1469 QCOMPARE(window.contentOrientation(), Qt::PortraitOrientation);
1470
1471 window.reportContentOrientationChange(orientation: Qt::PrimaryOrientation);
1472 QCOMPARE(window.contentOrientation(), Qt::PrimaryOrientation);
1473
1474 QSignalSpy spy(&window, SIGNAL(contentOrientationChanged(Qt::ScreenOrientation)));
1475 window.reportContentOrientationChange(orientation: Qt::LandscapeOrientation);
1476 QCOMPARE(spy.count(), 1);
1477}
1478
1479void tst_QWindow::sizes()
1480{
1481 QWindow window;
1482
1483 QSignalSpy minimumWidthSpy(&window, SIGNAL(minimumWidthChanged(int)));
1484 QSignalSpy minimumHeightSpy(&window, SIGNAL(minimumHeightChanged(int)));
1485 QSignalSpy maximumWidthSpy(&window, SIGNAL(maximumWidthChanged(int)));
1486 QSignalSpy maximumHeightSpy(&window, SIGNAL(maximumHeightChanged(int)));
1487
1488 QSize oldMaximum = window.maximumSize();
1489
1490 window.setMinimumWidth(10);
1491 QCOMPARE(window.minimumWidth(), 10);
1492 QCOMPARE(window.minimumHeight(), 0);
1493 QCOMPARE(window.minimumSize(), QSize(10, 0));
1494 QCOMPARE(window.maximumSize(), oldMaximum);
1495 QCOMPARE(minimumWidthSpy.count(), 1);
1496 QCOMPARE(minimumHeightSpy.count(), 0);
1497 QCOMPARE(maximumWidthSpy.count(), 0);
1498 QCOMPARE(maximumHeightSpy.count(), 0);
1499
1500 window.setMinimumHeight(10);
1501 QCOMPARE(window.minimumWidth(), 10);
1502 QCOMPARE(window.minimumHeight(), 10);
1503 QCOMPARE(window.minimumSize(), QSize(10, 10));
1504 QCOMPARE(window.maximumSize(), oldMaximum);
1505 QCOMPARE(minimumWidthSpy.count(), 1);
1506 QCOMPARE(minimumHeightSpy.count(), 1);
1507 QCOMPARE(maximumWidthSpy.count(), 0);
1508 QCOMPARE(maximumHeightSpy.count(), 0);
1509
1510 window.setMaximumWidth(100);
1511 QCOMPARE(window.maximumWidth(), 100);
1512 QCOMPARE(window.maximumHeight(), oldMaximum.height());
1513 QCOMPARE(window.minimumSize(), QSize(10, 10));
1514 QCOMPARE(window.maximumSize(), QSize(100, oldMaximum.height()));
1515 QCOMPARE(minimumWidthSpy.count(), 1);
1516 QCOMPARE(minimumHeightSpy.count(), 1);
1517 QCOMPARE(maximumWidthSpy.count(), 1);
1518 QCOMPARE(maximumHeightSpy.count(), 0);
1519
1520 window.setMaximumHeight(100);
1521 QCOMPARE(window.maximumWidth(), 100);
1522 QCOMPARE(window.maximumHeight(), 100);
1523 QCOMPARE(window.minimumSize(), QSize(10, 10));
1524 QCOMPARE(window.maximumSize(), QSize(100, 100));
1525 QCOMPARE(minimumWidthSpy.count(), 1);
1526 QCOMPARE(minimumHeightSpy.count(), 1);
1527 QCOMPARE(maximumWidthSpy.count(), 1);
1528 QCOMPARE(maximumHeightSpy.count(), 1);
1529}
1530
1531void tst_QWindow::close()
1532{
1533 QWindow a;
1534 a.setTitle(QLatin1String(QTest::currentTestFunction()));
1535 QWindow b;
1536 QWindow c(&a);
1537
1538 a.show();
1539 b.show();
1540
1541 // we can not close a non top level window
1542 QVERIFY(!c.close());
1543 QVERIFY(a.close());
1544 QVERIFY(b.close());
1545}
1546
1547void tst_QWindow::activateAndClose()
1548{
1549 if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::WindowActivation))
1550 QSKIP("QWindow::requestActivate() is not supported.");
1551
1552 for (int i = 0; i < 10; ++i) {
1553 QWindow window;
1554 window.setTitle(QLatin1String(QTest::currentTestFunction()) + QString::number(i));
1555#if defined(Q_OS_QNX)
1556 window.setSurfaceType(QSurface::OpenGLSurface);
1557#endif
1558 // qWaitForWindowActive will block for the duration of
1559 // of the timeout if the window is at 0,0
1560 window.setGeometry(QGuiApplication::primaryScreen()->availableGeometry().adjusted(xp1: 1, yp1: 1, xp2: -1, yp2: -1));
1561 window.showNormal();
1562#if defined(Q_OS_QNX) // We either need to create a eglSurface or a create a backing store
1563 // and then post the window in order for screen to show the window
1564 QVERIFY(QTest::qWaitForWindowExposed(&window));
1565 QOpenGLContext context;
1566 context.create();
1567 context.makeCurrent(&window);
1568 context.swapBuffers(&window);
1569#endif
1570 window.requestActivate();
1571 QVERIFY(QTest::qWaitForWindowActive(&window));
1572 QCOMPARE(QGuiApplication::focusWindow(), &window);
1573 }
1574}
1575
1576void tst_QWindow::mouseEventSequence()
1577{
1578 const auto doubleClickInterval = ulong(QGuiApplication::styleHints()->mouseDoubleClickInterval());
1579
1580 InputTestWindow window;
1581 window.setTitle(QLatin1String(QTest::currentTestFunction()));
1582 window.setGeometry(QRect(m_availableTopLeft + QPoint(80, 80), m_testWindowSize));
1583 window.show();
1584 QVERIFY(QTest::qWaitForWindowExposed(&window));
1585
1586 ulong timestamp = 0;
1587 QPointF local(12, 34);
1588 const QPointF deviceLocal = QHighDpi::toNativePixels(value: local, context: &window);
1589
1590 simulateMouseClick(target: &window, timeStamp&: timestamp, local: deviceLocal, global: deviceLocal);
1591 QCoreApplication::processEvents();
1592 QCOMPARE(window.mousePressedCount, 1);
1593 QCOMPARE(window.mouseReleasedCount, 1);
1594 QCOMPARE(window.mouseDoubleClickedCount, 0);
1595 QCOMPARE(window.mouseSequenceSignature, QLatin1String("pr"));
1596
1597 window.resetCounters();
1598 timestamp += doubleClickInterval;
1599
1600 // A double click must result in press, release, press, doubleclick, release.
1601 // Check that no unexpected event suppression occurs and that the order is correct.
1602 simulateMouseClick(target: &window, timeStamp&: timestamp, local, global: local);
1603 simulateMouseClick(target: &window, timeStamp&: timestamp, local, global: local);
1604 QCoreApplication::processEvents();
1605 QCOMPARE(window.mousePressedCount, 2);
1606 QCOMPARE(window.mouseReleasedCount, 2);
1607 QCOMPARE(window.mouseDoubleClickedCount, 1);
1608 QCOMPARE(window.mouseSequenceSignature, QLatin1String("prpdr"));
1609
1610 timestamp += doubleClickInterval;
1611 window.resetCounters();
1612
1613 // Triple click = double + single click
1614 simulateMouseClick(target: &window, timeStamp&: timestamp, local, global: local);
1615 simulateMouseClick(target: &window, timeStamp&: timestamp, local, global: local);
1616 simulateMouseClick(target: &window, timeStamp&: timestamp, local, global: local);
1617 QCoreApplication::processEvents();
1618 QCOMPARE(window.mousePressedCount, 3);
1619 QCOMPARE(window.mouseReleasedCount, 3);
1620 QCOMPARE(window.mouseDoubleClickedCount, 1);
1621 QCOMPARE(window.mouseSequenceSignature, QLatin1String("prpdrpr"));
1622
1623 timestamp += doubleClickInterval;
1624 window.resetCounters();
1625
1626 // Two double clicks.
1627 simulateMouseClick(target: &window, timeStamp&: timestamp, local, global: local);
1628 simulateMouseClick(target: &window, timeStamp&: timestamp, local, global: local);
1629 simulateMouseClick(target: &window, timeStamp&: timestamp, local, global: local);
1630 simulateMouseClick(target: &window, timeStamp&: timestamp, local, global: local);
1631 QCoreApplication::processEvents();
1632 QCOMPARE(window.mousePressedCount, 4);
1633 QCOMPARE(window.mouseReleasedCount, 4);
1634 QCOMPARE(window.mouseDoubleClickedCount, 2);
1635 QCOMPARE(window.mouseSequenceSignature, QLatin1String("prpdrprpdr"));
1636
1637 timestamp += doubleClickInterval;
1638 window.resetCounters();
1639
1640 // Four clicks, none of which qualifies as a double click.
1641 simulateMouseClick(target: &window, timeStamp&: timestamp, local, global: local);
1642 timestamp += doubleClickInterval;
1643 simulateMouseClick(target: &window, timeStamp&: timestamp, local, global: local);
1644 timestamp += doubleClickInterval;
1645 simulateMouseClick(target: &window, timeStamp&: timestamp, local, global: local);
1646 timestamp += doubleClickInterval;
1647 simulateMouseClick(target: &window, timeStamp&: timestamp, local, global: local);
1648 timestamp += doubleClickInterval;
1649 QCoreApplication::processEvents();
1650 QCOMPARE(window.mousePressedCount, 4);
1651 QCOMPARE(window.mouseReleasedCount, 4);
1652 QCOMPARE(window.mouseDoubleClickedCount, 0);
1653 QCOMPARE(window.mouseSequenceSignature, QLatin1String("prprprpr"));
1654}
1655
1656void tst_QWindow::windowModality()
1657{
1658 qRegisterMetaType<Qt::WindowModality>(typeName: "Qt::WindowModality");
1659
1660 QWindow window;
1661 QSignalSpy spy(&window, SIGNAL(modalityChanged(Qt::WindowModality)));
1662
1663 QCOMPARE(window.modality(), Qt::NonModal);
1664 window.setModality(Qt::NonModal);
1665 QCOMPARE(window.modality(), Qt::NonModal);
1666 QCOMPARE(spy.count(), 0);
1667
1668 window.setModality(Qt::WindowModal);
1669 QCOMPARE(window.modality(), Qt::WindowModal);
1670 QCOMPARE(spy.count(), 1);
1671 window.setModality(Qt::WindowModal);
1672 QCOMPARE(window.modality(), Qt::WindowModal);
1673 QCOMPARE(spy.count(), 1);
1674
1675 window.setModality(Qt::ApplicationModal);
1676 QCOMPARE(window.modality(), Qt::ApplicationModal);
1677 QCOMPARE(spy.count(), 2);
1678 window.setModality(Qt::ApplicationModal);
1679 QCOMPARE(window.modality(), Qt::ApplicationModal);
1680 QCOMPARE(spy.count(), 2);
1681
1682 window.setModality(Qt::NonModal);
1683 QCOMPARE(window.modality(), Qt::NonModal);
1684 QCOMPARE(spy.count(), 3);
1685}
1686
1687void tst_QWindow::inputReentrancy()
1688{
1689 InputTestWindow window;
1690 window.setTitle(QLatin1String(QTest::currentTestFunction()));
1691 window.spinLoopWhenPressed = true;
1692
1693 window.setGeometry(QRect(m_availableTopLeft + QPoint(80, 80), m_testWindowSize));
1694 window.show();
1695 QVERIFY(QTest::qWaitForWindowExposed(&window));
1696
1697 // Queue three events.
1698 QPointF local(12, 34);
1699 QWindowSystemInterface::handleMouseEvent(window: &window, local, global: local, state: {},
1700 button: Qt::LeftButton, type: QEvent::MouseButtonPress);
1701 local += QPointF(2, 2);
1702 QWindowSystemInterface::handleMouseEvent(window: &window, local, global: local,
1703 state: Qt::LeftButton, button: Qt::LeftButton, type: QEvent::MouseMove);
1704 QWindowSystemInterface::handleMouseEvent(window: &window, local, global: local, state: Qt::LeftButton,
1705 button: Qt::LeftButton, type: QEvent::MouseButtonRelease);
1706 // Process them. However, the event handler for the press will also call
1707 // processEvents() so the move and release will be delivered before returning
1708 // from mousePressEvent(). The point is that no events should get lost.
1709 QCoreApplication::processEvents();
1710 QCOMPARE(window.mousePressButton, int(Qt::LeftButton));
1711 QCOMPARE(window.mouseReleaseButton, int(Qt::LeftButton));
1712 QCOMPARE(window.mousePressedCount, 1);
1713 QCOMPARE(window.mouseMovedCount, 1);
1714 QCOMPARE(window.mouseReleasedCount, 1);
1715
1716 // Now the same for touch.
1717 QList<QWindowSystemInterface::TouchPoint> points;
1718 QWindowSystemInterface::TouchPoint tp1;
1719 tp1.id = 1;
1720 tp1.state = Qt::TouchPointPressed;
1721 tp1.area = QRectF(10, 10, 4, 4);
1722 points << tp1;
1723 QWindowSystemInterface::handleTouchEvent(window: &window, device: touchDevice, points);
1724 points[0].state = Qt::TouchPointMoved;
1725 points[0].area = QRectF(20, 20, 8, 8);
1726 QWindowSystemInterface::handleTouchEvent(window: &window, device: touchDevice, points);
1727 points[0].state = Qt::TouchPointReleased;
1728 QWindowSystemInterface::handleTouchEvent(window: &window, device: touchDevice, points);
1729 QCoreApplication::processEvents();
1730 QCOMPARE(window.touchPressedCount, 1);
1731 QCOMPARE(window.touchMovedCount, 1);
1732 QCOMPARE(window.touchReleasedCount, 1);
1733}
1734
1735#if QT_CONFIG(tabletevent)
1736class TabletTestWindow : public QWindow
1737{
1738public:
1739 void tabletEvent(QTabletEvent *ev) override
1740 {
1741 eventType = ev->type();
1742 eventGlobal = ev->globalPosF();
1743 eventLocal = ev->posF();
1744 eventDevice = ev->device();
1745 }
1746
1747 QEvent::Type eventType = QEvent::None;
1748 QPointF eventGlobal, eventLocal;
1749 int eventDevice = -1;
1750
1751 bool eventFilter(QObject *obj, QEvent *ev) override
1752 {
1753 if (ev->type() == QEvent::TabletEnterProximity
1754 || ev->type() == QEvent::TabletLeaveProximity) {
1755 eventType = ev->type();
1756 QTabletEvent *te = static_cast<QTabletEvent *>(ev);
1757 eventDevice = te->device();
1758 }
1759 return QWindow::eventFilter(watched: obj, event: ev);
1760 }
1761};
1762#endif
1763
1764void tst_QWindow::tabletEvents()
1765{
1766#if QT_CONFIG(tabletevent)
1767 TabletTestWindow window;
1768 window.setGeometry(QRect(m_availableTopLeft + QPoint(10, 10), m_testWindowSize));
1769 qGuiApp->installEventFilter(filterObj: &window);
1770
1771 const QPoint local(10, 10);
1772 const QPoint global = window.mapToGlobal(pos: local);
1773 const QPoint deviceLocal = QHighDpi::toNativeLocalPosition(value: local, context: &window);
1774 const QPoint deviceGlobal = QHighDpi::toNativePixels(value: global, context: window.screen());
1775 QWindowSystemInterface::handleTabletEvent(window: &window, local: deviceLocal, global: deviceGlobal,
1776 device: 1, pointerType: 2, buttons: Qt::LeftButton, pressure: 0.5, xTilt: 1, yTilt: 2, tangentialPressure: 0.1, rotation: 0, z: 0, uid: 0);
1777 QCoreApplication::processEvents();
1778 QTRY_VERIFY(window.eventType == QEvent::TabletPress);
1779 QTRY_COMPARE(window.eventGlobal.toPoint(), global);
1780 QTRY_COMPARE(window.eventLocal.toPoint(), local);
1781 QWindowSystemInterface::handleTabletEvent(window: &window, local: deviceLocal, global: deviceGlobal, device: 1, pointerType: 2,
1782 buttons: {}, pressure: 0.5, xTilt: 1, yTilt: 2, tangentialPressure: 0.1, rotation: 0, z: 0, uid: 0);
1783 QCoreApplication::processEvents();
1784 QTRY_COMPARE(window.eventType, QEvent::TabletRelease);
1785
1786 QWindowSystemInterface::handleTabletEnterProximityEvent(device: 1, pointerType: 2, uid: 3);
1787 QCoreApplication::processEvents();
1788 QTRY_COMPARE(window.eventType, QEvent::TabletEnterProximity);
1789 QTRY_COMPARE(window.eventDevice, 1);
1790
1791 QWindowSystemInterface::handleTabletLeaveProximityEvent(device: 1, pointerType: 2, uid: 3);
1792 QCoreApplication::processEvents();
1793 QTRY_COMPARE(window.eventType, QEvent::TabletLeaveProximity);
1794 QTRY_COMPARE(window.eventDevice, 1);
1795
1796#endif
1797}
1798
1799void tst_QWindow::windowModality_QTBUG27039()
1800{
1801 QWindow parent;
1802 parent.setTitle(QLatin1String(QTest::currentTestFunction()));
1803 parent.setGeometry(QRect(m_availableTopLeft + QPoint(10, 10), m_testWindowSize));
1804 parent.show();
1805
1806 InputTestWindow modalA;
1807 modalA.setTransientParent(&parent);
1808 modalA.setGeometry(QRect(m_availableTopLeft + QPoint(20, 10), m_testWindowSize));
1809 modalA.setModality(Qt::ApplicationModal);
1810 modalA.show();
1811
1812 InputTestWindow modalB;
1813 modalB.setTransientParent(&parent);
1814 modalA.setGeometry(QRect(m_availableTopLeft + QPoint(30, 10), m_testWindowSize));
1815 modalB.setModality(Qt::ApplicationModal);
1816 modalB.show();
1817
1818 QPoint local(5, 5);
1819 QTest::mouseClick(window: &modalA, button: Qt::LeftButton, stateKey: {}, pos: local);
1820 QTest::mouseClick(window: &modalB, button: Qt::LeftButton, stateKey: {}, pos: local);
1821 QCoreApplication::processEvents();
1822
1823 // modal A should be blocked since it was shown first, but modal B should not be blocked
1824 QCOMPARE(modalB.mousePressedCount, 1);
1825 QCOMPARE(modalA.mousePressedCount, 0);
1826
1827 modalB.hide();
1828 QTest::mouseClick(window: &modalA, button: Qt::LeftButton, stateKey: {}, pos: local);
1829 QCoreApplication::processEvents();
1830
1831 // modal B has been hidden, modal A should be unblocked again
1832 QCOMPARE(modalA.mousePressedCount, 1);
1833}
1834
1835void tst_QWindow::visibility()
1836{
1837 qRegisterMetaType<Qt::WindowModality>(typeName: "QWindow::Visibility");
1838
1839 Window window;
1840 QSignalSpy spy(&window, SIGNAL(visibilityChanged(QWindow::Visibility)));
1841
1842 window.setVisibility(QWindow::AutomaticVisibility);
1843 QVERIFY(window.isVisible());
1844 QVERIFY(window.visibility() != QWindow::Hidden);
1845 QVERIFY(window.visibility() != QWindow::AutomaticVisibility);
1846 QCOMPARE(spy.count(), 1);
1847 spy.clear();
1848
1849 window.setVisibility(QWindow::Hidden);
1850 QVERIFY(!window.isVisible());
1851 QCOMPARE(window.visibility(), QWindow::Hidden);
1852 QCOMPARE(spy.count(), 1);
1853 spy.clear();
1854
1855 window.setVisibility(QWindow::FullScreen);
1856 QVERIFY(window.isVisible());
1857 QCOMPARE(window.windowState(), Qt::WindowFullScreen);
1858 QCOMPARE(window.visibility(), QWindow::FullScreen);
1859 QCOMPARE(spy.count(), 1);
1860 QTRY_COMPARE(window.lastReceivedWindowState, Qt::WindowFullScreen);
1861 spy.clear();
1862
1863 window.setWindowState(Qt::WindowNoState);
1864 QCOMPARE(window.visibility(), QWindow::Windowed);
1865 QCOMPARE(spy.count(), 1);
1866 QTRY_COMPARE(window.lastReceivedWindowState, Qt::WindowNoState);
1867 spy.clear();
1868
1869 window.setVisible(false);
1870 QCOMPARE(window.visibility(), QWindow::Hidden);
1871 QCOMPARE(spy.count(), 1);
1872 spy.clear();
1873}
1874
1875void tst_QWindow::mask()
1876{
1877 QRegion mask = QRect(10, 10, 800 - 20, 600 - 20);
1878
1879 {
1880 QWindow window;
1881 window.resize(w: 800, h: 600);
1882 QCOMPARE(window.mask(), QRegion());
1883
1884 window.create();
1885 window.setMask(mask);
1886 QCOMPARE(window.mask(), mask);
1887 }
1888
1889 {
1890 QWindow window;
1891 window.resize(w: 800, h: 600);
1892 QCOMPARE(window.mask(), QRegion());
1893
1894 window.setMask(mask);
1895 QCOMPARE(window.mask(), mask);
1896 window.create();
1897 QCOMPARE(window.mask(), mask);
1898 }
1899
1900}
1901
1902void tst_QWindow::initialSize()
1903{
1904 if (isPlatformWayland())
1905 QSKIP("Wayland: This fails. See QTBUG-66818.");
1906
1907 QSize defaultSize(0,0);
1908 {
1909 Window w;
1910 w.setTitle(QLatin1String(QTest::currentTestFunction()));
1911 w.showNormal();
1912 QTRY_VERIFY(w.width() > 0);
1913 QTRY_VERIFY(w.height() > 0);
1914 defaultSize = QSize(w.width(), w.height());
1915 }
1916 {
1917 Window w;
1918 w.setTitle(QLatin1String(QTest::currentTestFunction()));
1919 w.setWidth(m_testWindowSize.width());
1920 w.showNormal();
1921 if (isPlatformWinRT())
1922 QEXPECT_FAIL("", "WinRT shows windows as fullscreen by default.", Continue);
1923 QTRY_COMPARE(w.width(), m_testWindowSize.width());
1924 QTRY_VERIFY(w.height() > 0);
1925 }
1926 {
1927 Window w;
1928 w.setTitle(QLatin1String(QTest::currentTestFunction()));
1929 const QSize testSize(m_testWindowSize.width(), 42);
1930 w.resize(newSize: testSize);
1931 w.showNormal();
1932
1933 const QSize expectedSize = testSize;
1934 if (isPlatformWinRT())
1935 QEXPECT_FAIL("", "WinRT shows windows as fullscreen by default.", Continue);
1936 QTRY_COMPARE(w.size(), expectedSize);
1937 }
1938}
1939
1940static bool isPlatformOffscreenOrMinimal()
1941{
1942 return ((QGuiApplication::platformName() == QLatin1String("offscreen"))
1943 || (QGuiApplication::platformName() == QLatin1String("minimal")));
1944}
1945
1946void tst_QWindow::modalDialog()
1947{
1948 if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::WindowActivation))
1949 QSKIP("QWindow::requestActivate() is not supported.");
1950
1951 if (QGuiApplication::platformName() == QLatin1String("cocoa"))
1952 QSKIP("Test fails due to QTBUG-61965, and is slow due to QTBUG-61964");
1953
1954 QWindow normalWindow;
1955 normalWindow.setTitle(QLatin1String(QTest::currentTestFunction()));
1956 normalWindow.setFramePosition(m_availableTopLeft + QPoint(80, 80));
1957 normalWindow.resize(newSize: m_testWindowSize);
1958 normalWindow.show();
1959 QVERIFY(QTest::qWaitForWindowExposed(&normalWindow));
1960
1961 QWindow dialog;
1962 dialog.setFramePosition(m_availableTopLeft + QPoint(200, 200));
1963 dialog.resize(newSize: m_testWindowSize);
1964 dialog.setModality(Qt::ApplicationModal);
1965 dialog.setFlags(Qt::Dialog);
1966 dialog.show();
1967 QVERIFY(QTest::qWaitForWindowExposed(&dialog));
1968
1969 normalWindow.requestActivate();
1970
1971 QGuiApplication::sync();
1972 QGuiApplication::processEvents();
1973
1974 if (isPlatformOffscreenOrMinimal()) {
1975 QWARN("Focus stays in normalWindow on offscreen/minimal platforms");
1976 QTRY_COMPARE(QGuiApplication::focusWindow(), &normalWindow);
1977 return;
1978 }
1979
1980 if (isPlatformWinRT())
1981 QEXPECT_FAIL("", "WinRT only support one native window.", Continue);
1982 QTRY_COMPARE(QGuiApplication::focusWindow(), &dialog);
1983}
1984
1985void tst_QWindow::modalDialogClosingOneOfTwoModal()
1986{
1987 if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::WindowActivation))
1988 QSKIP("QWindow::requestActivate() is not supported.");
1989
1990 QWindow normalWindow;
1991 normalWindow.setTitle(QLatin1String(QTest::currentTestFunction()));
1992 normalWindow.setFramePosition(m_availableTopLeft + QPoint(80, 80));
1993 normalWindow.resize(newSize: m_testWindowSize);
1994 normalWindow.show();
1995 QVERIFY(QTest::qWaitForWindowExposed(&normalWindow));
1996
1997 QWindow first_dialog;
1998 first_dialog.setFramePosition(m_availableTopLeft + QPoint(200, 200));
1999 first_dialog.resize(newSize: m_testWindowSize);
2000 first_dialog.setModality(Qt::ApplicationModal);
2001 first_dialog.setFlags(Qt::Dialog);
2002 first_dialog.show();
2003 QVERIFY(QTest::qWaitForWindowExposed(&first_dialog));
2004
2005 {
2006 QWindow second_dialog;
2007 second_dialog.setFramePosition(m_availableTopLeft + QPoint(300, 300));
2008 second_dialog.resize(newSize: m_testWindowSize);
2009 second_dialog.setModality(Qt::ApplicationModal);
2010 second_dialog.setFlags(Qt::Dialog);
2011 second_dialog.show();
2012 QVERIFY(QTest::qWaitForWindowExposed(&second_dialog));
2013
2014 QTRY_COMPARE(QGuiApplication::focusWindow(), &second_dialog);
2015
2016 second_dialog.close();
2017 }
2018
2019 QGuiApplication::sync();
2020 QGuiApplication::processEvents();
2021
2022 if (isPlatformOffscreenOrMinimal()) {
2023 QWARN("Focus is lost when closing modal dialog on offscreen/minimal platforms");
2024 QTRY_COMPARE(QGuiApplication::focusWindow(), nullptr);
2025 return;
2026 }
2027
2028 if (isPlatformWinRT())
2029 QEXPECT_FAIL("", "WinRT only support one native window.", Continue);
2030 QTRY_COMPARE(QGuiApplication::focusWindow(), &first_dialog);
2031}
2032
2033void tst_QWindow::modalWithChildWindow()
2034{
2035 if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::WindowActivation))
2036 QSKIP("QWindow::requestActivate() is not supported.");
2037
2038 QWindow normalWindow;
2039 normalWindow.setTitle(QLatin1String(QTest::currentTestFunction()));
2040 normalWindow.setFramePosition(m_availableTopLeft + QPoint(80, 80));
2041 normalWindow.resize(newSize: m_testWindowSize);
2042 normalWindow.show();
2043 QVERIFY(QTest::qWaitForWindowExposed(&normalWindow));
2044
2045 QWindow tlw_dialog;
2046 tlw_dialog.setFramePosition(m_availableTopLeft + QPoint(200, 200));
2047 tlw_dialog.resize(newSize: m_testWindowSize);
2048 tlw_dialog.setModality(Qt::ApplicationModal);
2049 tlw_dialog.setFlags(Qt::Dialog);
2050 tlw_dialog.create();
2051
2052 QWindow sub_window(&tlw_dialog);
2053 sub_window.resize(w: 200,h: 300);
2054 sub_window.show();
2055
2056 tlw_dialog.show();
2057 QVERIFY(QTest::qWaitForWindowExposed(&tlw_dialog));
2058 if (isPlatformWinRT())
2059 QEXPECT_FAIL("", "WinRT only support one native window.", Abort);
2060 QVERIFY(QTest::qWaitForWindowExposed(&sub_window));
2061
2062 QTRY_COMPARE(QGuiApplication::focusWindow(), &tlw_dialog);
2063
2064 sub_window.requestActivate();
2065 QGuiApplication::sync();
2066 QGuiApplication::processEvents();
2067 QTRY_COMPARE(QGuiApplication::focusWindow(), &sub_window);
2068}
2069
2070void tst_QWindow::modalWindowModallity()
2071{
2072 if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::WindowActivation))
2073 QSKIP("QWindow::requestActivate() is not supported.");
2074
2075 QWindow normal_window;
2076 normal_window.setTitle(QLatin1String(QTest::currentTestFunction()));
2077 normal_window.setFramePosition(m_availableTopLeft + QPoint(80, 80));
2078 normal_window.resize(newSize: m_testWindowSize);
2079 normal_window.show();
2080 QVERIFY(QTest::qWaitForWindowExposed(&normal_window));
2081
2082 QWindow parent_to_modal;
2083 parent_to_modal.setFramePosition(normal_window.geometry().topRight() + QPoint(100, 0));
2084 parent_to_modal.resize(newSize: m_testWindowSize);
2085 parent_to_modal.show();
2086 QVERIFY(QTest::qWaitForWindowExposed(&parent_to_modal));
2087 QTRY_COMPARE(QGuiApplication::focusWindow(), &parent_to_modal);
2088
2089 QWindow modal_dialog;
2090 modal_dialog.resize(newSize: m_testWindowSize);
2091 modal_dialog.setFramePosition(normal_window.geometry().bottomLeft() + QPoint(0, 100));
2092 modal_dialog.setModality(Qt::WindowModal);
2093 modal_dialog.setFlags(Qt::Dialog);
2094 modal_dialog.setTransientParent(&parent_to_modal);
2095 modal_dialog.show();
2096 QVERIFY(QTest::qWaitForWindowExposed(&modal_dialog));
2097 QTRY_COMPARE(QGuiApplication::focusWindow(), &modal_dialog);
2098
2099 normal_window.requestActivate();
2100 QTRY_COMPARE(QGuiApplication::focusWindow(), &normal_window);
2101
2102}
2103
2104void tst_QWindow::modalWindowPosition()
2105{
2106 QWindow window;
2107 window.setTitle(QLatin1String(QTest::currentTestFunction()));
2108 window.setGeometry(QRect(m_availableTopLeft + QPoint(100, 100), m_testWindowSize));
2109 // Allow for any potential resizing due to constraints
2110 QRect origGeo = window.geometry();
2111 window.setModality(Qt::WindowModal);
2112 window.show();
2113 QVERIFY(QTest::qWaitForWindowExposed(&window));
2114 if (isPlatformWinRT())
2115 QEXPECT_FAIL("", "WinRT windows are fullscreen by default.", Continue);
2116 QCOMPARE(window.geometry(), origGeo);
2117}
2118
2119#ifndef QT_NO_CURSOR
2120void tst_QWindow::modalWindowEnterEventOnHide_QTBUG35109()
2121{
2122 if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::WindowActivation))
2123 QSKIP("QWindow::requestActivate() is not supported.");
2124
2125 if (isPlatformOffscreenOrMinimal())
2126 QSKIP("Can't test window focusing on offscreen/minimal");
2127
2128 const QPoint center = QGuiApplication::primaryScreen()->availableGeometry().center();
2129
2130 const int childOffset = 16;
2131 const QPoint rootPos = center - QPoint(m_testWindowSize.width(),
2132 m_testWindowSize.height())/2;
2133 const QPoint modalPos = rootPos + QPoint(childOffset * 5,
2134 childOffset * 5);
2135 const QPoint cursorPos = rootPos - QPoint(80, 80);
2136
2137 // Test whether tlw can receive the enter event
2138 {
2139 QCursor::setPos(cursorPos);
2140 QCoreApplication::processEvents();
2141
2142 InputTestWindow root;
2143 root.setTitle(__FUNCTION__);
2144 root.setGeometry(QRect(rootPos, m_testWindowSize));
2145 root.show();
2146 QVERIFY(QTest::qWaitForWindowExposed(&root));
2147 root.requestActivate();
2148 QVERIFY(QTest::qWaitForWindowActive(&root));
2149
2150 // Move the mouse over the root window, but not over the modal window.
2151 QCursor::setPos(rootPos + QPoint(childOffset * 5 / 2,
2152 childOffset * 5 / 2));
2153
2154 // Wait for the enter event. It must be delivered here, otherwise second
2155 // compare can PASS because of this event even after "resetCounters()".
2156 QTRY_COMPARE(root.enterEventCount, 1);
2157 QTRY_COMPARE(root.leaveEventCount, 0);
2158
2159 QWindow modal;
2160 modal.setTitle(QLatin1String("Modal - ") + __FUNCTION__);
2161 modal.setTransientParent(&root);
2162 modal.resize(newSize: m_testWindowSize/2);
2163 modal.setFramePosition(modalPos);
2164 modal.setModality(Qt::ApplicationModal);
2165 modal.show();
2166 QVERIFY(QTest::qWaitForWindowExposed(&modal));
2167 modal.requestActivate();
2168 QVERIFY(QTest::qWaitForWindowActive(&modal));
2169
2170 QCoreApplication::processEvents();
2171 QTRY_COMPARE(root.leaveEventCount, 1);
2172
2173 root.resetCounters();
2174 modal.close();
2175
2176 if (isPlatformWinRT())
2177 QEXPECT_FAIL("", "WinRT does not trigger the enter event correctly"
2178 "- QTBUG-68297.", Abort);
2179 // Check for the enter event
2180 QTRY_COMPARE(root.enterEventCount, 1);
2181 }
2182
2183 // Test whether child window can receive the enter event
2184 {
2185 QCursor::setPos(cursorPos);
2186 QCoreApplication::processEvents();
2187
2188 QWindow root;
2189 root.setTitle(__FUNCTION__);
2190 root.setGeometry(QRect(rootPos, m_testWindowSize));
2191
2192 QWindow childLvl1;
2193 childLvl1.setParent(&root);
2194 childLvl1.setGeometry(posx: childOffset,
2195 posy: childOffset,
2196 w: m_testWindowSize.width() - childOffset,
2197 h: m_testWindowSize.height() - childOffset);
2198
2199 InputTestWindow childLvl2;
2200 childLvl2.setParent(&childLvl1);
2201 childLvl2.setGeometry(posx: childOffset,
2202 posy: childOffset,
2203 w: childLvl1.width() - childOffset,
2204 h: childLvl1.height() - childOffset);
2205
2206 root.show();
2207 childLvl1.show();
2208 childLvl2.show();
2209
2210 QVERIFY(QTest::qWaitForWindowExposed(&root));
2211 root.requestActivate();
2212 QVERIFY(QTest::qWaitForWindowActive(&root));
2213 QVERIFY(childLvl1.isVisible());
2214 QVERIFY(childLvl2.isVisible());
2215
2216 // Move the mouse over the child window, but not over the modal window.
2217 // Be sure that the value is almost left-top of second child window for
2218 // checking proper position mapping.
2219 QCursor::setPos(rootPos + QPoint(childOffset * 5 / 2,
2220 childOffset * 5 / 2));
2221
2222 // Wait for the enter event. It must be delivered here, otherwise second
2223 // compare can PASS because of this event even after "resetCounters()".
2224 QTRY_COMPARE(childLvl2.enterEventCount, 1);
2225 QTRY_COMPARE(childLvl2.leaveEventCount, 0);
2226
2227 QWindow modal;
2228 modal.setTitle(QLatin1String("Modal - ") + __FUNCTION__);
2229 modal.setTransientParent(&root);
2230 modal.resize(newSize: m_testWindowSize/2);
2231 modal.setFramePosition(modalPos);
2232 modal.setModality(Qt::ApplicationModal);
2233 modal.show();
2234 QVERIFY(QTest::qWaitForWindowExposed(&modal));
2235 modal.requestActivate();
2236 QVERIFY(QTest::qWaitForWindowActive(&modal));
2237
2238 QCoreApplication::processEvents();
2239 QTRY_COMPARE(childLvl2.leaveEventCount, 1);
2240
2241 childLvl2.resetCounters();
2242 modal.close();
2243
2244 // Check for the enter event
2245 QTRY_COMPARE(childLvl2.enterEventCount, 1);
2246 }
2247
2248 // Test whether tlw can receive the enter event if mouse is over the invisible child windnow
2249 {
2250 QCursor::setPos(cursorPos);
2251 QCoreApplication::processEvents();
2252
2253 InputTestWindow root;
2254 root.setTitle(__FUNCTION__);
2255 root.setGeometry(QRect(rootPos, m_testWindowSize));
2256
2257 QWindow child;
2258 child.setParent(&root);
2259 child.setGeometry(QRect(QPoint(), m_testWindowSize));
2260
2261 root.show();
2262
2263 QVERIFY(QTest::qWaitForWindowExposed(&root));
2264 root.requestActivate();
2265 QVERIFY(QTest::qWaitForWindowActive(&root));
2266 QVERIFY(!child.isVisible());
2267
2268 // Move the mouse over the child window, but not over the modal window.
2269 QCursor::setPos(rootPos + QPoint(childOffset * 5 / 2,
2270 childOffset * 5 / 2));
2271
2272 // Wait for the enter event. It must be delivered here, otherwise second
2273 // compare can PASS because of this event even after "resetCounters()".
2274 QTRY_COMPARE(root.enterEventCount, 1);
2275 QTRY_COMPARE(root.leaveEventCount, 0);
2276
2277 QWindow modal;
2278 modal.setTitle(QLatin1String("Modal - ") + __FUNCTION__);
2279 modal.setTransientParent(&root);
2280 modal.resize(newSize: m_testWindowSize/2);
2281 modal.setFramePosition(modalPos);
2282 modal.setModality(Qt::ApplicationModal);
2283 modal.show();
2284 QVERIFY(QTest::qWaitForWindowExposed(&modal));
2285 modal.requestActivate();
2286 QVERIFY(QTest::qWaitForWindowActive(&modal));
2287
2288 QCoreApplication::processEvents();
2289 QTRY_COMPARE(root.leaveEventCount, 1);
2290
2291 root.resetCounters();
2292 modal.close();
2293
2294 // Check for the enter event
2295 QTRY_COMPARE(root.enterEventCount, 1);
2296 }
2297}
2298
2299// Verify that no spurious mouse move events are received. On Windows, there is
2300// no enter event, the OS sends mouse move events instead. Test that the QPA
2301// plugin properly suppresses those since they can interfere with tests.
2302// Simulate a main window setup with a modal dialog on top, keep the cursor
2303// in the center and check that no mouse events are recorded.
2304void tst_QWindow::spuriousMouseMove()
2305{
2306 const QString &platformName = QGuiApplication::platformName();
2307 if (platformName == QLatin1String("offscreen") || platformName == QLatin1String("cocoa"))
2308 QSKIP("No enter events sent");
2309 if (isPlatformWayland() || isPlatformWinRT())
2310 QSKIP("QCursor::setPos() is not supported on this platform");
2311 const QRect screenGeometry = QGuiApplication::primaryScreen()->geometry();
2312 const QPoint center = screenGeometry.center();
2313 QCursor::setPos(center);
2314 QRect windowGeometry(QPoint(), 2 * m_testWindowSize);
2315 windowGeometry.moveCenter(p: center);
2316 QTRY_COMPARE(QCursor::pos(), center);
2317 InputTestWindow topLevel;
2318 topLevel.setTitle(QTest::currentTestFunction());
2319 topLevel.setGeometry(windowGeometry);
2320 topLevel.show();
2321 QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
2322 QTRY_VERIFY(topLevel.enterEventCount > 0);
2323 InputTestWindow dialog(Qt::yellow);
2324 dialog.setTransientParent(&topLevel);
2325 dialog.setTitle("Dialog " + topLevel.title());
2326 dialog.setModality(Qt::ApplicationModal);
2327 windowGeometry.setSize(m_testWindowSize);
2328 windowGeometry.moveCenter(p: center);
2329 dialog.setGeometry(windowGeometry);
2330 dialog.show();
2331 QVERIFY(QTest::qWaitForWindowExposed(&dialog));
2332 QTRY_VERIFY(dialog.enterEventCount > 0);
2333 dialog.setVisible(false);
2334 QCOMPARE(dialog.mousePressedCount, 0);
2335 QCOMPARE(dialog.mouseReleasedCount, 0);
2336 QCOMPARE(dialog.mouseMovedCount, 0);
2337 QCOMPARE(dialog.mouseDoubleClickedCount, 0);
2338 topLevel.setVisible(false);
2339 QCOMPARE(topLevel.mousePressedCount, 0);
2340 QCOMPARE(topLevel.mouseReleasedCount, 0);
2341 QCOMPARE(topLevel.mouseMovedCount, 0);
2342 QCOMPARE(topLevel.mouseDoubleClickedCount, 0);
2343}
2344#endif // !QT_NO_CURSOR
2345
2346static bool isNativeWindowVisible(const QWindow *window)
2347{
2348#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
2349 return IsWindowVisible(reinterpret_cast<HWND>(window->winId()));
2350#else
2351 Q_UNIMPLEMENTED();
2352 return window->isVisible();
2353#endif
2354}
2355
2356void tst_QWindow::windowsTransientChildren()
2357{
2358 if (QGuiApplication::platformName().compare(QStringLiteral("windows"), cs: Qt::CaseInsensitive))
2359 QSKIP("Windows only test");
2360
2361 ColoredWindow mainWindow(Qt::yellow);
2362 mainWindow.setGeometry(QRect(m_availableTopLeft + QPoint(100, 100), m_testWindowSize));
2363 mainWindow.setTitle(QStringLiteral("Main"));
2364 ColoredWindow child(Qt::blue, &mainWindow);
2365 child.setGeometry(QRect(QPoint(0, 0), m_testWindowSize / 2));
2366
2367 ColoredWindow dialog(Qt::red);
2368 dialog.setGeometry(QRect(m_availableTopLeft + QPoint(200, 200), m_testWindowSize));
2369 dialog.setTitle(QStringLiteral("Dialog"));
2370 dialog.setTransientParent(&mainWindow);
2371
2372 mainWindow.show();
2373 child.show();
2374 dialog.show();
2375
2376 QVERIFY(QTest::qWaitForWindowExposed(&dialog));
2377 mainWindow.setWindowState(Qt::WindowMinimized);
2378 QVERIFY(!isNativeWindowVisible(&dialog));
2379 dialog.hide();
2380 mainWindow.setWindowState(Qt::WindowNoState);
2381 // QTBUG-40696, transient children hidden by Qt should not be re-shown by Windows.
2382 QVERIFY(!isNativeWindowVisible(&dialog));
2383 QVERIFY(isNativeWindowVisible(&child)); // Real children should be visible.
2384}
2385
2386void tst_QWindow::requestUpdate()
2387{
2388 QRect geometry(m_availableTopLeft + QPoint(80, 80), m_testWindowSize);
2389
2390 Window window;
2391 window.setTitle(QLatin1String(QTest::currentTestFunction()));
2392 window.setGeometry(geometry);
2393 window.show();
2394 QCoreApplication::processEvents();
2395 QTRY_VERIFY(window.isExposed());
2396
2397 QCOMPARE(window.received(QEvent::UpdateRequest), 0);
2398
2399 window.requestUpdate();
2400 QTRY_COMPARE(window.received(QEvent::UpdateRequest), 1);
2401
2402 window.requestUpdate();
2403 QTRY_COMPARE(window.received(QEvent::UpdateRequest), 2);
2404}
2405
2406void tst_QWindow::flags()
2407{
2408 Window window;
2409 const auto baseFlags = window.flags();
2410 window.setFlags(window.flags() | Qt::FramelessWindowHint);
2411 QCOMPARE(window.flags(), baseFlags | Qt::FramelessWindowHint);
2412 window.setFlag(Qt::WindowStaysOnTopHint, on: true);
2413 QCOMPARE(window.flags(), baseFlags | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
2414 window.setFlag(Qt::FramelessWindowHint, on: false);
2415 QCOMPARE(window.flags(), baseFlags | Qt::WindowStaysOnTopHint);
2416}
2417
2418class EventWindow : public QWindow
2419{
2420public:
2421 bool gotBlocked = false;
2422
2423protected:
2424 bool event(QEvent *e) override
2425 {
2426 if (e->type() == QEvent::WindowBlocked)
2427 gotBlocked = true;
2428 return QWindow::event(e);
2429 }
2430};
2431
2432void tst_QWindow::testBlockingWindowShownAfterModalDialog()
2433{
2434 EventWindow normalWindow;
2435 normalWindow.setTitle(QLatin1String(QTest::currentTestFunction()));
2436 normalWindow.setFramePosition(m_availableTopLeft + QPoint(80, 80));
2437 normalWindow.resize(newSize: m_testWindowSize);
2438 normalWindow.show();
2439 QVERIFY(QTest::qWaitForWindowExposed(&normalWindow));
2440 QVERIFY(!normalWindow.gotBlocked);
2441
2442 QWindow dialog;
2443 dialog.setFramePosition(m_availableTopLeft + QPoint(200, 200));
2444 dialog.resize(newSize: m_testWindowSize);
2445 dialog.setModality(Qt::ApplicationModal);
2446 dialog.setFlags(Qt::Dialog);
2447 dialog.show();
2448 QVERIFY(QTest::qWaitForWindowExposed(&dialog));
2449 QVERIFY(normalWindow.gotBlocked);
2450
2451 EventWindow normalWindowAfter;
2452 normalWindowAfter.setFramePosition(m_availableTopLeft + QPoint(80, 80));
2453 normalWindowAfter.resize(newSize: m_testWindowSize);
2454 QVERIFY(!normalWindowAfter.gotBlocked);
2455 normalWindowAfter.show();
2456 QVERIFY(QTest::qWaitForWindowExposed(&normalWindowAfter));
2457 QVERIFY(normalWindowAfter.gotBlocked);
2458}
2459
2460void tst_QWindow::generatedMouseMove()
2461{
2462 InputTestWindow w;
2463 w.setTitle(QLatin1String(QTest::currentTestFunction()));
2464 w.setGeometry(QRect(m_availableTopLeft + QPoint(100, 100), m_testWindowSize));
2465 w.setFlags(w.flags() | Qt::FramelessWindowHint); // ### FIXME: QTBUG-63542
2466 w.show();
2467 QVERIFY(QTest::qWaitForWindowExposed(&w));
2468 QPoint point(10, 10);
2469 QPoint step(2, 2);
2470
2471 QVERIFY(w.mouseMovedCount == 0);
2472 QTest::mouseMove(window: &w, pos: point);
2473 QVERIFY(w.mouseMovedCount == 1);
2474 // A press event that does not change position should not generate mouse move
2475 QTest::mousePress(window: &w, button: Qt::LeftButton, stateKey: Qt::KeyboardModifiers(), pos: point);
2476 QTest::mousePress(window: &w, button: Qt::RightButton, stateKey: Qt::KeyboardModifiers(), pos: point);
2477
2478 QVERIFY(w.mouseMovedCount == 1);
2479
2480 // Verify that a move event is generated for a mouse release event that changes position
2481 point += step;
2482 QTest::mouseRelease(window: &w, button: Qt::LeftButton,stateKey: Qt::KeyboardModifiers(), pos: point);
2483 QVERIFY(w.mouseMovedCount == 2);
2484 QVERIFY(w.buttonStateInGeneratedMove == (Qt::LeftButton | Qt::RightButton));
2485 point += step;
2486 QTest::mouseRelease(window: &w, button: Qt::RightButton, stateKey: Qt::KeyboardModifiers(), pos: point);
2487 QVERIFY(w.mouseMovedCount == 3);
2488 QVERIFY(w.buttonStateInGeneratedMove == Qt::RightButton);
2489
2490 // Verify that a move event is generated for a mouse press event that changes position
2491 point += step;
2492 QTest::mousePress(window: &w, button: Qt::LeftButton, stateKey: Qt::KeyboardModifiers(), pos: point);
2493 QVERIFY(w.mouseMovedCount == 4);
2494 QVERIFY(w.buttonStateInGeneratedMove == Qt::NoButton);
2495 point += step;
2496 QTest::mousePress(window: &w, button: Qt::RightButton, stateKey: Qt::KeyboardModifiers(), pos: point);
2497 QVERIFY(w.mouseMovedCount == 5);
2498 QVERIFY(w.buttonStateInGeneratedMove == Qt::LeftButton);
2499
2500 // A release event that does not change position should not generate mouse move
2501 QTest::mouseRelease(window: &w, button: Qt::RightButton, stateKey: Qt::KeyboardModifiers(), pos: point);
2502 QTest::mouseRelease(window: &w, button: Qt::LeftButton, stateKey: Qt::KeyboardModifiers(), pos: point);
2503 QVERIFY(w.mouseMovedCount == 5);
2504}
2505
2506void tst_QWindow::keepPendingUpdateRequests()
2507{
2508 QRect geometry(m_availableTopLeft + QPoint(80, 80), m_testWindowSize);
2509
2510 Window window;
2511 window.setTitle(QLatin1String(QTest::currentTestFunction()));
2512 window.setGeometry(geometry);
2513 window.show();
2514 QCoreApplication::processEvents();
2515 QTRY_VERIFY(window.isExposed());
2516
2517 window.requestUpdate();
2518 window.close();
2519 window.setVisible(true);
2520
2521 QPlatformWindow *platformWindow = window.handle();
2522 QVERIFY(platformWindow);
2523
2524 QVERIFY(platformWindow->hasPendingUpdateRequest());
2525 QTRY_VERIFY(!platformWindow->hasPendingUpdateRequest());
2526}
2527
2528#include <tst_qwindow.moc>
2529QTEST_MAIN(tst_QWindow)
2530
2531

source code of qtbase/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp