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 <QtTest/QtTest>
30#include <QtTest/QSignalSpy>
31#include <QtQuick/private/qquickdrag_p.h>
32#include <QtQuick/private/qquickitem_p.h>
33#include <QtQuick/private/qquickmousearea_p.h>
34#include <QtQuick/private/qquickrectangle_p.h>
35#include <private/qquickflickable_p.h>
36#include <QtQuick/qquickview.h>
37#include <QtQml/qqmlcontext.h>
38#include <QtQml/qqmlengine.h>
39#include "../../shared/util.h"
40#include "../shared/viewtestutil.h"
41#include <QtGui/qstylehints.h>
42#include <QtGui/QCursor>
43#include <QtGui/QScreen>
44#include <qpa/qwindowsysteminterface.h>
45
46class CircleMask : public QObject
47{
48 Q_OBJECT
49 Q_PROPERTY(qreal radius READ radius WRITE setRadius NOTIFY radiusChanged)
50
51public:
52 virtual ~CircleMask() {}
53 qreal radius() const { return m_radius; }
54 void setRadius(qreal radius)
55 {
56 if (m_radius == radius)
57 return;
58 m_radius = radius;
59 emit radiusChanged();
60 }
61
62 Q_INVOKABLE bool contains(const QPointF &point) const
63 {
64 QPointF center(m_radius, m_radius);
65 QLineF line(center, point);
66 return line.length() <= m_radius;
67 }
68
69signals:
70 void radiusChanged();
71
72private:
73 qreal m_radius;
74};
75
76class EventSender : public QObject {
77 Q_OBJECT
78
79public:
80 Q_INVOKABLE void sendMouseClick(QObject* obj ,qreal x , qreal y) {
81 {
82 QMouseEvent event(QEvent::MouseButtonPress, QPointF(x , y), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
83 qApp->sendEvent(receiver: obj, event: &event);
84 }
85 {
86 QMouseEvent event(QEvent::MouseButtonRelease, QPointF(x , y), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
87 qApp->sendEvent(receiver: obj, event: &event);
88 }
89 }
90};
91
92class tst_QQuickMouseArea: public QQmlDataTest
93{
94 Q_OBJECT
95public:
96 tst_QQuickMouseArea()
97 : device(nullptr)
98 {
99 qmlRegisterType<CircleMask>(uri: "Test", versionMajor: 1, versionMinor: 0, qmlName: "CircleMask");
100 qmlRegisterType<EventSender>(uri: "Test", versionMajor: 1, versionMinor: 0, qmlName: "EventSender");
101 }
102
103private slots:
104 void initTestCase() override;
105 void dragProperties();
106 void resetDrag();
107 void dragging_data() { acceptedButton_data(); }
108 void dragging();
109 void selfDrag();
110 void dragSmoothed();
111 void dragThreshold_data();
112 void dragThreshold();
113 void invalidDrag_data() { rejectedButton_data(); }
114 void invalidDrag();
115 void cancelDragging();
116 void availableDistanceLessThanDragThreshold();
117 void setDragOnPressed();
118 void updateMouseAreaPosOnClick();
119 void updateMouseAreaPosOnResize();
120 void noOnClickedWithPressAndHold();
121 void onMousePressRejected();
122 void pressedCanceledOnWindowDeactivate_data();
123 void pressedCanceledOnWindowDeactivate();
124 void doubleClick_data() { acceptedButton_data(); }
125 void doubleClick();
126 void clickTwice_data() { acceptedButton_data(); }
127 void clickTwice();
128 void invalidClick_data() { rejectedButton_data(); }
129 void invalidClick();
130 void pressedOrdering();
131 void preventStealing();
132 void clickThrough();
133 void hoverPosition();
134 void hoverPropagation();
135 void hoverVisible();
136 void hoverAfterPress();
137 void subtreeHoverEnabled();
138 void disableAfterPress();
139 void onWheel();
140 void transformedMouseArea_data();
141 void transformedMouseArea();
142 void pressedMultipleButtons_data();
143 void pressedMultipleButtons();
144 void changeAxis();
145#if QT_CONFIG(cursor)
146 void cursorShape();
147#endif
148 void moveAndReleaseWithoutPress();
149 void nestedStopAtBounds();
150 void nestedStopAtBounds_data();
151 void nestedFlickableStopAtBounds();
152 void containsPress_data();
153 void containsPress();
154 void ignoreBySource();
155 void notPressedAfterStolenGrab();
156 void pressAndHold_data();
157 void pressAndHold();
158 void pressOneAndTapAnother_data();
159 void pressOneAndTapAnother();
160 void mask();
161 void nestedEventDelivery();
162 void settingHiddenInPressUngrabs();
163 void containsMouseAndVisibility();
164
165private:
166 int startDragDistance() const {
167 return QGuiApplication::styleHints()->startDragDistance();
168 }
169 void acceptedButton_data();
170 void rejectedButton_data();
171 QTouchDevice *device;
172};
173
174Q_DECLARE_METATYPE(Qt::MouseButton)
175Q_DECLARE_METATYPE(Qt::MouseButtons)
176
177void tst_QQuickMouseArea::initTestCase()
178{
179 QQmlDataTest::initTestCase();
180 if (!device) {
181 device = new QTouchDevice;
182 device->setType(QTouchDevice::TouchScreen);
183 QWindowSystemInterface::registerTouchDevice(device);
184 }
185}
186
187void tst_QQuickMouseArea::acceptedButton_data()
188{
189 QTest::addColumn<Qt::MouseButtons>(name: "acceptedButtons");
190 QTest::addColumn<Qt::MouseButton>(name: "button");
191
192 QTest::newRow(dataTag: "left") << Qt::MouseButtons(Qt::LeftButton) << Qt::LeftButton;
193 QTest::newRow(dataTag: "right") << Qt::MouseButtons(Qt::RightButton) << Qt::RightButton;
194 QTest::newRow(dataTag: "middle") << Qt::MouseButtons(Qt::MiddleButton) << Qt::MiddleButton;
195
196 QTest::newRow(dataTag: "left (left|right)") << Qt::MouseButtons(Qt::LeftButton | Qt::RightButton) << Qt::LeftButton;
197 QTest::newRow(dataTag: "right (right|middle)") << Qt::MouseButtons(Qt::RightButton | Qt::MiddleButton) << Qt::RightButton;
198 QTest::newRow(dataTag: "middle (left|middle)") << Qt::MouseButtons(Qt::LeftButton | Qt::MiddleButton) << Qt::MiddleButton;
199}
200
201void tst_QQuickMouseArea::rejectedButton_data()
202{
203 QTest::addColumn<Qt::MouseButtons>(name: "acceptedButtons");
204 QTest::addColumn<Qt::MouseButton>(name: "button");
205
206 QTest::newRow(dataTag: "middle (left|right)") << Qt::MouseButtons(Qt::LeftButton | Qt::RightButton) << Qt::MiddleButton;
207 QTest::newRow(dataTag: "left (right|middle)") << Qt::MouseButtons(Qt::RightButton | Qt::MiddleButton) << Qt::LeftButton;
208 QTest::newRow(dataTag: "right (left|middle)") << Qt::MouseButtons(Qt::LeftButton | Qt::MiddleButton) << Qt::RightButton;
209}
210
211void tst_QQuickMouseArea::dragProperties()
212{
213
214 QQuickView window;
215 QByteArray errorMessage;
216 QVERIFY2(QQuickTest::initView(window, testFileUrl("dragproperties.qml"), true, &errorMessage), errorMessage.constData());
217 window.show();
218 QVERIFY(QTest::qWaitForWindowExposed(&window));
219 QVERIFY(window.rootObject() != nullptr);
220
221 QQuickMouseArea *mouseRegion = window.rootObject()->findChild<QQuickMouseArea*>(aName: "mouseregion");
222 QQuickDrag *drag = mouseRegion->drag();
223 QVERIFY(mouseRegion != nullptr);
224 QVERIFY(drag != nullptr);
225
226 // target
227 QQuickItem *blackRect = window.rootObject()->findChild<QQuickItem*>(aName: "blackrect");
228 QVERIFY(blackRect != nullptr);
229 QCOMPARE(blackRect, drag->target());
230 QQuickItem *rootItem = qobject_cast<QQuickItem*>(object: window.rootObject());
231 QVERIFY(rootItem != nullptr);
232 QSignalSpy targetSpy(drag, SIGNAL(targetChanged()));
233 drag->setTarget(rootItem);
234 QCOMPARE(targetSpy.count(),1);
235 drag->setTarget(rootItem);
236 QCOMPARE(targetSpy.count(),1);
237
238 // axis
239 QCOMPARE(drag->axis(), QQuickDrag::XAndYAxis);
240 QSignalSpy axisSpy(drag, SIGNAL(axisChanged()));
241 drag->setAxis(QQuickDrag::XAxis);
242 QCOMPARE(drag->axis(), QQuickDrag::XAxis);
243 QCOMPARE(axisSpy.count(),1);
244 drag->setAxis(QQuickDrag::XAxis);
245 QCOMPARE(axisSpy.count(),1);
246
247 // minimum and maximum properties
248 QSignalSpy xminSpy(drag, SIGNAL(minimumXChanged()));
249 QSignalSpy xmaxSpy(drag, SIGNAL(maximumXChanged()));
250 QSignalSpy yminSpy(drag, SIGNAL(minimumYChanged()));
251 QSignalSpy ymaxSpy(drag, SIGNAL(maximumYChanged()));
252
253 QCOMPARE(drag->xmin(), 0.0);
254 QCOMPARE(drag->xmax(), rootItem->width()-blackRect->width());
255 QCOMPARE(drag->ymin(), 0.0);
256 QCOMPARE(drag->ymax(), rootItem->height()-blackRect->height());
257
258 drag->setXmin(10);
259 drag->setXmax(10);
260 drag->setYmin(10);
261 drag->setYmax(10);
262
263 QCOMPARE(drag->xmin(), 10.0);
264 QCOMPARE(drag->xmax(), 10.0);
265 QCOMPARE(drag->ymin(), 10.0);
266 QCOMPARE(drag->ymax(), 10.0);
267
268 QCOMPARE(xminSpy.count(),1);
269 QCOMPARE(xmaxSpy.count(),1);
270 QCOMPARE(yminSpy.count(),1);
271 QCOMPARE(ymaxSpy.count(),1);
272
273 drag->setXmin(10);
274 drag->setXmax(10);
275 drag->setYmin(10);
276 drag->setYmax(10);
277
278 QCOMPARE(xminSpy.count(),1);
279 QCOMPARE(xmaxSpy.count(),1);
280 QCOMPARE(yminSpy.count(),1);
281 QCOMPARE(ymaxSpy.count(),1);
282
283 // filterChildren
284 QSignalSpy filterChildrenSpy(drag, SIGNAL(filterChildrenChanged()));
285
286 drag->setFilterChildren(true);
287
288 QVERIFY(drag->filterChildren());
289 QCOMPARE(filterChildrenSpy.count(), 1);
290
291 drag->setFilterChildren(true);
292 QCOMPARE(filterChildrenSpy.count(), 1);
293
294 // threshold
295 QCOMPARE(int(drag->threshold()), qApp->styleHints()->startDragDistance());
296 QSignalSpy thresholdSpy(drag, SIGNAL(thresholdChanged()));
297 drag->setThreshold(0.0);
298 QCOMPARE(drag->threshold(), 0.0);
299 QCOMPARE(thresholdSpy.count(), 1);
300 drag->setThreshold(99);
301 QCOMPARE(thresholdSpy.count(), 2);
302 drag->setThreshold(99);
303 QCOMPARE(thresholdSpy.count(), 2);
304 drag->resetThreshold();
305 QCOMPARE(int(drag->threshold()), qApp->styleHints()->startDragDistance());
306 QCOMPARE(thresholdSpy.count(), 3);
307}
308
309void tst_QQuickMouseArea::resetDrag()
310{
311 QQuickView window;
312 QByteArray errorMessage;
313 window.setInitialProperties({{"haveTarget", true}});
314 QVERIFY2(QQuickTest::initView(window, testFileUrl("dragreset.qml"), true, &errorMessage), errorMessage.constData());
315 window.show();
316 QVERIFY(QTest::qWaitForWindowExposed(&window));
317 QVERIFY(window.rootObject() != nullptr);
318
319 QQuickMouseArea *mouseRegion = window.rootObject()->findChild<QQuickMouseArea*>(aName: "mouseregion");
320 QQuickDrag *drag = mouseRegion->drag();
321 QVERIFY(mouseRegion != nullptr);
322 QVERIFY(drag != nullptr);
323
324 // target
325 QQuickItem *blackRect = window.rootObject()->findChild<QQuickItem*>(aName: "blackrect");
326 QVERIFY(blackRect != nullptr);
327 QCOMPARE(blackRect, drag->target());
328 QQuickItem *rootItem = qobject_cast<QQuickItem*>(object: window.rootObject());
329 QVERIFY(rootItem != nullptr);
330 QSignalSpy targetSpy(drag, SIGNAL(targetChanged()));
331 QVERIFY(drag->target() != nullptr);
332 auto root = window.rootObject();
333 QQmlProperty haveTarget {root, "haveTarget"};
334 haveTarget.write(false);
335 QCOMPARE(targetSpy.count(),1);
336 QVERIFY(!drag->target());
337}
338
339void tst_QQuickMouseArea::dragging()
340{
341 QFETCH(Qt::MouseButtons, acceptedButtons);
342 QFETCH(Qt::MouseButton, button);
343
344 QQuickView window;
345 QByteArray errorMessage;
346 QVERIFY2(QQuickTest::initView(window, testFileUrl("dragging.qml"), true, &errorMessage), errorMessage.constData());
347
348 window.show();
349 QVERIFY(QTest::qWaitForWindowExposed(&window));
350 QVERIFY(window.rootObject() != nullptr);
351
352 QQuickMouseArea *mouseRegion = window.rootObject()->findChild<QQuickMouseArea*>(aName: "mouseregion");
353 QQuickDrag *drag = mouseRegion->drag();
354 QVERIFY(mouseRegion != nullptr);
355 QVERIFY(drag != nullptr);
356
357 mouseRegion->setAcceptedButtons(acceptedButtons);
358
359 // target
360 QQuickItem *blackRect = window.rootObject()->findChild<QQuickItem*>(aName: "blackrect");
361 QVERIFY(blackRect != nullptr);
362 QCOMPARE(blackRect, drag->target());
363
364 QVERIFY(!drag->active());
365
366 QPoint p = QPoint(100,100);
367 QTest::mousePress(window: &window, button, stateKey: Qt::NoModifier, pos: p);
368
369 QVERIFY(!drag->active());
370 QCOMPARE(blackRect->x(), 50.0);
371 QCOMPARE(blackRect->y(), 50.0);
372
373 // First move event triggers drag, second is acted upon.
374 // This is due to possibility of higher stacked area taking precedence.
375 // The item is moved relative to the position of the mouse when the drag
376 // was triggered, this prevents a sudden change in position when the drag
377 // threshold is exceeded.
378
379 int dragThreshold = QGuiApplication::styleHints()->startDragDistance();
380
381 // move the minimum distance to activate drag
382 p += QPoint(dragThreshold + 1, dragThreshold + 1);
383 QTest::mouseMove(window: &window, pos: p);
384 QVERIFY(!drag->active());
385
386 // from here on move the item
387 p += QPoint(1, 1);
388 QTest::mouseMove(window: &window, pos: p);
389 QTRY_VERIFY(drag->active());
390 // on macOS the cursor movement is going through a native event which
391 // means that it can actually take some time to show
392 QTRY_COMPARE(blackRect->x(), 50.0 + 1);
393 QCOMPARE(blackRect->y(), 50.0 + 1);
394
395 p += QPoint(10, 10);
396 QTest::mouseMove(window: &window, pos: p);
397 QTRY_VERIFY(drag->active());
398 QTRY_COMPARE(blackRect->x(), 61.0);
399 QCOMPARE(blackRect->y(), 61.0);
400
401 qreal relativeX = mouseRegion->mouseX();
402 qreal relativeY = mouseRegion->mouseY();
403 for (int i = 0; i < 20; i++) {
404 p += QPoint(1, 1);
405 QTest::mouseMove(window: &window, pos: p);
406 QTRY_COMPARE(mouseRegion->mouseX(), relativeX);
407 QCOMPARE(mouseRegion->mouseY(), relativeY);
408 }
409 QVERIFY(drag->active());
410
411 QTest::mouseRelease(window: &window, button, stateKey: Qt::NoModifier, pos: p);
412 QTRY_VERIFY(!drag->active());
413 QTRY_COMPARE(blackRect->x(), 81.0);
414 QCOMPARE(blackRect->y(), 81.0);
415}
416
417void tst_QQuickMouseArea::selfDrag() // QTBUG-85111
418{
419 QQuickView window;
420 QByteArray errorMessage;
421 QVERIFY2(QQuickTest::initView(window, testFileUrl("selfDrag.qml"), true, &errorMessage), errorMessage.constData());
422
423 window.show();
424 QVERIFY(QTest::qWaitForWindowExposed(&window));
425 QVERIFY(window.rootObject() != nullptr);
426
427 QQuickMouseArea *ma = window.rootObject()->findChild<QQuickMouseArea*>(aName: "ma");
428 QVERIFY(ma != nullptr);
429 QQuickDrag *drag = ma->drag();
430 QVERIFY(drag != nullptr);
431 QCOMPARE(ma, drag->target());
432
433 QQuickItem *fillRect = window.rootObject()->findChild<QQuickItem*>(aName: "fill");
434 QVERIFY(fillRect != nullptr);
435
436 QVERIFY(!drag->active());
437
438 QPoint p = QPoint(100,100);
439 QTest::mousePress(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p);
440
441 QVERIFY(!drag->active());
442 QCOMPARE(ma->x(), 0);
443 QCOMPARE(ma->y(), 0);
444
445 int dragThreshold = QGuiApplication::styleHints()->startDragDistance();
446
447 // First move event triggers drag, second is acted upon.
448 // move the minimum distance to activate drag
449 p += QPoint(dragThreshold + 1, dragThreshold + 1);
450 QTest::mouseMove(window: &window, pos: p);
451 QVERIFY(!drag->active());
452
453 // from here on move the item
454 p += QPoint(1, 1);
455 QTest::mouseMove(window: &window, pos: p);
456 QTRY_VERIFY(drag->active());
457 QTRY_COMPARE(ma->x(), 1);
458 QCOMPARE(ma->y(), 1);
459
460 p += QPoint(10, 10);
461 QTest::mouseMove(window: &window, pos: p);
462 QTRY_VERIFY(drag->active());
463 QTRY_COMPARE(ma->x(), 11);
464 QCOMPARE(ma->y(), 11);
465
466 qreal relativeX = ma->mouseX();
467 qreal relativeY = ma->mouseY();
468 for (int i = 0; i < 20; i++) {
469 p += QPoint(1, 1);
470 QTest::mouseMove(window: &window, pos: p);
471 QVERIFY(drag->active());
472 QTRY_COMPARE(ma->mouseX(), relativeX);
473 QCOMPARE(ma->mouseY(), relativeY);
474 }
475
476 QTest::mouseRelease(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p);
477 QTRY_VERIFY(!drag->active());
478 QTRY_COMPARE(ma->x(), 31);
479 QCOMPARE(ma->y(), 31);
480}
481
482void tst_QQuickMouseArea::dragSmoothed()
483{
484 QQuickView window;
485 QByteArray errorMessage;
486 QVERIFY2(QQuickTest::initView(window, testFileUrl("dragging.qml"), true, &errorMessage), errorMessage.constData());
487
488 window.show();
489 QVERIFY(QTest::qWaitForWindowExposed(&window));
490 QVERIFY(window.rootObject() != nullptr);
491
492 QQuickMouseArea *mouseRegion = window.rootObject()->findChild<QQuickMouseArea*>(aName: "mouseregion");
493 QQuickDrag *drag = mouseRegion->drag();
494 drag->setThreshold(5);
495
496 mouseRegion->setAcceptedButtons(Qt::LeftButton);
497 QQuickItem *blackRect = window.rootObject()->findChild<QQuickItem*>(aName: "blackrect");
498 QVERIFY(blackRect != nullptr);
499 QCOMPARE(blackRect, drag->target());
500 QVERIFY(!drag->active());
501 QTest::mousePress(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(100,100));
502 QVERIFY(!drag->active());
503 QTest::mouseMove(window: &window, pos: QPoint(100, 102), delay: 50);
504 QTest::mouseMove(window: &window, pos: QPoint(100, 106), delay: 50);
505 QTest::mouseMove(window: &window, pos: QPoint(100, 122), delay: 50);
506 QTRY_COMPARE(blackRect->x(), 50.0);
507 QTRY_COMPARE(blackRect->y(), 66.0);
508 QTest::mouseRelease(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(100,122));
509
510 // reset rect position
511 blackRect->setX(50.0);
512 blackRect->setY(50.0);
513
514 // now try with smoothed disabled
515 drag->setSmoothed(false);
516 QVERIFY(!drag->active());
517 QTest::mousePress(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(100,100));
518 QVERIFY(!drag->active());
519 QTest::mouseMove(window: &window, pos: QPoint(100, 102), delay: 50);
520 QTest::mouseMove(window: &window, pos: QPoint(100, 106), delay: 50);
521 QTest::mouseMove(window: &window, pos: QPoint(100, 122), delay: 50);
522 QTRY_COMPARE(blackRect->x(), 50.0);
523 QTRY_COMPARE(blackRect->y(), 72.0);
524 QTest::mouseRelease(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(100, 122));
525}
526
527void tst_QQuickMouseArea::dragThreshold_data()
528{
529 QTest::addColumn<bool>(name: "preventStealing");
530 QTest::newRow(dataTag: "without preventStealing") << false;
531 QTest::newRow(dataTag: "with preventStealing") << true;
532}
533
534void tst_QQuickMouseArea::dragThreshold()
535{
536 QFETCH(bool, preventStealing);
537
538 QQuickView window;
539 QByteArray errorMessage;
540 QVERIFY2(QQuickTest::initView(window, testFileUrl("dragging.qml"), true, &errorMessage), errorMessage.constData());
541
542 window.show();
543 QVERIFY(QTest::qWaitForWindowExposed(&window));
544 QVERIFY(window.rootObject() != nullptr);
545
546 QQuickMouseArea *mouseRegion = window.rootObject()->findChild<QQuickMouseArea*>(aName: "mouseregion");
547 mouseRegion->setPreventStealing(preventStealing);
548 QQuickDrag *drag = mouseRegion->drag();
549
550 drag->setThreshold(5);
551
552 mouseRegion->setAcceptedButtons(Qt::LeftButton);
553 QQuickItem *blackRect = window.rootObject()->findChild<QQuickItem*>(aName: "blackrect");
554 QVERIFY(blackRect != nullptr);
555 QCOMPARE(blackRect, drag->target());
556 QVERIFY(!drag->active());
557 QTest::mousePress(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(100,100));
558 QVERIFY(!drag->active());
559 QCOMPARE(blackRect->x(), 50.0);
560 QCOMPARE(blackRect->y(), 50.0);
561 QTest::mouseMove(window: &window, pos: QPoint(100, 102), delay: 50);
562 QVERIFY(!drag->active());
563 QTest::mouseMove(window: &window, pos: QPoint(100, 100), delay: 50);
564 QVERIFY(!drag->active());
565 QTest::mouseMove(window: &window, pos: QPoint(100, 104), delay: 50);
566 QTest::mouseMove(window: &window, pos: QPoint(100, 105), delay: 50);
567 QVERIFY(!drag->active());
568 QTest::mouseMove(window: &window, pos: QPoint(100, 106), delay: 50);
569 QTest::mouseMove(window: &window, pos: QPoint(100, 108), delay: 50);
570 QVERIFY(drag->active());
571 QTest::mouseMove(window: &window, pos: QPoint(100, 116), delay: 50);
572 QTest::mouseMove(window: &window, pos: QPoint(100, 122), delay: 50);
573 QTRY_VERIFY(drag->active());
574 QTRY_COMPARE(blackRect->x(), 50.0);
575 QTRY_COMPARE(blackRect->y(), 66.0);
576 QTest::mouseRelease(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(122,122));
577 QTRY_VERIFY(!drag->active());
578
579 // Immediate drag threshold
580 drag->setThreshold(0);
581 QTest::mousePress(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(100,100));
582 QTest::mouseMove(window: &window, pos: QPoint(100, 122), delay: 50);
583 QVERIFY(!drag->active());
584 QTest::mouseMove(window: &window, pos: QPoint(100, 123), delay: 50);
585 QVERIFY(drag->active());
586 QTest::mouseMove(window: &window, pos: QPoint(100, 124), delay: 50);
587 QTest::mouseRelease(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(100, 124));
588 QTRY_VERIFY(!drag->active());
589 drag->resetThreshold();
590}
591void tst_QQuickMouseArea::invalidDrag()
592{
593 QFETCH(Qt::MouseButtons, acceptedButtons);
594 QFETCH(Qt::MouseButton, button);
595
596 QQuickView window;
597 QByteArray errorMessage;
598 QVERIFY2(QQuickTest::initView(window, testFileUrl("dragging.qml"), true, &errorMessage), errorMessage.constData());
599 window.show();
600 QVERIFY(QTest::qWaitForWindowExposed(&window));
601 QVERIFY(window.rootObject() != nullptr);
602
603 QQuickMouseArea *mouseRegion = window.rootObject()->findChild<QQuickMouseArea*>(aName: "mouseregion");
604 QQuickDrag *drag = mouseRegion->drag();
605 QVERIFY(mouseRegion != nullptr);
606 QVERIFY(drag != nullptr);
607
608 mouseRegion->setAcceptedButtons(acceptedButtons);
609
610 // target
611 QQuickItem *blackRect = window.rootObject()->findChild<QQuickItem*>(aName: "blackrect");
612 QVERIFY(blackRect != nullptr);
613 QCOMPARE(blackRect, drag->target());
614
615 QVERIFY(!drag->active());
616
617 QTest::mousePress(window: &window, button, stateKey: Qt::NoModifier, pos: QPoint(100,100));
618
619 QVERIFY(!drag->active());
620 QCOMPARE(blackRect->x(), 50.0);
621 QCOMPARE(blackRect->y(), 50.0);
622
623 // First move event triggers drag, second is acted upon.
624 // This is due to possibility of higher stacked area taking precedence.
625
626 QTest::mouseMove(window: &window, pos: QPoint(111,111));
627 QTest::qWait(ms: 50);
628 QTest::mouseMove(window: &window, pos: QPoint(122,122));
629 QTest::qWait(ms: 50);
630
631 QVERIFY(!drag->active());
632 QCOMPARE(blackRect->x(), 50.0);
633 QCOMPARE(blackRect->y(), 50.0);
634
635 QTest::mouseRelease(window: &window, button, stateKey: Qt::NoModifier, pos: QPoint(122,122));
636 QTest::qWait(ms: 50);
637
638 QVERIFY(!drag->active());
639 QCOMPARE(blackRect->x(), 50.0);
640 QCOMPARE(blackRect->y(), 50.0);
641}
642
643void tst_QQuickMouseArea::cancelDragging()
644{
645 QQuickView window;
646 QByteArray errorMessage;
647 QVERIFY2(QQuickTest::initView(window, testFileUrl("dragging.qml"), true, &errorMessage), errorMessage.constData());
648
649 window.show();
650 QVERIFY(QTest::qWaitForWindowExposed(&window));
651 QVERIFY(window.rootObject() != nullptr);
652
653 QQuickMouseArea *mouseRegion = window.rootObject()->findChild<QQuickMouseArea*>(aName: "mouseregion");
654 QQuickDrag *drag = mouseRegion->drag();
655 QVERIFY(mouseRegion != nullptr);
656 QVERIFY(drag != nullptr);
657
658 mouseRegion->setAcceptedButtons(Qt::LeftButton);
659
660 // target
661 QQuickItem *blackRect = window.rootObject()->findChild<QQuickItem*>(aName: "blackrect");
662 QVERIFY(blackRect != nullptr);
663 QCOMPARE(blackRect, drag->target());
664
665 QVERIFY(!drag->active());
666
667 QPoint p = QPoint(100,100);
668 QTest::mousePress(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p);
669
670 QVERIFY(!drag->active());
671 QCOMPARE(blackRect->x(), 50.0);
672 QCOMPARE(blackRect->y(), 50.0);
673
674 p += QPoint(startDragDistance() + 1, 0);
675 QTest::mouseMove(window: &window, pos: p);
676
677 p += QPoint(11, 11);
678 QTest::mouseMove(window: &window, pos: p);
679
680 QTRY_VERIFY(drag->active());
681 QTRY_COMPARE(blackRect->x(), 61.0);
682 QCOMPARE(blackRect->y(), 61.0);
683
684 mouseRegion->QQuickItem::ungrabMouse();
685 QTRY_VERIFY(!drag->active());
686 QCOMPARE(blackRect->x(), 61.0);
687 QCOMPARE(blackRect->y(), 61.0);
688
689 QTest::mouseMove(window: &window, pos: QPoint(132,132), delay: 50);
690 QTRY_VERIFY(!drag->active());
691 QCOMPARE(blackRect->x(), 61.0);
692 QCOMPARE(blackRect->y(), 61.0);
693
694 QTest::mouseRelease(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(122,122));
695}
696
697// QTBUG-58347
698void tst_QQuickMouseArea::availableDistanceLessThanDragThreshold()
699{
700 QQuickView view;
701 QByteArray errorMessage;
702 QVERIFY2(QQuickTest::initView(view, testFileUrl("availableDistanceLessThanDragThreshold.qml"), true, &errorMessage),
703 errorMessage.constData());
704 view.show();
705 view.requestActivate();
706 QVERIFY(QTest::qWaitForWindowExposed(&view));
707 QVERIFY(view.rootObject());
708
709 QQuickMouseArea *mouseArea = view.rootObject()->findChild<QQuickMouseArea*>(aName: "mouseArea");
710 QVERIFY(mouseArea);
711
712 QPoint position(100, 100);
713 QTest::mousePress(window: &view, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: position);
714 QTest::qWait(ms: 10);
715 position.setX(301);
716 QTest::mouseMove(window: &view, pos: position);
717 position.setX(501);
718 QTest::mouseMove(window: &view, pos: position);
719 QVERIFY(mouseArea->drag()->active());
720 QTest::mouseRelease(window: &view, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: position);
721
722 QVERIFY(!mouseArea->drag()->active());
723 QCOMPARE(mouseArea->x(), 200.0);
724}
725
726void tst_QQuickMouseArea::setDragOnPressed()
727{
728 QQuickView window;
729 QByteArray errorMessage;
730 QVERIFY2(QQuickTest::initView(window, testFileUrl("setDragOnPressed.qml"), true, &errorMessage), errorMessage.constData());
731 window.show();
732 QVERIFY(QTest::qWaitForWindowExposed(&window));
733 QVERIFY(window.rootObject() != nullptr);
734
735 QQuickMouseArea *mouseArea = qobject_cast<QQuickMouseArea *>(object: window.rootObject());
736 QVERIFY(mouseArea);
737
738 // target
739 QQuickItem *target = mouseArea->findChild<QQuickItem*>(aName: "target");
740 QVERIFY(target);
741
742 QPoint p = QPoint(100, 100);
743 QTest::mousePress(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p);
744
745 QQuickDrag *drag = mouseArea->drag();
746 QVERIFY(drag);
747 QVERIFY(!drag->active());
748
749 QCOMPARE(target->x(), 50.0);
750 QCOMPARE(target->y(), 50.0);
751
752 // First move event triggers drag, second is acted upon.
753 // This is due to possibility of higher stacked area taking precedence.
754
755 p += QPoint(startDragDistance() + 1, 0);
756 QTest::mouseMove(window: &window, pos: p);
757
758 p += QPoint(11, 0);
759 QTest::mouseMove(window: &window, pos: p);
760 QTRY_VERIFY(drag->active());
761 QTRY_COMPARE(target->x(), 61.0);
762 QCOMPARE(target->y(), 50.0);
763
764 QTest::mouseRelease(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p);
765 QTRY_VERIFY(!drag->active());
766 QCOMPARE(target->x(), 61.0);
767 QCOMPARE(target->y(), 50.0);
768}
769
770void tst_QQuickMouseArea::updateMouseAreaPosOnClick()
771{
772 QQuickView window;
773 QByteArray errorMessage;
774 QVERIFY2(QQuickTest::initView(window, testFileUrl("updateMousePosOnClick.qml"), true, &errorMessage), errorMessage.constData());
775 window.show();
776 QVERIFY(QTest::qWaitForWindowExposed(&window));
777 QVERIFY(window.rootObject() != nullptr);
778
779 QQuickMouseArea *mouseRegion = window.rootObject()->findChild<QQuickMouseArea*>(aName: "mouseregion");
780 QVERIFY(mouseRegion != nullptr);
781
782 QQuickRectangle *rect = window.rootObject()->findChild<QQuickRectangle*>(aName: "ball");
783 QVERIFY(rect != nullptr);
784
785 QCOMPARE(mouseRegion->mouseX(), rect->x());
786 QCOMPARE(mouseRegion->mouseY(), rect->y());
787
788 QMouseEvent event(QEvent::MouseButtonPress, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, {});
789 QGuiApplication::sendEvent(receiver: &window, event: &event);
790
791 QCOMPARE(mouseRegion->mouseX(), 100.0);
792 QCOMPARE(mouseRegion->mouseY(), 100.0);
793
794 QCOMPARE(mouseRegion->mouseX(), rect->x());
795 QCOMPARE(mouseRegion->mouseY(), rect->y());
796}
797
798void tst_QQuickMouseArea::updateMouseAreaPosOnResize()
799{
800 QQuickView window;
801 QByteArray errorMessage;
802 QVERIFY2(QQuickTest::initView(window, testFileUrl("updateMousePosOnResize.qml"), true, &errorMessage), errorMessage.constData());
803 window.show();
804 QVERIFY(QTest::qWaitForWindowExposed(&window));
805 QVERIFY(window.rootObject() != nullptr);
806
807 QQuickMouseArea *mouseRegion = window.rootObject()->findChild<QQuickMouseArea*>(aName: "mouseregion");
808 QVERIFY(mouseRegion != nullptr);
809
810 QQuickRectangle *rect = window.rootObject()->findChild<QQuickRectangle*>(aName: "brother");
811 QVERIFY(rect != nullptr);
812
813 QCOMPARE(mouseRegion->mouseX(), 0.0);
814 QCOMPARE(mouseRegion->mouseY(), 0.0);
815
816 QMouseEvent event(QEvent::MouseButtonPress, rect->position().toPoint(), Qt::LeftButton, Qt::LeftButton, {});
817 QGuiApplication::sendEvent(receiver: &window, event: &event);
818
819 QVERIFY(!mouseRegion->property("emitPositionChanged").toBool());
820 QVERIFY(mouseRegion->property("mouseMatchesPos").toBool());
821
822 QCOMPARE(mouseRegion->property("x1").toReal(), 0.0);
823 QCOMPARE(mouseRegion->property("y1").toReal(), 0.0);
824
825 QCOMPARE(mouseRegion->property("x2").toReal(), rect->x());
826 QCOMPARE(mouseRegion->property("y2").toReal(), rect->y());
827
828 QCOMPARE(mouseRegion->mouseX(), rect->x());
829 QCOMPARE(mouseRegion->mouseY(), rect->y());
830}
831
832void tst_QQuickMouseArea::noOnClickedWithPressAndHold()
833{
834 {
835 // We handle onPressAndHold, therefore no onClicked
836 QQuickView window;
837 QByteArray errorMessage;
838 QVERIFY2(QQuickTest::initView(window, testFileUrl("clickandhold.qml"), true, &errorMessage), errorMessage.constData());
839 window.show();
840 QVERIFY(QTest::qWaitForWindowExposed(&window));
841 QVERIFY(window.rootObject() != nullptr);
842 QQuickMouseArea *mouseArea = qobject_cast<QQuickMouseArea*>(object: window.rootObject()->children().first());
843 QVERIFY(mouseArea);
844
845 QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, {});
846 QGuiApplication::sendEvent(receiver: &window, event: &pressEvent);
847
848 QCOMPARE(mouseArea->pressedButtons(), Qt::LeftButton);
849 QVERIFY(!window.rootObject()->property("clicked").toBool());
850 QVERIFY(!window.rootObject()->property("held").toBool());
851
852 // timeout is 800 (in qquickmousearea.cpp)
853 QTest::qWait(ms: 1000);
854 QCoreApplication::processEvents();
855
856 QVERIFY(!window.rootObject()->property("clicked").toBool());
857 QVERIFY(window.rootObject()->property("held").toBool());
858
859 QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, {});
860 QGuiApplication::sendEvent(receiver: &window, event: &releaseEvent);
861
862 QTRY_VERIFY(window.rootObject()->property("held").toBool());
863 QVERIFY(!window.rootObject()->property("clicked").toBool());
864 }
865
866 {
867 // We do not handle onPressAndHold, therefore we get onClicked
868 QQuickView window;
869 QByteArray errorMessage;
870 QVERIFY2(QQuickTest::initView(window, testFileUrl("noclickandhold.qml"), true, &errorMessage), errorMessage.constData());
871 window.show();
872 QVERIFY(QTest::qWaitForWindowExposed(&window));
873 QVERIFY(window.rootObject() != nullptr);
874
875 QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, {});
876 QGuiApplication::sendEvent(receiver: &window, event: &pressEvent);
877
878 QVERIFY(!window.rootObject()->property("clicked").toBool());
879
880 QTest::qWait(ms: 1000);
881
882 QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, {});
883 QGuiApplication::sendEvent(receiver: &window, event: &releaseEvent);
884
885 QVERIFY(window.rootObject()->property("clicked").toBool());
886 }
887}
888
889void tst_QQuickMouseArea::onMousePressRejected()
890{
891 QQuickView window;
892 QByteArray errorMessage;
893 QVERIFY2(QQuickTest::initView(window, testFileUrl("rejectEvent.qml"), true, &errorMessage), errorMessage.constData());
894 window.show();
895 QVERIFY(QTest::qWaitForWindowExposed(&window));
896 QVERIFY(window.rootObject() != nullptr);
897 QVERIFY(window.rootObject()->property("enabled").toBool());
898
899 QVERIFY(!window.rootObject()->property("mr1_pressed").toBool());
900 QVERIFY(!window.rootObject()->property("mr1_released").toBool());
901 QVERIFY(!window.rootObject()->property("mr1_canceled").toBool());
902 QVERIFY(!window.rootObject()->property("mr2_pressed").toBool());
903 QVERIFY(!window.rootObject()->property("mr2_released").toBool());
904 QVERIFY(!window.rootObject()->property("mr2_canceled").toBool());
905
906 QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, {});
907 QGuiApplication::sendEvent(receiver: &window, event: &pressEvent);
908
909 QVERIFY(window.rootObject()->property("mr1_pressed").toBool());
910 QVERIFY(!window.rootObject()->property("mr1_released").toBool());
911 QVERIFY(!window.rootObject()->property("mr1_canceled").toBool());
912 QVERIFY(window.rootObject()->property("mr2_pressed").toBool());
913 QVERIFY(!window.rootObject()->property("mr2_released").toBool());
914 QVERIFY(!window.rootObject()->property("mr2_canceled").toBool());
915
916 QTest::qWait(ms: 200);
917
918 QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, {});
919 QGuiApplication::sendEvent(receiver: &window, event: &releaseEvent);
920
921 QVERIFY(window.rootObject()->property("mr1_released").toBool());
922 QVERIFY(!window.rootObject()->property("mr1_canceled").toBool());
923 QVERIFY(!window.rootObject()->property("mr2_released").toBool());
924}
925
926void tst_QQuickMouseArea::pressedCanceledOnWindowDeactivate_data()
927{
928 QTest::addColumn<bool>(name: "doubleClick");
929 QTest::newRow(dataTag: "simple click") << false;
930 QTest::newRow(dataTag: "double click") << true;
931}
932
933
934void tst_QQuickMouseArea::pressedCanceledOnWindowDeactivate()
935{
936 QFETCH(bool, doubleClick);
937
938 QQuickView window;
939 QByteArray errorMessage;
940 QVERIFY2(QQuickTest::initView(window, testFileUrl("pressedCanceled.qml"), true, &errorMessage), errorMessage.constData());
941 window.show();
942 QVERIFY(QTest::qWaitForWindowExposed(&window));
943 QVERIFY(window.rootObject() != nullptr);
944 QVERIFY(!window.rootObject()->property("pressed").toBool());
945 QVERIFY(!window.rootObject()->property("canceled").toBool());
946
947 int expectedRelease = 0;
948 int expectedClicks = 0;
949 QCOMPARE(window.rootObject()->property("released").toInt(), expectedRelease);
950 QCOMPARE(window.rootObject()->property("clicked").toInt(), expectedClicks);
951
952
953 QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, {});
954 QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, {});
955
956 QGuiApplication::sendEvent(receiver: &window, event: &pressEvent);
957
958 QTRY_VERIFY(window.rootObject()->property("pressed").toBool());
959 QVERIFY(!window.rootObject()->property("canceled").toBool());
960 QCOMPARE(window.rootObject()->property("released").toInt(), expectedRelease);
961 QCOMPARE(window.rootObject()->property("clicked").toInt(), expectedClicks);
962
963 if (doubleClick) {
964 QGuiApplication::sendEvent(receiver: &window, event: &releaseEvent);
965 QTRY_VERIFY(!window.rootObject()->property("pressed").toBool());
966 QVERIFY(!window.rootObject()->property("canceled").toBool());
967 QCOMPARE(window.rootObject()->property("released").toInt(), ++expectedRelease);
968 QCOMPARE(window.rootObject()->property("clicked").toInt(), ++expectedClicks);
969
970 QGuiApplication::sendEvent(receiver: &window, event: &pressEvent);
971 QMouseEvent pressEvent2(QEvent::MouseButtonDblClick, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, {});
972 QGuiApplication::sendEvent(receiver: &window, event: &pressEvent2);
973
974 QTRY_VERIFY(window.rootObject()->property("pressed").toBool());
975 QVERIFY(!window.rootObject()->property("canceled").toBool());
976 QCOMPARE(window.rootObject()->property("released").toInt(), expectedRelease);
977 QCOMPARE(window.rootObject()->property("clicked").toInt(), expectedClicks);
978 QCOMPARE(window.rootObject()->property("doubleClicked").toInt(), 1);
979 }
980
981
982 QWindow *secondWindow = qvariant_cast<QWindow*>(v: window.rootObject()->property(name: "secondWindow"));
983 secondWindow->setProperty(name: "visible", value: true);
984 QVERIFY(QTest::qWaitForWindowExposed(secondWindow));
985
986 QTRY_VERIFY(!window.rootObject()->property("pressed").toBool());
987 QVERIFY(window.rootObject()->property("canceled").toBool());
988 QCOMPARE(window.rootObject()->property("released").toInt(), expectedRelease);
989 QCOMPARE(window.rootObject()->property("clicked").toInt(), expectedClicks);
990
991 //press again
992 QGuiApplication::sendEvent(receiver: &window, event: &pressEvent);
993 QTRY_VERIFY(window.rootObject()->property("pressed").toBool());
994 QVERIFY(!window.rootObject()->property("canceled").toBool());
995 QCOMPARE(window.rootObject()->property("released").toInt(), expectedRelease);
996 QCOMPARE(window.rootObject()->property("clicked").toInt(), expectedClicks);
997
998 //release
999 QGuiApplication::sendEvent(receiver: &window, event: &releaseEvent);
1000 QTRY_VERIFY(!window.rootObject()->property("pressed").toBool());
1001 QVERIFY(!window.rootObject()->property("canceled").toBool());
1002 QCOMPARE(window.rootObject()->property("released").toInt(), ++expectedRelease);
1003 QCOMPARE(window.rootObject()->property("clicked").toInt(), ++expectedClicks);
1004}
1005
1006void tst_QQuickMouseArea::doubleClick()
1007{
1008 QFETCH(Qt::MouseButtons, acceptedButtons);
1009 QFETCH(Qt::MouseButton, button);
1010
1011 QQuickView window;
1012 QByteArray errorMessage;
1013 QVERIFY2(QQuickTest::initView(window, testFileUrl("doubleclick.qml"), true, &errorMessage), errorMessage.constData());
1014 window.show();
1015 QVERIFY(QTest::qWaitForWindowExposed(&window));
1016 QVERIFY(window.rootObject() != nullptr);
1017
1018 QQuickMouseArea *mouseArea = window.rootObject()->findChild<QQuickMouseArea *>(aName: "mousearea");
1019 QVERIFY(mouseArea);
1020 mouseArea->setAcceptedButtons(acceptedButtons);
1021
1022 // The sequence for a double click is:
1023 // press, release, (click), press, double click, release
1024 QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), button, button, {});
1025 QGuiApplication::sendEvent(receiver: &window, event: &pressEvent);
1026
1027 QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), button, button, {});
1028 QGuiApplication::sendEvent(receiver: &window, event: &releaseEvent);
1029
1030 QCOMPARE(window.rootObject()->property("released").toInt(), 1);
1031
1032 QGuiApplication::sendEvent(receiver: &window, event: &pressEvent);
1033 pressEvent = QMouseEvent(QEvent::MouseButtonDblClick, QPoint(100, 100), button, button, {});
1034 QGuiApplication::sendEvent(receiver: &window, event: &pressEvent);
1035 QGuiApplication::sendEvent(receiver: &window, event: &releaseEvent);
1036
1037 QCOMPARE(window.rootObject()->property("clicked").toInt(), 1);
1038 QCOMPARE(window.rootObject()->property("doubleClicked").toInt(), 1);
1039 QCOMPARE(window.rootObject()->property("released").toInt(), 2);
1040}
1041
1042// QTBUG-14832
1043void tst_QQuickMouseArea::clickTwice()
1044{
1045 QFETCH(Qt::MouseButtons, acceptedButtons);
1046 QFETCH(Qt::MouseButton, button);
1047
1048 QQuickView window;
1049 QByteArray errorMessage;
1050 QVERIFY2(QQuickTest::initView(window, testFileUrl("clicktwice.qml"), true, &errorMessage), errorMessage.constData());
1051 window.show();
1052 QVERIFY(QTest::qWaitForWindowExposed(&window));
1053 QVERIFY(window.rootObject() != nullptr);
1054
1055 QQuickMouseArea *mouseArea = window.rootObject()->findChild<QQuickMouseArea *>(aName: "mousearea");
1056 QVERIFY(mouseArea);
1057 mouseArea->setAcceptedButtons(acceptedButtons);
1058
1059 QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), button, button, {});
1060 QGuiApplication::sendEvent(receiver: &window, event: &pressEvent);
1061
1062 QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), button, button, {});
1063 QGuiApplication::sendEvent(receiver: &window, event: &releaseEvent);
1064
1065 QCOMPARE(window.rootObject()->property("pressed").toInt(), 1);
1066 QCOMPARE(window.rootObject()->property("released").toInt(), 1);
1067 QCOMPARE(window.rootObject()->property("clicked").toInt(), 1);
1068
1069 QGuiApplication::sendEvent(receiver: &window, event: &pressEvent);
1070 pressEvent = QMouseEvent(QEvent::MouseButtonDblClick, QPoint(100, 100), button, button, {});
1071 QGuiApplication::sendEvent(receiver: &window, event: &pressEvent);
1072 QGuiApplication::sendEvent(receiver: &window, event: &releaseEvent);
1073
1074 QCOMPARE(window.rootObject()->property("pressed").toInt(), 2);
1075 QCOMPARE(window.rootObject()->property("released").toInt(), 2);
1076 QCOMPARE(window.rootObject()->property("clicked").toInt(), 2);
1077}
1078
1079void tst_QQuickMouseArea::invalidClick()
1080{
1081 QFETCH(Qt::MouseButtons, acceptedButtons);
1082 QFETCH(Qt::MouseButton, button);
1083
1084 QQuickView window;
1085 QByteArray errorMessage;
1086 QVERIFY2(QQuickTest::initView(window, testFileUrl("doubleclick.qml"), true, &errorMessage), errorMessage.constData());
1087 window.show();
1088 QVERIFY(QTest::qWaitForWindowExposed(&window));
1089 QVERIFY(window.rootObject() != nullptr);
1090
1091 QQuickMouseArea *mouseArea = window.rootObject()->findChild<QQuickMouseArea *>(aName: "mousearea");
1092 QVERIFY(mouseArea);
1093 mouseArea->setAcceptedButtons(acceptedButtons);
1094
1095 // The sequence for a double click is:
1096 // press, release, (click), press, double click, release
1097 QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), button, button, {});
1098 QGuiApplication::sendEvent(receiver: &window, event: &pressEvent);
1099
1100 QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), button, button, {});
1101 QGuiApplication::sendEvent(receiver: &window, event: &releaseEvent);
1102
1103 QCOMPARE(window.rootObject()->property("released").toInt(), 0);
1104
1105 QGuiApplication::sendEvent(receiver: &window, event: &pressEvent);
1106 pressEvent = QMouseEvent(QEvent::MouseButtonDblClick, QPoint(100, 100), button, button, {});
1107 QGuiApplication::sendEvent(receiver: &window, event: &pressEvent);
1108 QGuiApplication::sendEvent(receiver: &window, event: &releaseEvent);
1109
1110 QCOMPARE(window.rootObject()->property("clicked").toInt(), 0);
1111 QCOMPARE(window.rootObject()->property("doubleClicked").toInt(), 0);
1112 QCOMPARE(window.rootObject()->property("released").toInt(), 0);
1113}
1114
1115void tst_QQuickMouseArea::pressedOrdering()
1116{
1117 QQuickView window;
1118 QByteArray errorMessage;
1119 QVERIFY2(QQuickTest::initView(window, testFileUrl("pressedOrdering.qml"), true, &errorMessage), errorMessage.constData());
1120 window.show();
1121 QVERIFY(QTest::qWaitForWindowExposed(&window));
1122 QVERIFY(window.rootObject() != nullptr);
1123
1124 QCOMPARE(window.rootObject()->property("value").toString(), QLatin1String("base"));
1125
1126 QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, {});
1127 QGuiApplication::sendEvent(receiver: &window, event: &pressEvent);
1128
1129 QCOMPARE(window.rootObject()->property("value").toString(), QLatin1String("pressed"));
1130
1131 QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, {});
1132 QGuiApplication::sendEvent(receiver: &window, event: &releaseEvent);
1133
1134 QCOMPARE(window.rootObject()->property("value").toString(), QLatin1String("toggled"));
1135
1136 QGuiApplication::sendEvent(receiver: &window, event: &pressEvent);
1137
1138 QCOMPARE(window.rootObject()->property("value").toString(), QLatin1String("pressed"));
1139}
1140
1141void tst_QQuickMouseArea::preventStealing()
1142{
1143 QQuickView window;
1144 QByteArray errorMessage;
1145 QVERIFY2(QQuickTest::initView(window, testFileUrl("preventstealing.qml"), true, &errorMessage), errorMessage.constData());
1146 window.show();
1147 QVERIFY(QTest::qWaitForWindowExposed(&window));
1148 QVERIFY(window.rootObject() != nullptr);
1149
1150 QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(object: window.rootObject());
1151 QVERIFY(flickable != nullptr);
1152
1153 QQuickMouseArea *mouseArea = window.rootObject()->findChild<QQuickMouseArea*>(aName: "mousearea");
1154 QVERIFY(mouseArea != nullptr);
1155
1156 QSignalSpy mousePositionSpy(mouseArea, SIGNAL(positionChanged(QQuickMouseEvent*)));
1157
1158 QPoint p = QPoint(80, 80);
1159 QTest::mousePress(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p);
1160
1161 // Without preventStealing, mouse movement over MouseArea would
1162 // cause the Flickable to steal mouse and trigger content movement.
1163
1164 p += QPoint(-startDragDistance() * 2, -startDragDistance() * 2);
1165 QTest::mouseMove(window: &window, pos: p);
1166 p += QPoint(-10, -10);
1167 QTest::mouseMove(window: &window, pos: p);
1168 p += QPoint(-10, -10);
1169 QTest::mouseMove(window: &window, pos: p);
1170 p += QPoint(-10, -10);
1171 QTest::mouseMove(window: &window, pos: p);
1172
1173 // We should have received all four move events
1174 QTRY_COMPARE(mousePositionSpy.count(), 4);
1175 mousePositionSpy.clear();
1176 QVERIFY(mouseArea->pressed());
1177
1178 // Flickable content should not have moved.
1179 QCOMPARE(flickable->contentX(), 0.);
1180 QCOMPARE(flickable->contentY(), 0.);
1181
1182 QTest::mouseRelease(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p);
1183
1184 // Now allow stealing and confirm Flickable does its thing.
1185 window.rootObject()->setProperty(name: "stealing", value: false);
1186
1187 p = QPoint(80, 80);
1188 QTest::mousePress(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p);
1189
1190 // Without preventStealing, mouse movement over MouseArea would
1191 // cause the Flickable to steal mouse and trigger content movement.
1192
1193 p += QPoint(-startDragDistance() * 2, -startDragDistance() * 2);
1194 QTest::mouseMove(window: &window, pos: p);
1195 p += QPoint(-10, -10);
1196 QTest::mouseMove(window: &window, pos: p);
1197 p += QPoint(-10, -10);
1198 QTest::mouseMove(window: &window, pos: p);
1199 p += QPoint(-10, -10);
1200 QTest::mouseMove(window: &window, pos: p);
1201
1202 // We should only have received the first move event
1203 QTRY_COMPARE(mousePositionSpy.count(), 1);
1204 // Our press should be taken away
1205 QVERIFY(!mouseArea->pressed());
1206
1207 // Flickable swallows the first move, then moves 2*10 px
1208 QTRY_COMPARE(flickable->contentX(), 20.);
1209 QCOMPARE(flickable->contentY(), 20.);
1210
1211 QTest::mouseRelease(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p);
1212}
1213
1214void tst_QQuickMouseArea::clickThrough()
1215{
1216 //With no handlers defined click, doubleClick and PressAndHold should propagate to those with handlers
1217 QScopedPointer<QQuickView> window(new QQuickView);
1218 QByteArray errorMessage;
1219 QVERIFY2(QQuickTest::initView(*window.data(), testFileUrl("clickThrough.qml"), true, &errorMessage), errorMessage.constData());
1220 window->show();
1221 QVERIFY(QTest::qWaitForWindowExposed(window.data()));
1222 QVERIFY(window->rootObject() != nullptr);
1223
1224 // to avoid generating a double click.
1225 const int doubleClickInterval = qApp->styleHints()->mouseDoubleClickInterval() + 10;
1226
1227 QTest::mousePress(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(100,100));
1228 QTest::mouseRelease(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(100,100));
1229
1230 QTRY_COMPARE(window->rootObject()->property("presses").toInt(), 0);
1231 QTRY_COMPARE(window->rootObject()->property("clicks").toInt(), 1);
1232
1233 QCOMPARE(window->rootObject()->property("doubleClicks").toInt(), 0);
1234 QTest::mousePress(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(100,100), delay: doubleClickInterval);
1235 QTest::qWait(ms: 1000);
1236 QTest::mouseRelease(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(100,100));
1237
1238 QTRY_COMPARE(window->rootObject()->property("presses").toInt(), 0);
1239 QTRY_COMPARE(window->rootObject()->property("clicks").toInt(), 1);
1240 QTRY_COMPARE(window->rootObject()->property("pressAndHolds").toInt(), 1);
1241
1242 QTest::mouseDClick(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(100,100));
1243 QTest::qWait(ms: 100);
1244
1245 QCOMPARE(window->rootObject()->property("presses").toInt(), 0);
1246 QTRY_COMPARE(window->rootObject()->property("clicks").toInt(), 2);
1247 QTRY_COMPARE(window->rootObject()->property("doubleClicks").toInt(), 1);
1248 QCOMPARE(window->rootObject()->property("pressAndHolds").toInt(), 1);
1249
1250 window.reset(other: new QQuickView);
1251
1252 //With handlers defined click, doubleClick and PressAndHold should propagate only when explicitly ignored
1253 QVERIFY2(QQuickTest::initView(*window.data(), testFileUrl("clickThrough2.qml"), true, &errorMessage), errorMessage.constData());
1254 window->show();
1255 QVERIFY(QTest::qWaitForWindowExposed(window.data()));
1256 QVERIFY(window->rootObject() != nullptr);
1257
1258 QTest::mousePress(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(100,100));
1259 QTest::mouseRelease(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(100,100));
1260
1261 QCOMPARE(window->rootObject()->property("presses").toInt(), 0);
1262 QCOMPARE(window->rootObject()->property("clicks").toInt(), 0);
1263
1264 QTest::mousePress(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(100,100), delay: doubleClickInterval);
1265 QTest::qWait(ms: 1000);
1266 QTest::mouseRelease(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(100,100));
1267 QTest::qWait(ms: 100);
1268
1269 QCOMPARE(window->rootObject()->property("presses").toInt(), 0);
1270 QCOMPARE(window->rootObject()->property("clicks").toInt(), 0);
1271 QCOMPARE(window->rootObject()->property("pressAndHolds").toInt(), 0);
1272
1273 QTest::mouseDClick(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(100,100));
1274 QTest::qWait(ms: 100);
1275
1276 QCOMPARE(window->rootObject()->property("presses").toInt(), 0);
1277 QCOMPARE(window->rootObject()->property("clicks").toInt(), 0);
1278 QCOMPARE(window->rootObject()->property("doubleClicks").toInt(), 0);
1279 QCOMPARE(window->rootObject()->property("pressAndHolds").toInt(), 0);
1280
1281 window->rootObject()->setProperty(name: "letThrough", value: QVariant(true));
1282
1283 QTest::mousePress(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(100,100), delay: doubleClickInterval);
1284 QTest::mouseRelease(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(100,100));
1285
1286 QCOMPARE(window->rootObject()->property("presses").toInt(), 0);
1287 QTRY_COMPARE(window->rootObject()->property("clicks").toInt(), 1);
1288
1289 QTest::mousePress(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(100,100), delay: doubleClickInterval);
1290 QTest::qWait(ms: 1000);
1291 QTest::mouseRelease(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(100,100));
1292 QTest::qWait(ms: 100);
1293
1294 QCOMPARE(window->rootObject()->property("presses").toInt(), 0);
1295 QCOMPARE(window->rootObject()->property("clicks").toInt(), 1);
1296 QCOMPARE(window->rootObject()->property("pressAndHolds").toInt(), 1);
1297
1298 QTest::mouseDClick(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(100,100));
1299 QTest::qWait(ms: 100);
1300
1301 QCOMPARE(window->rootObject()->property("presses").toInt(), 0);
1302 QTRY_COMPARE(window->rootObject()->property("clicks").toInt(), 2);
1303 QCOMPARE(window->rootObject()->property("doubleClicks").toInt(), 1);
1304 QCOMPARE(window->rootObject()->property("pressAndHolds").toInt(), 1);
1305
1306 window->rootObject()->setProperty(name: "noPropagation", value: QVariant(true));
1307
1308 QTest::mousePress(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(100,100), delay: doubleClickInterval);
1309 QTest::mouseRelease(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(100,100));
1310
1311 QTest::mousePress(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(100,100), delay: doubleClickInterval);
1312 QTest::qWait(ms: 1000);
1313 QTest::mouseRelease(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(100,100));
1314 QTest::qWait(ms: 100);
1315
1316 QTest::mouseDClick(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(100,100));
1317 QTest::qWait(ms: 100);
1318
1319 QCOMPARE(window->rootObject()->property("presses").toInt(), 0);
1320 QTRY_COMPARE(window->rootObject()->property("clicks").toInt(), 2);
1321 QCOMPARE(window->rootObject()->property("doubleClicks").toInt(), 1);
1322 QCOMPARE(window->rootObject()->property("pressAndHolds").toInt(), 1);
1323
1324 window.reset(other: new QQuickView);
1325
1326 //QTBUG-34368 - Shouldn't propagate to disabled mouse areas
1327 QVERIFY2(QQuickTest::initView(*window.data(), testFileUrl("qtbug34368.qml"), true, &errorMessage), errorMessage.constData());
1328 window->show();
1329 QVERIFY(QTest::qWaitForWindowExposed(window.data()));
1330 QVERIFY(window->rootObject() != nullptr);
1331
1332 QTest::mousePress(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(100,100), delay: doubleClickInterval);
1333 QTest::mouseRelease(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(100,100));
1334
1335 QCOMPARE(window->rootObject()->property("clicksEnabled").toInt(), 1);
1336 QCOMPARE(window->rootObject()->property("clicksDisabled").toInt(), 1); //Not disabled yet
1337
1338 window->rootObject()->setProperty(name: "disableLower", value: QVariant(true));
1339
1340 QTest::mousePress(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(100,100), delay: doubleClickInterval);
1341 QTest::mouseRelease(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(100,100));
1342
1343 QCOMPARE(window->rootObject()->property("clicksEnabled").toInt(), 2);
1344 QCOMPARE(window->rootObject()->property("clicksDisabled").toInt(), 1); //disabled, shouldn't increment
1345
1346 window.reset(other: new QQuickView);
1347
1348 //QTBUG-49100
1349 QVERIFY2(QQuickTest::initView(*window.data(), testFileUrl("qtbug49100.qml"), true, &errorMessage), errorMessage.constData());
1350 window->show();
1351 QVERIFY(QTest::qWaitForWindowExposed(window.data()));
1352 QVERIFY(window->rootObject() != nullptr);
1353
1354 QTest::mousePress(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(100,100));
1355 QTest::mouseRelease(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(100,100));
1356
1357 QVERIFY(window->rootObject() != nullptr);
1358}
1359
1360void tst_QQuickMouseArea::hoverPosition()
1361{
1362 QQuickView window;
1363 QByteArray errorMessage;
1364 QVERIFY2(QQuickTest::initView(window, testFileUrl("hoverPosition.qml"), true, &errorMessage), errorMessage.constData());
1365 QQuickItem *root = window.rootObject();
1366 QVERIFY(root != nullptr);
1367
1368 QCOMPARE(root->property("mouseX").toReal(), qreal(0));
1369 QCOMPARE(root->property("mouseY").toReal(), qreal(0));
1370
1371 QTest::mouseMove(window: &window,pos: QPoint(10,32));
1372
1373
1374 QCOMPARE(root->property("mouseX").toReal(), qreal(10));
1375 QCOMPARE(root->property("mouseY").toReal(), qreal(32));
1376}
1377
1378void tst_QQuickMouseArea::hoverPropagation()
1379{
1380 //QTBUG-18175, to behave like GV did.
1381 QQuickView window;
1382 QByteArray errorMessage;
1383 QVERIFY2(QQuickTest::initView(window, testFileUrl("hoverPropagation.qml"), true, &errorMessage), errorMessage.constData());
1384 QQuickItem *root = window.rootObject();
1385 QVERIFY(root != nullptr);
1386
1387 QCOMPARE(root->property("point1").toBool(), false);
1388 QCOMPARE(root->property("point2").toBool(), false);
1389
1390 QMouseEvent moveEvent(QEvent::MouseMove, QPoint(32, 32), Qt::NoButton, Qt::NoButton, {});
1391 QGuiApplication::sendEvent(receiver: &window, event: &moveEvent);
1392
1393 QCOMPARE(root->property("point1").toBool(), true);
1394 QCOMPARE(root->property("point2").toBool(), false);
1395
1396 QMouseEvent moveEvent2(QEvent::MouseMove, QPoint(232, 32), Qt::NoButton, Qt::NoButton, {});
1397 QGuiApplication::sendEvent(receiver: &window, event: &moveEvent2);
1398 QCOMPARE(root->property("point1").toBool(), false);
1399 QCOMPARE(root->property("point2").toBool(), true);
1400}
1401
1402void tst_QQuickMouseArea::hoverVisible()
1403{
1404 if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
1405 || (QGuiApplication::platformName() == QLatin1String("minimal")))
1406 QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms");
1407
1408 QQuickView window;
1409 QByteArray errorMessage;
1410 QVERIFY2(QQuickTest::initView(window, testFileUrl("hoverVisible.qml"), true, &errorMessage), errorMessage.constData());
1411 QQuickItem *root = window.rootObject();
1412 QVERIFY(root != nullptr);
1413
1414 QQuickMouseArea *mouseTracker = window.rootObject()->findChild<QQuickMouseArea*>(aName: "mousetracker");
1415 QVERIFY(mouseTracker != nullptr);
1416
1417 QSignalSpy enteredSpy(mouseTracker, SIGNAL(entered()));
1418
1419 // Note: We need to use a position that is different from the position in the last event
1420 // generated in the previous test case. Otherwise it is not interpreted as a move.
1421 QTest::mouseMove(window: &window,pos: QPoint(11,33));
1422
1423 QCOMPARE(mouseTracker->hovered(), false);
1424 QCOMPARE(enteredSpy.count(), 0);
1425
1426 mouseTracker->setVisible(true);
1427
1428 QCOMPARE(mouseTracker->hovered(), true);
1429 QCOMPARE(enteredSpy.count(), 1);
1430
1431 QCOMPARE(QPointF(mouseTracker->mouseX(), mouseTracker->mouseY()), QPointF(11,33));
1432
1433 // QTBUG-77983
1434 mouseTracker->setVisible(false);
1435 mouseTracker->setEnabled(false);
1436
1437 QCOMPARE(mouseTracker->hovered(), false);
1438 mouseTracker->setVisible(true);
1439 // if the enabled property is false, the containsMouse property shouldn't become true
1440 // when an invisible mousearea become visible
1441 QCOMPARE(mouseTracker->hovered(), false);
1442
1443 mouseTracker->parentItem()->setEnabled(false);
1444 mouseTracker->setVisible(false);
1445 mouseTracker->setEnabled(true);
1446
1447 QCOMPARE(mouseTracker->hovered(), false);
1448 mouseTracker->setVisible(true);
1449 // if the parent item is not enabled, the containsMouse property will be false, even if
1450 // the mousearea is enabled
1451 QCOMPARE(mouseTracker->hovered(), false);
1452
1453 mouseTracker->parentItem()->setEnabled(true);
1454 mouseTracker->setVisible(false);
1455 mouseTracker->setEnabled(true);
1456
1457 QCOMPARE(mouseTracker->hovered(), false);
1458 mouseTracker->setVisible(true);
1459 QCOMPARE(mouseTracker->hovered(), true);
1460}
1461
1462void tst_QQuickMouseArea::hoverAfterPress()
1463{
1464 QQuickView window;
1465 QByteArray errorMessage;
1466 QVERIFY2(QQuickTest::initView(window, testFileUrl("hoverAfterPress.qml"), true, &errorMessage), errorMessage.constData());
1467 QQuickItem *root = window.rootObject();
1468 QVERIFY(root != nullptr);
1469
1470 QQuickMouseArea *mouseArea = window.rootObject()->findChild<QQuickMouseArea*>(aName: "mouseArea");
1471 QVERIFY(mouseArea != nullptr);
1472 QTest::mouseMove(window: &window, pos: QPoint(22,33));
1473 QCOMPARE(mouseArea->hovered(), false);
1474 QTest::mouseMove(window: &window, pos: QPoint(200,200));
1475 QCOMPARE(mouseArea->hovered(), true);
1476 QTest::mouseMove(window: &window, pos: QPoint(22,33));
1477 QCOMPARE(mouseArea->hovered(), false);
1478 QTest::mouseMove(window: &window, pos: QPoint(200,200));
1479 QCOMPARE(mouseArea->hovered(), true);
1480 QTest::mousePress(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(200,200));
1481 QCOMPARE(mouseArea->hovered(), true);
1482 QTest::mouseRelease(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(200,200));
1483 QCOMPARE(mouseArea->hovered(), true);
1484 QTest::mouseMove(window: &window, pos: QPoint(22,33));
1485 QCOMPARE(mouseArea->hovered(), false);
1486}
1487
1488void tst_QQuickMouseArea::subtreeHoverEnabled()
1489{
1490 QQuickView window;
1491 QByteArray errorMessage;
1492 QVERIFY2(QQuickTest::initView(window, testFileUrl("qtbug54019.qml"), true, &errorMessage), errorMessage.constData());
1493 QQuickItem *root = window.rootObject();
1494 QVERIFY(root != nullptr);
1495
1496 QQuickMouseArea *mouseArea = root->findChild<QQuickMouseArea*>();
1497 QQuickItemPrivate *rootPrivate = QQuickItemPrivate::get(item: root);
1498 QVERIFY(mouseArea != nullptr);
1499 QTest::mouseMove(window: &window, pos: QPoint(10, 160));
1500 QCOMPARE(mouseArea->hovered(), false);
1501 QVERIFY(rootPrivate->subtreeHoverEnabled);
1502 QTest::mouseMove(window: &window, pos: QPoint(10, 10));
1503 QCOMPARE(mouseArea->hovered(), true);
1504 QTest::mouseMove(window: &window, pos: QPoint(160, 10));
1505 QCOMPARE(mouseArea->hovered(), false);
1506}
1507
1508void tst_QQuickMouseArea::disableAfterPress()
1509{
1510 QQuickView window;
1511 QByteArray errorMessage;
1512 QVERIFY2(QQuickTest::initView(window, testFileUrl("dragging.qml"), true, &errorMessage), errorMessage.constData());
1513 window.show();
1514 QVERIFY(QTest::qWaitForWindowExposed(&window));
1515 QVERIFY(window.rootObject() != nullptr);
1516
1517 QQuickMouseArea *mouseArea = window.rootObject()->findChild<QQuickMouseArea*>(aName: "mouseregion");
1518 QQuickDrag *drag = mouseArea->drag();
1519 QVERIFY(mouseArea != nullptr);
1520 QVERIFY(drag != nullptr);
1521
1522 QSignalSpy mousePositionSpy(mouseArea, SIGNAL(positionChanged(QQuickMouseEvent*)));
1523 QSignalSpy mousePressSpy(mouseArea, SIGNAL(pressed(QQuickMouseEvent*)));
1524 QSignalSpy mouseReleaseSpy(mouseArea, SIGNAL(released(QQuickMouseEvent*)));
1525
1526 // target
1527 QQuickItem *blackRect = window.rootObject()->findChild<QQuickItem*>(aName: "blackrect");
1528 QVERIFY(blackRect != nullptr);
1529 QCOMPARE(blackRect, drag->target());
1530
1531 QVERIFY(!drag->active());
1532 QPoint p = QPoint(100,100);
1533 QTest::mousePress(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p);
1534 QTRY_COMPARE(mousePressSpy.count(), 1);
1535
1536 QVERIFY(!drag->active());
1537 QCOMPARE(blackRect->x(), 50.0);
1538 QCOMPARE(blackRect->y(), 50.0);
1539
1540 // First move event triggers drag, second is acted upon.
1541 // This is due to possibility of higher stacked area taking precedence.
1542
1543 p += QPoint(startDragDistance() + 1, 0);
1544 QTest::mouseMove(window: &window, pos: p);
1545 p += QPoint(11, 11);
1546 QTest::mouseMove(window: &window, pos: p);
1547
1548 QTRY_COMPARE(mousePositionSpy.count(), 2);
1549
1550 QTRY_VERIFY(drag->active());
1551 QTRY_COMPARE(blackRect->x(), 61.0);
1552 QCOMPARE(blackRect->y(), 61.0);
1553
1554 mouseArea->setEnabled(false);
1555
1556 // move should still be acted upon
1557 p += QPoint(11, 11);
1558 QTest::mouseMove(window: &window, pos: p);
1559 p += QPoint(11, 11);
1560 QTest::mouseMove(window: &window, pos: p);
1561
1562 QTRY_COMPARE(mousePositionSpy.count(), 4);
1563
1564 QVERIFY(drag->active());
1565 QCOMPARE(blackRect->x(), 83.0);
1566 QCOMPARE(blackRect->y(), 83.0);
1567
1568 QVERIFY(mouseArea->pressed());
1569 QVERIFY(mouseArea->hovered());
1570
1571 QTest::mouseRelease(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p);
1572
1573 QTRY_COMPARE(mouseReleaseSpy.count(), 1);
1574
1575 QVERIFY(!drag->active());
1576 QCOMPARE(blackRect->x(), 83.0);
1577 QCOMPARE(blackRect->y(), 83.0);
1578
1579 QVERIFY(!mouseArea->pressed());
1580 QVERIFY(!mouseArea->hovered()); // since hover is not enabled
1581
1582 // Next press will be ignored
1583 blackRect->setX(50);
1584 blackRect->setY(50);
1585
1586 mousePressSpy.clear();
1587 mousePositionSpy.clear();
1588 mouseReleaseSpy.clear();
1589
1590 QTest::mousePress(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(100,100));
1591 QTest::qWait(ms: 50);
1592 QCOMPARE(mousePressSpy.count(), 0);
1593
1594 QTest::mouseMove(window: &window, pos: QPoint(111,111));
1595 QTest::qWait(ms: 50);
1596 QTest::mouseMove(window: &window, pos: QPoint(122,122));
1597 QTest::qWait(ms: 50);
1598
1599 QCOMPARE(mousePositionSpy.count(), 0);
1600
1601 QVERIFY(!drag->active());
1602 QCOMPARE(blackRect->x(), 50.0);
1603 QCOMPARE(blackRect->y(), 50.0);
1604
1605 QTest::mouseRelease(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(122,122));
1606 QTest::qWait(ms: 50);
1607
1608 QCOMPARE(mouseReleaseSpy.count(), 0);
1609}
1610
1611void tst_QQuickMouseArea::onWheel()
1612{
1613 QQuickView window;
1614 QByteArray errorMessage;
1615 QVERIFY2(QQuickTest::initView(window, testFileUrl("wheel.qml"), true, &errorMessage), errorMessage.constData());
1616 QQuickItem *root = window.rootObject();
1617 QVERIFY(root != nullptr);
1618
1619 QWheelEvent wheelEvent(QPoint(10, 32), QPoint(10, 32), QPoint(60, 20), QPoint(0, 120),
1620 Qt::NoButton, Qt::ControlModifier, Qt::NoScrollPhase, false);
1621 QGuiApplication::sendEvent(receiver: &window, event: &wheelEvent);
1622
1623 QCOMPARE(root->property("angleDeltaY").toInt(), 120);
1624 QCOMPARE(root->property("mouseX").toReal(), qreal(10));
1625 QCOMPARE(root->property("mouseY").toReal(), qreal(32));
1626 QCOMPARE(root->property("controlPressed").toBool(), true);
1627}
1628
1629void tst_QQuickMouseArea::transformedMouseArea_data()
1630{
1631 QTest::addColumn<bool>(name: "insideTarget");
1632 QTest::addColumn<QList<QPoint> >(name: "points");
1633
1634 QList<QPoint> pointsInside;
1635 pointsInside << QPoint(200, 140)
1636 << QPoint(140, 200)
1637 << QPoint(200, 200)
1638 << QPoint(260, 200)
1639 << QPoint(200, 260);
1640 QTest::newRow(dataTag: "checking points inside") << true << pointsInside;
1641
1642 QList<QPoint> pointsOutside;
1643 pointsOutside << QPoint(140, 140)
1644 << QPoint(260, 140)
1645 << QPoint(120, 200)
1646 << QPoint(280, 200)
1647 << QPoint(140, 260)
1648 << QPoint(260, 260);
1649 QTest::newRow(dataTag: "checking points outside") << false << pointsOutside;
1650}
1651
1652void tst_QQuickMouseArea::transformedMouseArea()
1653{
1654 QFETCH(bool, insideTarget);
1655 QFETCH(QList<QPoint>, points);
1656
1657 QQuickView window;
1658 QByteArray errorMessage;
1659 QVERIFY2(QQuickTest::initView(window, testFileUrl("transformedMouseArea.qml"), true, &errorMessage), errorMessage.constData());
1660 window.show();
1661 QVERIFY(QTest::qWaitForWindowExposed(&window));
1662 QVERIFY(window.rootObject() != nullptr);
1663
1664 QQuickMouseArea *mouseArea = window.rootObject()->findChild<QQuickMouseArea *>(aName: "mouseArea");
1665 QVERIFY(mouseArea != nullptr);
1666
1667 foreach (const QPoint &point, points) {
1668 // check hover
1669 QTest::mouseMove(window: &window, pos: point);
1670 QTRY_COMPARE(mouseArea->property("containsMouse").toBool(), insideTarget);
1671
1672 // check mouse press
1673 QTest::mousePress(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: point);
1674 QTRY_COMPARE(mouseArea->property("pressed").toBool(), insideTarget);
1675
1676 // check mouse release
1677 QTest::mouseRelease(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: point);
1678 QTRY_COMPARE(mouseArea->property("pressed").toBool(), false);
1679 }
1680}
1681
1682struct MouseEvent {
1683 QEvent::Type type;
1684 Qt::MouseButton button;
1685};
1686Q_DECLARE_METATYPE(MouseEvent)
1687
1688void tst_QQuickMouseArea::pressedMultipleButtons_data()
1689{
1690 QTest::addColumn<Qt::MouseButtons>(name: "accepted");
1691 QTest::addColumn<QList<MouseEvent> >(name: "mouseEvents");
1692 QTest::addColumn<QList<bool> >(name: "pressed");
1693 QTest::addColumn<QList<Qt::MouseButtons> >(name: "pressedButtons");
1694 QTest::addColumn<int>(name: "changeCount");
1695
1696 Qt::MouseButtons accepted;
1697 QList<MouseEvent> mouseEvents;
1698 QList<bool> pressed;
1699 QList<Qt::MouseButtons> pressedButtons;
1700 int changeCount;
1701
1702 MouseEvent leftPress = { .type: QEvent::MouseButtonPress, .button: Qt::LeftButton };
1703 MouseEvent leftRelease = { .type: QEvent::MouseButtonRelease, .button: Qt::LeftButton };
1704 MouseEvent rightPress = { .type: QEvent::MouseButtonPress, .button: Qt::RightButton };
1705 MouseEvent rightRelease = { .type: QEvent::MouseButtonRelease, .button: Qt::RightButton };
1706
1707 auto addRowWithFormattedTitleAndReset = [&]() {
1708 QByteArray title;
1709 title.append(s: "Accept:");
1710 if (accepted & Qt::LeftButton)
1711 title.append(s: " LeftButton");
1712 if (accepted & Qt::RightButton)
1713 title.append(s: " RightButton");
1714 title.append(s: " | Events:");
1715 for (MouseEvent event : mouseEvents) {
1716 title.append(s: event.type == QEvent::MouseButtonPress ? " Press" : " Release");
1717 title.append(s: event.button == Qt::LeftButton ? " Left," : " Right,");
1718 }
1719 title.chop(n: 1); // remove last comma
1720 QTest::newRow(dataTag: title) << accepted << mouseEvents << pressed << pressedButtons << changeCount;
1721
1722 mouseEvents.clear();
1723 pressed.clear();
1724 pressedButtons.clear();
1725 };
1726
1727 accepted = Qt::LeftButton;
1728 changeCount = 2;
1729 mouseEvents << leftPress << rightPress << rightRelease << leftRelease;
1730 pressed << true << true << true << false;
1731 pressedButtons << Qt::LeftButton << Qt::LeftButton << Qt::LeftButton << Qt::NoButton;
1732 addRowWithFormattedTitleAndReset();
1733
1734 accepted = Qt::LeftButton;
1735 changeCount = 2;
1736 mouseEvents << leftPress << rightPress << leftRelease << rightRelease;
1737 pressed << true << true << false << false;
1738 pressedButtons << Qt::LeftButton << Qt::LeftButton << Qt::NoButton << Qt::NoButton;
1739 addRowWithFormattedTitleAndReset();
1740
1741 accepted = Qt::LeftButton | Qt::RightButton;
1742 changeCount = 4;
1743 mouseEvents << leftPress << rightPress << rightRelease << leftRelease;
1744 pressed << true << true << true << false;
1745 pressedButtons << Qt::LeftButton << (Qt::LeftButton | Qt::RightButton) << Qt::LeftButton
1746 << Qt::NoButton;
1747 addRowWithFormattedTitleAndReset();
1748
1749 accepted = Qt::RightButton;
1750 changeCount = 2;
1751 mouseEvents << rightPress << leftPress << rightRelease << leftRelease;
1752 pressed << true << true << false << false;
1753 pressedButtons << Qt::RightButton << Qt::RightButton << Qt::NoButton << Qt::NoButton;
1754 addRowWithFormattedTitleAndReset();
1755}
1756
1757void tst_QQuickMouseArea::pressedMultipleButtons()
1758{
1759 QFETCH(Qt::MouseButtons, accepted);
1760 QFETCH(QList<MouseEvent>, mouseEvents);
1761 QFETCH(QList<bool>, pressed);
1762 QFETCH(QList<Qt::MouseButtons>, pressedButtons);
1763 QFETCH(int, changeCount);
1764
1765 QQuickView view;
1766 QByteArray errorMessage;
1767 QVERIFY2(QQuickTest::initView(view, testFileUrl("simple.qml"), true, &errorMessage), errorMessage.constData());
1768 view.show();
1769 QVERIFY(QTest::qWaitForWindowExposed(&view));
1770 QVERIFY(view.rootObject() != nullptr);
1771
1772 QQuickMouseArea *mouseArea = view.rootObject()->findChild<QQuickMouseArea *>(aName: "mousearea");
1773 QVERIFY(mouseArea != nullptr);
1774
1775 QSignalSpy pressedSpy(mouseArea, SIGNAL(pressedChanged()));
1776 QSignalSpy pressedButtonsSpy(mouseArea, SIGNAL(pressedButtonsChanged()));
1777 mouseArea->setAcceptedMouseButtons(accepted);
1778
1779 QPoint point(10, 10);
1780 for (int i = 0; i < mouseEvents.count(); ++i) {
1781 const MouseEvent mouseEvent = mouseEvents.at(i);
1782 if (mouseEvent.type == QEvent::MouseButtonPress)
1783 QTest::mousePress(window: &view, button: mouseEvent.button, stateKey: Qt::NoModifier, pos: point);
1784 else
1785 QTest::mouseRelease(window: &view, button: mouseEvent.button, stateKey: Qt::NoModifier, pos: point);
1786 QCOMPARE(mouseArea->pressed(), pressed.at(i));
1787 QCOMPARE(mouseArea->pressedButtons(), pressedButtons.at(i));
1788 }
1789
1790 QCOMPARE(pressedSpy.count(), 2);
1791 QCOMPARE(pressedButtonsSpy.count(), changeCount);
1792}
1793
1794void tst_QQuickMouseArea::changeAxis()
1795{
1796 QQuickView view;
1797 QByteArray errorMessage;
1798 QVERIFY2(QQuickTest::initView(view, testFileUrl("changeAxis.qml"), true, &errorMessage), errorMessage.constData());
1799 view.show();
1800 QVERIFY(QTest::qWaitForWindowExposed(&view));
1801 QTRY_VERIFY(view.rootObject() != nullptr);
1802
1803 QQuickMouseArea *mouseRegion = view.rootObject()->findChild<QQuickMouseArea*>(aName: "mouseregion");
1804 QQuickDrag *drag = mouseRegion->drag();
1805 QVERIFY(mouseRegion != nullptr);
1806 QVERIFY(drag != nullptr);
1807
1808 mouseRegion->setAcceptedButtons(Qt::LeftButton);
1809
1810 // target
1811 QQuickItem *blackRect = view.rootObject()->findChild<QQuickItem*>(aName: "blackrect");
1812 QVERIFY(blackRect != nullptr);
1813 QCOMPARE(blackRect, drag->target());
1814
1815 QVERIFY(!drag->active());
1816
1817 // Start a diagonal drag
1818 QPoint p = QPoint(100, 100);
1819 QTest::mousePress(window: &view, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p);
1820
1821 QVERIFY(!drag->active());
1822 QCOMPARE(blackRect->x(), 50.0);
1823 QCOMPARE(blackRect->y(), 50.0);
1824
1825 p += QPoint(startDragDistance() + 1, startDragDistance() + 1);
1826 QTest::mouseMove(window: &view, pos: p);
1827 p += QPoint(11, 11);
1828 QTest::mouseMove(window: &view, pos: p);
1829 QTRY_VERIFY(drag->active());
1830 QTRY_COMPARE(blackRect->x(), 61.0);
1831 QCOMPARE(blackRect->y(), 61.0);
1832 QCOMPARE(drag->axis(), QQuickDrag::XAndYAxis);
1833
1834 /* When blackRect.x becomes bigger than 75, the drag axis is changed to
1835 * Drag.YAxis by the QML code. Verify that this happens, and that the drag
1836 * movement is effectively constrained to the Y axis. */
1837 p += QPoint(22, 22);
1838 QTest::mouseMove(window: &view, pos: p);
1839
1840 QTRY_COMPARE(blackRect->x(), 83.0);
1841 QTRY_COMPARE(blackRect->y(), 83.0);
1842 QTRY_COMPARE(drag->axis(), QQuickDrag::YAxis);
1843
1844 p += QPoint(11, 11);
1845 QTest::mouseMove(window: &view, pos: p);
1846
1847 QTRY_COMPARE(blackRect->y(), 94.0);
1848 QCOMPARE(blackRect->x(), 83.0);
1849
1850 QTest::mouseRelease(window: &view, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p);
1851
1852 QTRY_VERIFY(!drag->active());
1853 QCOMPARE(blackRect->x(), 83.0);
1854 QCOMPARE(blackRect->y(), 94.0);
1855}
1856
1857#if QT_CONFIG(cursor)
1858void tst_QQuickMouseArea::cursorShape()
1859{
1860 QQmlEngine engine;
1861 QQmlComponent component(&engine);
1862 component.setData("import QtQuick 2.0\n MouseArea {}", baseUrl: QUrl());
1863 QScopedPointer<QObject> object(component.create());
1864 QQuickMouseArea *mouseArea = qobject_cast<QQuickMouseArea *>(object: object.data());
1865 QVERIFY(mouseArea);
1866
1867 QSignalSpy spy(mouseArea, SIGNAL(cursorShapeChanged()));
1868
1869 QCOMPARE(mouseArea->cursorShape(), Qt::ArrowCursor);
1870 QCOMPARE(mouseArea->cursor().shape(), Qt::ArrowCursor);
1871
1872 mouseArea->setCursorShape(Qt::IBeamCursor);
1873 QCOMPARE(mouseArea->cursorShape(), Qt::IBeamCursor);
1874 QCOMPARE(mouseArea->cursor().shape(), Qt::IBeamCursor);
1875 QCOMPARE(spy.count(), 1);
1876
1877 mouseArea->setCursorShape(Qt::IBeamCursor);
1878 QCOMPARE(spy.count(), 1);
1879
1880 mouseArea->setCursorShape(Qt::WaitCursor);
1881 QCOMPARE(mouseArea->cursorShape(), Qt::WaitCursor);
1882 QCOMPARE(mouseArea->cursor().shape(), Qt::WaitCursor);
1883 QCOMPARE(spy.count(), 2);
1884}
1885#endif
1886
1887void tst_QQuickMouseArea::moveAndReleaseWithoutPress()
1888{
1889 QQuickView window;
1890 QByteArray errorMessage;
1891 QVERIFY2(QQuickTest::initView(window, testFileUrl("moveAndReleaseWithoutPress.qml"), true, &errorMessage), errorMessage.constData());
1892 window.show();
1893 QVERIFY(QTest::qWaitForWindowExposed(&window));
1894
1895 QObject *root = window.rootObject();
1896 QVERIFY(root);
1897
1898 QTest::mousePress(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(100,100));
1899
1900 // the press was not accepted, make sure there is no move or release event
1901 QTest::mouseMove(window: &window, pos: QPoint(110,110), delay: 50);
1902
1903 // use qwait here because we want to make sure an event does NOT happen
1904 // the test fails if the default state changes, while it shouldn't
1905 QTest::qWait(ms: 100);
1906 QCOMPARE(root->property("hadMove").toBool(), false);
1907
1908 QTest::mouseRelease(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(110,110));
1909 QTest::qWait(ms: 100);
1910 QCOMPARE(root->property("hadRelease").toBool(), false);
1911}
1912
1913void tst_QQuickMouseArea::nestedStopAtBounds_data()
1914{
1915 QTest::addColumn<bool>(name: "transpose");
1916 QTest::addColumn<bool>(name: "invert");
1917
1918 QTest::newRow(dataTag: "left") << false << false;
1919 QTest::newRow(dataTag: "right") << false << true;
1920 QTest::newRow(dataTag: "top") << true << false;
1921 QTest::newRow(dataTag: "bottom") << true << true;
1922}
1923
1924void tst_QQuickMouseArea::nestedStopAtBounds()
1925{
1926 QFETCH(bool, transpose);
1927 QFETCH(bool, invert);
1928
1929 QQuickView view;
1930 QByteArray errorMessage;
1931 QVERIFY2(QQuickTest::initView(view, testFileUrl("nestedStopAtBounds.qml"), true, &errorMessage), errorMessage.constData());
1932 view.show();
1933 view.requestActivate();
1934 QVERIFY(QTest::qWaitForWindowExposed(&view));
1935 QVERIFY(view.rootObject());
1936
1937 QQuickMouseArea *outer = view.rootObject()->findChild<QQuickMouseArea*>(aName: "outer");
1938 QVERIFY(outer);
1939
1940 QQuickMouseArea *inner = outer->findChild<QQuickMouseArea*>(aName: "inner");
1941 QVERIFY(inner);
1942 inner->drag()->setAxis(transpose ? QQuickDrag::YAxis : QQuickDrag::XAxis);
1943 inner->setX(invert ? 100 : 0);
1944 inner->setY(invert ? 100 : 0);
1945
1946 const int threshold = qApp->styleHints()->startDragDistance();
1947
1948 QPoint position(200, 200);
1949 int &axis = transpose ? position.ry() : position.rx();
1950
1951 // drag toward the aligned boundary. Outer mouse area dragged.
1952 QTest::mousePress(window: &view, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: position);
1953 QTest::qWait(ms: 10);
1954 axis += invert ? threshold * 2 : -threshold * 2;
1955 QTest::mouseMove(window: &view, pos: position);
1956 axis += invert ? threshold : -threshold;
1957 QTest::mouseMove(window: &view, pos: position);
1958 QTRY_COMPARE(outer->drag()->active(), true);
1959 QCOMPARE(inner->drag()->active(), false);
1960 QTest::mouseRelease(window: &view, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: position);
1961
1962 QVERIFY(!outer->drag()->active());
1963
1964 axis = 200;
1965 outer->setX(50);
1966 outer->setY(50);
1967
1968 // drag away from the aligned boundary. Inner mouse area dragged.
1969 QTest::mousePress(window: &view, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: position);
1970 QTest::qWait(ms: 10);
1971 axis += invert ? -threshold * 2 : threshold * 2;
1972 QTest::mouseMove(window: &view, pos: position);
1973 axis += invert ? -threshold : threshold;
1974 QTest::mouseMove(window: &view, pos: position);
1975 QTRY_COMPARE(outer->drag()->active(), false);
1976 QTRY_COMPARE(inner->drag()->active(), true);
1977 QTest::mouseRelease(window: &view, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: position);
1978}
1979
1980void tst_QQuickMouseArea::nestedFlickableStopAtBounds()
1981{
1982 QQuickView view;
1983 QByteArray errorMessage;
1984 QVERIFY2(QQuickTest::initView(view, testFileUrl("nestedFlickableStopAtBounds.qml"), false, &errorMessage), errorMessage.constData());
1985 view.show();
1986 view.requestActivate();
1987 QVERIFY(QTest::qWaitForWindowExposed(&view));
1988 QVERIFY(view.rootObject());
1989
1990 QQuickMouseArea *mouseArea = view.rootObject()->findChild<QQuickMouseArea*>(aName: "mouseArea");
1991 QVERIFY(mouseArea);
1992
1993 QQuickFlickable *flickable = mouseArea->findChild<QQuickFlickable*>(aName: "flickable");
1994 QVERIFY(flickable);
1995
1996 const int threshold = qApp->styleHints()->startDragDistance();
1997
1998 QPoint position(200, 280);
1999 int &pos = position.ry();
2000
2001 // Drag up - should move the Flickable to end
2002 QTest::mousePress(window: &view, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: position);
2003 QTest::qWait(ms: 10);
2004 pos -= threshold * 2;
2005 QTest::mouseMove(window: &view, pos: position);
2006 pos -= threshold * 2;
2007 QTest::mouseMove(window: &view, pos: position);
2008 QTest::qWait(ms: 10);
2009 pos -= 150;
2010 QTest::mouseMove(window: &view, pos: position);
2011 QVERIFY(flickable->isDragging());
2012 QVERIFY(!mouseArea->drag()->active());
2013 QCOMPARE(flickable->isAtYEnd(), true);
2014 QTest::mouseRelease(window: &view, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: position);
2015
2016 QTRY_VERIFY(!flickable->isMoving());
2017
2018 pos = 280;
2019
2020 // Drag up again - should activate MouseArea drag
2021 QVERIFY(!mouseArea->drag()->active());
2022 QTest::mousePress(window: &view, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: position);
2023 QTest::qWait(ms: 10);
2024 pos -= threshold * 2;
2025 QTest::mouseMove(window: &view, pos: position);
2026 pos -= threshold * 2;
2027 QTest::mouseMove(window: &view, pos: position);
2028 QTest::qWait(ms: 10);
2029 pos -= 20;
2030 QTest::mouseMove(window: &view, pos: position);
2031 QVERIFY(mouseArea->drag()->active());
2032 QCOMPARE(flickable->isAtYEnd(), true);
2033 QVERIFY(!flickable->isDragging());
2034 QTest::mouseRelease(window: &view, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: position);
2035
2036 // Drag to the top and verify that the MouseArea doesn't steal the grab when we drag back (QTBUG-56036)
2037 pos = 50;
2038
2039 QTest::mousePress(window: &view, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: position);
2040 QTest::qWait(ms: 10);
2041 pos += threshold;
2042 QTest::mouseMove(window: &view, pos: position);
2043 pos += threshold;
2044 QTest::mouseMove(window: &view, pos: position);
2045 QTest::qWait(ms: 10);
2046 pos += 150;
2047 QTest::mouseMove(window: &view, pos: position);
2048 QVERIFY(flickable->isDragging());
2049 QVERIFY(!mouseArea->drag()->active());
2050 QCOMPARE(flickable->isAtYBeginning(), true);
2051 QTest::mouseRelease(window: &view, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: position);
2052
2053 QTRY_VERIFY(!flickable->isMoving());
2054
2055 pos = 280;
2056
2057 // Drag up again - should not activate MouseArea drag
2058 QTest::mousePress(window: &view, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: position);
2059 QTest::qWait(ms: 10);
2060 pos -= threshold;
2061 QTest::mouseMove(window: &view, pos: position);
2062 pos -= threshold;
2063 QTest::mouseMove(window: &view, pos: position);
2064 QTest::qWait(ms: 10);
2065 pos -= 100;
2066 QTest::mouseMove(window: &view, pos: position);
2067 QVERIFY(flickable->isDragging());
2068 QVERIFY(!mouseArea->drag()->active());
2069 QTest::mouseRelease(window: &view, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: position);
2070}
2071
2072void tst_QQuickMouseArea::containsPress_data()
2073{
2074 QTest::addColumn<bool>(name: "hoverEnabled");
2075
2076 QTest::newRow(dataTag: "hover enabled") << true;
2077 QTest::newRow(dataTag: "hover disaabled") << false;
2078}
2079
2080void tst_QQuickMouseArea::containsPress()
2081{
2082 QFETCH(bool, hoverEnabled);
2083
2084 QQuickView window;
2085 QByteArray errorMessage;
2086 QVERIFY2(QQuickTest::initView(window, testFileUrl("containsPress.qml"), true, &errorMessage), errorMessage.constData());
2087 window.show();
2088 window.requestActivate();
2089 QVERIFY(QTest::qWaitForWindowExposed(&window));
2090 QQuickItem *root = window.rootObject();
2091 QVERIFY(root != nullptr);
2092
2093 QQuickMouseArea *mouseArea = window.rootObject()->findChild<QQuickMouseArea*>(aName: "mouseArea");
2094 QVERIFY(mouseArea != nullptr);
2095
2096 QSignalSpy containsPressSpy(mouseArea, SIGNAL(containsPressChanged()));
2097
2098 mouseArea->setHoverEnabled(hoverEnabled);
2099
2100 QTest::mouseMove(window: &window, pos: QPoint(22,33));
2101 QCOMPARE(mouseArea->hovered(), false);
2102 QCOMPARE(mouseArea->pressed(), false);
2103 QCOMPARE(mouseArea->containsPress(), false);
2104
2105 QTest::mouseMove(window: &window, pos: QPoint(200,200));
2106 QCOMPARE(mouseArea->hovered(), hoverEnabled);
2107 QCOMPARE(mouseArea->pressed(), false);
2108 QCOMPARE(mouseArea->containsPress(), false);
2109
2110 QTest::mousePress(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(200,200));
2111 QCOMPARE(mouseArea->hovered(), true);
2112 QTRY_COMPARE(mouseArea->pressed(), true);
2113 QCOMPARE(mouseArea->containsPress(), true);
2114 QCOMPARE(containsPressSpy.count(), 1);
2115
2116 QTest::mouseMove(window: &window, pos: QPoint(22,33));
2117 QCOMPARE(mouseArea->hovered(), false);
2118 QCOMPARE(mouseArea->pressed(), true);
2119 QCOMPARE(mouseArea->containsPress(), false);
2120 QCOMPARE(containsPressSpy.count(), 2);
2121
2122 QTest::mouseMove(window: &window, pos: QPoint(200,200));
2123 QCOMPARE(mouseArea->hovered(), true);
2124 QCOMPARE(mouseArea->pressed(), true);
2125 QCOMPARE(mouseArea->containsPress(), true);
2126 QCOMPARE(containsPressSpy.count(), 3);
2127
2128 QTest::mouseRelease(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(200,200));
2129 QCOMPARE(mouseArea->hovered(), hoverEnabled);
2130 QCOMPARE(mouseArea->pressed(), false);
2131 QCOMPARE(mouseArea->containsPress(), false);
2132 QCOMPARE(containsPressSpy.count(), 4);
2133}
2134
2135void tst_QQuickMouseArea::ignoreBySource()
2136{
2137 QQuickView window;
2138 QByteArray errorMessage;
2139 QVERIFY2(QQuickTest::initView(window, testFileUrl("ignoreBySource.qml"), true, &errorMessage), errorMessage.constData());
2140 window.show();
2141 QVERIFY(QTest::qWaitForWindowExposed(&window));
2142 QVERIFY(window.rootObject());
2143
2144 QQuickItem *root = qobject_cast<QQuickItem*>(object: window.rootObject());
2145 QVERIFY(root);
2146
2147 QQuickMouseArea *mouseArea = root->findChild<QQuickMouseArea*>(aName: "mousearea");
2148 QVERIFY(mouseArea);
2149
2150 QQuickFlickable *flickable = root->findChild<QQuickFlickable*>(aName: "flickable");
2151 QVERIFY(flickable);
2152
2153 // MouseArea should grab the press because it's interested in non-synthesized mouse events
2154 QPoint p = QPoint(80, 80);
2155 QTest::mousePress(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p);
2156 QCOMPARE(window.mouseGrabberItem(), mouseArea);
2157 // That was a real mouse event
2158 QCOMPARE(root->property("lastEventSource").toInt(), int(Qt::MouseEventNotSynthesized));
2159
2160 // Flickable content should not move
2161 p -= QPoint(startDragDistance() + 1, startDragDistance() + 1);
2162 QTest::mouseMove(window: &window, pos: p);
2163 p -= QPoint(11, 11);
2164 QTest::mouseMove(window: &window, pos: p);
2165 p -= QPoint(11, 11);
2166 QTest::mouseMove(window: &window, pos: p);
2167 QCOMPARE(flickable->contentX(), 0.);
2168 QCOMPARE(flickable->contentY(), 0.);
2169
2170 QTest::mouseRelease(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p);
2171 QCOMPARE(window.mouseGrabberItem(), nullptr);
2172
2173 // Now try touch events and confirm that MouseArea ignores them, while Flickable does its thing
2174 p = QPoint(80, 80);
2175 QTest::touchEvent(window: &window, device).press(touchId: 0, pt: p, window: &window);
2176 QQuickTouchUtils::flush(window: &window);
2177 QCOMPARE(window.mouseGrabberItem(), flickable);
2178
2179 // That was a fake mouse event
2180 QCOMPARE(root->property("lastEventSource").toInt(), int(Qt::MouseEventSynthesizedByQt));
2181 p -= QPoint(startDragDistance() + 1, startDragDistance() + 1);
2182 QTest::touchEvent(window: &window, device).move(touchId: 0, pt: p, window: &window);
2183 p -= QPoint(11, 11);
2184 QTest::touchEvent(window: &window, device).move(touchId: 0, pt: p, window: &window);
2185 p -= QPoint(11, 11);
2186 QTest::touchEvent(window: &window, device).move(touchId: 0, pt: p, window: &window);
2187
2188 QQuickTouchUtils::flush(window: &window);
2189 QCOMPARE(window.mouseGrabberItem(), flickable);
2190 QTest::touchEvent(window: &window, device).release(touchId: 0, pt: p, window: &window);
2191 QQuickTouchUtils::flush(window: &window);
2192
2193 // Flickable content should have moved
2194 QTRY_VERIFY(flickable->contentX() > 1);
2195 QVERIFY(flickable->contentY() > 1);
2196
2197
2198 // Now tell the MouseArea to accept only synthesized events, and repeat the tests
2199 root->setProperty(name: "allowedSource", value: Qt::MouseEventSynthesizedByQt);
2200 flickable->setContentX(0);
2201 flickable->setContentY(0);
2202
2203
2204 // MouseArea should ignore the press because it's interested in synthesized mouse events
2205 p = QPoint(80, 80);
2206 QTest::mousePress(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p);
2207 QVERIFY(window.mouseGrabberItem() != mouseArea);
2208 // That was a real mouse event
2209 QVERIFY(root->property("lastEventSource").toInt() == Qt::MouseEventNotSynthesized);
2210
2211 // Flickable content should move
2212 p -= QPoint(startDragDistance() + 1, startDragDistance() + 1);
2213 QTest::mouseMove(window: &window, pos: p);
2214 p -= QPoint(11, 11);
2215 QTest::mouseMove(window: &window, pos: p);
2216 p -= QPoint(11, 11);
2217 QTest::mouseMove(window: &window, pos: p);
2218 QTRY_VERIFY(flickable->contentX() > 1);
2219 QVERIFY(flickable->contentY() > 1);
2220
2221 QTest::mouseRelease(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(47, 47));
2222 flickable->setContentX(0);
2223 flickable->setContentY(0);
2224
2225 // Now try touch events and confirm that MouseArea gets them, while Flickable doesn't
2226 p = QPoint(80, 80);
2227 QTest::touchEvent(window: &window, device).press(touchId: 0, pt: p, window: &window);
2228 QQuickTouchUtils::flush(window: &window);
2229 QCOMPARE(window.mouseGrabberItem(), mouseArea);
2230 p -= QPoint(startDragDistance() + 1, startDragDistance() + 1);
2231 QTest::touchEvent(window: &window, device).move(touchId: 0, pt: p, window: &window);
2232 p -= QPoint(11, 11);
2233 QTest::touchEvent(window: &window, device).move(touchId: 0, pt: p, window: &window);
2234 p -= QPoint(11, 11);
2235 QTest::touchEvent(window: &window, device).move(touchId: 0, pt: p, window: &window);
2236 QQuickTouchUtils::flush(window: &window);
2237 QCOMPARE(window.mouseGrabberItem(), mouseArea);
2238 QTest::touchEvent(window: &window, device).release(touchId: 0, pt: QPoint(47,47), window: &window);
2239 QQuickTouchUtils::flush(window: &window);
2240
2241 // Flickable content should not have moved
2242 QCOMPARE(flickable->contentX(), 0.);
2243 QCOMPARE(flickable->contentY(), 0.);
2244}
2245
2246void tst_QQuickMouseArea::notPressedAfterStolenGrab()
2247{
2248 QQuickWindow window;
2249 window.resize(w: 200, h: 200);
2250 window.show();
2251 QVERIFY(QTest::qWaitForWindowExposed(&window));
2252
2253 QQuickMouseArea *ma = new QQuickMouseArea(window.contentItem());
2254 ma->setSize(window.size());
2255 QObject::connect(sender: ma,
2256 signal: static_cast<void (QQuickMouseArea::*)(QQuickMouseEvent*)>(&QQuickMouseArea::pressed),
2257 slot: [&]() { window.contentItem()->grabMouse(); });
2258
2259 QTest::mouseClick(window: &window, button: Qt::LeftButton);
2260 QVERIFY(!ma->pressed());
2261}
2262
2263void tst_QQuickMouseArea::pressAndHold_data()
2264{
2265 QTest::addColumn<int>(name: "pressAndHoldInterval");
2266 QTest::addColumn<int>(name: "waitTime");
2267
2268 QTest::newRow(dataTag: "default") << -1 << QGuiApplication::styleHints()->mousePressAndHoldInterval();
2269 QTest::newRow(dataTag: "short") << 500 << 500;
2270 QTest::newRow(dataTag: "long") << 1000 << 1000;
2271}
2272
2273void tst_QQuickMouseArea::pressAndHold()
2274{
2275 QFETCH(int, pressAndHoldInterval);
2276 QFETCH(int, waitTime);
2277
2278 QQuickView window;
2279 QByteArray errorMessage;
2280 QVERIFY2(QQuickTest::initView(window, testFileUrl("pressAndHold.qml"), true, &errorMessage), errorMessage.constData());
2281 window.show();
2282 window.requestActivate();
2283 QVERIFY(QTest::qWaitForWindowExposed(&window));
2284 QQuickItem *root = window.rootObject();
2285 QVERIFY(root != nullptr);
2286
2287 QQuickMouseArea *mouseArea = window.rootObject()->findChild<QQuickMouseArea*>(aName: "mouseArea");
2288 QVERIFY(mouseArea != nullptr);
2289
2290 QSignalSpy pressAndHoldSpy(mouseArea, &QQuickMouseArea::pressAndHold);
2291
2292 if (pressAndHoldInterval > -1)
2293 mouseArea->setPressAndHoldInterval(pressAndHoldInterval);
2294 else
2295 mouseArea->resetPressAndHoldInterval();
2296
2297 QElapsedTimer t;
2298 t.start();
2299 QTest::mousePress(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(50, 50));
2300 QVERIFY(pressAndHoldSpy.wait());
2301 // should be off by no more than 20% of waitTime
2302 QVERIFY(qAbs(t.elapsed() - waitTime) < (waitTime * 0.2));
2303 QTest::mouseRelease(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(50, 50));
2304}
2305
2306void tst_QQuickMouseArea::pressOneAndTapAnother_data()
2307{
2308 QTest::addColumn<bool>(name: "pressMouseFirst");
2309 QTest::addColumn<bool>(name: "releaseMouseFirst");
2310
2311 QTest::newRow(dataTag: "press mouse, tap touch, release mouse") << true << false; // QTBUG-64249 as written
2312 QTest::newRow(dataTag: "press touch, press mouse, release touch, release mouse") << false << false;
2313 QTest::newRow(dataTag: "press mouse, press touch, release mouse, release touch") << true << true;
2314 // TODO fix in a separate patch after the 5.9->5.10 merge
2315 // QTest::newRow("press touch, click mouse, release touch") << false << true;
2316}
2317
2318void tst_QQuickMouseArea::pressOneAndTapAnother()
2319{
2320 QFETCH(bool, pressMouseFirst);
2321 QFETCH(bool, releaseMouseFirst);
2322
2323 QQuickView window;
2324 QByteArray errorMessage;
2325 QVERIFY2(QQuickTest::initView(window, testFileUrl("twoMouseAreas.qml"), true, &errorMessage), errorMessage.constData());
2326 window.show();
2327 window.requestActivate();
2328 QVERIFY(QTest::qWaitForWindowExposed(&window));
2329 QQuickItem *root = window.rootObject();
2330 QVERIFY(root);
2331 QQuickMouseArea *bottomMA = window.rootObject()->findChild<QQuickMouseArea*>(aName: "bottom");
2332 QVERIFY(bottomMA);
2333 QQuickMouseArea *topMA = window.rootObject()->findChild<QQuickMouseArea*>(aName: "top");
2334 QVERIFY(topMA);
2335
2336 QPoint upper(32, 32);
2337 QPoint lower(32, window.height() - 32);
2338
2339 // press them both
2340 if (pressMouseFirst) {
2341 QTest::mousePress(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: lower);
2342 QTRY_COMPARE(bottomMA->pressed(), true);
2343
2344 QTest::touchEvent(window: &window, device).press(touchId: 0, pt: lower, window: &window);
2345 QQuickTouchUtils::flush(window: &window);
2346 QTRY_COMPARE(bottomMA->pressed(), true);
2347 } else {
2348 QTest::touchEvent(window: &window, device).press(touchId: 0, pt: lower, window: &window);
2349 QQuickTouchUtils::flush(window: &window);
2350 QTRY_COMPARE(bottomMA->pressed(), true);
2351
2352 QTest::mousePress(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: lower);
2353 QTRY_COMPARE(bottomMA->pressed(), true);
2354 }
2355
2356 // release them both and make sure neither one gets stuck
2357 if (releaseMouseFirst) {
2358 QTest::mouseRelease(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: lower);
2359 QTRY_COMPARE(bottomMA->pressed(), false);
2360
2361 QTest::touchEvent(window: &window, device).release(touchId: 0, pt: upper, window: &window);
2362 QQuickTouchUtils::flush(window: &window);
2363 QTRY_COMPARE(topMA->pressed(), false);
2364 } else {
2365 QTest::touchEvent(window: &window, device).release(touchId: 0, pt: upper, window: &window);
2366 QQuickTouchUtils::flush(window: &window);
2367
2368 QTRY_COMPARE(topMA->pressed(), false);
2369 QTest::mouseRelease(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: lower);
2370 QTRY_COMPARE(bottomMA->pressed(), false);
2371 }
2372}
2373
2374void tst_QQuickMouseArea::mask()
2375{
2376 QQuickView window;
2377 QByteArray errorMessage;
2378 QVERIFY2(QQuickTest::initView(window, testFileUrl("mask.qml"), true, &errorMessage), errorMessage.constData());
2379 window.show();
2380 window.requestActivate();
2381 QVERIFY(QTest::qWaitForWindowExposed(&window));
2382 QQuickItem *root = window.rootObject();
2383 QVERIFY(root != nullptr);
2384
2385 // click inside the mask, and verify it registers
2386 QTest::mousePress(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(100,100));
2387 QTest::mouseRelease(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(100,100));
2388
2389 QCOMPARE(window.rootObject()->property("pressed").toInt(), 1);
2390 QCOMPARE(window.rootObject()->property("released").toInt(), 1);
2391 QCOMPARE(window.rootObject()->property("clicked").toInt(), 1);
2392
2393 // click outside the mask (but inside the MouseArea), and verify it doesn't register
2394 QTest::mousePress(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(10,10));
2395 QTest::mouseRelease(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(10,10));
2396
2397 QCOMPARE(window.rootObject()->property("pressed").toInt(), 1);
2398 QCOMPARE(window.rootObject()->property("released").toInt(), 1);
2399 QCOMPARE(window.rootObject()->property("clicked").toInt(), 1);
2400}
2401
2402void tst_QQuickMouseArea::nestedEventDelivery() // QTBUG-70898
2403{
2404 QQmlEngine engine;
2405 QQmlComponent c(&engine, testFileUrl(fileName: "nestedSendEvent.qml"));
2406 QScopedPointer<QQuickWindow> window(qmlobject_cast<QQuickWindow *>(object: c.create()));
2407 QVERIFY(window.data());
2408
2409 // Click each MouseArea and verify that it doesn't crash
2410 QByteArray message = "event went missing during delivery! (nested sendEvent() is not allowed)";
2411 QTest::ignoreMessage(type: QtWarningMsg, message);
2412 QTest::mouseClick(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(50,50));
2413 QTest::ignoreMessage(type: QtWarningMsg, message); // twice though, actually
2414 QTest::mouseClick(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(50,150));
2415}
2416
2417void tst_QQuickMouseArea::settingHiddenInPressUngrabs()
2418{
2419 // When an item sets itself hidden, while handling pressed, it doesn't receive the grab.
2420 // But that in turn means it doesn't see any release events, so we need to make sure it
2421 // receives an ungrab event.
2422
2423 QQmlEngine engine;
2424 QQmlComponent c(&engine, testFileUrl(fileName: "settingHiddenInPressUngrabs.qml"));
2425 QScopedPointer<QQuickWindow> window(qmlobject_cast<QQuickWindow *>(object: c.create()));
2426 QVERIFY(window.data());
2427 window->show();
2428 window->requestActivate();
2429 QVERIFY(QTest::qWaitForWindowExposed(window.data()));
2430
2431 QQuickMouseArea *catArea = window->findChild<QQuickMouseArea*>(aName: "cat");
2432 QVERIFY(catArea != nullptr);
2433 auto pointOnCatArea = catArea->mapToScene(point: QPointF(5.0, 5.0)).toPoint();
2434 QTest::mouseClick(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: pointOnCatArea);
2435
2436 QCoreApplication::processEvents();
2437 // The click hides the cat area
2438 QTRY_VERIFY(!catArea->isVisible());
2439 // The cat area is not stuck in pressed state.
2440 QVERIFY(!catArea->pressed());
2441
2442 QQuickMouseArea *mouseArea = window->findChild<QQuickMouseArea*>(aName: "mouse");
2443 QVERIFY(mouseArea != nullptr);
2444 auto pointOnMouseArea = mouseArea->mapToScene(point: QPointF(5.0, 5.0)).toPoint();
2445 QTest::mouseClick(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: pointOnMouseArea);
2446
2447 QCoreApplication::processEvents();
2448 // The click disables the mouse area
2449 QTRY_VERIFY(!mouseArea->isEnabled());
2450 // The mouse area is not stuck in pressed state.
2451 QVERIFY(!mouseArea->pressed());
2452}
2453
2454// QTBUG-87197
2455void tst_QQuickMouseArea::containsMouseAndVisibility()
2456{
2457 QQuickView window;
2458 QByteArray errorMessage;
2459 QVERIFY2(QQuickTest::initView(window, testFileUrl("containsMouse.qml"), true, &errorMessage), errorMessage.constData());
2460 window.show();
2461 window.requestActivate();
2462 QVERIFY(QTest::qWaitForWindowExposed(&window));
2463
2464 QQuickMouseArea *mouseArea = window.rootObject()->findChild<QQuickMouseArea*>(aName: "mouseArea");
2465 QVERIFY(mouseArea != nullptr);
2466 QVERIFY(!mouseArea->isVisible());
2467
2468 QTest::mouseMove(window: &window, pos: QPoint(10, 10));
2469 QTRY_VERIFY(!mouseArea->hovered());
2470
2471 mouseArea->setVisible(true);
2472 QVERIFY(mouseArea->isVisible());
2473 QTRY_VERIFY(mouseArea->hovered());
2474
2475 /* we (ab-)use QPointF() as the 'reset' value in QQuickWindow's leave-event handling,
2476 but can't verify that this leaves an invalid interpretation of states for position
2477 QPoint(0, 0) as QTest::mouseMove interprets a null-position as "center of the window".
2478
2479 So instead, verify the current (unexpectedly expected) behavior as far as testing is
2480 concern.
2481 */
2482 QTest::mouseMove(window: &window, pos: QPoint(0, 0));
2483 QTRY_VERIFY(mouseArea->hovered());
2484 QTRY_VERIFY(mouseArea->isUnderMouse());
2485
2486 // move to the edge (can't move outside)
2487 QTest::mouseMove(window: &window, pos: QPoint(window.width() - 1, window.height() / 2));
2488 // then pretend we left
2489 QEvent event(QEvent::Leave);
2490 QGuiApplication::sendEvent(receiver: &window, event: &event);
2491 QVERIFY(!mouseArea->hovered());
2492
2493 // toggle mouse area visibility - the hover state should not change
2494 mouseArea->setVisible(false);
2495 QVERIFY(!mouseArea->isVisible());
2496 QVERIFY(!mouseArea->hovered());
2497
2498 mouseArea->setVisible(true);
2499 QVERIFY(mouseArea->isVisible());
2500 QVERIFY(!mouseArea->hovered());
2501}
2502
2503QTEST_MAIN(tst_QQuickMouseArea)
2504
2505#include "tst_qquickmousearea.moc"
2506

source code of qtdeclarative/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp