@@ -19,23 +19,38 @@
19#include <qpa/qwindowsysteminterface.h>
19#include <qpa/qwindowsysteminterface.h>
20#include <QtQuick/QQuickView>
20#include <QtQuick/QQuickView>
21#include <QtQml/QQmlEngine>
21#include <QtQml/QQmlEngine>
23#include <DirectionalDragArea.h>
24#include <DirectionalDragArea.h>
26+using namespace UbuntuGestures;
25−class FakeTimer : public UbuntuGestures::AbstractTimer
28+class FakeTimer : public AbstractTimer
29− FakeTimer(QObject *parent = 0)
32+ FakeTimer(const SharedTimeSource &timeSource, QObject *parent = 0)
30− : UbuntuGestures::AbstractTimer(parent)
33+ : UbuntuGestures::AbstractTimer(parent),
34+ m_timeSource(timeSource)
33− virtual int interval() const { return m_duration; }
37+ int interval() const override { return m_interval; }
34− virtual void setInterval(int msecs) { m_duration = msecs; }
38+ void setInterval(int msecs) override { m_interval = msecs; }
39+ void start() override {
40+ AbstractTimer::start();
41+ m_nextTimeoutTime = m_timeSource->msecsSinceReference() + (qint64)m_interval;
45+ m_nextTimeoutTime += m_interval;
36− void emitTimeout() {Q_EMIT timeout();}
49+ qint64 nextTimeoutTime() const { return m_nextTimeoutTime; }
38− int m_duration;
51+ int m_interval;
52+ SharedTimeSource m_timeSource;
53+ qint64 m_nextTimeoutTime;
41class FakeTimeSource : public UbuntuGestures::TimeSource
56class FakeTimeSource : public UbuntuGestures::TimeSource
@@ -67,14 +82,17 @@ private Q_SLOTS:
67 void maxSilenceTime();
82 void maxSilenceTime();
68 void sceneXAndX();
83 void sceneXAndX();
69 void sceneYAndY();
84 void sceneYAndY();
70− void emitDraggingChangedOnDirectRecognition();
85+ void twoFingerTap();
87+ void ignoreOldFinger();
73 QQuickView *createView();
90 QQuickView *createView();
91+ void passTime(qint64 timeSpan);
74 QQuickView *view;
92 QQuickView *view;
75 QTouchDevice *device;
93 QTouchDevice *device;
76 FakeTimer *fakeTimer;
94 FakeTimer *fakeTimer;
77− FakeTimeSource *fakeTimeSource;
95+ QSharedPointer<FakeTimeSource> fakeTimeSource;
80void tst_DirectionalDragArea::initTestCase()
98void tst_DirectionalDragArea::initTestCase()
@@ -101,8 +119,8 @@ void tst_DirectionalDragArea::init()
101 QVERIFY(view->rootObject() != 0);
119 QVERIFY(view->rootObject() != 0);
102 qApp->processEvents();
120 qApp->processEvents();
122+ fakeTimeSource.reset(new FakeTimeSource);
104− fakeTimer = new FakeTimer;
123+ fakeTimer = new FakeTimer(fakeTimeSource);
105− fakeTimeSource = new FakeTimeSource;
108void tst_DirectionalDragArea::cleanup()
126void tst_DirectionalDragArea::cleanup()
@@ -113,8 +131,7 @@ void tst_DirectionalDragArea::cleanup()
113 delete fakeTimer;
131 delete fakeTimer;
114 fakeTimer = 0;
132 fakeTimer = 0;
116− delete fakeTimeSource;
134+ fakeTimeSource.reset();
120QQuickView *tst_DirectionalDragArea::createView()
137QQuickView *tst_DirectionalDragArea::createView()
@@ -127,6 +144,23 @@ QQuickView *tst_DirectionalDragArea::createView()
127 return window;
144 return window;
147+void tst_DirectionalDragArea::passTime(qint64 timeSpan)
149+ qint64 finalTime = fakeTimeSource->m_msecsSinceReference + timeSpan;
151+ if (fakeTimer->isRunning() && finalTime >= fakeTimer->nextTimeoutTime()) {
152+ fakeTimeSource->m_msecsSinceReference = fakeTimer->nextTimeoutTime();
153+ fakeTimer->emitTimeout();
155+ qint64 timeSpanRemainder = finalTime - fakeTimeSource->m_msecsSinceReference;
156+ if (timeSpanRemainder > 0) {
157+ passTime(timeSpanRemainder);
160+ fakeTimeSource->m_msecsSinceReference = finalTime;
130namespace {
164namespace {
131QPointF calculateInitialTouchPos(DirectionalDragArea *edgeDragArea, QQuickView *view)
165QPointF calculateInitialTouchPos(DirectionalDragArea *edgeDragArea, QQuickView *view)
@@ -180,7 +214,7 @@ void tst_DirectionalDragArea::edgeDrag()
180 QFETCH(QString, dragAreaObjectName);
214 QFETCH(QString, dragAreaObjectName);
181 QFETCH(qreal, wideningAngleMultiplier);
215 QFETCH(qreal, wideningAngleMultiplier);
182 QFETCH(qreal, dragDistanceFactor);
216 QFETCH(qreal, dragDistanceFactor);
183− QFETCH(int, expectedGestureRecognition);
217+ QFETCH(bool, expectGestureRecognition);
185 DirectionalDragArea *edgeDragArea =
219 DirectionalDragArea *edgeDragArea =
186 view->rootObject()->findChild<DirectionalDragArea*>(dragAreaObjectName);
220 view->rootObject()->findChild<DirectionalDragArea*>(dragAreaObjectName);
@@ -196,7 +230,10 @@ void tst_DirectionalDragArea::edgeDrag()
196 qreal desiredDragDistance = edgeDragArea->distanceThreshold()*dragDistanceFactor;
230 qreal desiredDragDistance = edgeDragArea->distanceThreshold()*dragDistanceFactor;
197 QPointF dragDirectionVector = calculateDirectionVector(edgeDragArea,
231 QPointF dragDirectionVector = calculateDirectionVector(edgeDragArea,
198 wideningAngleMultiplier);
232 wideningAngleMultiplier);
199− QPointF touchMovement = dragDirectionVector * (edgeDragArea->distanceThreshold() * 0.1f);
233+ qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
234+ QPointF touchMovement = dragDirectionVector * movementStepDistance;
235+ int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
236+ int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
201 QTest::touchEvent(view, device).press(0, touchPoint.toPoint());
238 QTest::touchEvent(view, device).press(0, touchPoint.toPoint());
@@ -211,15 +248,16 @@ void tst_DirectionalDragArea::edgeDrag()
211 QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
248 QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
214− do {
251+ for (int i = 0; i < totalMovementSteps; ++i) {
215 touchPoint += touchMovement;
252 touchPoint += touchMovement;
253+ passTime(movementTimeStepMs);
216 QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
254 QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
217− } while ((touchPoint - initialTouchPos).manhattanLength() < desiredDragDistance);
255+ }
219− if (expectedGestureRecognition)
257+ if (expectGestureRecognition)
220 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
258 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
222− if (edgeDragArea->status() == DirectionalDragArea::Rejected) {
260+ if (edgeDragArea->status() == DirectionalDragArea::WaitingForTouch) {
223 QCOMPARE(edgeDragArea->dragging(), false);
261 QCOMPARE(edgeDragArea->dragging(), false);
224 QCOMPARE(draggingSpy.count(), 2);
262 QCOMPARE(draggingSpy.count(), 2);
@@ -235,49 +273,49 @@ void tst_DirectionalDragArea::edgeDrag_data()
235 QTest::addColumn<QString>("dragAreaObjectName");
273 QTest::addColumn<QString>("dragAreaObjectName");
236 QTest::addColumn<qreal>("wideningAngleMultiplier");
274 QTest::addColumn<qreal>("wideningAngleMultiplier");
237 QTest::addColumn<qreal>("dragDistanceFactor");
275 QTest::addColumn<qreal>("dragDistanceFactor");
238− QTest::addColumn<int>("expectedGestureRecognition");
276+ QTest::addColumn<bool>("expectGestureRecognition");
240 QTest::newRow("rightwards, tiny drag")
278 QTest::newRow("rightwards, tiny drag")
241− << "hpDragArea" << 0.0 << 0.2 << 0;
279+ << "hpDragArea" << 0.0 << 0.2 << false;
243 QTest::newRow("rightwards, straight drag")
281 QTest::newRow("rightwards, straight drag")
244− << "hpDragArea" << 0.0 << 3.0 << 1;
282+ << "hpDragArea" << 0.0 << 3.0 << true;
246 QTest::newRow("rightwards, diagonal drag")
284 QTest::newRow("rightwards, diagonal drag")
247− << "hpDragArea" << 0.9 << 3.0 << 1;
285+ << "hpDragArea" << 0.9 << 3.0 << true;
249 QTest::newRow("rightwards, overly diagonal drag")
287 QTest::newRow("rightwards, overly diagonal drag")
250− << "hpDragArea" << 2.0 << 3.0 << 0;
288+ << "hpDragArea" << 2.0 << 3.0 << false;
252 QTest::newRow("leftwards, tiny drag")
290 QTest::newRow("leftwards, tiny drag")
253− << "hnDragArea" << 0.0 << 0.2 << 0;
291+ << "hnDragArea" << 0.0 << 0.2 << false;
255 QTest::newRow("leftwards, straight drag")
293 QTest::newRow("leftwards, straight drag")
256− << "hnDragArea" << 0.0 << 3.0 << 1;
294+ << "hnDragArea" << 0.0 << 3.0 << true;
258 QTest::newRow("leftwards, diagonal drag")
296 QTest::newRow("leftwards, diagonal drag")
259− << "hnDragArea" << 0.9 << 3.0 << 1;
297+ << "hnDragArea" << 0.9 << 3.0 << true;
261 QTest::newRow("downwards, tiny drag")
299 QTest::newRow("downwards, tiny drag")
262− << "vpDragArea" << 0.0 << 0.2 << 0;
300+ << "vpDragArea" << 0.0 << 0.2 << false;
264 QTest::newRow("downwards, straight drag")
302 QTest::newRow("downwards, straight drag")
265− << "vpDragArea" << 0.0 << 3.0 << 1;
303+ << "vpDragArea" << 0.0 << 3.0 << true;
267 QTest::newRow("downwards, diagonal drag")
305 QTest::newRow("downwards, diagonal drag")
268− << "vpDragArea" << 0.9 << 3.0 << 1;
306+ << "vpDragArea" << 0.9 << 3.0 << true;
270 QTest::newRow("upwards, tiny drag")
308 QTest::newRow("upwards, tiny drag")
271− << "vnDragArea" << 0.0 << 0.2 << 0;
309+ << "vnDragArea" << 0.0 << 0.2 << false;
273 QTest::newRow("upwards, straight drag")
311 QTest::newRow("upwards, straight drag")
274− << "vnDragArea" << 0.0 << 3.0 << 1;
312+ << "vnDragArea" << 0.0 << 3.0 << true;
276 QTest::newRow("upwards, diagonal drag")
314 QTest::newRow("upwards, diagonal drag")
277− << "vnDragArea" << 0.9 << 3.0 << 1;
315+ << "vnDragArea" << 0.9 << 3.0 << true;
279 QTest::newRow("upwards, overly diagonal drag")
317 QTest::newRow("upwards, overly diagonal drag")
280− << "vnDragArea" << 2.0 << 3.0 << 0;
318+ << "vnDragArea" << 2.0 << 3.0 << false;
@@ -298,25 +336,33 @@ void tst_DirectionalDragArea::dragWithShortDirectionChange()
299 qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0;
337 qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0;
300 QPointF dragDirectionVector(1.0, 0.0);
338 QPointF dragDirectionVector(1.0, 0.0);
301− QPointF touchMovement = dragDirectionVector * (edgeDragArea->distanceThreshold() * 0.1f);
339+ qreal touchStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
340+ // make sure we are above minimum speed
341+ int touchStepTimeMs = (touchStepDistance / (edgeDragArea->minSpeed() * 5.0f)) * 1000.0f;
342+ QPointF touchMovement = dragDirectionVector * touchStepDistance;
303 QTest::touchEvent(view, device).press(0, touchPoint.toPoint());
344 QTest::touchEvent(view, device).press(0, touchPoint.toPoint());
305 // Move a bit in the proper direction
346 // Move a bit in the proper direction
306 for (int i=0; i < 3; ++i) {
347 for (int i=0; i < 3; ++i) {
307 touchPoint += touchMovement;
348 touchPoint += touchMovement;
349+ passTime(touchStepTimeMs);
308 QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
350 QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
311 // Then a sudden and small movement to the opposite direction
353 // Then a sudden and small movement to the opposite direction
312− touchPoint -= dragDirectionVector * (edgeDragArea->maxDeviation() * 0.7);
354+ touchPoint -= touchMovement*0.2;
355+ passTime(touchStepTimeMs*0.2);
313 QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
356 QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
315− // And then resume movment in the correct direction until it crosses the distance threshold.
358+ // And then resume movment in the correct direction until it crosses the distance and time
317 touchPoint += touchMovement;
361 touchPoint += touchMovement;
362+ passTime(touchStepTimeMs);
318 QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
363 QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
319− } while ((touchPoint - initialTouchPos).manhattanLength() < desiredDragDistance);
364+ } while ((touchPoint - initialTouchPos).manhattanLength() < desiredDragDistance
365+ || fakeTimeSource->m_msecsSinceReference < (edgeDragArea->compositionTime() * 1.5f));
321 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
367 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
@@ -329,7 +375,8 @@ void tst_DirectionalDragArea::dragWithShortDirectionChange()
330void tst_DirectionalDragArea::minSpeed()
376void tst_DirectionalDragArea::minSpeed()
332− QFETCH(int, minSpeedMsecsDeviation);
378+ QFETCH(qreal, minSpeed);
379+ QFETCH(qreal, speed);
333 QFETCH(int, expectedStatusAfterSpeedCheck);
380 QFETCH(int, expectedStatusAfterSpeedCheck);
335 DirectionalDragArea *edgeDragArea =
382 DirectionalDragArea *edgeDragArea =
@@ -338,44 +385,40 @@ void tst_DirectionalDragArea::minSpeed()
338 edgeDragArea->setRecognitionTimer(fakeTimer);
385 edgeDragArea->setRecognitionTimer(fakeTimer);
339 edgeDragArea->setTimeSource(fakeTimeSource);
386 edgeDragArea->setTimeSource(fakeTimeSource);
388+ // A really long, unattainable, number. We don't want it getting recognized before
389+ // the speed checks we want have been performed
390+ edgeDragArea->setDistanceThreshold(500000);
392+ edgeDragArea->setMinSpeed(minSpeed);
341 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, view);
394 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, view);
342 QPointF touchPoint = initialTouchPos;
395 QPointF touchPoint = initialTouchPos;
344 QPointF dragDirectionVector(1.0, 0.0);
397 QPointF dragDirectionVector(1.0, 0.0);
345− qreal distanceStep = edgeDragArea->distanceThreshold() * 0.1f;
398+ qint64 timeStepMsecs = 10;
399+ qreal distanceStep = (speed / 1000.0f) * timeStepMsecs;
346 QPointF touchMovement = dragDirectionVector * distanceStep;
400 QPointF touchMovement = dragDirectionVector * distanceStep;
347− qreal minSpeedMsecs = edgeDragArea->minSpeed() / 1000.0;
348− qint64 timeStepMsecs = qFloor(distanceStep / minSpeedMsecs) + minSpeedMsecsDeviation;
350− // if it fails it means the params set in the QML file are not in harmony with the values
351− // used in this test
352− Q_ASSERT(timeStepMsecs > 0);
354− fakeTimeSource->m_msecsSinceReference = 0;
355 QTest::touchEvent(view, device).press(0, touchPoint.toPoint());
402 QTest::touchEvent(view, device).press(0, touchPoint.toPoint());
357− // Move a bit in the proper direction
404+ // Move for a while to ensure our speed check is performed a couple of times
358− for (int i=0; i < 4; ++i) {
405+ for (int i=0; i < 20; ++i) {
359 touchPoint += touchMovement;
406 touchPoint += touchMovement;
360− fakeTimeSource->m_msecsSinceReference += timeStepMsecs;
407+ passTime(timeStepMsecs);
361 QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
408 QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
364− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
366− // Force the periodic speed check.
367− fakeTimer->emitTimeout();
369 QCOMPARE((int)edgeDragArea->status(), expectedStatusAfterSpeedCheck);
411 QCOMPARE((int)edgeDragArea->status(), expectedStatusAfterSpeedCheck);
372void tst_DirectionalDragArea::minSpeed_data()
414void tst_DirectionalDragArea::minSpeed_data()
374− QTest::addColumn<int>("minSpeedMsecsDeviation");
416+ QTest::addColumn<qreal>("minSpeed");
417+ QTest::addColumn<qreal>("speed");
375 QTest::addColumn<int>("expectedStatusAfterSpeedCheck");
418 QTest::addColumn<int>("expectedStatusAfterSpeedCheck");
377− QTest::newRow("slower than minSpeed") << 20 << (int)DirectionalDragArea::Rejected;
420+ QTest::newRow("slower than minSpeed") << 100.0 << 50.0 << (int)DirectionalDragArea::WaitingForTouch;
378− QTest::newRow("faster than minSpeed") << -20 << (int)DirectionalDragArea::Undecided;
421+ QTest::newRow("faster than minSpeed") << 100.0 << 150.0 << (int)DirectionalDragArea::Undecided;
@@ -391,32 +434,37 @@ void tst_DirectionalDragArea::recognitionTimerUsage()
391 edgeDragArea->setRecognitionTimer(fakeTimer);
434 edgeDragArea->setRecognitionTimer(fakeTimer);
392 edgeDragArea->setTimeSource(fakeTimeSource);
435 edgeDragArea->setTimeSource(fakeTimeSource);
437+ // don't let it interfere with our test
438+ edgeDragArea->setMinSpeed(0.0);
440+ int timeStepMs = 5; // some arbitrary small value.
394 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, view);
442 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, view);
395 QPointF touchPoint = initialTouchPos;
443 QPointF touchPoint = initialTouchPos;
397 QPointF dragDirectionVector(1.0, 0.0);
445 QPointF dragDirectionVector(1.0, 0.0);
398 QPointF touchMovement = dragDirectionVector * (edgeDragArea->distanceThreshold() * 0.2f);
446 QPointF touchMovement = dragDirectionVector * (edgeDragArea->distanceThreshold() * 0.2f);
448+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
400 QVERIFY(!fakeTimer->isRunning());
449 QVERIFY(!fakeTimer->isRunning());
402 QTest::touchEvent(view, device).press(0, touchPoint.toPoint());
451 QTest::touchEvent(view, device).press(0, touchPoint.toPoint());
453+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
404 QVERIFY(fakeTimer->isRunning());
454 QVERIFY(fakeTimer->isRunning());
406− // Move a bit in the proper direction
456+ // Move beyond distance threshold and composition time to ensure recognition
407− for (int i=0; i < 3; ++i) {
457+ while (fakeTimeSource->m_msecsSinceReference <= edgeDragArea->compositionTime() ||
458+ (touchPoint - initialTouchPos).manhattanLength() <= edgeDragArea->distanceThreshold()) {
460+ QCOMPARE(edgeDragArea->status() == DirectionalDragArea::Undecided, fakeTimer->isRunning());
408 touchPoint += touchMovement;
462 touchPoint += touchMovement;
463+ passTime(timeStepMs);
409 QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
464 QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
412− QVERIFY(fakeTimer->isRunning());
414− // Move beyond distance threshold
415− touchPoint += 3*touchMovement;
416− QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
418 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
467 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
420 QVERIFY(!fakeTimer->isRunning());
468 QVERIFY(!fakeTimer->isRunning());
@@ -432,29 +480,23 @@ void tst_DirectionalDragArea::maxSilenceTime()
432 edgeDragArea->setRecognitionTimer(fakeTimer);
480 edgeDragArea->setRecognitionTimer(fakeTimer);
433 edgeDragArea->setTimeSource(fakeTimeSource);
481 edgeDragArea->setTimeSource(fakeTimeSource);
483+ // Make sure this property is not disabled
484+ edgeDragArea->setMaxSilenceTime(100);
435 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, view);
486 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, view);
436 QPointF touchPoint = initialTouchPos;
487 QPointF touchPoint = initialTouchPos;
438− fakeTimeSource->m_msecsSinceReference = 0;
439 QTest::touchEvent(view, device).press(0, touchPoint.toPoint());
489 QTest::touchEvent(view, device).press(0, touchPoint.toPoint());
441 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
491 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
492+ QVERIFY(fakeTimer->isRunning());
443− int neededTimeouts = qCeil((qreal)edgeDragArea->maxSilenceTime() / (qreal)fakeTimer->interval());
494+ // Force timer to timeout until after maxSilenceTime has been reached
444− if ((edgeDragArea->maxSilenceTime() % fakeTimer->interval()) == 0) {
495+ while (fakeTimeSource->m_msecsSinceReference < edgeDragArea->maxSilenceTime()) {
445− ++neededTimeouts;
496+ passTime(fakeTimer->interval());
448− // Force the periodic speed check.
449− for (int i = 0; i < neededTimeouts; ++i) {
450− fakeTimer->emitTimeout();
452− if (i < neededTimeouts - 1) {
453− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
457− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Rejected);
499+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
@@ -471,7 +513,6 @@ void tst_DirectionalDragArea::sceneXAndX()
472 QPointF touchScenePos(view->width() - (edgeDragArea->width()/2.0f), view->height()/2.0f);
514 QPointF touchScenePos(view->width() - (edgeDragArea->width()/2.0f), view->height()/2.0f);
474− fakeTimeSource->m_msecsSinceReference = 0;
475 QTest::touchEvent(view, device).press(0, touchScenePos.toPoint());
516 QTest::touchEvent(view, device).press(0, touchScenePos.toPoint());
477 QSignalSpy touchXSpy(edgeDragArea, SIGNAL(touchXChanged(qreal)));
518 QSignalSpy touchXSpy(edgeDragArea, SIGNAL(touchXChanged(qreal)));
@@ -500,7 +541,6 @@ void tst_DirectionalDragArea::sceneYAndY()
501 QPointF touchScenePos(view->width()/2.0f, view->height() - (edgeDragArea->height()/2.0f));
542 QPointF touchScenePos(view->width()/2.0f, view->height() - (edgeDragArea->height()/2.0f));
503− fakeTimeSource->m_msecsSinceReference = 0;
504 QTest::touchEvent(view, device).press(0, touchScenePos.toPoint());
544 QTest::touchEvent(view, device).press(0, touchScenePos.toPoint());
506 QSignalSpy touchYSpy(edgeDragArea, SIGNAL(touchYChanged(qreal)));
546 QSignalSpy touchYSpy(edgeDragArea, SIGNAL(touchYChanged(qreal)));
@@ -516,11 +556,12 @@ void tst_DirectionalDragArea::sceneYAndY()
519− Emit draggingChanged() when status change from WaitingForTouch directly to Recognized.
559+ Regression test for https://bugs.launchpad.net/bugs/1228336
520− This happens when the gesture recognition is effectively disabled (e.g. by setting a
560+
521− distanceThreshold of zero)
561+ Perform a quick two-finger double-tap and check that DirectionalDragArea properly
562+ rejects those touch points. In the bug above it got confused and crashed.
523−void tst_DirectionalDragArea::emitDraggingChangedOnDirectRecognition()
564+void tst_DirectionalDragArea::twoFingerTap()
525 DirectionalDragArea *edgeDragArea =
566 DirectionalDragArea *edgeDragArea =
526 view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
567 view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
@@ -528,22 +569,170 @@ void tst_DirectionalDragArea::emitDraggingChangedOnDirectRecognition()
528 edgeDragArea->setRecognitionTimer(fakeTimer);
569 edgeDragArea->setRecognitionTimer(fakeTimer);
529 edgeDragArea->setTimeSource(fakeTimeSource);
570 edgeDragArea->setTimeSource(fakeTimeSource);
531− QPointF touchPos = calculateInitialTouchPos(edgeDragArea, view);
572+ // Make touches evenly spaced along the edgeDragArea
573+ QPoint touchAPos(edgeDragArea->width()/2.0f, view->height()*0.33f);
574+ QPoint touchBPos(edgeDragArea->width()/2.0f, view->height()*0.66f);
533− QSignalSpy draggingSpy(edgeDragArea, SIGNAL(draggingChanged(bool)));
576+ qint64 timeStepMsecs = 5; // some arbitrary, small value
535− // That will make it completetly skip the recognition phase ("Undecided" status).
578+ // Perform the first two-finger tap
536− edgeDragArea->setDistanceThreshold(0.0);
579+ // NB: using move() instead of stationary() becasue in the latter you cannot provide
580+ // the touch position and therefore it's left with some garbage numbers.
581+ QTest::touchEvent(view, device)
582+ .press(0, touchAPos);
584+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
586+ passTime(timeStepMsecs);
587+ QTest::touchEvent(view, device)
589+ .press(1, touchBPos);
591+ // A second touch point appeared during recognition, reject immediately as this
592+ // can't be a single-touch gesture anymore.
538 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
593 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
539− QCOMPARE(edgeDragArea->dragging(), false);
541− fakeTimeSource->m_msecsSinceReference = 0;
595+ passTime(timeStepMsecs);
596+ QTest::touchEvent(view, device)
597+ .release(0, touchAPos)
598+ .move(1, touchBPos);
600+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
602+ passTime(timeStepMsecs);
603+ QTest::touchEvent(view, device)
604+ .release(1, touchBPos);
606+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
608+ // Perform the second two-finger tap
610+ passTime(timeStepMsecs);
611+ QTest::touchEvent(view, device)
612+ .press(0, touchAPos);
614+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
616+ passTime(timeStepMsecs);
617+ QTest::touchEvent(view, device)
619+ .press(1, touchBPos);
621+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
623+ passTime(timeStepMsecs);
624+ QTest::touchEvent(view, device)
625+ .release(0, touchAPos)
626+ .move(1, touchBPos);
628+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
630+ passTime(timeStepMsecs);
631+ QTest::touchEvent(view, device)
632+ .release(1, touchBPos);
634+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
638+ Tests that gesture recognition works normally even if the DirectionalDragArea moves
640+ This effectively means that we have to do gesture recognition with scene coordinates
641+ instead of local item coordinates.
643+void tst_DirectionalDragArea::movingDDA()
645+ QQuickItem *rightwardsLauncher = view->rootObject()->findChild<QQuickItem*>("rightwardsLauncher");
646+ Q_ASSERT(rightwardsLauncher != 0);
648+ DirectionalDragArea *edgeDragArea =
649+ rightwardsLauncher->findChild<DirectionalDragArea*>("hpDragArea");
650+ Q_ASSERT(edgeDragArea != 0);
651+ edgeDragArea->setRecognitionTimer(fakeTimer);
652+ edgeDragArea->setTimeSource(fakeTimeSource);
654+ QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, view);
655+ QPointF touchPoint = initialTouchPos;
657+ qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;
658+ QPointF dragDirectionVector(1.0f, 0.0f);
660+ qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
661+ QPointF touchMovement = dragDirectionVector * movementStepDistance;
662+ int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
663+ int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
542− QTest::touchEvent(view, device).press(0, touchPos.toPoint());
665+ QTest::touchEvent(view, device).press(0, touchPoint.toPoint());
667+ // Move it far ahead along the direction of the gesture
668+ // rightwardsLauncher is a parent of our DirectionalDragArea. So moving it will move our DDA
669+ rightwardsLauncher->setX(rightwardsLauncher->x() + edgeDragArea->distanceThreshold() * 5.0f);
671+ for (int i = 0; i < totalMovementSteps; ++i) {
672+ touchPoint += touchMovement;
673+ passTime(movementTimeStepMs);
674+ QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
544 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
677 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
545− QCOMPARE(draggingSpy.count(), 1);
678+
679+ QTest::touchEvent(view, device).release(0, touchPoint.toPoint());
683+ The presence of and old, rejected, touch point lying on the DirectionalDragArea
684+ shouldn't impede the recognition of a gesture from a new touch point.
686+void tst_DirectionalDragArea::ignoreOldFinger()
688+ DirectionalDragArea *edgeDragArea =
689+ view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
690+ Q_ASSERT(edgeDragArea != 0);
691+ edgeDragArea->setRecognitionTimer(fakeTimer);
692+ edgeDragArea->setTimeSource(fakeTimeSource);
694+ // Make touches evenly spaced along the edgeDragArea
695+ QPoint touch0Pos(edgeDragArea->width()/2.0f, view->height()*0.33f);
696+ QPoint touch1Pos(edgeDragArea->width()/2.0f, view->height()*0.66f);
698+ QTest::touchEvent(view, device).press(0, touch0Pos);
700+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
702+ // leave it lying around for some time
703+ passTime(qMax(edgeDragArea->maxSilenceTime(), edgeDragArea->compositionTime()) * 10);
705+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
707+ qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;
708+ QPointF dragDirectionVector(1.0f, 0.0f);
710+ qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
711+ QPointF touchMovement = dragDirectionVector * movementStepDistance;
712+ int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
713+ int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
715+ QTest::touchEvent(view, device)
717+ .press(1, touch1Pos);
546− QCOMPARE(edgeDragArea->dragging(), true);
719+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
721+ for (int i = 0; i < totalMovementSteps; ++i) {
722+ touch1Pos += touchMovement.toPoint();
723+ passTime(movementTimeStepMs);
724+ QTest::touchEvent(view, device)
726+ .move(1, touch1Pos);
729+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
731+ QTest::touchEvent(view, device)
733+ .release(1, touch1Pos);
735+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
549QTEST_MAIN(tst_DirectionalDragArea)
738QTEST_MAIN(tst_DirectionalDragArea)