on 4126d90 help sign in
unity8/tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.cpp
846 lines 31 kB history raw
1/*
2 * Copyright (C) 2013 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
17#include <QtTest/QtTest>
18#include <QtCore/QObject>
19#include <QtQuick/QQuickView>
20#include <QtQml/QQmlEngine>
21#include <QPointer>
23#include <DirectionalDragArea.h>
25#include "GestureTest.h"
27using namespace UbuntuGestures;
29class FakeTimer : public AbstractTimer
31 Q_OBJECT
32public:
33 FakeTimer(const SharedTimeSource &timeSource, QObject *parent = 0)
34 : UbuntuGestures::AbstractTimer(parent),
35 m_timeSource(timeSource)
36 {}
38 int interval() const override { return m_interval; }
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;
43 }
45 void emitTimeout() {
46 m_nextTimeoutTime += m_interval;
47 Q_EMIT timeout();
48 }
50 qint64 nextTimeoutTime() const { return m_nextTimeoutTime; }
51private:
52 int m_interval;
53 SharedTimeSource m_timeSource;
54 qint64 m_nextTimeoutTime;
55};
57class FakeTimeSource : public UbuntuGestures::TimeSource
59public:
60 FakeTimeSource() { m_msecsSinceReference = 0; }
61 virtual qint64 msecsSinceReference() {return m_msecsSinceReference;}
62 qint64 m_msecsSinceReference;
63};
65class tst_DirectionalDragArea: public GestureTest
67 Q_OBJECT
68public:
69 tst_DirectionalDragArea();
70private Q_SLOTS:
71 void init(); // called right before each and every test function is executed
72 void cleanup(); // called right after each and every test function is executed
74 void edgeDrag();
75 void edgeDrag_data();
76 void dragWithShortDirectionChange();
77 void minSpeed();
78 void minSpeed_data();
79 void recognitionTimerUsage();
80 void maxSilenceTime();
81 void sceneXAndX();
82 void sceneYAndY();
83 void twoFingerTap();
84 void movingDDA();
85 void ignoreOldFinger();
86 void rotated();
87 void sceneDistance();
88 void sceneDistance_data();
89 void disabledWhileDragging();
91private:
92 void passTime(qint64 timeSpan);
93 FakeTimer *fakeTimer;
94 QSharedPointer<FakeTimeSource> fakeTimeSource;
95};
97tst_DirectionalDragArea::tst_DirectionalDragArea()
98 : GestureTest(QStringLiteral("tst_DirectionalDragArea.qml"))
102void tst_DirectionalDragArea::init()
104 GestureTest::init();
106 fakeTimeSource.reset(new FakeTimeSource);
107 fakeTimer = new FakeTimer(fakeTimeSource);
110void tst_DirectionalDragArea::cleanup()
112 delete fakeTimer;
113 fakeTimer = 0;
115 fakeTimeSource.reset();
117 GestureTest::cleanup();
120void tst_DirectionalDragArea::passTime(qint64 timeSpan)
122 qint64 finalTime = fakeTimeSource->m_msecsSinceReference + timeSpan;
124 if (fakeTimer->isRunning() && finalTime >= fakeTimer->nextTimeoutTime()) {
125 fakeTimeSource->m_msecsSinceReference = fakeTimer->nextTimeoutTime();
126 fakeTimer->emitTimeout();
128 qint64 timeSpanRemainder = finalTime - fakeTimeSource->m_msecsSinceReference;
129 if (timeSpanRemainder > 0) {
130 passTime(timeSpanRemainder);
131 }
132 } else {
133 fakeTimeSource->m_msecsSinceReference = finalTime;
134 }
137namespace {
138QPointF calculateInitialTouchPos(DirectionalDragArea *edgeDragArea)
140 QPointF localCenter(edgeDragArea->width() / 2., edgeDragArea->height() / 2.);
141 return edgeDragArea->mapToScene(localCenter);
144QPointF calculateDirectionVector(DirectionalDragArea *edgeDragArea,
145 qreal wideningAngleMultiplier)
147 qreal angleRadians = edgeDragArea->wideningAngle() * wideningAngleMultiplier
148 * M_PI / 180.0;
150 qreal angleCos = qCos(angleRadians);
151 qreal angleSin = qSin(angleRadians);
153 switch (edgeDragArea->direction()) {
154 case Direction::Upwards:
155 return QPointF(angleSin, -angleCos);
156 case Direction::Downwards:
157 return QPointF(angleSin, angleCos);
158 case Direction::Leftwards:
159 return QPointF(-angleCos, angleSin);
160 default: // Direction::Rightwards:
161 return QPointF(angleCos, angleSin);
162 }
165QPointF createTouchDeviation(DirectionalDragArea *edgeDragArea)
167 qreal deviation = edgeDragArea->maxDeviation() * 0.8;
169 if (Direction::isHorizontal(edgeDragArea->direction())) {
170 return QPointF(0, deviation);
171 } else {
172 return QPointF(deviation, 0);
173 }
177void tst_DirectionalDragArea::edgeDrag()
179 QFETCH(QString, dragAreaObjectName);
180 QFETCH(qreal, wideningAngleMultiplier);
181 QFETCH(qreal, dragDistanceFactor);
182 QFETCH(bool, expectGestureRecognition);
184 DirectionalDragArea *edgeDragArea =
185 m_view->rootObject()->findChild<DirectionalDragArea*>(dragAreaObjectName);
186 QVERIFY(edgeDragArea != 0);
187 edgeDragArea->setRecognitionTimer(fakeTimer);
188 edgeDragArea->setTimeSource(fakeTimeSource);
190 QSignalSpy draggingSpy(edgeDragArea, SIGNAL(draggingChanged(bool)));
192 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
193 QPointF touchPoint = initialTouchPos;
195 qreal desiredDragDistance = edgeDragArea->distanceThreshold()*dragDistanceFactor;
196 QPointF dragDirectionVector = calculateDirectionVector(edgeDragArea,
197 wideningAngleMultiplier);
198 qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
199 QPointF touchMovement = dragDirectionVector * movementStepDistance;
200 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
201 int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
203 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
205 QCOMPARE(draggingSpy.count(), 1);
206 QCOMPARE(edgeDragArea->dragging(), true);
208 if (wideningAngleMultiplier > 0) {
209 // go close to the border of the valid area for this touch point
210 // in order to make it easier to leave it by dragging at an angle
211 // slightly bigger than the widening angle
212 touchPoint += createTouchDeviation(edgeDragArea);
213 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
214 }
216 for (int i = 0; i < totalMovementSteps; ++i) {
217 touchPoint += touchMovement;
218 passTime(movementTimeStepMs);
219 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
220 }
222 if (expectGestureRecognition)
223 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
225 if (edgeDragArea->status() == DirectionalDragArea::WaitingForTouch) {
226 QCOMPARE(edgeDragArea->dragging(), false);
227 QCOMPARE(draggingSpy.count(), 2);
228 }
230 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
232 QCOMPARE(draggingSpy.count(), 2);
233 QCOMPARE(edgeDragArea->dragging(), false);
236void tst_DirectionalDragArea::edgeDrag_data()
238 QTest::addColumn<QString>("dragAreaObjectName");
239 QTest::addColumn<qreal>("wideningAngleMultiplier");
240 QTest::addColumn<qreal>("dragDistanceFactor");
241 QTest::addColumn<bool>("expectGestureRecognition");
243 QTest::newRow("rightwards, tiny drag")
244 << "hpDragArea" << 0.0 << 0.2 << false;
246 QTest::newRow("rightwards, straight drag")
247 << "hpDragArea" << 0.0 << 3.0 << true;
249 QTest::newRow("rightwards, diagonal drag")
250 << "hpDragArea" << 0.9 << 3.0 << true;
252 QTest::newRow("rightwards, overly diagonal drag")
253 << "hpDragArea" << 2.0 << 3.0 << false;
255 QTest::newRow("leftwards, tiny drag")
256 << "hnDragArea" << 0.0 << 0.2 << false;
258 QTest::newRow("leftwards, straight drag")
259 << "hnDragArea" << 0.0 << 3.0 << true;
261 QTest::newRow("leftwards, diagonal drag")
262 << "hnDragArea" << 0.9 << 3.0 << true;
264 QTest::newRow("downwards, tiny drag")
265 << "vpDragArea" << 0.0 << 0.2 << false;
267 QTest::newRow("downwards, straight drag")
268 << "vpDragArea" << 0.0 << 3.0 << true;
270 QTest::newRow("downwards, diagonal drag")
271 << "vpDragArea" << 0.9 << 3.0 << true;
273 QTest::newRow("upwards, tiny drag")
274 << "vnDragArea" << 0.0 << 0.2 << false;
276 QTest::newRow("upwards, straight drag")
277 << "vnDragArea" << 0.0 << 3.0 << true;
279 QTest::newRow("upwards, diagonal drag")
280 << "vnDragArea" << 0.9 << 3.0 << true;
282 QTest::newRow("upwards, overly diagonal drag")
283 << "vnDragArea" << 2.0 << 3.0 << false;
286/*
287 A directional drag should still be recognized if there is a momentaneous, small,
288 change in the direction of a drag. That should be accounted as input noise and
289 therefore ignored.
290 */
291void tst_DirectionalDragArea::dragWithShortDirectionChange()
293 DirectionalDragArea *edgeDragArea =
294 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
295 QVERIFY(edgeDragArea != 0);
296 edgeDragArea->setRecognitionTimer(fakeTimer);
297 edgeDragArea->setTimeSource(fakeTimeSource);
299 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
300 QPointF touchPoint = initialTouchPos;
302 qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0;
303 QPointF dragDirectionVector(1.0, 0.0);
304 qreal touchStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
305 // make sure we are above minimum speed
306 int touchStepTimeMs = (touchStepDistance / (edgeDragArea->minSpeed() * 5.0f)) * 1000.0f;
307 QPointF touchMovement = dragDirectionVector * touchStepDistance;
309 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
311 // Move a bit in the proper direction
312 for (int i=0; i < 3; ++i) {
313 touchPoint += touchMovement;
314 passTime(touchStepTimeMs);
315 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
316 }
318 // Then a sudden and small movement to the opposite direction
319 touchPoint -= touchMovement*0.2;
320 passTime(touchStepTimeMs*0.2);
321 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
323 // And then resume movment in the correct direction until it crosses the distance and time
324 // thresholds.
325 do {
326 touchPoint += touchMovement;
327 passTime(touchStepTimeMs);
328 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
329 } while ((touchPoint - initialTouchPos).manhattanLength() < desiredDragDistance
330 || fakeTimeSource->m_msecsSinceReference < (edgeDragArea->compositionTime() * 1.5f));
332 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
334 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
337/*
338 Checks that a gesture will be rejected if it's slower than minSpeed while
339 status is Undecided.
340 */
341void tst_DirectionalDragArea::minSpeed()
343 QFETCH(qreal, minSpeed);
344 QFETCH(qreal, speed);
345 QFETCH(int, expectedStatusAfterSpeedCheck);
347 DirectionalDragArea *edgeDragArea =
348 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
349 QVERIFY(edgeDragArea != 0);
350 edgeDragArea->setRecognitionTimer(fakeTimer);
351 edgeDragArea->setTimeSource(fakeTimeSource);
353 // A really long, unattainable, number. We don't want it getting recognized before
354 // the speed checks we want have been performed
355 edgeDragArea->setDistanceThreshold(500000);
357 edgeDragArea->setMinSpeed(minSpeed);
359 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
360 QPointF touchPoint = initialTouchPos;
362 QPointF dragDirectionVector(1.0, 0.0);
363 qint64 timeStepMsecs = 10;
364 qreal distanceStep = (speed / 1000.0f) * timeStepMsecs;
365 QPointF touchMovement = dragDirectionVector * distanceStep;
367 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
369 // Move for a while to ensure our speed check is performed a couple of times
370 for (int i=0; i < 20; ++i) {
371 touchPoint += touchMovement;
372 passTime(timeStepMsecs);
373 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
374 }
376 QCOMPARE((int)edgeDragArea->status(), expectedStatusAfterSpeedCheck);
379void tst_DirectionalDragArea::minSpeed_data()
381 QTest::addColumn<qreal>("minSpeed");
382 QTest::addColumn<qreal>("speed");
383 QTest::addColumn<int>("expectedStatusAfterSpeedCheck");
385 QTest::newRow("slower than minSpeed") << 100.0 << 50.0 << (int)DirectionalDragArea::WaitingForTouch;
386 QTest::newRow("faster than minSpeed") << 100.0 << 150.0 << (int)DirectionalDragArea::Undecided;
389/*
390 Checks that the recognition timer is started and stopped appropriately.
391 I.e., check that it's running only while gesture recognition is taking place
392 (status == Undecided)
393 */
394void tst_DirectionalDragArea::recognitionTimerUsage()
396 DirectionalDragArea *edgeDragArea =
397 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
398 QVERIFY(edgeDragArea != 0);
399 edgeDragArea->setRecognitionTimer(fakeTimer);
400 edgeDragArea->setTimeSource(fakeTimeSource);
402 // don't let it interfere with our test
403 edgeDragArea->setMinSpeed(0.0);
405 int timeStepMs = 5; // some arbitrary small value.
407 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
408 QPointF touchPoint = initialTouchPos;
410 QPointF dragDirectionVector(1.0, 0.0);
411 QPointF touchMovement = dragDirectionVector * (edgeDragArea->distanceThreshold() * 0.2f);
413 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
414 QVERIFY(!fakeTimer->isRunning());
416 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
418 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
419 QVERIFY(fakeTimer->isRunning());
421 // Move beyond distance threshold and composition time to ensure recognition
422 while (fakeTimeSource->m_msecsSinceReference <= edgeDragArea->compositionTime() ||
423 (touchPoint - initialTouchPos).manhattanLength() <= edgeDragArea->distanceThreshold()) {
425 QCOMPARE(edgeDragArea->status() == DirectionalDragArea::Undecided, fakeTimer->isRunning());
427 touchPoint += touchMovement;
428 passTime(timeStepMs);
429 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
430 }
432 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
433 QVERIFY(!fakeTimer->isRunning());
436/*
437 A gesture should be rejected if too much time has passed without any new input
438 events from it.
439 */
440void tst_DirectionalDragArea::maxSilenceTime()
442 DirectionalDragArea *edgeDragArea =
443 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
444 QVERIFY(edgeDragArea != 0);
445 edgeDragArea->setRecognitionTimer(fakeTimer);
446 edgeDragArea->setTimeSource(fakeTimeSource);
448 // Make sure this property is not disabled
449 edgeDragArea->setMaxSilenceTime(100);
451 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
452 QPointF touchPoint = initialTouchPos;
454 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
456 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
457 QVERIFY(fakeTimer->isRunning());
459 // Force timer to timeout until after maxSilenceTime has been reached
460 while (fakeTimeSource->m_msecsSinceReference < edgeDragArea->maxSilenceTime()) {
461 passTime(fakeTimer->interval());
462 }
464 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
467/*
468 Checks that it informs the X coordinate of the touch point in local and scene coordinates
469 correctly.
470 */
471void tst_DirectionalDragArea::sceneXAndX()
473 DirectionalDragArea *edgeDragArea =
474 m_view->rootObject()->findChild<DirectionalDragArea*>("hnDragArea");
475 QVERIFY(edgeDragArea != 0);
476 edgeDragArea->setRecognitionTimer(fakeTimer);
477 edgeDragArea->setTimeSource(fakeTimeSource);
479 QPointF touchScenePos(m_view->width() - (edgeDragArea->width()/2.0f), m_view->height()/2.0f);
481 QTest::touchEvent(m_view, m_device).press(0, touchScenePos.toPoint());
483 QSignalSpy touchXSpy(edgeDragArea, SIGNAL(touchXChanged(qreal)));
484 QSignalSpy touchSceneXSpy(edgeDragArea, SIGNAL(touchSceneXChanged(qreal)));
486 touchScenePos.rx() = m_view->width() / 2;
487 QTest::touchEvent(m_view, m_device).move(0, touchScenePos.toPoint());
489 QCOMPARE(touchXSpy.count(), 1);
490 QCOMPARE(touchSceneXSpy.count(), 1);
491 QCOMPARE(edgeDragArea->touchX(), touchScenePos.x() - edgeDragArea->x());
492 QCOMPARE(edgeDragArea->touchSceneX(), touchScenePos.x());
495/*
496 Checks that it informs the Y coordinate of the touch point in local and scene coordinates
497 correctly.
498 */
499void tst_DirectionalDragArea::sceneYAndY()
501 DirectionalDragArea *edgeDragArea =
502 m_view->rootObject()->findChild<DirectionalDragArea*>("vnDragArea");
503 QVERIFY(edgeDragArea != 0);
504 edgeDragArea->setRecognitionTimer(fakeTimer);
505 edgeDragArea->setTimeSource(fakeTimeSource);
507 QPointF touchScenePos(m_view->width()/2.0f, m_view->height() - (edgeDragArea->height()/2.0f));
509 QTest::touchEvent(m_view, m_device).press(0, touchScenePos.toPoint());
511 QSignalSpy touchYSpy(edgeDragArea, SIGNAL(touchYChanged(qreal)));
512 QSignalSpy touchSceneYSpy(edgeDragArea, SIGNAL(touchSceneYChanged(qreal)));
514 touchScenePos.ry() = m_view->height() / 2;
515 QTest::touchEvent(m_view, m_device).move(0, touchScenePos.toPoint());
517 QCOMPARE(touchYSpy.count(), 1);
518 QCOMPARE(touchSceneYSpy.count(), 1);
519 QCOMPARE(edgeDragArea->touchY(), touchScenePos.y() - edgeDragArea->y());
520 QCOMPARE(edgeDragArea->touchSceneY(), touchScenePos.y());
523/*
524 Regression test for https://bugs.launchpad.net/bugs/1228336
526 Perform a quick two-finger double-tap and check that DirectionalDragArea properly
527 rejects those touch points. In the bug above it got confused and crashed.
528 */
529void tst_DirectionalDragArea::twoFingerTap()
531 DirectionalDragArea *edgeDragArea =
532 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
533 QVERIFY(edgeDragArea != 0);
534 edgeDragArea->setRecognitionTimer(fakeTimer);
535 edgeDragArea->setTimeSource(fakeTimeSource);
537 // Make touches evenly spaced along the edgeDragArea
538 QPoint touchAPos(edgeDragArea->width()/2.0f, m_view->height()*0.33f);
539 QPoint touchBPos(edgeDragArea->width()/2.0f, m_view->height()*0.66f);
541 qint64 timeStepMsecs = 5; // some arbitrary, small value
543 // Perform the first two-finger tap
544 // NB: using move() instead of stationary() becasue in the latter you cannot provide
545 // the touch position and therefore it's left with some garbage numbers.
546 QTest::touchEvent(m_view, m_device)
547 .press(0, touchAPos);
549 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
551 passTime(timeStepMsecs);
552 QTest::touchEvent(m_view, m_device)
553 .move(0, touchAPos)
554 .press(1, touchBPos);
556 // A second touch point appeared during recognition, reject immediately as this
557 // can't be a single-touch gesture anymore.
558 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
560 passTime(timeStepMsecs);
561 QTest::touchEvent(m_view, m_device)
562 .release(0, touchAPos)
563 .move(1, touchBPos);
565 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
567 passTime(timeStepMsecs);
568 QTest::touchEvent(m_view, m_device)
569 .release(1, touchBPos);
571 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
573 // Perform the second two-finger tap
575 passTime(timeStepMsecs);
576 QTest::touchEvent(m_view, m_device)
577 .press(0, touchAPos);
579 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
581 passTime(timeStepMsecs);
582 QTest::touchEvent(m_view, m_device)
583 .move(0, touchAPos)
584 .press(1, touchBPos);
586 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
588 passTime(timeStepMsecs);
589 QTest::touchEvent(m_view, m_device)
590 .release(0, touchAPos)
591 .move(1, touchBPos);
593 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
595 passTime(timeStepMsecs);
596 QTest::touchEvent(m_view, m_device)
597 .release(1, touchBPos);
599 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
602/*
603 Tests that gesture recognition works normally even if the DirectionalDragArea moves
604 during recognition.
605 This effectively means that we have to do gesture recognition with scene coordinates
606 instead of local item coordinates.
607 */
608void tst_DirectionalDragArea::movingDDA()
610 QQuickItem *rightwardsLauncher = m_view->rootObject()->findChild<QQuickItem*>("rightwardsLauncher");
611 Q_ASSERT(rightwardsLauncher != 0);
613 DirectionalDragArea *edgeDragArea =
614 rightwardsLauncher->findChild<DirectionalDragArea*>("hpDragArea");
615 Q_ASSERT(edgeDragArea != 0);
616 edgeDragArea->setRecognitionTimer(fakeTimer);
617 edgeDragArea->setTimeSource(fakeTimeSource);
619 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
620 QPointF touchPoint = initialTouchPos;
622 qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;
623 QPointF dragDirectionVector(1.0f, 0.0f);
625 qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
626 QPointF touchMovement = dragDirectionVector * movementStepDistance;
627 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
628 int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
630 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
632 // Move it far ahead along the direction of the gesture
633 // rightwardsLauncher is a parent of our DirectionalDragArea. So moving it will move our DDA
634 rightwardsLauncher->setX(rightwardsLauncher->x() + edgeDragArea->distanceThreshold() * 5.0f);
636 for (int i = 0; i < totalMovementSteps; ++i) {
637 touchPoint += touchMovement;
638 passTime(movementTimeStepMs);
639 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
640 }
642 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
644 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
647/*
648 The presence of and old, rejected, touch point lying on the DirectionalDragArea
649 shouldn't impede the recognition of a gesture from a new touch point.
650 */
651void tst_DirectionalDragArea::ignoreOldFinger()
653 DirectionalDragArea *edgeDragArea =
654 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
655 Q_ASSERT(edgeDragArea != 0);
656 edgeDragArea->setRecognitionTimer(fakeTimer);
657 edgeDragArea->setTimeSource(fakeTimeSource);
659 // Make touches evenly spaced along the edgeDragArea
660 QPoint touch0Pos(edgeDragArea->width()/2.0f, m_view->height()*0.33f);
661 QPoint touch1Pos(edgeDragArea->width()/2.0f, m_view->height()*0.66f);
663 QTest::touchEvent(m_view, m_device).press(0, touch0Pos);
665 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
667 // leave it lying around for some time
668 passTime(qMax(edgeDragArea->maxSilenceTime(), edgeDragArea->compositionTime()) * 10);
670 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
672 qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;
673 QPointF dragDirectionVector(1.0f, 0.0f);
675 qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
676 QPointF touchMovement = dragDirectionVector * movementStepDistance;
677 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
678 int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
680 QTest::touchEvent(m_view, m_device)
681 .move(0, touch0Pos)
682 .press(1, touch1Pos);
684 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
686 for (int i = 0; i < totalMovementSteps; ++i) {
687 touch1Pos += touchMovement.toPoint();
688 passTime(movementTimeStepMs);
689 QTest::touchEvent(m_view, m_device)
690 .move(0, touch0Pos)
691 .move(1, touch1Pos);
692 }
694 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
696 QTest::touchEvent(m_view, m_device)
697 .move(0, touch0Pos)
698 .release(1, touch1Pos);
700 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
703/*
704 A Rightwards DDA that is rotated 90 degrees clockwise should recognize gestures
705 that are done downwards in scene coordinates. I.e. the gesture recognition direction
706 should be in local coordinates, not scene coordinates.
707 */
708void tst_DirectionalDragArea::rotated()
710 QQuickItem *baseItem = m_view->rootObject()->findChild<QQuickItem*>("baseItem");
711 baseItem->setRotation(90.);
713 QQuickItem *rightwardsLauncher = m_view->rootObject()->findChild<QQuickItem*>("rightwardsLauncher");
714 Q_ASSERT(rightwardsLauncher != 0);
716 DirectionalDragArea *edgeDragArea =
717 rightwardsLauncher->findChild<DirectionalDragArea*>("hpDragArea");
718 Q_ASSERT(edgeDragArea != 0);
719 edgeDragArea->setRecognitionTimer(fakeTimer);
720 edgeDragArea->setTimeSource(fakeTimeSource);
722 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
723 QPointF touchPoint = initialTouchPos;
725 qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;
726 QPointF dragDirectionVector(0.0f, 1.0f);
728 qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
729 QPointF touchMovement = dragDirectionVector * movementStepDistance;
730 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
731 int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
733 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
735 for (int i = 0; i < totalMovementSteps; ++i) {
736 touchPoint += touchMovement;
737 passTime(movementTimeStepMs);
738 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
739 }
741 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
743 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
746void tst_DirectionalDragArea::sceneDistance()
748 QQuickItem *baseItem = m_view->rootObject()->findChild<QQuickItem*>("baseItem");
749 QFETCH(qreal, rotation);
750 QFETCH(QPointF, dragDirectionVector);
751 baseItem->setRotation(rotation);
753 QQuickItem *rightwardsLauncher = m_view->rootObject()->findChild<QQuickItem*>("rightwardsLauncher");
754 Q_ASSERT(rightwardsLauncher != 0);
756 DirectionalDragArea *edgeDragArea =
757 rightwardsLauncher->findChild<DirectionalDragArea*>("hpDragArea");
758 Q_ASSERT(edgeDragArea != 0);
759 edgeDragArea->setRecognitionTimer(fakeTimer);
760 edgeDragArea->setTimeSource(fakeTimeSource);
762 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
763 QPointF touchPoint = initialTouchPos;
765 qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;
767 qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
768 QPointF touchMovement = dragDirectionVector * movementStepDistance;
769 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
770 int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
772 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
774 for (int i = 0; i < totalMovementSteps; ++i) {
775 touchPoint += touchMovement;
776 passTime(movementTimeStepMs);
777 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
778 }
780 qreal actualDragDistance = ((qreal)totalMovementSteps) * movementStepDistance;
782 // DirectionalDragArea::sceneDistance() must match the actual drag distance as the
783 // drag was aligned with the gesture direction
784 // NB: qFuzzyCompare(), used internally by QCOMPARE(), is broken.
785 QVERIFY(qAbs(edgeDragArea->sceneDistance() - actualDragDistance) < 0.001);
787 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
790void tst_DirectionalDragArea::sceneDistance_data()
792 QTest::addColumn<qreal>("rotation");
793 QTest::addColumn<QPointF>("dragDirectionVector");
795 QTest::newRow("not rotated") << 0. << QPointF(1., 0.);
796 QTest::newRow("rotated by 90 degrees") << 90. << QPointF(0., 1.);
799/*
800 Regression test for https://bugs.launchpad.net/unity8/+bug/1276122
802 The bug:
803 If setting "enabled: false" while the DirectionalDragArea's (DDA) dragging
804 property is true, the DDA stays in that state and doesn't recover any more.
805*/
806void tst_DirectionalDragArea::disabledWhileDragging()
808 DirectionalDragArea *edgeDragArea =
809 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
810 Q_ASSERT(edgeDragArea != 0);
811 edgeDragArea->setRecognitionTimer(fakeTimer);
812 edgeDragArea->setTimeSource(fakeTimeSource);
814 QPointF touchPoint = calculateInitialTouchPos(edgeDragArea);
816 qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;
817 QPointF dragDirectionVector(1., 0.); // horizontal positive
819 qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
820 QPointF touchMovement = dragDirectionVector * movementStepDistance;
821 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
822 int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
824 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
826 for (int i = 0; i < totalMovementSteps; ++i) {
827 touchPoint += touchMovement;
828 passTime(movementTimeStepMs);
829 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
830 }
832 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
833 QCOMPARE(edgeDragArea->dragging(), true);
835 // disable the dragArea while it's being dragged.
836 edgeDragArea->setEnabled(false);
838 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
839 QCOMPARE(edgeDragArea->dragging(), false);
841 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
844QTEST_MAIN(tst_DirectionalDragArea)
846#include "tst_DirectionalDragArea.moc"