2 * Copyright (C) 2013 Canonical, Ltd.
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.
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.
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/>.
17#include <QtTest/QtTest>
18#include <QtCore/QObject>
19#include <QtQuick/QQuickView>
20#include <QtQml/QQmlEngine>
23#include <DirectionalDragArea.h>
25#include "GestureTest.h"
27using namespace UbuntuGestures;
29class FakeTimer : public AbstractTimer
33 FakeTimer(const SharedTimeSource &timeSource, QObject *parent = 0)
34 : UbuntuGestures::AbstractTimer(parent),
35 m_timeSource(timeSource)
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;
46 m_nextTimeoutTime += m_interval;
50 qint64 nextTimeoutTime() const { return m_nextTimeoutTime; }
53 SharedTimeSource m_timeSource;
54 qint64 m_nextTimeoutTime;
57class FakeTimeSource : public UbuntuGestures::TimeSource
60 FakeTimeSource() { m_msecsSinceReference = 0; }
61 virtual qint64 msecsSinceReference() {return m_msecsSinceReference;}
62 qint64 m_msecsSinceReference;
65class tst_DirectionalDragArea: public GestureTest
69 tst_DirectionalDragArea();
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
76 void dragWithShortDirectionChange();
79 void recognitionTimerUsage();
80 void maxSilenceTime();
85 void ignoreOldFinger();
88 void sceneDistance_data();
91 void passTime(qint64 timeSpan);
93 QSharedPointer<FakeTimeSource> fakeTimeSource;
96tst_DirectionalDragArea::tst_DirectionalDragArea()
97 : GestureTest(QStringLiteral("tst_DirectionalDragArea.qml"))
101void tst_DirectionalDragArea::init()
105 fakeTimeSource.reset(new FakeTimeSource);
106 fakeTimer = new FakeTimer(fakeTimeSource);
109void tst_DirectionalDragArea::cleanup()
114 fakeTimeSource.reset();
116 GestureTest::cleanup();
119void tst_DirectionalDragArea::passTime(qint64 timeSpan)
121 qint64 finalTime = fakeTimeSource->m_msecsSinceReference + timeSpan;
123 if (fakeTimer->isRunning() && finalTime >= fakeTimer->nextTimeoutTime()) {
124 fakeTimeSource->m_msecsSinceReference = fakeTimer->nextTimeoutTime();
125 fakeTimer->emitTimeout();
127 qint64 timeSpanRemainder = finalTime - fakeTimeSource->m_msecsSinceReference;
128 if (timeSpanRemainder > 0) {
129 passTime(timeSpanRemainder);
132 fakeTimeSource->m_msecsSinceReference = finalTime;
137QPointF calculateInitialTouchPos(DirectionalDragArea *edgeDragArea)
139 QPointF localCenter(edgeDragArea->width() / 2., edgeDragArea->height() / 2.);
140 return edgeDragArea->mapToScene(localCenter);
143QPointF calculateDirectionVector(DirectionalDragArea *edgeDragArea,
144 qreal wideningAngleMultiplier)
146 qreal angleRadians = edgeDragArea->wideningAngle() * wideningAngleMultiplier
149 qreal angleCos = qCos(angleRadians);
150 qreal angleSin = qSin(angleRadians);
152 switch (edgeDragArea->direction()) {
153 case Direction::Upwards:
154 return QPointF(angleSin, -angleCos);
155 case Direction::Downwards:
156 return QPointF(angleSin, angleCos);
157 case Direction::Leftwards:
158 return QPointF(-angleCos, angleSin);
159 default: // Direction::Rightwards:
160 return QPointF(angleCos, angleSin);
164QPointF createTouchDeviation(DirectionalDragArea *edgeDragArea)
166 qreal deviation = edgeDragArea->maxDeviation() * 0.8;
168 if (Direction::isHorizontal(edgeDragArea->direction())) {
169 return QPointF(0, deviation);
171 return QPointF(deviation, 0);
176void tst_DirectionalDragArea::edgeDrag()
178 QFETCH(QString, dragAreaObjectName);
179 QFETCH(qreal, wideningAngleMultiplier);
180 QFETCH(qreal, dragDistanceFactor);
181 QFETCH(bool, expectGestureRecognition);
183 DirectionalDragArea *edgeDragArea =
184 m_view->rootObject()->findChild<DirectionalDragArea*>(dragAreaObjectName);
185 QVERIFY(edgeDragArea != 0);
186 edgeDragArea->setRecognitionTimer(fakeTimer);
187 edgeDragArea->setTimeSource(fakeTimeSource);
189 QSignalSpy draggingSpy(edgeDragArea, SIGNAL(draggingChanged(bool)));
191 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
192 QPointF touchPoint = initialTouchPos;
194 qreal desiredDragDistance = edgeDragArea->distanceThreshold()*dragDistanceFactor;
195 QPointF dragDirectionVector = calculateDirectionVector(edgeDragArea,
196 wideningAngleMultiplier);
197 qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
198 QPointF touchMovement = dragDirectionVector * movementStepDistance;
199 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
200 int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
202 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
204 QCOMPARE(draggingSpy.count(), 1);
205 QCOMPARE(edgeDragArea->dragging(), true);
207 if (wideningAngleMultiplier > 0) {
208 // go close to the border of the valid area for this touch point
209 // in order to make it easier to leave it by dragging at an angle
210 // slightly bigger than the widening angle
211 touchPoint += createTouchDeviation(edgeDragArea);
212 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
215 for (int i = 0; i < totalMovementSteps; ++i) {
216 touchPoint += touchMovement;
217 passTime(movementTimeStepMs);
218 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
221 if (expectGestureRecognition)
222 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
224 if (edgeDragArea->status() == DirectionalDragArea::WaitingForTouch) {
225 QCOMPARE(edgeDragArea->dragging(), false);
226 QCOMPARE(draggingSpy.count(), 2);
229 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
231 QCOMPARE(draggingSpy.count(), 2);
232 QCOMPARE(edgeDragArea->dragging(), false);
235void tst_DirectionalDragArea::edgeDrag_data()
237 QTest::addColumn<QString>("dragAreaObjectName");
238 QTest::addColumn<qreal>("wideningAngleMultiplier");
239 QTest::addColumn<qreal>("dragDistanceFactor");
240 QTest::addColumn<bool>("expectGestureRecognition");
242 QTest::newRow("rightwards, tiny drag")
243 << "hpDragArea" << 0.0 << 0.2 << false;
245 QTest::newRow("rightwards, straight drag")
246 << "hpDragArea" << 0.0 << 3.0 << true;
248 QTest::newRow("rightwards, diagonal drag")
249 << "hpDragArea" << 0.9 << 3.0 << true;
251 QTest::newRow("rightwards, overly diagonal drag")
252 << "hpDragArea" << 2.0 << 3.0 << false;
254 QTest::newRow("leftwards, tiny drag")
255 << "hnDragArea" << 0.0 << 0.2 << false;
257 QTest::newRow("leftwards, straight drag")
258 << "hnDragArea" << 0.0 << 3.0 << true;
260 QTest::newRow("leftwards, diagonal drag")
261 << "hnDragArea" << 0.9 << 3.0 << true;
263 QTest::newRow("downwards, tiny drag")
264 << "vpDragArea" << 0.0 << 0.2 << false;
266 QTest::newRow("downwards, straight drag")
267 << "vpDragArea" << 0.0 << 3.0 << true;
269 QTest::newRow("downwards, diagonal drag")
270 << "vpDragArea" << 0.9 << 3.0 << true;
272 QTest::newRow("upwards, tiny drag")
273 << "vnDragArea" << 0.0 << 0.2 << false;
275 QTest::newRow("upwards, straight drag")
276 << "vnDragArea" << 0.0 << 3.0 << true;
278 QTest::newRow("upwards, diagonal drag")
279 << "vnDragArea" << 0.9 << 3.0 << true;
281 QTest::newRow("upwards, overly diagonal drag")
282 << "vnDragArea" << 2.0 << 3.0 << false;
286 A directional drag should still be recognized if there is a momentaneous, small,
287 change in the direction of a drag. That should be accounted as input noise and
290void tst_DirectionalDragArea::dragWithShortDirectionChange()
292 DirectionalDragArea *edgeDragArea =
293 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
294 QVERIFY(edgeDragArea != 0);
295 edgeDragArea->setRecognitionTimer(fakeTimer);
296 edgeDragArea->setTimeSource(fakeTimeSource);
298 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
299 QPointF touchPoint = initialTouchPos;
301 qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0;
302 QPointF dragDirectionVector(1.0, 0.0);
303 qreal touchStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
304 // make sure we are above minimum speed
305 int touchStepTimeMs = (touchStepDistance / (edgeDragArea->minSpeed() * 5.0f)) * 1000.0f;
306 QPointF touchMovement = dragDirectionVector * touchStepDistance;
308 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
310 // Move a bit in the proper direction
311 for (int i=0; i < 3; ++i) {
312 touchPoint += touchMovement;
313 passTime(touchStepTimeMs);
314 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
317 // Then a sudden and small movement to the opposite direction
318 touchPoint -= touchMovement*0.2;
319 passTime(touchStepTimeMs*0.2);
320 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
322 // And then resume movment in the correct direction until it crosses the distance and time
325 touchPoint += touchMovement;
326 passTime(touchStepTimeMs);
327 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
328 } while ((touchPoint - initialTouchPos).manhattanLength() < desiredDragDistance
329 || fakeTimeSource->m_msecsSinceReference < (edgeDragArea->compositionTime() * 1.5f));
331 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
333 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
337 Checks that a gesture will be rejected if it's slower than minSpeed while
340void tst_DirectionalDragArea::minSpeed()
342 QFETCH(qreal, minSpeed);
343 QFETCH(qreal, speed);
344 QFETCH(int, expectedStatusAfterSpeedCheck);
346 DirectionalDragArea *edgeDragArea =
347 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
348 QVERIFY(edgeDragArea != 0);
349 edgeDragArea->setRecognitionTimer(fakeTimer);
350 edgeDragArea->setTimeSource(fakeTimeSource);
352 // A really long, unattainable, number. We don't want it getting recognized before
353 // the speed checks we want have been performed
354 edgeDragArea->setDistanceThreshold(500000);
356 edgeDragArea->setMinSpeed(minSpeed);
358 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
359 QPointF touchPoint = initialTouchPos;
361 QPointF dragDirectionVector(1.0, 0.0);
362 qint64 timeStepMsecs = 10;
363 qreal distanceStep = (speed / 1000.0f) * timeStepMsecs;
364 QPointF touchMovement = dragDirectionVector * distanceStep;
366 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
368 // Move for a while to ensure our speed check is performed a couple of times
369 for (int i=0; i < 20; ++i) {
370 touchPoint += touchMovement;
371 passTime(timeStepMsecs);
372 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
375 QCOMPARE((int)edgeDragArea->status(), expectedStatusAfterSpeedCheck);
378void tst_DirectionalDragArea::minSpeed_data()
380 QTest::addColumn<qreal>("minSpeed");
381 QTest::addColumn<qreal>("speed");
382 QTest::addColumn<int>("expectedStatusAfterSpeedCheck");
384 QTest::newRow("slower than minSpeed") << 100.0 << 50.0 << (int)DirectionalDragArea::WaitingForTouch;
385 QTest::newRow("faster than minSpeed") << 100.0 << 150.0 << (int)DirectionalDragArea::Undecided;
389 Checks that the recognition timer is started and stopped appropriately.
390 I.e., check that it's running only while gesture recognition is taking place
391 (status == Undecided)
393void tst_DirectionalDragArea::recognitionTimerUsage()
395 DirectionalDragArea *edgeDragArea =
396 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
397 QVERIFY(edgeDragArea != 0);
398 edgeDragArea->setRecognitionTimer(fakeTimer);
399 edgeDragArea->setTimeSource(fakeTimeSource);
401 // don't let it interfere with our test
402 edgeDragArea->setMinSpeed(0.0);
404 int timeStepMs = 5; // some arbitrary small value.
406 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
407 QPointF touchPoint = initialTouchPos;
409 QPointF dragDirectionVector(1.0, 0.0);
410 QPointF touchMovement = dragDirectionVector * (edgeDragArea->distanceThreshold() * 0.2f);
412 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
413 QVERIFY(!fakeTimer->isRunning());
415 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
417 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
418 QVERIFY(fakeTimer->isRunning());
420 // Move beyond distance threshold and composition time to ensure recognition
421 while (fakeTimeSource->m_msecsSinceReference <= edgeDragArea->compositionTime() ||
422 (touchPoint - initialTouchPos).manhattanLength() <= edgeDragArea->distanceThreshold()) {
424 QCOMPARE(edgeDragArea->status() == DirectionalDragArea::Undecided, fakeTimer->isRunning());
426 touchPoint += touchMovement;
427 passTime(timeStepMs);
428 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
431 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
432 QVERIFY(!fakeTimer->isRunning());
436 A gesture should be rejected if too much time has passed without any new input
439void tst_DirectionalDragArea::maxSilenceTime()
441 DirectionalDragArea *edgeDragArea =
442 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
443 QVERIFY(edgeDragArea != 0);
444 edgeDragArea->setRecognitionTimer(fakeTimer);
445 edgeDragArea->setTimeSource(fakeTimeSource);
447 // Make sure this property is not disabled
448 edgeDragArea->setMaxSilenceTime(100);
450 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
451 QPointF touchPoint = initialTouchPos;
453 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
455 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
456 QVERIFY(fakeTimer->isRunning());
458 // Force timer to timeout until after maxSilenceTime has been reached
459 while (fakeTimeSource->m_msecsSinceReference < edgeDragArea->maxSilenceTime()) {
460 passTime(fakeTimer->interval());
463 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
467 Checks that it informs the X coordinate of the touch point in local and scene coordinates
470void tst_DirectionalDragArea::sceneXAndX()
472 DirectionalDragArea *edgeDragArea =
473 m_view->rootObject()->findChild<DirectionalDragArea*>("hnDragArea");
474 QVERIFY(edgeDragArea != 0);
475 edgeDragArea->setRecognitionTimer(fakeTimer);
476 edgeDragArea->setTimeSource(fakeTimeSource);
478 QPointF touchScenePos(m_view->width() - (edgeDragArea->width()/2.0f), m_view->height()/2.0f);
480 QTest::touchEvent(m_view, m_device).press(0, touchScenePos.toPoint());
482 QSignalSpy touchXSpy(edgeDragArea, SIGNAL(touchXChanged(qreal)));
483 QSignalSpy touchSceneXSpy(edgeDragArea, SIGNAL(touchSceneXChanged(qreal)));
485 touchScenePos.rx() = m_view->width() / 2;
486 QTest::touchEvent(m_view, m_device).move(0, touchScenePos.toPoint());
488 QCOMPARE(touchXSpy.count(), 1);
489 QCOMPARE(touchSceneXSpy.count(), 1);
490 QCOMPARE(edgeDragArea->touchX(), touchScenePos.x() - edgeDragArea->x());
491 QCOMPARE(edgeDragArea->touchSceneX(), touchScenePos.x());
495 Checks that it informs the Y coordinate of the touch point in local and scene coordinates
498void tst_DirectionalDragArea::sceneYAndY()
500 DirectionalDragArea *edgeDragArea =
501 m_view->rootObject()->findChild<DirectionalDragArea*>("vnDragArea");
502 QVERIFY(edgeDragArea != 0);
503 edgeDragArea->setRecognitionTimer(fakeTimer);
504 edgeDragArea->setTimeSource(fakeTimeSource);
506 QPointF touchScenePos(m_view->width()/2.0f, m_view->height() - (edgeDragArea->height()/2.0f));
508 QTest::touchEvent(m_view, m_device).press(0, touchScenePos.toPoint());
510 QSignalSpy touchYSpy(edgeDragArea, SIGNAL(touchYChanged(qreal)));
511 QSignalSpy touchSceneYSpy(edgeDragArea, SIGNAL(touchSceneYChanged(qreal)));
513 touchScenePos.ry() = m_view->height() / 2;
514 QTest::touchEvent(m_view, m_device).move(0, touchScenePos.toPoint());
516 QCOMPARE(touchYSpy.count(), 1);
517 QCOMPARE(touchSceneYSpy.count(), 1);
518 QCOMPARE(edgeDragArea->touchY(), touchScenePos.y() - edgeDragArea->y());
519 QCOMPARE(edgeDragArea->touchSceneY(), touchScenePos.y());
523 Regression test for https://bugs.launchpad.net/bugs/1228336
525 Perform a quick two-finger double-tap and check that DirectionalDragArea properly
526 rejects those touch points. In the bug above it got confused and crashed.
528void tst_DirectionalDragArea::twoFingerTap()
530 DirectionalDragArea *edgeDragArea =
531 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
532 QVERIFY(edgeDragArea != 0);
533 edgeDragArea->setRecognitionTimer(fakeTimer);
534 edgeDragArea->setTimeSource(fakeTimeSource);
536 // Make touches evenly spaced along the edgeDragArea
537 QPoint touchAPos(edgeDragArea->width()/2.0f, m_view->height()*0.33f);
538 QPoint touchBPos(edgeDragArea->width()/2.0f, m_view->height()*0.66f);
540 qint64 timeStepMsecs = 5; // some arbitrary, small value
542 // Perform the first two-finger tap
543 // NB: using move() instead of stationary() becasue in the latter you cannot provide
544 // the touch position and therefore it's left with some garbage numbers.
545 QTest::touchEvent(m_view, m_device)
546 .press(0, touchAPos);
548 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
550 passTime(timeStepMsecs);
551 QTest::touchEvent(m_view, m_device)
553 .press(1, touchBPos);
555 // A second touch point appeared during recognition, reject immediately as this
556 // can't be a single-touch gesture anymore.
557 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
559 passTime(timeStepMsecs);
560 QTest::touchEvent(m_view, m_device)
561 .release(0, touchAPos)
564 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
566 passTime(timeStepMsecs);
567 QTest::touchEvent(m_view, m_device)
568 .release(1, touchBPos);
570 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
572 // Perform the second two-finger tap
574 passTime(timeStepMsecs);
575 QTest::touchEvent(m_view, m_device)
576 .press(0, touchAPos);
578 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
580 passTime(timeStepMsecs);
581 QTest::touchEvent(m_view, m_device)
583 .press(1, touchBPos);
585 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
587 passTime(timeStepMsecs);
588 QTest::touchEvent(m_view, m_device)
589 .release(0, touchAPos)
592 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
594 passTime(timeStepMsecs);
595 QTest::touchEvent(m_view, m_device)
596 .release(1, touchBPos);
598 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
602 Tests that gesture recognition works normally even if the DirectionalDragArea moves
604 This effectively means that we have to do gesture recognition with scene coordinates
605 instead of local item coordinates.
607void tst_DirectionalDragArea::movingDDA()
609 QQuickItem *rightwardsLauncher = m_view->rootObject()->findChild<QQuickItem*>("rightwardsLauncher");
610 Q_ASSERT(rightwardsLauncher != 0);
612 DirectionalDragArea *edgeDragArea =
613 rightwardsLauncher->findChild<DirectionalDragArea*>("hpDragArea");
614 Q_ASSERT(edgeDragArea != 0);
615 edgeDragArea->setRecognitionTimer(fakeTimer);
616 edgeDragArea->setTimeSource(fakeTimeSource);
618 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
619 QPointF touchPoint = initialTouchPos;
621 qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;
622 QPointF dragDirectionVector(1.0f, 0.0f);
624 qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
625 QPointF touchMovement = dragDirectionVector * movementStepDistance;
626 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
627 int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
629 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
631 // Move it far ahead along the direction of the gesture
632 // rightwardsLauncher is a parent of our DirectionalDragArea. So moving it will move our DDA
633 rightwardsLauncher->setX(rightwardsLauncher->x() + edgeDragArea->distanceThreshold() * 5.0f);
635 for (int i = 0; i < totalMovementSteps; ++i) {
636 touchPoint += touchMovement;
637 passTime(movementTimeStepMs);
638 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
641 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
643 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
647 The presence of and old, rejected, touch point lying on the DirectionalDragArea
648 shouldn't impede the recognition of a gesture from a new touch point.
650void tst_DirectionalDragArea::ignoreOldFinger()
652 DirectionalDragArea *edgeDragArea =
653 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
654 Q_ASSERT(edgeDragArea != 0);
655 edgeDragArea->setRecognitionTimer(fakeTimer);
656 edgeDragArea->setTimeSource(fakeTimeSource);
658 // Make touches evenly spaced along the edgeDragArea
659 QPoint touch0Pos(edgeDragArea->width()/2.0f, m_view->height()*0.33f);
660 QPoint touch1Pos(edgeDragArea->width()/2.0f, m_view->height()*0.66f);
662 QTest::touchEvent(m_view, m_device).press(0, touch0Pos);
664 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
666 // leave it lying around for some time
667 passTime(qMax(edgeDragArea->maxSilenceTime(), edgeDragArea->compositionTime()) * 10);
669 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
671 qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;
672 QPointF dragDirectionVector(1.0f, 0.0f);
674 qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
675 QPointF touchMovement = dragDirectionVector * movementStepDistance;
676 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
677 int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
679 QTest::touchEvent(m_view, m_device)
681 .press(1, touch1Pos);
683 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
685 for (int i = 0; i < totalMovementSteps; ++i) {
686 touch1Pos += touchMovement.toPoint();
687 passTime(movementTimeStepMs);
688 QTest::touchEvent(m_view, m_device)
693 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
695 QTest::touchEvent(m_view, m_device)
697 .release(1, touch1Pos);
699 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
703 A Rightwards DDA that is rotated 90 degrees clockwise should recognize gestures
704 that are done downwards in scene coordinates. I.e. the gesture recognition direction
705 should be in local coordinates, not scene coordinates.
707void tst_DirectionalDragArea::rotated()
709 QQuickItem *baseItem = m_view->rootObject()->findChild<QQuickItem*>("baseItem");
710 baseItem->setRotation(90.);
712 QQuickItem *rightwardsLauncher = m_view->rootObject()->findChild<QQuickItem*>("rightwardsLauncher");
713 Q_ASSERT(rightwardsLauncher != 0);
715 DirectionalDragArea *edgeDragArea =
716 rightwardsLauncher->findChild<DirectionalDragArea*>("hpDragArea");
717 Q_ASSERT(edgeDragArea != 0);
718 edgeDragArea->setRecognitionTimer(fakeTimer);
719 edgeDragArea->setTimeSource(fakeTimeSource);
721 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
722 QPointF touchPoint = initialTouchPos;
724 qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;
725 QPointF dragDirectionVector(0.0f, 1.0f);
727 qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
728 QPointF touchMovement = dragDirectionVector * movementStepDistance;
729 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
730 int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
732 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
734 for (int i = 0; i < totalMovementSteps; ++i) {
735 touchPoint += touchMovement;
736 passTime(movementTimeStepMs);
737 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
740 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
742 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
745void tst_DirectionalDragArea::sceneDistance()
747 QQuickItem *baseItem = m_view->rootObject()->findChild<QQuickItem*>("baseItem");
748 QFETCH(qreal, rotation);
749 QFETCH(QPointF, dragDirectionVector);
750 baseItem->setRotation(rotation);
752 QQuickItem *rightwardsLauncher = m_view->rootObject()->findChild<QQuickItem*>("rightwardsLauncher");
753 Q_ASSERT(rightwardsLauncher != 0);
755 DirectionalDragArea *edgeDragArea =
756 rightwardsLauncher->findChild<DirectionalDragArea*>("hpDragArea");
757 Q_ASSERT(edgeDragArea != 0);
758 edgeDragArea->setRecognitionTimer(fakeTimer);
759 edgeDragArea->setTimeSource(fakeTimeSource);
761 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
762 QPointF touchPoint = initialTouchPos;
764 qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;
766 qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
767 QPointF touchMovement = dragDirectionVector * movementStepDistance;
768 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
769 int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
771 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
773 for (int i = 0; i < totalMovementSteps; ++i) {
774 touchPoint += touchMovement;
775 passTime(movementTimeStepMs);
776 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
779 qreal actualDragDistance = ((qreal)totalMovementSteps) * movementStepDistance;
781 // DirectionalDragArea::sceneDistance() must match the actual drag distance as the
782 // drag was aligned with the gesture direction
783 // NB: qFuzzyCompare(), used internally by QCOMPARE(), is broken.
784 QVERIFY(qAbs(edgeDragArea->sceneDistance() - actualDragDistance) < 0.001);
786 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
789void tst_DirectionalDragArea::sceneDistance_data()
791 QTest::addColumn<qreal>("rotation");
792 QTest::addColumn<QPointF>("dragDirectionVector");
794 QTest::newRow("not rotated") << 0. << QPointF(1., 0.);
795 QTest::newRow("rotated by 90 degrees") << 90. << QPointF(0., 1.);
798QTEST_MAIN(tst_DirectionalDragArea)
800#include "tst_DirectionalDragArea.moc"
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list