1/****************************************************************************
2**
3** Copyright (C) 2019 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtQml module 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
31#include <QtQml/qqmlengine.h>
32#include <QtQml/qqmlproperty.h>
33#include <QtQuick/private/qquickdraghandler_p.h>
34#include <QtQuick/private/qquickmousearea_p.h>
35#include <QtQuick/qquickitem.h>
36#include <QtQuick/qquickview.h>
37
38#include "../../../shared/util.h"
39#include "../../shared/viewtestutil.h"
40
41Q_LOGGING_CATEGORY(lcPointerTests, "qt.quick.pointer.tests")
42
43class tst_MouseAreaInterop : public QQmlDataTest
44{
45 Q_OBJECT
46public:
47 tst_MouseAreaInterop()
48 : touchDevice(QTest::createTouchDevice())
49 , touchPointerDevice(QQuickPointerDevice::touchDevice(d: touchDevice))
50 {}
51
52private slots:
53 void dragHandlerInSiblingStealingGrabFromMouseAreaViaMouse();
54 void dragHandlerInSiblingStealingGrabFromMouseAreaViaTouch_data();
55 void dragHandlerInSiblingStealingGrabFromMouseAreaViaTouch();
56
57private:
58 void createView(QScopedPointer<QQuickView> &window, const char *fileName);
59 QTouchDevice *touchDevice;
60 QQuickPointerDevice *touchPointerDevice;
61};
62
63void tst_MouseAreaInterop::createView(QScopedPointer<QQuickView> &window, const char *fileName)
64{
65 window.reset(other: new QQuickView);
66 window->setSource(testFileUrl(fileName));
67 QTRY_COMPARE(window->status(), QQuickView::Ready);
68 QQuickViewTestUtil::centerOnScreen(window: window.data());
69 QQuickViewTestUtil::moveMouseAway(window: window.data());
70
71 window->show();
72 QVERIFY(QTest::qWaitForWindowActive(window.data()));
73 QVERIFY(window->rootObject() != nullptr);
74}
75
76void tst_MouseAreaInterop::dragHandlerInSiblingStealingGrabFromMouseAreaViaMouse()
77{
78 const int dragThreshold = QGuiApplication::styleHints()->startDragDistance();
79 QScopedPointer<QQuickView> windowPtr;
80 createView(window&: windowPtr, fileName: "dragTakeOverFromSibling.qml");
81 QQuickView * window = windowPtr.data();
82 auto pointerEvent = QQuickWindowPrivate::get(c: window)->pointerEventInstance(device: QQuickPointerDevice::genericMouseDevice());
83
84 QPointer<QQuickPointerHandler> handler = window->rootObject()->findChild<QQuickPointerHandler*>();
85 QVERIFY(handler);
86 QQuickMouseArea *ma = window->rootObject()->findChild<QQuickMouseArea*>();
87 QVERIFY(ma);
88
89 QPoint p1(150, 150);
90 QTest::mousePress(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1);
91 QCOMPARE(window->mouseGrabberItem(), ma);
92 QCOMPARE(ma->pressed(), true);
93
94 // Start dragging
95 // DragHandler keeps monitoring, due to its passive grab,
96 // and eventually steals the exclusive grab from MA
97 int dragStoleGrab = 0;
98 for (int i = 0; i < 4; ++i) {
99 p1 += QPoint(dragThreshold / 2, 0);
100 QTest::mouseMove(window, pos: p1);
101 if (!dragStoleGrab && pointerEvent->point(i: 0)->exclusiveGrabber() == handler)
102 dragStoleGrab = i;
103 }
104 if (dragStoleGrab)
105 qCDebug(lcPointerTests, "DragHandler stole the grab after %d events", dragStoleGrab);
106 QVERIFY(dragStoleGrab > 1);
107 QCOMPARE(handler->active(), true);
108 QCOMPARE(ma->pressed(), false);
109
110 QTest::mouseRelease(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1);
111 QCOMPARE(handler->active(), false);
112}
113
114void tst_MouseAreaInterop::dragHandlerInSiblingStealingGrabFromMouseAreaViaTouch_data()
115{
116 QTest::addColumn<bool>(name: "preventStealing");
117
118 QTest::newRow(dataTag: "allow stealing") << false;
119 QTest::newRow(dataTag: "prevent stealing") << true;
120}
121
122void tst_MouseAreaInterop::dragHandlerInSiblingStealingGrabFromMouseAreaViaTouch() // QTBUG-77624 and QTBUG-79163
123{
124 QFETCH(bool, preventStealing);
125
126 const int dragThreshold = QGuiApplication::styleHints()->startDragDistance();
127 QScopedPointer<QQuickView> windowPtr;
128 createView(window&: windowPtr, fileName: "dragTakeOverFromSibling.qml");
129 QQuickView * window = windowPtr.data();
130 auto pointerEvent = QQuickWindowPrivate::get(c: window)->pointerEventInstance(device: touchPointerDevice);
131
132 QPointer<QQuickPointerHandler> handler = window->rootObject()->findChild<QQuickPointerHandler*>();
133 QVERIFY(handler);
134 QQuickMouseArea *ma = window->rootObject()->findChild<QQuickMouseArea*>();
135 QVERIFY(ma);
136 ma->setPreventStealing(preventStealing);
137
138 QPoint p1(150, 150);
139 QTest::QTouchEventSequence touch = QTest::touchEvent(window, device: touchDevice);
140
141 touch.press(touchId: 1, pt: p1).commit();
142 QQuickTouchUtils::flush(window);
143 QTRY_VERIFY(pointerEvent->point(0)->passiveGrabbers().contains(handler));
144 QCOMPARE(pointerEvent->point(0)->grabberItem(), ma);
145 QCOMPARE(window->mouseGrabberItem(), ma);
146 QCOMPARE(ma->pressed(), true);
147
148 // Start dragging
149 // DragHandler keeps monitoring, due to its passive grab,
150 // and eventually steals the exclusive grab from MA if MA allows it
151 int dragStoleGrab = 0;
152 for (int i = 0; i < 4; ++i) {
153 p1 += QPoint(dragThreshold / 2, 0);
154 touch.move(touchId: 1, pt: p1).commit();
155 QQuickTouchUtils::flush(window);
156 if (!dragStoleGrab && pointerEvent->point(i: 0)->exclusiveGrabber() == handler)
157 dragStoleGrab = i;
158 }
159 if (dragStoleGrab)
160 qCDebug(lcPointerTests, "DragHandler stole the grab after %d events", dragStoleGrab);
161 if (preventStealing) {
162 QCOMPARE(dragStoleGrab, 0);
163 QCOMPARE(handler->active(), false);
164 QCOMPARE(ma->pressed(), true);
165 } else {
166 QVERIFY(dragStoleGrab > 1);
167 QCOMPARE(handler->active(), true);
168 QCOMPARE(ma->pressed(), false);
169 }
170
171 touch.release(touchId: 1, pt: p1).commit();
172 QQuickTouchUtils::flush(window);
173 QCOMPARE(handler->active(), false);
174}
175
176QTEST_MAIN(tst_MouseAreaInterop)
177
178#include "tst_mousearea_interop.moc"
179

source code of qtdeclarative/tests/auto/quick/pointerhandlers/mousearea_interop/tst_mousearea_interop.cpp