@@ -19,6 +19,7 @@
19#include <QtQuick/QQuickView>
19#include <QtQuick/QQuickView>
20#include <QtQml/QQmlEngine>
20#include <QtQml/QQmlEngine>
21#include <QPointer>
21#include <QPointer>
22+#include <private/qquickmousearea_p.h>
23// C++ std lib
24// C++ std lib
24#include <functional>
25#include <functional>
@@ -87,6 +88,30 @@ public:
87 qint64 m_msecsSinceReference;
88 qint64 m_msecsSinceReference;
92+ QQuickMouseArea::canceled() signal is not registered in the meta object system.
93+ So using a QSignalSpy to track it won't work. Thus the only way to connect to it
94+ is using its method address directly.
96+class MouseAreaSpy : public QObject
100+ MouseAreaSpy(QQuickMouseArea *mouseArea)
103+ connect(mouseArea, &QQuickMouseArea::canceled,
104+ this, &MouseAreaSpy::onMouseAreaCanceled);
110+ void onMouseAreaCanceled() {
90class tst_DirectionalDragArea: public GestureTest
115class tst_DirectionalDragArea: public GestureTest
@@ -120,6 +145,7 @@ private Q_SLOTS:
120 void withdrawTouchOwnershipCandidacyIfDisabledDuringRecognition_data();
145 void withdrawTouchOwnershipCandidacyIfDisabledDuringRecognition_data();
121 void tappedSignal();
146 void tappedSignal();
122 void tappedSignal_data();
147 void tappedSignal_data();
148+ void gettingTouchOwnershipMakesMouseAreaBehindGetCanceled();
125 void passTime(qint64 timeSpanMs);
151 void passTime(qint64 timeSpanMs);
@@ -1213,6 +1239,56 @@ void tst_DirectionalDragArea::tappedSignal_data()
1213 QTest::newRow("default gesture recognition") << false;
1239 QTest::newRow("default gesture recognition") << false;
1242+void tst_DirectionalDragArea::gettingTouchOwnershipMakesMouseAreaBehindGetCanceled()
1244+ DirectionalDragArea *edgeDragArea =
1245+ m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
1246+ QVERIFY(edgeDragArea != nullptr);
1247+ edgeDragArea->setRecognitionTimer(fakeTimer);
1248+ edgeDragArea->setTimeSource(fakeTimeSource);
1250+ QQuickMouseArea *mouseArea =
1251+ m_view->rootObject()->findChild<QQuickMouseArea*>("mouseArea");
1252+ QVERIFY(mouseArea != nullptr);
1254+ MouseAreaSpy mouseAreaSpy(mouseArea);
1256+ QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
1257+ QPointF touchPoint = initialTouchPos;
1259+ qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2;
1260+ QPointF dragDirectionVector(1.0f, 0.0f); // rightwards
1261+ qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
1262+ QPointF touchMovement = dragDirectionVector * movementStepDistance;
1263+ int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
1264+ int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
1266+ QCOMPARE(mouseArea->pressed(), false);
1268+ QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
1270+ // The TouchBegin passes through the DirectionalDragArea and reaches the MouseArea behind it,
1271+ // where it's converted to a MouseEvent by QQuickWindow and sent to the MouseArea which then
1272+ // accepts it. Making it get pressed.
1273+ QCOMPARE(mouseArea->pressed(), true);
1274+ QCOMPARE(mouseAreaSpy.canceledCount, 0);
1276+ for (int i = 0; i < totalMovementSteps; ++i) {
1277+ touchPoint += touchMovement;
1278+ passTime(movementTimeStepMs);
1279+ QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
1282+ // As the DirectionalDragArea recognizes the gesture, it grabs the touch from the MouseArea,
1283+ // which should make the MouseArea get a cancelation event, which will then cause it to
1284+ // reset its state (going back to "unpressed"/"released").
1285+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
1286+ QCOMPARE(mouseArea->pressed(), false);
1287+ QCOMPARE(mouseAreaSpy.canceledCount, 1);
1289+ QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
1216////////////////////////// TouchMemento /////////////////////////////
1292////////////////////////// TouchMemento /////////////////////////////
1218TouchMemento::TouchMemento(const QTouchEvent *touchEvent)
1294TouchMemento::TouchMemento(const QTouchEvent *touchEvent)