1 /****************************************************************************
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the test suite of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
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 Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
40 ****************************************************************************/
43 #include <qpa/qwindowsysteminterface.h>
45 #include <QtTest/QtTest>
48 #include <QStyleHints>
50 // For QSignalSpy slot connections.
51 Q_DECLARE_METATYPE(Qt::ScreenOrientation)
53 class tst_QWindow: public QObject
58 void eventOrderOnShow();
63 void testInputEvents();
64 void touchToMouseTranslation();
65 void mouseToTouchTranslation();
66 void mouseToTouchLoop();
68 void touchCancelWithTouchToMouse();
72 void activateAndClose();
73 void mouseEventSequence();
74 void windowModality();
75 void inputReentrancy();
77 void windowModality_QTBUG27039();
81 touchDevice = new QTouchDevice;
82 touchDevice->setType(QTouchDevice::TouchScreen);
83 QWindowSystemInterface::registerTouchDevice(touchDevice);
87 QTouchDevice *touchDevice;
91 void tst_QWindow::mapGlobal()
97 a.setGeometry(10, 10, 300, 300);
98 b.setGeometry(20, 20, 200, 200);
99 c.setGeometry(40, 40, 100, 100);
101 QCOMPARE(a.mapToGlobal(QPoint(100, 100)), QPoint(110, 110));
102 QCOMPARE(b.mapToGlobal(QPoint(100, 100)), QPoint(130, 130));
103 QCOMPARE(c.mapToGlobal(QPoint(100, 100)), QPoint(170, 170));
105 QCOMPARE(a.mapFromGlobal(QPoint(100, 100)), QPoint(90, 90));
106 QCOMPARE(b.mapFromGlobal(QPoint(100, 100)), QPoint(70, 70));
107 QCOMPARE(c.mapFromGlobal(QPoint(100, 100)), QPoint(30, 30));
110 class Window : public QWindow
116 setFlags(Qt::Window | Qt::WindowTitleHint | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint);
124 bool event(QEvent *event)
126 m_received[event->type()]++;
127 m_order << event->type();
129 return QWindow::event(event);
132 int received(QEvent::Type type)
134 return m_received.value(type, 0);
137 int eventIndex(QEvent::Type type)
139 return m_order.indexOf(type);
143 QHash<QEvent::Type, int> m_received;
144 QVector<QEvent::Type> m_order;
147 void tst_QWindow::eventOrderOnShow()
149 // Some platforms enforce minimum widths for windows, which can cause extra resize
150 // events, so set the width to suitably large value to avoid those.
151 QRect geometry(80, 80, 300, 40);
154 window.setGeometry(geometry);
156 QCoreApplication::processEvents();
158 QTRY_COMPARE(window.received(QEvent::Show), 1);
159 QTRY_COMPARE(window.received(QEvent::Resize), 1);
160 QTRY_VERIFY(window.isExposed());
162 QVERIFY(window.eventIndex(QEvent::Show) < window.eventIndex(QEvent::Resize));
163 QVERIFY(window.eventIndex(QEvent::Resize) < window.eventIndex(QEvent::Expose));
166 void tst_QWindow::positioning()
169 // the fullscreen animation delay on OS X Lion also causes failures in
170 // the isActive() test below, so it's best to just skip it for now
171 QSKIP("Multiple failures in this test on Mac OS X, see QTBUG-23059");
174 // Some platforms enforce minimum widths for windows, which can cause extra resize
175 // events, so set the width to suitably large value to avoid those.
176 QRect geometry(80, 80, 300, 40);
179 window.setGeometry(geometry);
180 QCOMPARE(window.geometry(), geometry);
182 QCoreApplication::processEvents();
184 QTRY_COMPARE(window.received(QEvent::Resize), 1);
185 QTRY_VERIFY(window.received(QEvent::Expose) > 0);
187 QMargins originalMargins = window.frameMargins();
189 QCOMPARE(window.pos(), window.framePos() + QPoint(originalMargins.left(), originalMargins.top()));
190 QVERIFY(window.frameGeometry().contains(window.geometry()));
192 QPoint originalPos = window.pos();
193 QPoint originalFramePos = window.framePos();
195 window.setWindowState(Qt::WindowFullScreen);
196 QCoreApplication::processEvents();
197 QTRY_COMPARE(window.received(QEvent::Resize), 2);
199 window.setWindowState(Qt::WindowNoState);
200 QCoreApplication::processEvents();
201 QTRY_COMPARE(window.received(QEvent::Resize), 3);
203 QTRY_COMPARE(originalPos, window.pos());
204 QTRY_COMPARE(originalFramePos, window.framePos());
205 QTRY_COMPARE(originalMargins, window.frameMargins());
207 // if our positioning is actually fully respected by the window manager
208 // test whether it correctly handles frame positioning as well
209 if (originalPos == geometry.topLeft() && (originalMargins.top() != 0 || originalMargins.left() != 0)) {
210 QPoint framePos(40, 40);
213 window.setFramePos(framePos);
215 QTRY_VERIFY(window.received(QEvent::Move));
216 QTRY_COMPARE(framePos, window.framePos());
217 QTRY_COMPARE(originalMargins, window.frameMargins());
218 QCOMPARE(window.pos(), window.framePos() + QPoint(originalMargins.left(), originalMargins.top()));
220 // and back to regular positioning
223 window.setPos(originalPos);
224 QTRY_VERIFY(window.received(QEvent::Move));
225 QTRY_COMPARE(originalPos, window.pos());
229 void tst_QWindow::isExposed()
231 QRect geometry(80, 80, 40, 40);
234 window.setGeometry(geometry);
235 QCOMPARE(window.geometry(), geometry);
237 QCoreApplication::processEvents();
239 QTRY_VERIFY(window.received(QEvent::Expose) > 0);
240 QTRY_VERIFY(window.isExposed());
244 QCoreApplication::processEvents();
245 QTRY_VERIFY(window.received(QEvent::Expose) > 1);
246 QTRY_VERIFY(!window.isExposed());
250 void tst_QWindow::isActive()
253 // Some platforms enforce minimum widths for windows, which can cause extra resize
254 // events, so set the width to suitably large value to avoid those.
255 window.setGeometry(80, 80, 300, 40);
257 QCoreApplication::processEvents();
259 QTRY_VERIFY(window.isExposed());
260 QTRY_COMPARE(window.received(QEvent::Resize), 1);
261 QTRY_VERIFY(QGuiApplication::focusWindow() == &window);
262 QVERIFY(window.isActive());
265 child.setParent(&window);
266 child.setGeometry(10, 10, 20, 20);
269 QTRY_VERIFY(child.isExposed());
271 child.requestActivate();
273 QTRY_VERIFY(QGuiApplication::focusWindow() == &child);
274 QVERIFY(child.isActive());
276 // parent shouldn't receive new resize events from child being shown
277 QCoreApplication::processEvents();
278 QTRY_COMPARE(window.received(QEvent::Resize), 1);
279 QTRY_COMPARE(window.received(QEvent::FocusIn), 1);
280 QTRY_COMPARE(window.received(QEvent::FocusOut), 1);
281 QTRY_COMPARE(child.received(QEvent::FocusIn), 1);
284 QVERIFY(window.isActive());
287 dialog.setTransientParent(&window);
288 dialog.setGeometry(110, 110, 300, 30);
291 dialog.requestActivate();
293 QTRY_VERIFY(dialog.isExposed());
294 QCoreApplication::processEvents();
295 QTRY_COMPARE(dialog.received(QEvent::Resize), 1);
296 QTRY_VERIFY(QGuiApplication::focusWindow() == &dialog);
297 QVERIFY(dialog.isActive());
299 // transient child has focus
300 QVERIFY(window.isActive());
303 QVERIFY(child.isActive());
305 window.requestActivate();
307 QTRY_VERIFY(QGuiApplication::focusWindow() == &window);
308 QCoreApplication::processEvents();
309 QTRY_COMPARE(dialog.received(QEvent::FocusOut), 1);
310 QTRY_COMPARE(window.received(QEvent::FocusIn), 2);
312 QVERIFY(window.isActive());
314 // transient parent has focus
315 QVERIFY(dialog.isActive());
318 QVERIFY(child.isActive());
321 class InputTestWindow : public QWindow
324 void keyPressEvent(QKeyEvent *event) {
325 keyPressCode = event->key();
327 void keyReleaseEvent(QKeyEvent *event) {
328 keyReleaseCode = event->key();
330 void mousePressEvent(QMouseEvent *event) {
335 mouseSequenceSignature += 'p';
336 mousePressButton = event->button();
337 mousePressScreenPos = event->screenPos();
338 mousePressLocalPos = event->localPos();
339 if (spinLoopWhenPressed)
340 QCoreApplication::processEvents();
343 void mouseReleaseEvent(QMouseEvent *event) {
347 ++mouseReleasedCount;
348 mouseSequenceSignature += 'r';
349 mouseReleaseButton = event->button();
352 void mouseMoveEvent(QMouseEvent *event) {
357 mouseMoveButton = event->button();
358 mouseMoveScreenPos = event->screenPos();
361 void mouseDoubleClickEvent(QMouseEvent *event) {
365 ++mouseDoubleClickedCount;
366 mouseSequenceSignature += 'd';
369 void touchEvent(QTouchEvent *event) {
374 touchEventType = event->type();
375 QList<QTouchEvent::TouchPoint> points = event->touchPoints();
376 for (int i = 0; i < points.count(); ++i) {
377 switch (points.at(i).state()) {
378 case Qt::TouchPointPressed:
380 if (spinLoopWhenPressed)
381 QCoreApplication::processEvents();
383 case Qt::TouchPointReleased:
384 ++touchReleasedCount;
386 case Qt::TouchPointMoved:
394 void resetCounters() {
395 mousePressedCount = mouseReleasedCount = mouseMovedCount = mouseDoubleClickedCount = 0;
396 mouseSequenceSignature = QString();
397 touchPressedCount = touchReleasedCount = touchMovedCount = 0;
401 keyPressCode = keyReleaseCode = 0;
402 mousePressButton = mouseReleaseButton = mouseMoveButton = 0;
403 ignoreMouse = ignoreTouch = false;
404 spinLoopWhenPressed = false;
408 int keyPressCode, keyReleaseCode;
409 int mousePressButton, mouseReleaseButton, mouseMoveButton;
410 int mousePressedCount, mouseReleasedCount, mouseMovedCount, mouseDoubleClickedCount;
411 QString mouseSequenceSignature;
412 QPointF mousePressScreenPos, mouseMoveScreenPos, mousePressLocalPos;
413 int touchPressedCount, touchReleasedCount, touchMovedCount;
414 QEvent::Type touchEventType;
416 bool ignoreMouse, ignoreTouch;
418 bool spinLoopWhenPressed;
421 void tst_QWindow::testInputEvents()
423 InputTestWindow window;
424 window.setGeometry(80, 80, 40, 40);
426 QVERIFY(QTest::qWaitForWindowExposed(&window));
428 QWindowSystemInterface::handleKeyEvent(&window, QEvent::KeyPress, Qt::Key_A, Qt::NoModifier);
429 QWindowSystemInterface::handleKeyEvent(&window, QEvent::KeyRelease, Qt::Key_A, Qt::NoModifier);
430 QCoreApplication::processEvents();
431 QCOMPARE(window.keyPressCode, int(Qt::Key_A));
432 QCOMPARE(window.keyReleaseCode, int(Qt::Key_A));
434 QPointF local(12, 34);
435 QWindowSystemInterface::handleMouseEvent(&window, local, local, Qt::LeftButton);
436 QWindowSystemInterface::handleMouseEvent(&window, local, local, Qt::NoButton);
437 QCoreApplication::processEvents();
438 QCOMPARE(window.mousePressButton, int(Qt::LeftButton));
439 QCOMPARE(window.mouseReleaseButton, int(Qt::LeftButton));
440 QCOMPARE(window.mousePressLocalPos, local);
442 QList<QWindowSystemInterface::TouchPoint> points;
443 QWindowSystemInterface::TouchPoint tp1, tp2;
445 tp1.state = Qt::TouchPointPressed;
446 tp1.area = QRect(10, 10, 4, 4);
448 tp2.state = Qt::TouchPointPressed;
449 tp2.area = QRect(20, 20, 4, 4);
450 points << tp1 << tp2;
451 QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
452 points[0].state = Qt::TouchPointReleased;
453 points[1].state = Qt::TouchPointReleased;
454 QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
455 QCoreApplication::processEvents();
456 QTRY_COMPARE(window.touchPressedCount, 2);
457 QTRY_COMPARE(window.touchReleasedCount, 2);
459 // Now with null pointer as window. local param should not be utilized:
460 // handleMouseEvent() with tlw == 0 means the event is in global coords only.
461 window.mousePressButton = window.mouseReleaseButton = 0;
462 QPointF nonWindowGlobal(500, 500); // not inside the window
463 QWindowSystemInterface::handleMouseEvent(0, nonWindowGlobal, nonWindowGlobal, Qt::LeftButton);
464 QWindowSystemInterface::handleMouseEvent(0, nonWindowGlobal, nonWindowGlobal, Qt::NoButton);
465 QCoreApplication::processEvents();
466 QCOMPARE(window.mousePressButton, 0);
467 QCOMPARE(window.mouseReleaseButton, 0);
468 QPointF windowGlobal = window.mapToGlobal(local.toPoint());
469 QWindowSystemInterface::handleMouseEvent(0, windowGlobal, windowGlobal, Qt::LeftButton);
470 QWindowSystemInterface::handleMouseEvent(0, windowGlobal, windowGlobal, Qt::NoButton);
471 QCoreApplication::processEvents();
472 QCOMPARE(window.mousePressButton, int(Qt::LeftButton));
473 QCOMPARE(window.mouseReleaseButton, int(Qt::LeftButton));
474 QCOMPARE(window.mousePressScreenPos, windowGlobal);
475 QCOMPARE(window.mousePressLocalPos, local); // the local we passed was bogus, verify that qGuiApp calculated the proper one
478 void tst_QWindow::touchToMouseTranslation()
480 InputTestWindow window;
481 window.ignoreTouch = true;
482 window.setGeometry(80, 80, 40, 40);
484 QVERIFY(QTest::qWaitForWindowExposed(&window));
486 QList<QWindowSystemInterface::TouchPoint> points;
487 QWindowSystemInterface::TouchPoint tp1, tp2;
488 const QRectF pressArea(101, 102, 4, 4);
489 const QRectF moveArea(105, 108, 4, 4);
491 tp1.state = Qt::TouchPointPressed;
492 tp1.area = pressArea;
494 tp2.state = Qt::TouchPointPressed;
495 points << tp1 << tp2;
496 QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
497 // Now an update but with changed list order. The mouse event should still
498 // be generated from the point with id 1.
500 tp1.state = Qt::TouchPointStationary;
502 tp2.state = Qt::TouchPointMoved;
505 points << tp1 << tp2;
506 QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
507 points[0].state = Qt::TouchPointReleased;
508 points[1].state = Qt::TouchPointReleased;
509 QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
510 QCoreApplication::processEvents();
512 QTRY_COMPARE(window.mousePressButton, int(Qt::LeftButton));
513 QTRY_COMPARE(window.mouseReleaseButton, int(Qt::LeftButton));
514 QTRY_COMPARE(window.mousePressScreenPos, pressArea.center());
515 QTRY_COMPARE(window.mouseMoveScreenPos, moveArea.center());
517 window.mousePressButton = 0;
518 window.mouseReleaseButton = 0;
520 window.ignoreTouch = false;
522 points[0].state = Qt::TouchPointPressed;
523 points[1].state = Qt::TouchPointPressed;
524 QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
525 points[0].state = Qt::TouchPointReleased;
526 points[1].state = Qt::TouchPointReleased;
527 QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
528 QCoreApplication::processEvents();
530 // no new mouse events should be generated since the input window handles the touch events
531 QTRY_COMPARE(window.mousePressButton, 0);
532 QTRY_COMPARE(window.mouseReleaseButton, 0);
534 qApp->setAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents, false);
536 window.ignoreTouch = true;
537 points[0].state = Qt::TouchPointPressed;
538 points[1].state = Qt::TouchPointPressed;
539 QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
540 points[0].state = Qt::TouchPointReleased;
541 points[1].state = Qt::TouchPointReleased;
542 QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
543 QCoreApplication::processEvents();
545 qApp->setAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents, true);
547 // mouse event synthesizing disabled
548 QTRY_COMPARE(window.mousePressButton, 0);
549 QTRY_COMPARE(window.mouseReleaseButton, 0);
552 void tst_QWindow::mouseToTouchTranslation()
554 qApp->setAttribute(Qt::AA_SynthesizeTouchForUnhandledMouseEvents, true);
556 InputTestWindow window;
557 window.ignoreMouse = true;
558 window.setGeometry(80, 80, 40, 40);
560 QVERIFY(QTest::qWaitForWindowExposed(&window));
562 QWindowSystemInterface::handleMouseEvent(&window, QPoint(10, 10), window.mapToGlobal(QPoint(10, 10)), Qt::LeftButton);
563 QWindowSystemInterface::handleMouseEvent(&window, QPoint(10, 10), window.mapToGlobal(QPoint(10, 10)), Qt::NoButton);
564 QCoreApplication::processEvents();
566 qApp->setAttribute(Qt::AA_SynthesizeTouchForUnhandledMouseEvents, false);
568 QTRY_COMPARE(window.touchPressedCount, 1);
569 QTRY_COMPARE(window.touchReleasedCount, 1);
571 qApp->setAttribute(Qt::AA_SynthesizeTouchForUnhandledMouseEvents, true);
573 window.ignoreMouse = false;
575 QWindowSystemInterface::handleMouseEvent(&window, QPoint(10, 10), window.mapToGlobal(QPoint(10, 10)), Qt::LeftButton);
576 QWindowSystemInterface::handleMouseEvent(&window, QPoint(10, 10), window.mapToGlobal(QPoint(10, 10)), Qt::NoButton);
577 QCoreApplication::processEvents();
579 qApp->setAttribute(Qt::AA_SynthesizeTouchForUnhandledMouseEvents, false);
581 // no new touch events should be generated since the input window handles the mouse events
582 QTRY_COMPARE(window.touchPressedCount, 1);
583 QTRY_COMPARE(window.touchReleasedCount, 1);
585 window.ignoreMouse = true;
587 QWindowSystemInterface::handleMouseEvent(&window, QPoint(10, 10), window.mapToGlobal(QPoint(10, 10)), Qt::LeftButton);
588 QWindowSystemInterface::handleMouseEvent(&window, QPoint(10, 10), window.mapToGlobal(QPoint(10, 10)), Qt::NoButton);
589 QCoreApplication::processEvents();
591 // touch event synthesis disabled
592 QTRY_COMPARE(window.touchPressedCount, 1);
593 QTRY_COMPARE(window.touchReleasedCount, 1);
598 void tst_QWindow::mouseToTouchLoop()
600 // make sure there's no infinite loop when synthesizing both ways
601 qApp->setAttribute(Qt::AA_SynthesizeTouchForUnhandledMouseEvents, true);
602 qApp->setAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents, true);
604 InputTestWindow window;
605 window.ignoreMouse = true;
606 window.ignoreTouch = true;
607 window.setGeometry(80, 80, 40, 40);
609 QVERIFY(QTest::qWaitForWindowExposed(&window));
611 QWindowSystemInterface::handleMouseEvent(&window, QPoint(10, 10), window.mapToGlobal(QPoint(10, 10)), Qt::LeftButton);
612 QWindowSystemInterface::handleMouseEvent(&window, QPoint(10, 10), window.mapToGlobal(QPoint(10, 10)), Qt::NoButton);
613 QCoreApplication::processEvents();
615 qApp->setAttribute(Qt::AA_SynthesizeTouchForUnhandledMouseEvents, false);
616 qApp->setAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents, true);
619 void tst_QWindow::touchCancel()
621 InputTestWindow window;
622 window.setGeometry(80, 80, 40, 40);
624 QVERIFY(QTest::qWaitForWindowExposed(&window));
626 QList<QWindowSystemInterface::TouchPoint> points;
627 QWindowSystemInterface::TouchPoint tp1;
631 tp1.state = Qt::TouchPointPressed;
632 tp1.area = QRect(10, 10, 4, 4);
634 QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
635 QCoreApplication::processEvents();
636 QTRY_COMPARE(window.touchEventType, QEvent::TouchBegin);
637 QTRY_COMPARE(window.touchPressedCount, 1);
639 // Cancel the active touch sequence.
640 QWindowSystemInterface::handleTouchCancelEvent(&window, touchDevice);
641 QCoreApplication::processEvents();
642 QTRY_COMPARE(window.touchEventType, QEvent::TouchCancel);
644 // Send a move -> will not be delivered due to the cancellation.
645 QTRY_COMPARE(window.touchMovedCount, 0);
646 points[0].state = Qt::TouchPointMoved;
647 tp1.area.adjust(2, 2, 2, 2);
648 QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
649 QCoreApplication::processEvents();
650 QTRY_COMPARE(window.touchMovedCount, 0);
652 // Likewise. The only allowed transition is TouchCancel -> TouchBegin.
653 QTRY_COMPARE(window.touchReleasedCount, 0);
654 points[0].state = Qt::TouchPointReleased;
655 QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
656 QCoreApplication::processEvents();
657 QTRY_COMPARE(window.touchReleasedCount, 0);
659 // Start a new sequence -> from this point on everything should go through normally.
660 points[0].state = Qt::TouchPointPressed;
661 QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
662 QCoreApplication::processEvents();
663 QTRY_COMPARE(window.touchEventType, QEvent::TouchBegin);
664 QTRY_COMPARE(window.touchPressedCount, 2);
666 points[0].state = Qt::TouchPointMoved;
667 tp1.area.adjust(2, 2, 2, 2);
668 QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
669 QCoreApplication::processEvents();
670 QTRY_COMPARE(window.touchMovedCount, 1);
672 points[0].state = Qt::TouchPointReleased;
673 QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
674 QCoreApplication::processEvents();
675 QTRY_COMPARE(window.touchReleasedCount, 1);
678 void tst_QWindow::touchCancelWithTouchToMouse()
680 InputTestWindow window;
681 window.ignoreTouch = true;
682 window.setGeometry(80, 80, 40, 40);
684 QVERIFY(QTest::qWaitForWindowExposed(&window));
686 QList<QWindowSystemInterface::TouchPoint> points;
687 QWindowSystemInterface::TouchPoint tp1;
690 tp1.state = Qt::TouchPointPressed;
691 tp1.area = QRect(100, 100, 4, 4);
693 QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
694 QCoreApplication::processEvents();
695 QTRY_COMPARE(window.mousePressButton, int(Qt::LeftButton));
696 QTRY_COMPARE(window.mousePressScreenPos, points[0].area.center());
698 // Cancel the touch. Should result in a mouse release for windows that have
699 // have an active touch-to-mouse sequence.
700 QWindowSystemInterface::handleTouchCancelEvent(0, touchDevice);
701 QCoreApplication::processEvents();
703 QTRY_COMPARE(window.mouseReleaseButton, int(Qt::LeftButton));
705 // Now change the window to accept touches.
706 window.mousePressButton = window.mouseReleaseButton = 0;
707 window.ignoreTouch = false;
709 // Send a touch, there will be no mouse event generated.
710 QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
711 QCoreApplication::processEvents();
712 QTRY_COMPARE(window.mousePressButton, 0);
714 // Cancel the touch. It should not result in a mouse release with this window.
715 QWindowSystemInterface::handleTouchCancelEvent(0, touchDevice);
716 QCoreApplication::processEvents();
717 QTRY_COMPARE(window.mouseReleaseButton, 0);
720 void tst_QWindow::orientation()
722 qRegisterMetaType<Qt::ScreenOrientation>("Qt::ScreenOrientation");
725 window.setGeometry(80, 80, 40, 40);
728 window.reportContentOrientationChange(Qt::PortraitOrientation);
729 QCOMPARE(window.contentOrientation(), Qt::PortraitOrientation);
731 window.reportContentOrientationChange(Qt::PrimaryOrientation);
732 QCOMPARE(window.contentOrientation(), Qt::PrimaryOrientation);
734 QVERIFY(!window.requestOrientation(Qt::LandscapeOrientation) || window.orientation() == Qt::LandscapeOrientation);
735 QVERIFY(!window.requestOrientation(Qt::PortraitOrientation) || window.orientation() == Qt::PortraitOrientation);
736 QVERIFY(!window.requestOrientation(Qt::PrimaryOrientation) || window.orientation() == Qt::PrimaryOrientation);
738 QSignalSpy spy(&window, SIGNAL(contentOrientationChanged(Qt::ScreenOrientation)));
739 window.reportContentOrientationChange(Qt::LandscapeOrientation);
740 QCOMPARE(spy.count(), 1);
743 void tst_QWindow::sizes()
747 QSignalSpy minimumWidthSpy(&window, SIGNAL(minimumWidthChanged(int)));
748 QSignalSpy minimumHeightSpy(&window, SIGNAL(minimumHeightChanged(int)));
749 QSignalSpy maximumWidthSpy(&window, SIGNAL(maximumWidthChanged(int)));
750 QSignalSpy maximumHeightSpy(&window, SIGNAL(maximumHeightChanged(int)));
752 QSize oldMaximum = window.maximumSize();
754 window.setMinimumWidth(10);
755 QCOMPARE(window.minimumWidth(), 10);
756 QCOMPARE(window.minimumHeight(), 0);
757 QCOMPARE(window.minimumSize(), QSize(10, 0));
758 QCOMPARE(window.maximumSize(), oldMaximum);
759 QCOMPARE(minimumWidthSpy.count(), 1);
760 QCOMPARE(minimumHeightSpy.count(), 0);
761 QCOMPARE(maximumWidthSpy.count(), 0);
762 QCOMPARE(maximumHeightSpy.count(), 0);
764 window.setMinimumHeight(10);
765 QCOMPARE(window.minimumWidth(), 10);
766 QCOMPARE(window.minimumHeight(), 10);
767 QCOMPARE(window.minimumSize(), QSize(10, 10));
768 QCOMPARE(window.maximumSize(), oldMaximum);
769 QCOMPARE(minimumWidthSpy.count(), 1);
770 QCOMPARE(minimumHeightSpy.count(), 1);
771 QCOMPARE(maximumWidthSpy.count(), 0);
772 QCOMPARE(maximumHeightSpy.count(), 0);
774 window.setMaximumWidth(100);
775 QCOMPARE(window.maximumWidth(), 100);
776 QCOMPARE(window.maximumHeight(), oldMaximum.height());
777 QCOMPARE(window.minimumSize(), QSize(10, 10));
778 QCOMPARE(window.maximumSize(), QSize(100, oldMaximum.height()));
779 QCOMPARE(minimumWidthSpy.count(), 1);
780 QCOMPARE(minimumHeightSpy.count(), 1);
781 QCOMPARE(maximumWidthSpy.count(), 1);
782 QCOMPARE(maximumHeightSpy.count(), 0);
784 window.setMaximumHeight(100);
785 QCOMPARE(window.maximumWidth(), 100);
786 QCOMPARE(window.maximumHeight(), 100);
787 QCOMPARE(window.minimumSize(), QSize(10, 10));
788 QCOMPARE(window.maximumSize(), QSize(100, 100));
789 QCOMPARE(minimumWidthSpy.count(), 1);
790 QCOMPARE(minimumHeightSpy.count(), 1);
791 QCOMPARE(maximumWidthSpy.count(), 1);
792 QCOMPARE(maximumHeightSpy.count(), 1);
795 void tst_QWindow::close()
804 // we can not close a non top level window
810 void tst_QWindow::activateAndClose()
812 for (int i = 0; i < 10; ++i) {
815 window.requestActivate();
816 QVERIFY(QTest::qWaitForWindowActive(&window));
817 QCOMPARE(qGuiApp->focusWindow(), &window);
821 void tst_QWindow::mouseEventSequence()
823 int doubleClickInterval = qGuiApp->styleHints()->mouseDoubleClickInterval();
825 InputTestWindow window;
826 window.setGeometry(80, 80, 40, 40);
828 QVERIFY(QTest::qWaitForWindowExposed(&window));
831 QPointF local(12, 34);
832 QWindowSystemInterface::handleMouseEvent(&window, timestamp++, local, local, Qt::LeftButton);
833 QWindowSystemInterface::handleMouseEvent(&window, timestamp++, local, local, Qt::NoButton);
834 QCoreApplication::processEvents();
835 QCOMPARE(window.mousePressedCount, 1);
836 QCOMPARE(window.mouseReleasedCount, 1);
837 QCOMPARE(window.mouseDoubleClickedCount, 0);
838 QCOMPARE(window.mouseSequenceSignature, QLatin1String("pr"));
840 window.resetCounters();
841 timestamp += doubleClickInterval;
843 // A double click must result in press, release, press, doubleclick, release.
844 // Check that no unexpected event suppression occurs and that the order is correct.
845 QWindowSystemInterface::handleMouseEvent(&window, timestamp++, local, local, Qt::LeftButton);
846 QWindowSystemInterface::handleMouseEvent(&window, timestamp++, local, local, Qt::NoButton);
847 QWindowSystemInterface::handleMouseEvent(&window, timestamp++, local, local, Qt::LeftButton);
848 QWindowSystemInterface::handleMouseEvent(&window, timestamp++, local, local, Qt::NoButton);
849 QCoreApplication::processEvents();
850 QCOMPARE(window.mousePressedCount, 2);
851 QCOMPARE(window.mouseReleasedCount, 2);
852 QCOMPARE(window.mouseDoubleClickedCount, 1);
853 QCOMPARE(window.mouseSequenceSignature, QLatin1String("prpdr"));
855 timestamp += doubleClickInterval;
856 window.resetCounters();
858 // Triple click = double + single click
859 QWindowSystemInterface::handleMouseEvent(&window, timestamp++, local, local, Qt::LeftButton);
860 QWindowSystemInterface::handleMouseEvent(&window, timestamp++, local, local, Qt::NoButton);
861 QWindowSystemInterface::handleMouseEvent(&window, timestamp++, local, local, Qt::LeftButton);
862 QWindowSystemInterface::handleMouseEvent(&window, timestamp++, local, local, Qt::NoButton);
863 QWindowSystemInterface::handleMouseEvent(&window, timestamp++, local, local, Qt::LeftButton);
864 QWindowSystemInterface::handleMouseEvent(&window, timestamp++, local, local, Qt::NoButton);
865 QCoreApplication::processEvents();
866 QCOMPARE(window.mousePressedCount, 3);
867 QCOMPARE(window.mouseReleasedCount, 3);
868 QCOMPARE(window.mouseDoubleClickedCount, 1);
869 QCOMPARE(window.mouseSequenceSignature, QLatin1String("prpdrpr"));
871 timestamp += doubleClickInterval;
872 window.resetCounters();
874 // Two double clicks.
875 QWindowSystemInterface::handleMouseEvent(&window, timestamp++, local, local, Qt::LeftButton);
876 QWindowSystemInterface::handleMouseEvent(&window, timestamp++, local, local, Qt::NoButton);
877 QWindowSystemInterface::handleMouseEvent(&window, timestamp++, local, local, Qt::LeftButton);
878 QWindowSystemInterface::handleMouseEvent(&window, timestamp++, local, local, Qt::NoButton);
879 QWindowSystemInterface::handleMouseEvent(&window, timestamp++, local, local, Qt::LeftButton);
880 QWindowSystemInterface::handleMouseEvent(&window, timestamp++, local, local, Qt::NoButton);
881 QWindowSystemInterface::handleMouseEvent(&window, timestamp++, local, local, Qt::LeftButton);
882 QWindowSystemInterface::handleMouseEvent(&window, timestamp++, local, local, Qt::NoButton);
883 QCoreApplication::processEvents();
884 QCOMPARE(window.mousePressedCount, 4);
885 QCOMPARE(window.mouseReleasedCount, 4);
886 QCOMPARE(window.mouseDoubleClickedCount, 2);
887 QCOMPARE(window.mouseSequenceSignature, QLatin1String("prpdrprpdr"));
889 timestamp += doubleClickInterval;
890 window.resetCounters();
892 // Four clicks, none of which qualifies as a double click.
893 QWindowSystemInterface::handleMouseEvent(&window, timestamp++, local, local, Qt::LeftButton);
894 QWindowSystemInterface::handleMouseEvent(&window, timestamp++, local, local, Qt::NoButton);
895 timestamp += doubleClickInterval;
896 QWindowSystemInterface::handleMouseEvent(&window, timestamp++, local, local, Qt::LeftButton);
897 QWindowSystemInterface::handleMouseEvent(&window, timestamp++, local, local, Qt::NoButton);
898 timestamp += doubleClickInterval;
899 QWindowSystemInterface::handleMouseEvent(&window, timestamp++, local, local, Qt::LeftButton);
900 QWindowSystemInterface::handleMouseEvent(&window, timestamp++, local, local, Qt::NoButton);
901 timestamp += doubleClickInterval;
902 QWindowSystemInterface::handleMouseEvent(&window, timestamp++, local, local, Qt::LeftButton);
903 QWindowSystemInterface::handleMouseEvent(&window, timestamp++, local, local, Qt::NoButton);
904 timestamp += doubleClickInterval;
905 QCoreApplication::processEvents();
906 QCOMPARE(window.mousePressedCount, 4);
907 QCOMPARE(window.mouseReleasedCount, 4);
908 QCOMPARE(window.mouseDoubleClickedCount, 0);
909 QCOMPARE(window.mouseSequenceSignature, QLatin1String("prprprpr"));
912 void tst_QWindow::windowModality()
914 qRegisterMetaType<Qt::WindowModality>("Qt::WindowModality");
917 QSignalSpy spy(&window, SIGNAL(modalityChanged(Qt::WindowModality)));
919 QCOMPARE(window.modality(), Qt::NonModal);
920 window.setModality(Qt::NonModal);
921 QCOMPARE(window.modality(), Qt::NonModal);
922 QCOMPARE(spy.count(), 0);
924 window.setModality(Qt::WindowModal);
925 QCOMPARE(window.modality(), Qt::WindowModal);
926 QCOMPARE(spy.count(), 1);
927 window.setModality(Qt::WindowModal);
928 QCOMPARE(window.modality(), Qt::WindowModal);
929 QCOMPARE(spy.count(), 1);
931 window.setModality(Qt::ApplicationModal);
932 QCOMPARE(window.modality(), Qt::ApplicationModal);
933 QCOMPARE(spy.count(), 2);
934 window.setModality(Qt::ApplicationModal);
935 QCOMPARE(window.modality(), Qt::ApplicationModal);
936 QCOMPARE(spy.count(), 2);
938 window.setModality(Qt::NonModal);
939 QCOMPARE(window.modality(), Qt::NonModal);
940 QCOMPARE(spy.count(), 3);
943 void tst_QWindow::inputReentrancy()
945 InputTestWindow window;
946 window.spinLoopWhenPressed = true;
948 window.setGeometry(80, 80, 40, 40);
950 QVERIFY(QTest::qWaitForWindowExposed(&window));
952 // Queue three events.
953 QPointF local(12, 34);
954 QWindowSystemInterface::handleMouseEvent(&window, local, local, Qt::LeftButton);
955 local += QPointF(2, 2);
956 QWindowSystemInterface::handleMouseEvent(&window, local, local, Qt::LeftButton);
957 QWindowSystemInterface::handleMouseEvent(&window, local, local, Qt::NoButton);
958 // Process them. However, the event handler for the press will also call
959 // processEvents() so the move and release will be delivered before returning
960 // from mousePressEvent(). The point is that no events should get lost.
961 QCoreApplication::processEvents();
962 QCOMPARE(window.mousePressButton, int(Qt::LeftButton));
963 QCOMPARE(window.mouseReleaseButton, int(Qt::LeftButton));
964 QCOMPARE(window.mousePressedCount, 1);
965 QCOMPARE(window.mouseMovedCount, 1);
966 QCOMPARE(window.mouseReleasedCount, 1);
968 // Now the same for touch.
969 QList<QWindowSystemInterface::TouchPoint> points;
970 QWindowSystemInterface::TouchPoint tp1;
972 tp1.state = Qt::TouchPointPressed;
973 tp1.area = QRectF(10, 10, 4, 4);
975 QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
976 points[0].state = Qt::TouchPointMoved;
977 points[0].area = QRectF(20, 20, 8, 8);
978 QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
979 points[0].state = Qt::TouchPointReleased;
980 QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
981 QCoreApplication::processEvents();
982 QCOMPARE(window.touchPressedCount, 1);
983 QCOMPARE(window.touchMovedCount, 1);
984 QCOMPARE(window.touchReleasedCount, 1);
987 #ifndef QT_NO_TABLETEVENT
988 class TabletTestWindow : public QWindow
991 TabletTestWindow() : eventType(0) { }
992 void tabletEvent(QTabletEvent *ev) {
993 eventType = ev->type();
994 eventGlobal = ev->globalPosF();
995 eventLocal = ev->posF();
996 eventDevice = ev->device();
999 QPointF eventGlobal, eventLocal;
1001 bool eventFilter(QObject *obj, QEvent *ev) {
1002 if (ev->type() == QEvent::TabletEnterProximity
1003 || ev->type() == QEvent::TabletLeaveProximity) {
1004 eventType = ev->type();
1005 QTabletEvent *te = static_cast<QTabletEvent *>(ev);
1006 eventDevice = te->device();
1008 return QWindow::eventFilter(obj, ev);
1013 void tst_QWindow::tabletEvents()
1015 #ifndef QT_NO_TABLETEVENT
1016 TabletTestWindow window;
1017 window.setGeometry(10, 10, 100, 100);
1018 qGuiApp->installEventFilter(&window);
1020 QPoint local(10, 10);
1021 QPoint global = window.mapToGlobal(local);
1022 QWindowSystemInterface::handleTabletEvent(&window, true, local, global, 1, 2, 0.5, 1, 2, 0.1, 0, 0, 0);
1023 QCoreApplication::processEvents();
1024 QTRY_VERIFY(window.eventType == QEvent::TabletPress);
1025 QTRY_COMPARE(window.eventGlobal.toPoint(), global);
1026 QTRY_COMPARE(window.eventLocal.toPoint(), local);
1027 QWindowSystemInterface::handleTabletEvent(&window, false, local, global, 1, 2, 0.5, 1, 2, 0.1, 0, 0, 0);
1028 QCoreApplication::processEvents();
1029 QTRY_VERIFY(window.eventType == QEvent::TabletRelease);
1031 QWindowSystemInterface::handleTabletEnterProximityEvent(1, 2, 3);
1032 QCoreApplication::processEvents();
1033 QTRY_VERIFY(window.eventType == QEvent::TabletEnterProximity);
1034 QTRY_COMPARE(window.eventDevice, 1);
1036 QWindowSystemInterface::handleTabletLeaveProximityEvent(1, 2, 3);
1037 QCoreApplication::processEvents();
1038 QTRY_VERIFY(window.eventType == QEvent::TabletLeaveProximity);
1039 QTRY_COMPARE(window.eventDevice, 1);
1044 void tst_QWindow::windowModality_QTBUG27039()
1047 parent.setGeometry(10, 10, 100, 100);
1050 InputTestWindow modalA;
1051 modalA.setTransientParent(&parent);
1052 modalA.setGeometry(10, 10, 20, 20);
1053 modalA.setModality(Qt::ApplicationModal);
1056 InputTestWindow modalB;
1057 modalB.setTransientParent(&parent);
1058 modalB.setGeometry(30, 10, 20, 20);
1059 modalB.setModality(Qt::ApplicationModal);
1062 QPointF local(5, 5);
1063 QWindowSystemInterface::handleMouseEvent(&modalA, local, local, Qt::LeftButton);
1064 QWindowSystemInterface::handleMouseEvent(&modalA, local, local, Qt::NoButton);
1065 QWindowSystemInterface::handleMouseEvent(&modalB, local, local, Qt::LeftButton);
1066 QWindowSystemInterface::handleMouseEvent(&modalB, local, local, Qt::NoButton);
1067 QCoreApplication::processEvents();
1069 // modal A should be blocked since it was shown first, but modal B should not be blocked
1070 QCOMPARE(modalB.mousePressedCount, 1);
1071 QCOMPARE(modalA.mousePressedCount, 0);
1074 QWindowSystemInterface::handleMouseEvent(&modalA, local, local, Qt::LeftButton);
1075 QWindowSystemInterface::handleMouseEvent(&modalA, local, local, Qt::NoButton);
1076 QCoreApplication::processEvents();
1078 // modal B has been hidden, modal A should be unblocked again
1079 QCOMPARE(modalA.mousePressedCount, 1);
1082 #include <tst_qwindow.moc>
1083 QTEST_MAIN(tst_QWindow)