@@ -16,26 +16,42 @@
17#include <QtTest/QtTest>
17#include <QtTest/QtTest>
18#include <QtCore/QObject>
18#include <QtCore/QObject>
19−#include <qpa/qwindowsysteminterface.h>
20#include <QtQuick/QQuickView>
19#include <QtQuick/QQuickView>
21#include <QtQml/QQmlEngine>
20#include <QtQml/QQmlEngine>
23#include <DirectionalDragArea.h>
23#include <DirectionalDragArea.h>
25+#include "GestureTest.h"
27+using namespace UbuntuGestures;
25−class FakeTimer : public UbuntuGestures::AbstractTimer
29+class FakeTimer : public AbstractTimer
29− FakeTimer(QObject *parent = 0)
33+ FakeTimer(const SharedTimeSource &timeSource, QObject *parent = 0)
30− : UbuntuGestures::AbstractTimer(parent)
34+ : UbuntuGestures::AbstractTimer(parent),
35+ m_timeSource(timeSource)
33− virtual int interval() const { return m_duration; }
38+ int interval() const override { return m_interval; }
34− virtual void setInterval(int msecs) { m_duration = msecs; }
39+ void setInterval(int msecs) override { m_interval = msecs; }
40+ void start() override {
41+ AbstractTimer::start();
42+ m_nextTimeoutTime = m_timeSource->msecsSinceReference() + (qint64)m_interval;
46+ m_nextTimeoutTime += m_interval;
36− void emitTimeout() {Q_EMIT timeout();}
50+ qint64 nextTimeoutTime() const { return m_nextTimeoutTime; }
38− int m_duration;
52+ int m_interval;
53+ SharedTimeSource m_timeSource;
54+ qint64 m_nextTimeoutTime;
41class FakeTimeSource : public UbuntuGestures::TimeSource
57class FakeTimeSource : public UbuntuGestures::TimeSource
@@ -46,15 +62,12 @@ public:
46 qint64 m_msecsSinceReference;
62 qint64 m_msecsSinceReference;
49−class tst_DirectionalDragArea: public QObject
65+class tst_DirectionalDragArea: public GestureTest
53− tst_DirectionalDragArea() : device(0) { }
69+ tst_DirectionalDragArea();
54private Q_SLOTS:
70private Q_SLOTS:
55− void initTestCase(); // will be called before the first test function is executed
56− void cleanupTestCase(); // will be called after the last test function was executed.
58 void init(); // called right before each and every test function is executed
71 void init(); // called right before each and every test function is executed
59 void cleanup(); // called right after each and every test function is executed
72 void cleanup(); // called right after each and every test function is executed
@@ -67,64 +80,54 @@ private Q_SLOTS:
67 void maxSilenceTime();
80 void maxSilenceTime();
68 void sceneXAndX();
81 void sceneXAndX();
69 void sceneYAndY();
82 void sceneYAndY();
70− void emitDraggingChangedOnDirectRecognition();
83+ void twoFingerTap();
85+ void ignoreOldFinger();
73− QQuickView *createView();
88+ void passTime(qint64 timeSpan);
75− QTouchDevice *device;
76 FakeTimer *fakeTimer;
89 FakeTimer *fakeTimer;
77− FakeTimeSource *fakeTimeSource;
90+ QSharedPointer<FakeTimeSource> fakeTimeSource;
80−void tst_DirectionalDragArea::initTestCase()
93+tst_DirectionalDragArea::tst_DirectionalDragArea()
81−{
94+ : GestureTest(QStringLiteral("tst_DirectionalDragArea.qml"))
83− device = new QTouchDevice;
84− device->setType(QTouchDevice::TouchScreen);
85− QWindowSystemInterface::registerTouchDevice(device);
91−void tst_DirectionalDragArea::cleanupTestCase()
95void tst_DirectionalDragArea::init()
98void tst_DirectionalDragArea::init()
97− view = createView();
100+ GestureTest::init();
98− view->setSource(QUrl::fromLocalFile("edgeDragExample.qml"));
101+
99− view->show();
102+ fakeTimeSource.reset(new FakeTimeSource);
100− QVERIFY(QTest::qWaitForWindowExposed(view));
101− QVERIFY(view->rootObject() != 0);
102− qApp->processEvents();
104− fakeTimer = new FakeTimer;
103+ fakeTimer = new FakeTimer(fakeTimeSource);
105− fakeTimeSource = new FakeTimeSource;
108void tst_DirectionalDragArea::cleanup()
106void tst_DirectionalDragArea::cleanup()
113 delete fakeTimer;
108 delete fakeTimer;
114 fakeTimer = 0;
109 fakeTimer = 0;
116− delete fakeTimeSource;
111+ fakeTimeSource.reset();
117− fakeTimeSource = 0;
112+
113+ GestureTest::cleanup();
120−QQuickView *tst_DirectionalDragArea::createView()
116+void tst_DirectionalDragArea::passTime(qint64 timeSpan)
122− QQuickView *window = new QQuickView(0);
118+ qint64 finalTime = fakeTimeSource->m_msecsSinceReference + timeSpan;
123− window->setResizeMode(QQuickView::SizeRootObjectToView);
119+
124− window->resize(600, 600);
120+ if (fakeTimer->isRunning() && finalTime >= fakeTimer->nextTimeoutTime()) {
125− window->engine()->addImportPath(QLatin1String(UBUNTU_GESTURES_PLUGIN_DIR));
121+ fakeTimeSource->m_msecsSinceReference = fakeTimer->nextTimeoutTime();
122+ fakeTimer->emitTimeout();
127− return window;
124+ qint64 timeSpanRemainder = finalTime - fakeTimeSource->m_msecsSinceReference;
125+ if (timeSpanRemainder > 0) {
126+ passTime(timeSpanRemainder);
129+ fakeTimeSource->m_msecsSinceReference = finalTime;
130namespace {
133namespace {
@@ -180,25 +183,28 @@ void tst_DirectionalDragArea::edgeDrag()
180 QFETCH(QString, dragAreaObjectName);
183 QFETCH(QString, dragAreaObjectName);
181 QFETCH(qreal, wideningAngleMultiplier);
184 QFETCH(qreal, wideningAngleMultiplier);
182 QFETCH(qreal, dragDistanceFactor);
185 QFETCH(qreal, dragDistanceFactor);
183− QFETCH(int, expectedGestureRecognition);
186+ QFETCH(bool, expectGestureRecognition);
185 DirectionalDragArea *edgeDragArea =
188 DirectionalDragArea *edgeDragArea =
186− view->rootObject()->findChild<DirectionalDragArea*>(dragAreaObjectName);
189+ m_view->rootObject()->findChild<DirectionalDragArea*>(dragAreaObjectName);
187 QVERIFY(edgeDragArea != 0);
190 QVERIFY(edgeDragArea != 0);
188 edgeDragArea->setRecognitionTimer(fakeTimer);
191 edgeDragArea->setRecognitionTimer(fakeTimer);
189 edgeDragArea->setTimeSource(fakeTimeSource);
192 edgeDragArea->setTimeSource(fakeTimeSource);
191 QSignalSpy draggingSpy(edgeDragArea, SIGNAL(draggingChanged(bool)));
194 QSignalSpy draggingSpy(edgeDragArea, SIGNAL(draggingChanged(bool)));
193− QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, view);
196+ QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, m_view);
194 QPointF touchPoint = initialTouchPos;
197 QPointF touchPoint = initialTouchPos;
196 qreal desiredDragDistance = edgeDragArea->distanceThreshold()*dragDistanceFactor;
199 qreal desiredDragDistance = edgeDragArea->distanceThreshold()*dragDistanceFactor;
197 QPointF dragDirectionVector = calculateDirectionVector(edgeDragArea,
200 QPointF dragDirectionVector = calculateDirectionVector(edgeDragArea,
198 wideningAngleMultiplier);
201 wideningAngleMultiplier);
199− QPointF touchMovement = dragDirectionVector * (edgeDragArea->distanceThreshold() * 0.1f);
202+ qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
203+ QPointF touchMovement = dragDirectionVector * movementStepDistance;
204+ int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
205+ int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
201− QTest::touchEvent(view, device).press(0, touchPoint.toPoint());
207+ QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
203 QCOMPARE(draggingSpy.count(), 1);
209 QCOMPARE(draggingSpy.count(), 1);
204 QCOMPARE(edgeDragArea->dragging(), true);
210 QCOMPARE(edgeDragArea->dragging(), true);
@@ -208,23 +214,24 @@ void tst_DirectionalDragArea::edgeDrag()
208 // in order to make it easier to leave it by dragging at an angle
214 // in order to make it easier to leave it by dragging at an angle
209 // slightly bigger than the widening angle
215 // slightly bigger than the widening angle
210 touchPoint += createTouchDeviation(edgeDragArea);
216 touchPoint += createTouchDeviation(edgeDragArea);
211− QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
217+ QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
214− do {
220+ for (int i = 0; i < totalMovementSteps; ++i) {
215 touchPoint += touchMovement;
221 touchPoint += touchMovement;
222+ passTime(movementTimeStepMs);
216− QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
223+ QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
217− } while ((touchPoint - initialTouchPos).manhattanLength() < desiredDragDistance);
224+ }
219− if (expectedGestureRecognition)
226+ if (expectGestureRecognition)
220 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
227 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
222− if (edgeDragArea->status() == DirectionalDragArea::Rejected) {
229+ if (edgeDragArea->status() == DirectionalDragArea::WaitingForTouch) {
223 QCOMPARE(edgeDragArea->dragging(), false);
230 QCOMPARE(edgeDragArea->dragging(), false);
224 QCOMPARE(draggingSpy.count(), 2);
231 QCOMPARE(draggingSpy.count(), 2);
227− QTest::touchEvent(view, device).release(0, touchPoint.toPoint());
234+ QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
229 QCOMPARE(draggingSpy.count(), 2);
236 QCOMPARE(draggingSpy.count(), 2);
230 QCOMPARE(edgeDragArea->dragging(), false);
237 QCOMPARE(edgeDragArea->dragging(), false);
@@ -235,49 +242,49 @@ void tst_DirectionalDragArea::edgeDrag_data()
235 QTest::addColumn<QString>("dragAreaObjectName");
242 QTest::addColumn<QString>("dragAreaObjectName");
236 QTest::addColumn<qreal>("wideningAngleMultiplier");
243 QTest::addColumn<qreal>("wideningAngleMultiplier");
237 QTest::addColumn<qreal>("dragDistanceFactor");
244 QTest::addColumn<qreal>("dragDistanceFactor");
238− QTest::addColumn<int>("expectedGestureRecognition");
245+ QTest::addColumn<bool>("expectGestureRecognition");
240 QTest::newRow("rightwards, tiny drag")
247 QTest::newRow("rightwards, tiny drag")
241− << "hpDragArea" << 0.0 << 0.2 << 0;
248+ << "hpDragArea" << 0.0 << 0.2 << false;
243 QTest::newRow("rightwards, straight drag")
250 QTest::newRow("rightwards, straight drag")
244− << "hpDragArea" << 0.0 << 3.0 << 1;
251+ << "hpDragArea" << 0.0 << 3.0 << true;
246 QTest::newRow("rightwards, diagonal drag")
253 QTest::newRow("rightwards, diagonal drag")
247− << "hpDragArea" << 0.9 << 3.0 << 1;
254+ << "hpDragArea" << 0.9 << 3.0 << true;
249 QTest::newRow("rightwards, overly diagonal drag")
256 QTest::newRow("rightwards, overly diagonal drag")
250− << "hpDragArea" << 2.0 << 3.0 << 0;
257+ << "hpDragArea" << 2.0 << 3.0 << false;
252 QTest::newRow("leftwards, tiny drag")
259 QTest::newRow("leftwards, tiny drag")
253− << "hnDragArea" << 0.0 << 0.2 << 0;
260+ << "hnDragArea" << 0.0 << 0.2 << false;
255 QTest::newRow("leftwards, straight drag")
262 QTest::newRow("leftwards, straight drag")
256− << "hnDragArea" << 0.0 << 3.0 << 1;
263+ << "hnDragArea" << 0.0 << 3.0 << true;
258 QTest::newRow("leftwards, diagonal drag")
265 QTest::newRow("leftwards, diagonal drag")
259− << "hnDragArea" << 0.9 << 3.0 << 1;
266+ << "hnDragArea" << 0.9 << 3.0 << true;
261 QTest::newRow("downwards, tiny drag")
268 QTest::newRow("downwards, tiny drag")
262− << "vpDragArea" << 0.0 << 0.2 << 0;
269+ << "vpDragArea" << 0.0 << 0.2 << false;
264 QTest::newRow("downwards, straight drag")
271 QTest::newRow("downwards, straight drag")
265− << "vpDragArea" << 0.0 << 3.0 << 1;
272+ << "vpDragArea" << 0.0 << 3.0 << true;
267 QTest::newRow("downwards, diagonal drag")
274 QTest::newRow("downwards, diagonal drag")
268− << "vpDragArea" << 0.9 << 3.0 << 1;
275+ << "vpDragArea" << 0.9 << 3.0 << true;
270 QTest::newRow("upwards, tiny drag")
277 QTest::newRow("upwards, tiny drag")
271− << "vnDragArea" << 0.0 << 0.2 << 0;
278+ << "vnDragArea" << 0.0 << 0.2 << false;
273 QTest::newRow("upwards, straight drag")
280 QTest::newRow("upwards, straight drag")
274− << "vnDragArea" << 0.0 << 3.0 << 1;
281+ << "vnDragArea" << 0.0 << 3.0 << true;
276 QTest::newRow("upwards, diagonal drag")
283 QTest::newRow("upwards, diagonal drag")
277− << "vnDragArea" << 0.9 << 3.0 << 1;
284+ << "vnDragArea" << 0.9 << 3.0 << true;
279 QTest::newRow("upwards, overly diagonal drag")
286 QTest::newRow("upwards, overly diagonal drag")
280− << "vnDragArea" << 2.0 << 3.0 << 0;
287+ << "vnDragArea" << 2.0 << 3.0 << false;
@@ -288,39 +295,47 @@ void tst_DirectionalDragArea::edgeDrag_data()
288void tst_DirectionalDragArea::dragWithShortDirectionChange()
295void tst_DirectionalDragArea::dragWithShortDirectionChange()
290 DirectionalDragArea *edgeDragArea =
297 DirectionalDragArea *edgeDragArea =
291− view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
298+ m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
292 QVERIFY(edgeDragArea != 0);
299 QVERIFY(edgeDragArea != 0);
293 edgeDragArea->setRecognitionTimer(fakeTimer);
300 edgeDragArea->setRecognitionTimer(fakeTimer);
294 edgeDragArea->setTimeSource(fakeTimeSource);
301 edgeDragArea->setTimeSource(fakeTimeSource);
296− QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, view);
303+ QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, m_view);
297 QPointF touchPoint = initialTouchPos;
304 QPointF touchPoint = initialTouchPos;
299 qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0;
306 qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0;
300 QPointF dragDirectionVector(1.0, 0.0);
307 QPointF dragDirectionVector(1.0, 0.0);
301− QPointF touchMovement = dragDirectionVector * (edgeDragArea->distanceThreshold() * 0.1f);
308+ qreal touchStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
309+ // make sure we are above minimum speed
310+ int touchStepTimeMs = (touchStepDistance / (edgeDragArea->minSpeed() * 5.0f)) * 1000.0f;
311+ QPointF touchMovement = dragDirectionVector * touchStepDistance;
303− QTest::touchEvent(view, device).press(0, touchPoint.toPoint());
313+ QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
305 // Move a bit in the proper direction
315 // Move a bit in the proper direction
306 for (int i=0; i < 3; ++i) {
316 for (int i=0; i < 3; ++i) {
307 touchPoint += touchMovement;
317 touchPoint += touchMovement;
318+ passTime(touchStepTimeMs);
308− QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
319+ QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
311 // Then a sudden and small movement to the opposite direction
322 // Then a sudden and small movement to the opposite direction
312− touchPoint -= dragDirectionVector * (edgeDragArea->maxDeviation() * 0.7);
323+ touchPoint -= touchMovement*0.2;
324+ passTime(touchStepTimeMs*0.2);
313− QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
325+ QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
315− // And then resume movment in the correct direction until it crosses the distance threshold.
327+ // And then resume movment in the correct direction until it crosses the distance and time
317 touchPoint += touchMovement;
330 touchPoint += touchMovement;
331+ passTime(touchStepTimeMs);
318− QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
332+ QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
319− } while ((touchPoint - initialTouchPos).manhattanLength() < desiredDragDistance);
333+ } while ((touchPoint - initialTouchPos).manhattanLength() < desiredDragDistance
334+ || fakeTimeSource->m_msecsSinceReference < (edgeDragArea->compositionTime() * 1.5f));
321 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
336 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
323− QTest::touchEvent(view, device).release(0, touchPoint.toPoint());
338+ QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
@@ -329,53 +344,50 @@ void tst_DirectionalDragArea::dragWithShortDirectionChange()
330void tst_DirectionalDragArea::minSpeed()
345void tst_DirectionalDragArea::minSpeed()
332− QFETCH(int, minSpeedMsecsDeviation);
347+ QFETCH(qreal, minSpeed);
348+ QFETCH(qreal, speed);
333 QFETCH(int, expectedStatusAfterSpeedCheck);
349 QFETCH(int, expectedStatusAfterSpeedCheck);
335 DirectionalDragArea *edgeDragArea =
351 DirectionalDragArea *edgeDragArea =
336− view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
352+ m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
337 QVERIFY(edgeDragArea != 0);
353 QVERIFY(edgeDragArea != 0);
338 edgeDragArea->setRecognitionTimer(fakeTimer);
354 edgeDragArea->setRecognitionTimer(fakeTimer);
339 edgeDragArea->setTimeSource(fakeTimeSource);
355 edgeDragArea->setTimeSource(fakeTimeSource);
357+ // A really long, unattainable, number. We don't want it getting recognized before
358+ // the speed checks we want have been performed
359+ edgeDragArea->setDistanceThreshold(500000);
361+ edgeDragArea->setMinSpeed(minSpeed);
341− QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, view);
363+ QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, m_view);
342 QPointF touchPoint = initialTouchPos;
364 QPointF touchPoint = initialTouchPos;
344 QPointF dragDirectionVector(1.0, 0.0);
366 QPointF dragDirectionVector(1.0, 0.0);
345− qreal distanceStep = edgeDragArea->distanceThreshold() * 0.1f;
367+ qint64 timeStepMsecs = 10;
368+ qreal distanceStep = (speed / 1000.0f) * timeStepMsecs;
346 QPointF touchMovement = dragDirectionVector * distanceStep;
369 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
371+ QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
351− // used in this test
352− Q_ASSERT(timeStepMsecs > 0);
354− fakeTimeSource->m_msecsSinceReference = 0;
373+ // Move for a while to ensure our speed check is performed a couple of times
355− QTest::touchEvent(view, device).press(0, touchPoint.toPoint());
357− // Move a bit in the proper direction
358− for (int i=0; i < 4; ++i) {
374+ for (int i=0; i < 20; ++i) {
359 touchPoint += touchMovement;
375 touchPoint += touchMovement;
360− fakeTimeSource->m_msecsSinceReference += timeStepMsecs;
376+ passTime(timeStepMsecs);
361− QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
377+ QTest::touchEvent(m_view, m_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);
380 QCOMPARE((int)edgeDragArea->status(), expectedStatusAfterSpeedCheck);
372void tst_DirectionalDragArea::minSpeed_data()
383void tst_DirectionalDragArea::minSpeed_data()
374− QTest::addColumn<int>("minSpeedMsecsDeviation");
385+ QTest::addColumn<qreal>("minSpeed");
386+ QTest::addColumn<qreal>("speed");
375 QTest::addColumn<int>("expectedStatusAfterSpeedCheck");
387 QTest::addColumn<int>("expectedStatusAfterSpeedCheck");
377− QTest::newRow("slower than minSpeed") << 20 << (int)DirectionalDragArea::Rejected;
389+ QTest::newRow("slower than minSpeed") << 100.0 << 50.0 << (int)DirectionalDragArea::WaitingForTouch;
378− QTest::newRow("faster than minSpeed") << -20 << (int)DirectionalDragArea::Undecided;
390+ QTest::newRow("faster than minSpeed") << 100.0 << 150.0 << (int)DirectionalDragArea::Undecided;
@@ -386,37 +398,42 @@ void tst_DirectionalDragArea::minSpeed_data()
386void tst_DirectionalDragArea::recognitionTimerUsage()
398void tst_DirectionalDragArea::recognitionTimerUsage()
388 DirectionalDragArea *edgeDragArea =
400 DirectionalDragArea *edgeDragArea =
389− view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
401+ m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
390 QVERIFY(edgeDragArea != 0);
402 QVERIFY(edgeDragArea != 0);
391 edgeDragArea->setRecognitionTimer(fakeTimer);
403 edgeDragArea->setRecognitionTimer(fakeTimer);
392 edgeDragArea->setTimeSource(fakeTimeSource);
404 edgeDragArea->setTimeSource(fakeTimeSource);
406+ // don't let it interfere with our test
407+ edgeDragArea->setMinSpeed(0.0);
409+ int timeStepMs = 5; // some arbitrary small value.
394− QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, view);
411+ QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, m_view);
395 QPointF touchPoint = initialTouchPos;
412 QPointF touchPoint = initialTouchPos;
397 QPointF dragDirectionVector(1.0, 0.0);
414 QPointF dragDirectionVector(1.0, 0.0);
398 QPointF touchMovement = dragDirectionVector * (edgeDragArea->distanceThreshold() * 0.2f);
415 QPointF touchMovement = dragDirectionVector * (edgeDragArea->distanceThreshold() * 0.2f);
417+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
400 QVERIFY(!fakeTimer->isRunning());
418 QVERIFY(!fakeTimer->isRunning());
402− QTest::touchEvent(view, device).press(0, touchPoint.toPoint());
420+ QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
422+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
404 QVERIFY(fakeTimer->isRunning());
423 QVERIFY(fakeTimer->isRunning());
406− // Move a bit in the proper direction
425+ // Move beyond distance threshold and composition time to ensure recognition
407− for (int i=0; i < 3; ++i) {
426+ while (fakeTimeSource->m_msecsSinceReference <= edgeDragArea->compositionTime() ||
408− touchPoint += touchMovement;
427+ (touchPoint - initialTouchPos).manhattanLength() <= edgeDragArea->distanceThreshold()) {
409− QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
412− QVERIFY(fakeTimer->isRunning());
429+ QCOMPARE(edgeDragArea->status() == DirectionalDragArea::Undecided, fakeTimer->isRunning());
414− // Move beyond distance threshold
415− touchPoint += 3*touchMovement;
431+ touchPoint += touchMovement;
432+ passTime(timeStepMs);
416− QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
433+ QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
418 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
436 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
420 QVERIFY(!fakeTimer->isRunning());
437 QVERIFY(!fakeTimer->isRunning());
@@ -427,34 +444,28 @@ void tst_DirectionalDragArea::recognitionTimerUsage()
427void tst_DirectionalDragArea::maxSilenceTime()
444void tst_DirectionalDragArea::maxSilenceTime()
429 DirectionalDragArea *edgeDragArea =
446 DirectionalDragArea *edgeDragArea =
430− view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
447+ m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
431 QVERIFY(edgeDragArea != 0);
448 QVERIFY(edgeDragArea != 0);
432 edgeDragArea->setRecognitionTimer(fakeTimer);
449 edgeDragArea->setRecognitionTimer(fakeTimer);
433 edgeDragArea->setTimeSource(fakeTimeSource);
450 edgeDragArea->setTimeSource(fakeTimeSource);
452+ // Make sure this property is not disabled
453+ edgeDragArea->setMaxSilenceTime(100);
435− QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, view);
455+ QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, m_view);
436 QPointF touchPoint = initialTouchPos;
456 QPointF touchPoint = initialTouchPos;
438− fakeTimeSource->m_msecsSinceReference = 0;
439− QTest::touchEvent(view, device).press(0, touchPoint.toPoint());
458+ QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
441 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
460 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
461+ QVERIFY(fakeTimer->isRunning());
443− int neededTimeouts = qCeil((qreal)edgeDragArea->maxSilenceTime() / (qreal)fakeTimer->interval());
463+ // Force timer to timeout until after maxSilenceTime has been reached
444− if ((edgeDragArea->maxSilenceTime() % fakeTimer->interval()) == 0) {
464+ while (fakeTimeSource->m_msecsSinceReference < edgeDragArea->maxSilenceTime()) {
445− ++neededTimeouts;
465+ 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);
468+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
@@ -464,21 +475,20 @@ void tst_DirectionalDragArea::maxSilenceTime()
464void tst_DirectionalDragArea::sceneXAndX()
475void tst_DirectionalDragArea::sceneXAndX()
466 DirectionalDragArea *edgeDragArea =
477 DirectionalDragArea *edgeDragArea =
467− view->rootObject()->findChild<DirectionalDragArea*>("hnDragArea");
478+ m_view->rootObject()->findChild<DirectionalDragArea*>("hnDragArea");
468 QVERIFY(edgeDragArea != 0);
479 QVERIFY(edgeDragArea != 0);
469 edgeDragArea->setRecognitionTimer(fakeTimer);
480 edgeDragArea->setRecognitionTimer(fakeTimer);
470 edgeDragArea->setTimeSource(fakeTimeSource);
481 edgeDragArea->setTimeSource(fakeTimeSource);
472− QPointF touchScenePos(view->width() - (edgeDragArea->width()/2.0f), view->height()/2.0f);
483+ QPointF touchScenePos(m_view->width() - (edgeDragArea->width()/2.0f), m_view->height()/2.0f);
474− fakeTimeSource->m_msecsSinceReference = 0;
475− QTest::touchEvent(view, device).press(0, touchScenePos.toPoint());
485+ QTest::touchEvent(m_view, m_device).press(0, touchScenePos.toPoint());
477 QSignalSpy touchXSpy(edgeDragArea, SIGNAL(touchXChanged(qreal)));
487 QSignalSpy touchXSpy(edgeDragArea, SIGNAL(touchXChanged(qreal)));
478 QSignalSpy touchSceneXSpy(edgeDragArea, SIGNAL(touchSceneXChanged(qreal)));
488 QSignalSpy touchSceneXSpy(edgeDragArea, SIGNAL(touchSceneXChanged(qreal)));
480− touchScenePos.rx() = view->width() / 2;
490+ touchScenePos.rx() = m_view->width() / 2;
481− QTest::touchEvent(view, device).move(0, touchScenePos.toPoint());
491+ QTest::touchEvent(m_view, m_device).move(0, touchScenePos.toPoint());
483 QCOMPARE(touchXSpy.count(), 1);
493 QCOMPARE(touchXSpy.count(), 1);
484 QCOMPARE(touchSceneXSpy.count(), 1);
494 QCOMPARE(touchSceneXSpy.count(), 1);
@@ -493,21 +503,20 @@ void tst_DirectionalDragArea::sceneXAndX()
493void tst_DirectionalDragArea::sceneYAndY()
503void tst_DirectionalDragArea::sceneYAndY()
495 DirectionalDragArea *edgeDragArea =
505 DirectionalDragArea *edgeDragArea =
496− view->rootObject()->findChild<DirectionalDragArea*>("vnDragArea");
506+ m_view->rootObject()->findChild<DirectionalDragArea*>("vnDragArea");
497 QVERIFY(edgeDragArea != 0);
507 QVERIFY(edgeDragArea != 0);
498 edgeDragArea->setRecognitionTimer(fakeTimer);
508 edgeDragArea->setRecognitionTimer(fakeTimer);
499 edgeDragArea->setTimeSource(fakeTimeSource);
509 edgeDragArea->setTimeSource(fakeTimeSource);
501− QPointF touchScenePos(view->width()/2.0f, view->height() - (edgeDragArea->height()/2.0f));
511+ QPointF touchScenePos(m_view->width()/2.0f, m_view->height() - (edgeDragArea->height()/2.0f));
503− fakeTimeSource->m_msecsSinceReference = 0;
504− QTest::touchEvent(view, device).press(0, touchScenePos.toPoint());
513+ QTest::touchEvent(m_view, m_device).press(0, touchScenePos.toPoint());
506 QSignalSpy touchYSpy(edgeDragArea, SIGNAL(touchYChanged(qreal)));
515 QSignalSpy touchYSpy(edgeDragArea, SIGNAL(touchYChanged(qreal)));
507 QSignalSpy touchSceneYSpy(edgeDragArea, SIGNAL(touchSceneYChanged(qreal)));
516 QSignalSpy touchSceneYSpy(edgeDragArea, SIGNAL(touchSceneYChanged(qreal)));
509− touchScenePos.ry() = view->height() / 2;
518+ touchScenePos.ry() = m_view->height() / 2;
510− QTest::touchEvent(view, device).move(0, touchScenePos.toPoint());
519+ QTest::touchEvent(m_view, m_device).move(0, touchScenePos.toPoint());
512 QCOMPARE(touchYSpy.count(), 1);
521 QCOMPARE(touchYSpy.count(), 1);
513 QCOMPARE(touchSceneYSpy.count(), 1);
522 QCOMPARE(touchSceneYSpy.count(), 1);
@@ -516,34 +525,183 @@ void tst_DirectionalDragArea::sceneYAndY()
519− Emit draggingChanged() when status change from WaitingForTouch directly to Recognized.
528+ Regression test for https://bugs.launchpad.net/bugs/1228336
520− This happens when the gesture recognition is effectively disabled (e.g. by setting a
529+
521− distanceThreshold of zero)
530+ Perform a quick two-finger double-tap and check that DirectionalDragArea properly
531+ rejects those touch points. In the bug above it got confused and crashed.
523−void tst_DirectionalDragArea::emitDraggingChangedOnDirectRecognition()
533+void tst_DirectionalDragArea::twoFingerTap()
525 DirectionalDragArea *edgeDragArea =
535 DirectionalDragArea *edgeDragArea =
526− view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
536+ m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
527 QVERIFY(edgeDragArea != 0);
537 QVERIFY(edgeDragArea != 0);
528 edgeDragArea->setRecognitionTimer(fakeTimer);
538 edgeDragArea->setRecognitionTimer(fakeTimer);
529 edgeDragArea->setTimeSource(fakeTimeSource);
539 edgeDragArea->setTimeSource(fakeTimeSource);
531− QPointF touchPos = calculateInitialTouchPos(edgeDragArea, view);
541+ // Make touches evenly spaced along the edgeDragArea
542+ QPoint touchAPos(edgeDragArea->width()/2.0f, m_view->height()*0.33f);
543+ QPoint touchBPos(edgeDragArea->width()/2.0f, m_view->height()*0.66f);
533− QSignalSpy draggingSpy(edgeDragArea, SIGNAL(draggingChanged(bool)));
545+ qint64 timeStepMsecs = 5; // some arbitrary, small value
547+ // Perform the first two-finger tap
548+ // NB: using move() instead of stationary() becasue in the latter you cannot provide
549+ // the touch position and therefore it's left with some garbage numbers.
550+ QTest::touchEvent(m_view, m_device)
551+ .press(0, touchAPos);
553+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
535− // That will make it completetly skip the recognition phase ("Undecided" status).
555+ passTime(timeStepMsecs);
536− edgeDragArea->setDistanceThreshold(0.0);
556+ QTest::touchEvent(m_view, m_device)
558+ .press(1, touchBPos);
560+ // A second touch point appeared during recognition, reject immediately as this
561+ // can't be a single-touch gesture anymore.
562+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
564+ passTime(timeStepMsecs);
565+ QTest::touchEvent(m_view, m_device)
566+ .release(0, touchAPos)
567+ .move(1, touchBPos);
569+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
571+ passTime(timeStepMsecs);
572+ QTest::touchEvent(m_view, m_device)
573+ .release(1, touchBPos);
538 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
575 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
539− QCOMPARE(edgeDragArea->dragging(), false);
541− fakeTimeSource->m_msecsSinceReference = 0;
577+ // Perform the second two-finger tap
579+ passTime(timeStepMsecs);
580+ QTest::touchEvent(m_view, m_device)
581+ .press(0, touchAPos);
583+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
585+ passTime(timeStepMsecs);
586+ QTest::touchEvent(m_view, m_device)
588+ .press(1, touchBPos);
590+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
592+ passTime(timeStepMsecs);
593+ QTest::touchEvent(m_view, m_device)
594+ .release(0, touchAPos)
595+ .move(1, touchBPos);
597+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
599+ passTime(timeStepMsecs);
600+ QTest::touchEvent(m_view, m_device)
601+ .release(1, touchBPos);
603+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
607+ Tests that gesture recognition works normally even if the DirectionalDragArea moves
609+ This effectively means that we have to do gesture recognition with scene coordinates
610+ instead of local item coordinates.
612+void tst_DirectionalDragArea::movingDDA()
614+ QQuickItem *rightwardsLauncher = m_view->rootObject()->findChild<QQuickItem*>("rightwardsLauncher");
615+ Q_ASSERT(rightwardsLauncher != 0);
617+ DirectionalDragArea *edgeDragArea =
618+ rightwardsLauncher->findChild<DirectionalDragArea*>("hpDragArea");
619+ Q_ASSERT(edgeDragArea != 0);
620+ edgeDragArea->setRecognitionTimer(fakeTimer);
621+ edgeDragArea->setTimeSource(fakeTimeSource);
623+ QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, m_view);
624+ QPointF touchPoint = initialTouchPos;
626+ qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;
627+ QPointF dragDirectionVector(1.0f, 0.0f);
629+ qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
630+ QPointF touchMovement = dragDirectionVector * movementStepDistance;
631+ int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
632+ int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
542− QTest::touchEvent(view, device).press(0, touchPos.toPoint());
634+ QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
636+ // Move it far ahead along the direction of the gesture
637+ // rightwardsLauncher is a parent of our DirectionalDragArea. So moving it will move our DDA
638+ rightwardsLauncher->setX(rightwardsLauncher->x() + edgeDragArea->distanceThreshold() * 5.0f);
640+ for (int i = 0; i < totalMovementSteps; ++i) {
641+ touchPoint += touchMovement;
642+ passTime(movementTimeStepMs);
643+ QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
544 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
646 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
545− QCOMPARE(draggingSpy.count(), 1);
647+
648+ QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
652+ The presence of and old, rejected, touch point lying on the DirectionalDragArea
653+ shouldn't impede the recognition of a gesture from a new touch point.
655+void tst_DirectionalDragArea::ignoreOldFinger()
657+ DirectionalDragArea *edgeDragArea =
658+ m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
659+ Q_ASSERT(edgeDragArea != 0);
660+ edgeDragArea->setRecognitionTimer(fakeTimer);
661+ edgeDragArea->setTimeSource(fakeTimeSource);
663+ // Make touches evenly spaced along the edgeDragArea
664+ QPoint touch0Pos(edgeDragArea->width()/2.0f, m_view->height()*0.33f);
665+ QPoint touch1Pos(edgeDragArea->width()/2.0f, m_view->height()*0.66f);
667+ QTest::touchEvent(m_view, m_device).press(0, touch0Pos);
669+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
671+ // leave it lying around for some time
672+ passTime(qMax(edgeDragArea->maxSilenceTime(), edgeDragArea->compositionTime()) * 10);
674+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
676+ qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;
677+ QPointF dragDirectionVector(1.0f, 0.0f);
679+ qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
680+ QPointF touchMovement = dragDirectionVector * movementStepDistance;
681+ int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
682+ int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
684+ QTest::touchEvent(m_view, m_device)
686+ .press(1, touch1Pos);
546− QCOMPARE(edgeDragArea->dragging(), true);
688+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
690+ for (int i = 0; i < totalMovementSteps; ++i) {
691+ touch1Pos += touchMovement.toPoint();
692+ passTime(movementTimeStepMs);
693+ QTest::touchEvent(m_view, m_device)
695+ .move(1, touch1Pos);
698+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
700+ QTest::touchEvent(m_view, m_device)
702+ .release(1, touch1Pos);
704+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
549QTEST_MAIN(tst_DirectionalDragArea)
707QTEST_MAIN(tst_DirectionalDragArea)