on xenial help sign in
DirectionalDragArea: added minSpeed and maxSilenceTime constraints.
0ffb04fDaniel d'Andrada
Components/Stage.qml+2−1
@@ -441,7 +441,8 @@ Showable {
441 direction: DirectionalDragArea.Leftwards
441 direction: DirectionalDragArea.Leftwards
442 maxDeviation: units.gu(1)
442 maxDeviation: units.gu(1)
443 wideningAngle: 20
443 wideningAngle: 20
444− distanceThreshold: units.gu(2)
444+ distanceThreshold: units.gu(3)
445+ minSpeed: units.gu(5)
445
446
446 onDistanceChanged: {
447 onDistanceChanged: {
447 if (status !== DirectionalDragArea.Recognized)
448 if (status !== DirectionalDragArea.Recognized)
Launcher/Launcher.qml+3−2
@@ -188,9 +188,10 @@ Item {
188
188
189 // values to be tweaked and later removed from here and set in stone as defaults
189 // values to be tweaked and later removed from here and set in stone as defaults
190 // once we are confident it's all good.
190 // once we are confident it's all good.
191− maxDeviation: units.gu(1.5)
191+ maxDeviation: units.gu(1)
192 wideningAngle: 30
192 wideningAngle: 30
193− distanceThreshold: units.gu(4)
193+ distanceThreshold: units.gu(3)
194+ minSpeed: units.gu(5)
194
195
195 enabled: root.available && root.state !== "visible"
196 enabled: root.available && root.state !== "visible"
196 width: root.dragAreaWidth
197 width: root.dragAreaWidth
plugins/Ubuntu/Gestures/AxisVelocityCalculator.cpp+1−1
@@ -85,7 +85,7 @@ void AxisVelocityCalculator::processMovement(qreal movement)
85
85
86qreal AxisVelocityCalculator::calculate() const
86qreal AxisVelocityCalculator::calculate() const
87{
87{
88− if (numSamples() < 2) {
88+ if (numSamples() < MIN_SAMPLES_NEEDED) {
89 return 0.0;
89 return 0.0;
90 }
90 }
91
91
plugins/Ubuntu/Gestures/AxisVelocityCalculator.h+7−2
@@ -95,6 +95,13 @@ public:
95 */
95 */
96 Q_INVOKABLE void reset();
96 Q_INVOKABLE void reset();
97
97
98+ int numSamples() const;
99+
100+ /*
101+ The minimum amount of samples needed for a velocity calculation.
102+ */
103+ static const int MIN_SAMPLES_NEEDED = 2;
104+
98 /*
105 /*
99 Maximum number of movement samples stored
106 Maximum number of movement samples stored
100 */
107 */
@@ -115,8 +122,6 @@ private:
115 */
122 */
116 void processMovement(qreal movement);
123 void processMovement(qreal movement);
117
124
118− int numSamples() const;
119−
120 class Sample
125 class Sample
121 {
126 {
122 public:
127 public:
plugins/Ubuntu/Gestures/CMakeLists.txt+3
@@ -16,6 +16,9 @@ add_library(UbuntuGestureQml MODULE ${UbuntuGestureQml_SOURCES})
16
16
17qt5_use_modules(UbuntuGestureQml Core Quick)
17qt5_use_modules(UbuntuGestureQml Core Quick)
18
18
19+# So that Foo.cpp can #include "Foo.moc"
20+include_directories(${CMAKE_CURRENT_BINARY_DIR})
21+
19# copy files into build directory for shadow builds
22# copy files into build directory for shadow builds
20add_custom_target(UbuntuGestureQmlDirFile ALL
23add_custom_target(UbuntuGestureQmlDirFile ALL
21 COMMAND cp "${CMAKE_CURRENT_SOURCE_DIR}/qmldir" ${CMAKE_CURRENT_BINARY_DIR}
24 COMMAND cp "${CMAKE_CURRENT_SOURCE_DIR}/qmldir" ${CMAKE_CURRENT_BINARY_DIR}
plugins/Ubuntu/Gestures/DirectionalDragArea.cpp+121
@@ -17,6 +17,25 @@
17#include "DirectionalDragArea.h"
17#include "DirectionalDragArea.h"
18
18
19#include <QtCore/qmath.h>
19#include <QtCore/qmath.h>
20+#include <QtCore/QTimer>
21+
22+// Essentially a QTimer wrapper
23+class RecognitionTimer : public UbuntuGestures::AbstractTimer
24+{
25+ Q_OBJECT
26+public:
27+ RecognitionTimer(QObject *parent) : UbuntuGestures::AbstractTimer(parent) {
28+ m_timer.setSingleShot(false);
29+ connect(&m_timer, &QTimer::timeout,
30+ this, &UbuntuGestures::AbstractTimer::timeout);
31+ }
32+ virtual int interval() const { return m_timer.interval(); }
33+ virtual void setInterval(int msecs) { m_timer.setInterval(msecs); }
34+ virtual void start() { m_timer.start(); UbuntuGestures::AbstractTimer::start(); }
35+ virtual void stop() { m_timer.stop(); UbuntuGestures::AbstractTimer::stop(); }
36+private:
37+ QTimer m_timer;
38+};
20
39
21DirectionalDragArea::DirectionalDragArea(QQuickItem *parent)
40DirectionalDragArea::DirectionalDragArea(QQuickItem *parent)
22 : QQuickItem(parent)
41 : QQuickItem(parent)
@@ -26,7 +45,17 @@ DirectionalDragArea::DirectionalDragArea(QQuickItem *parent)
26 , m_wideningAngle(0)
45 , m_wideningAngle(0)
27 , m_wideningFactor(0)
46 , m_wideningFactor(0)
28 , m_distanceThreshold(0)
47 , m_distanceThreshold(0)
48+ , m_minSpeed(0)
49+ , m_maxSilenceTime(200)
50+ , m_silenceTime(0)
51+ , m_numSamplesOnLastSpeedCheck(0)
52+ , m_recognitionTimer(0)
53+ , m_velocityCalculator(0)
29{
54{
55+ setRecognitionTimer(new RecognitionTimer(this));
56+ m_recognitionTimer->setInterval(60);
57+
58+ m_velocityCalculator = new AxisVelocityCalculator(this);
30}
59}
31
60
32DirectionalDragArea::Direction DirectionalDragArea::direction() const
61DirectionalDragArea::Direction DirectionalDragArea::direction() const
@@ -73,6 +102,53 @@ void DirectionalDragArea::setDistanceThreshold(qreal value)
73 }
102 }
74}
103}
75
104
105+void DirectionalDragArea::setMinSpeed(qreal value)
106+{
107+ if (m_minSpeed != value) {
108+ m_minSpeed = value;
109+ Q_EMIT minSpeedChanged(value);
110+ }
111+}
112+
113+void DirectionalDragArea::setMaxSilenceTime(int value)
114+{
115+ if (m_maxSilenceTime != value) {
116+ m_maxSilenceTime = value;
117+ Q_EMIT maxSilenceTimeChanged(value);
118+ }
119+}
120+
121+void DirectionalDragArea::setRecognitionTimer(UbuntuGestures::AbstractTimer *timer)
122+{
123+ int interval = 0;
124+ bool timerWasRunning = false;
125+
126+ // can be null when called from the constructor
127+ if (m_recognitionTimer) {
128+ interval = m_recognitionTimer->interval();
129+ timerWasRunning = m_recognitionTimer->isRunning();
130+ if (m_recognitionTimer->parent() == this) {
131+ delete m_recognitionTimer;
132+ }
133+ }
134+
135+ m_recognitionTimer = timer;
136+ timer->setInterval(interval);
137+ connect(timer, &UbuntuGestures::AbstractTimer::timeout,
138+ this, &DirectionalDragArea::checkSpeed);
139+ if (timerWasRunning) {
140+ m_recognitionTimer->start();
141+ }
142+}
143+
144+void DirectionalDragArea::setAxisVelocityCalculator(AxisVelocityCalculator *newVelCalc)
145+{
146+ if (m_velocityCalculator->parent() == this)
147+ delete m_velocityCalculator;
148+
149+ m_velocityCalculator = newVelCalc;
150+}
151+
76qreal DirectionalDragArea::distance() const
152qreal DirectionalDragArea::distance() const
77{
153{
78 if (directionIsHorizontal()) {
154 if (directionIsHorizontal()) {
@@ -122,6 +198,10 @@ void DirectionalDragArea::touchEvent_absent(QTouchEvent *event)
122 m_startPos = event->touchPoints()[0].pos();
198 m_startPos = event->touchPoints()[0].pos();
123 m_touchId = event->touchPoints()[0].id();
199 m_touchId = event->touchPoints()[0].id();
124 m_dampedPos.reset(m_startPos);
200 m_dampedPos.reset(m_startPos);
201+ updateVelocityCalculator(m_startPos);
202+ m_velocityCalculator->reset();
203+ m_numSamplesOnLastSpeedCheck = 0;
204+ m_silenceTime = 0;
125 setPreviousPos(m_startPos);
205 setPreviousPos(m_startPos);
126 setStatus(Undecided);
206 setStatus(Undecided);
127 }
207 }
@@ -148,6 +228,7 @@ void DirectionalDragArea::touchEvent_undecided(QTouchEvent *event)
148 m_previousDampedPos.setX(m_dampedPos.x());
228 m_previousDampedPos.setX(m_dampedPos.x());
149 m_previousDampedPos.setY(m_dampedPos.y());
229 m_previousDampedPos.setY(m_dampedPos.y());
150 m_dampedPos.update(touchPos);
230 m_dampedPos.update(touchPos);
231+ updateVelocityCalculator(touchPos);
151
232
152 if (!pointInsideAllowedArea()) {
233 if (!pointInsideAllowedArea()) {
153 setStatus(Rejected);
234 setStatus(Rejected);
@@ -238,6 +319,29 @@ bool DirectionalDragArea::movedFarEnough(const QPointF &point) const
238 return qFabs(point.y() - m_startPos.y()) > m_distanceThreshold;
319 return qFabs(point.y() - m_startPos.y()) > m_distanceThreshold;
239}
320}
240
321
322+void DirectionalDragArea::checkSpeed()
323+{
324+ if (m_velocityCalculator->numSamples() >= AxisVelocityCalculator::MIN_SAMPLES_NEEDED) {
325+ qreal speed = qFabs(m_velocityCalculator->calculate());
326+ qreal minSpeedMsecs = m_minSpeed / 1000.0;
327+
328+ if (speed < minSpeedMsecs) {
329+ setStatus(Rejected);
330+ }
331+ }
332+
333+ if (m_velocityCalculator->numSamples() == m_numSamplesOnLastSpeedCheck) {
334+ m_silenceTime += m_recognitionTimer->interval();
335+
336+ if (m_silenceTime > m_maxSilenceTime) {
337+ setStatus(Rejected);
338+ }
339+ } else {
340+ m_silenceTime = 0;
341+ }
342+ m_numSamplesOnLastSpeedCheck = m_velocityCalculator->numSamples();
343+}
344+
241void DirectionalDragArea::setStatus(DirectionalDragArea::Status newStatus)
345void DirectionalDragArea::setStatus(DirectionalDragArea::Status newStatus)
242{
346{
243 if (newStatus == m_status)
347 if (newStatus == m_status)
@@ -245,6 +349,10 @@ void DirectionalDragArea::setStatus(DirectionalDragArea::Status newStatus)
245
349
246 DirectionalDragArea::Status oldStatus = m_status;
350 DirectionalDragArea::Status oldStatus = m_status;
247
351
352+ if (oldStatus == Undecided) {
353+ m_recognitionTimer->stop();
354+ }
355+
248 m_status = newStatus;
356 m_status = newStatus;
249 Q_EMIT statusChanged(m_status);
357 Q_EMIT statusChanged(m_status);
250
358
@@ -255,6 +363,7 @@ void DirectionalDragArea::setStatus(DirectionalDragArea::Status newStatus)
255 }
363 }
256 break;
364 break;
257 case Undecided:
365 case Undecided:
366+ m_recognitionTimer->start();
258 Q_EMIT draggingChanged(true);
367 Q_EMIT draggingChanged(true);
259 break;
368 break;
260 case Rejected:
369 case Rejected:
@@ -288,6 +397,15 @@ void DirectionalDragArea::setPreviousPos(QPointF point)
288 }
397 }
289}
398}
290
399
400+void DirectionalDragArea::updateVelocityCalculator(QPointF point)
401+{
402+ if (directionIsHorizontal()) {
403+ m_velocityCalculator->setTrackedPosition(point.x());
404+ } else {
405+ m_velocityCalculator->setTrackedPosition(point.y());
406+ }
407+}
408+
291bool DirectionalDragArea::directionIsHorizontal() const
409bool DirectionalDragArea::directionIsHorizontal() const
292{
410{
293 return m_direction == Leftwards || m_direction == Rightwards;
411 return m_direction == Leftwards || m_direction == Rightwards;
@@ -297,3 +415,6 @@ bool DirectionalDragArea::directionIsVertical() const
297{
415{
298 return m_direction == Upwards || m_direction == Downwards;
416 return m_direction == Upwards || m_direction == Downwards;
299}
417}
418+
419+// Because we are defining a new QObject-based class (RecognitionTimer) here.
420+#include "DirectionalDragArea.moc"
plugins/Ubuntu/Gestures/DirectionalDragArea.h+60
@@ -18,9 +18,28 @@
18#define DIRECTIONAL_DRAG_AREA_H
18#define DIRECTIONAL_DRAG_AREA_H
19
19
20#include <QtQuick/QQuickItem>
20#include <QtQuick/QQuickItem>
21+#include "AxisVelocityCalculator.h"
21#include "UbuntuGesturesGlobal.h"
22#include "UbuntuGesturesGlobal.h"
22#include "Damper.h"
23#include "Damper.h"
23
24
25+namespace UbuntuGestures {
26+/* Defines an interface for a Timer. */
27+class UBUNTUGESTURES_EXPORT AbstractTimer : public QObject {
28+ Q_OBJECT
29+public:
30+ AbstractTimer(QObject *parent) : QObject(parent), m_isRunning(false) {}
31+ virtual int interval() const = 0;
32+ virtual void setInterval(int msecs) = 0;
33+ virtual void start() { m_isRunning = true; };
34+ virtual void stop() { m_isRunning = false; }
35+ bool isRunning() const { return m_isRunning; }
36+Q_SIGNALS:
37+ void timeout();
38+private:
39+ bool m_isRunning;
40+};
41+}
42+
24/*
43/*
25 An area that detects axis-aligned single-finger drag gestures
44 An area that detects axis-aligned single-finger drag gestures
26
45
@@ -74,6 +93,21 @@ class UBUNTUGESTURES_EXPORT DirectionalDragArea : public QQuickItem {
74 Q_PROPERTY(qreal distanceThreshold READ distanceThreshold WRITE setDistanceThreshold
93 Q_PROPERTY(qreal distanceThreshold READ distanceThreshold WRITE setDistanceThreshold
75 NOTIFY distanceThresholdChanged)
94 NOTIFY distanceThresholdChanged)
76
95
96+ // Minimum speed a gesture needs to have in order to be recognized as a
97+ // directional drag.
98+ // In pixels per second
99+ Q_PROPERTY(qreal minSpeed READ minSpeed WRITE setMinSpeed NOTIFY minSpeedChanged)
100+
101+ // A gesture will be rejected if more than maxSilenceTime milliseconds has
102+ // passed since we last got an input event from it (during Undecided state).
103+ //
104+ // Silence (i.e., lack of new input events) doesn't necessarily mean that the user's
105+ // finger is still (zero drag speed). In some cases the finger might be moving but
106+ // the driver's high noise filtering might cause those silence periods, specially
107+ // in the moments succeeding a press (talking about Galaxy Nexus here).
108+ Q_PROPERTY(int maxSilenceTime READ maxSilenceTime
109+ WRITE setMaxSilenceTime
110+ NOTIFY maxSilenceTimeChanged)
77 //
111 //
78 /////
112 /////
79
113
@@ -130,6 +164,20 @@ public:
130 qreal distanceThreshold() const { return m_distanceThreshold; }
164 qreal distanceThreshold() const { return m_distanceThreshold; }
131 void setDistanceThreshold(qreal value);
165 void setDistanceThreshold(qreal value);
132
166
167+ qreal minSpeed() const { return m_minSpeed; }
168+ void setMinSpeed(qreal value);
169+
170+ int maxSilenceTime() const { return m_maxSilenceTime; }
171+ void setMaxSilenceTime(int value);
172+
173+ // Replaces the existing Timer with the given one.
174+ //
175+ // Useful for providing a fake timer when testing.
176+ void setRecognitionTimer(UbuntuGestures::AbstractTimer *timer);
177+
178+ // Useful for testing, where a calculator with a fake time source can be supplied
179+ void setAxisVelocityCalculator(AxisVelocityCalculator *velCalc);
180+
133Q_SIGNALS:
181Q_SIGNALS:
134 void directionChanged(Direction direction);
182 void directionChanged(Direction direction);
135 void statusChanged(Status value);
183 void statusChanged(Status value);
@@ -138,12 +186,17 @@ Q_SIGNALS:
138 void maxDeviationChanged(qreal value);
186 void maxDeviationChanged(qreal value);
139 void wideningAngleChanged(qreal value);
187 void wideningAngleChanged(qreal value);
140 void distanceThresholdChanged(qreal value);
188 void distanceThresholdChanged(qreal value);
189+ void minSpeedChanged(qreal value);
190+ void maxSilenceTimeChanged(int value);
141 void touchXChanged(qreal value);
191 void touchXChanged(qreal value);
142 void touchYChanged(qreal value);
192 void touchYChanged(qreal value);
143
193
144protected:
194protected:
145 virtual void touchEvent(QTouchEvent *event);
195 virtual void touchEvent(QTouchEvent *event);
146
196
197+private Q_SLOTS:
198+ void checkSpeed();
199+
147private:
200private:
148 void touchEvent_absent(QTouchEvent *event);
201 void touchEvent_absent(QTouchEvent *event);
149 void touchEvent_undecided(QTouchEvent *event);
202 void touchEvent_undecided(QTouchEvent *event);
@@ -155,6 +208,7 @@ private:
155 const QTouchEvent::TouchPoint *fetchTargetTouchPoint(QTouchEvent *event);
208 const QTouchEvent::TouchPoint *fetchTargetTouchPoint(QTouchEvent *event);
156 void setStatus(Status newStatus);
209 void setStatus(Status newStatus);
157 void setPreviousPos(QPointF point);
210 void setPreviousPos(QPointF point);
211+ void updateVelocityCalculator(QPointF point);
158
212
159 // convenience functions
213 // convenience functions
160 bool directionIsHorizontal() const;
214 bool directionIsHorizontal() const;
@@ -175,6 +229,12 @@ private:
175 qreal m_wideningAngle; // in degrees
229 qreal m_wideningAngle; // in degrees
176 qreal m_wideningFactor; // it's tan(degreesToRadian(m_wideningAngle))
230 qreal m_wideningFactor; // it's tan(degreesToRadian(m_wideningAngle))
177 qreal m_distanceThreshold;
231 qreal m_distanceThreshold;
232+ qreal m_minSpeed;
233+ int m_maxSilenceTime; // in milliseconds
234+ int m_silenceTime; // in milliseconds
235+ int m_numSamplesOnLastSpeedCheck;
236+ UbuntuGestures::AbstractTimer *m_recognitionTimer;
237+ AxisVelocityCalculator *m_velocityCalculator;
178};
238};
179
239
180#endif // DIRECTIONAL_DRAG_AREA_H
240#endif // DIRECTIONAL_DRAG_AREA_H
tests/plugins/Ubuntu/Gestures/CMakeLists.txt+5−3
@@ -11,9 +11,9 @@ set(testCommand
11 -o ${CMAKE_BINARY_DIR}/DirectionalDragAreaTest.xml,xunitxml
11 -o ${CMAKE_BINARY_DIR}/DirectionalDragAreaTest.xml,xunitxml
12 -o -,txt)
12 -o -,txt)
13
13
14−add_custom_target(DirectionalDragAreaTest ${testCommand})
14+add_custom_target(testDirectionalDragArea ${testCommand})
15−add_dependencies(qmluitests DirectionalDragAreaTest)
15+add_dependencies(qmluitests testDirectionalDragArea)
16−add_dependencies(DirectionalDragAreaTest DirectionalDragAreaTestExec UbutunGesturesTestQmlFiles)
16+add_dependencies(testDirectionalDragArea DirectionalDragAreaTestExec UbutunGesturesTestQmlFiles)
17
17
18add_executable(DirectionalDragAreaTestExec tst_DirectionalDragArea.cpp)
18add_executable(DirectionalDragAreaTestExec tst_DirectionalDragArea.cpp)
19qt5_use_modules(DirectionalDragAreaTestExec Test Core Qml Gui Quick)
19qt5_use_modules(DirectionalDragAreaTestExec Test Core Qml Gui Quick)
@@ -27,6 +27,8 @@ add_custom_target(UbuntuGesturesTestQmlFiles ALL
27 DEPENDS ${qmlFiles}
27 DEPENDS ${qmlFiles}
28)
28)
29
29
30+########## other tests
31+
30macro(add_gesture_test CLASSNAME)
32macro(add_gesture_test CLASSNAME)
31 set(testCommand
33 set(testCommand
32 ${CLASSNAME}TestExec -o ${CMAKE_BINARY_DIR}/test${CLASSNAME}.xml,xunitxml -o -,txt)
34 ${CLASSNAME}TestExec -o ${CMAKE_BINARY_DIR}/test${CLASSNAME}.xml,xunitxml -o -,txt)
tests/plugins/Ubuntu/Gestures/RightwardsLauncher.qml+1
@@ -54,6 +54,7 @@ Item {
54 maxDeviation: units.gu(2)
54 maxDeviation: units.gu(2)
55 wideningAngle: 10
55 wideningAngle: 10
56 distanceThreshold: units.gu(4)
56 distanceThreshold: units.gu(4)
57+ minSpeed: 50
57
58
58 onStatusChanged: {
59 onStatusChanged: {
59 switch (status) {
60 switch (status) {
tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.cpp+203−16
@@ -22,6 +22,30 @@
22
22
23#include <DirectionalDragArea.h>
23#include <DirectionalDragArea.h>
24
24
25+class FakeTimer : public UbuntuGestures::AbstractTimer
26+{
27+ Q_OBJECT
28+public:
29+ FakeTimer(QObject *parent = 0)
30+ : UbuntuGestures::AbstractTimer(parent)
31+ {}
32+
33+ virtual int interval() const { return m_duration; }
34+ virtual void setInterval(int msecs) { m_duration = msecs; }
35+
36+ void emitTimeout() {Q_EMIT timeout();}
37+private:
38+ int m_duration;
39+};
40+
41+class FakeTimeSource : public AxisVelocityCalculator::TimeSource
42+{
43+public:
44+ FakeTimeSource() { m_msecsSinceReference = 0; }
45+ virtual qint64 msecsSinceReference() {return m_msecsSinceReference;}
46+ qint64 m_msecsSinceReference;
47+};
48+
25class tst_DirectionalDragArea: public QObject
49class tst_DirectionalDragArea: public QObject
26{
50{
27 Q_OBJECT
51 Q_OBJECT
@@ -31,13 +55,24 @@ private Q_SLOTS:
31 void initTestCase(); // will be called before the first test function is executed
55 void initTestCase(); // will be called before the first test function is executed
32 void cleanupTestCase(); // will be called after the last test function was executed.
56 void cleanupTestCase(); // will be called after the last test function was executed.
33
57
58+ 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
60+
34 void edgeDrag();
61 void edgeDrag();
35 void edgeDrag_data();
62 void edgeDrag_data();
36 void dragWithShortDirectionChange();
63 void dragWithShortDirectionChange();
64+ void minSpeed();
65+ void minSpeed_data();
66+ void recognitionTimerUsage();
67+ void maxSilenceTime();
37
68
38private:
69private:
39 QQuickView *createView();
70 QQuickView *createView();
71+ QQuickView *view;
40 QTouchDevice *device;
72 QTouchDevice *device;
73+ FakeTimer *fakeTimer;
74+ FakeTimeSource *fakeTimeSource;
75+ AxisVelocityCalculator *velCalcWithFakeTiming;
41};
76};
42
77
43void tst_DirectionalDragArea::initTestCase()
78void tst_DirectionalDragArea::initTestCase()
@@ -47,12 +82,42 @@ void tst_DirectionalDragArea::initTestCase()
47 device->setType(QTouchDevice::TouchScreen);
82 device->setType(QTouchDevice::TouchScreen);
48 QWindowSystemInterface::registerTouchDevice(device);
83 QWindowSystemInterface::registerTouchDevice(device);
49 }
84 }
85+
86+ view = 0;
50}
87}
51
88
52void tst_DirectionalDragArea::cleanupTestCase()
89void tst_DirectionalDragArea::cleanupTestCase()
53{
90{
54}
91}
55
92
93+void tst_DirectionalDragArea::init()
94+{
95+ view = createView();
96+ view->setSource(QUrl::fromLocalFile("edgeDragExample.qml"));
97+ view->show();
98+ QVERIFY(QTest::qWaitForWindowExposed(view));
99+ QVERIFY(view->rootObject() != 0);
100+ qApp->processEvents();
101+
102+ fakeTimer = new FakeTimer;
103+ fakeTimeSource = new FakeTimeSource;
104+ velCalcWithFakeTiming = new AxisVelocityCalculator(fakeTimeSource);
105+}
106+
107+void tst_DirectionalDragArea::cleanup()
108+{
109+ delete view;
110+ view = 0;
111+
112+ delete fakeTimer;
113+ fakeTimer = 0;
114+
115+ // will bring fakeTimeSource down with it
116+ delete velCalcWithFakeTiming;
117+ fakeTimeSource = 0;
118+ velCalcWithFakeTiming = 0;
119+}
120+
56QQuickView *tst_DirectionalDragArea::createView()
121QQuickView *tst_DirectionalDragArea::createView()
57{
122{
58 QQuickView *window = new QQuickView(0);
123 QQuickView *window = new QQuickView(0);
@@ -119,17 +184,11 @@ void tst_DirectionalDragArea::edgeDrag()
119 QFETCH(qreal, dragDistanceFactor);
184 QFETCH(qreal, dragDistanceFactor);
120 QFETCH(int, expectedGestureRecognition);
185 QFETCH(int, expectedGestureRecognition);
121
186
122− QQuickView *view = createView();
123− QScopedPointer<QQuickView> scope(view);
124− view->setSource(QUrl::fromLocalFile("edgeDragExample.qml"));
125− view->show();
126− QVERIFY(QTest::qWaitForWindowExposed(view));
127− QVERIFY(view->rootObject() != 0);
128− qApp->processEvents();
129−
130 DirectionalDragArea *edgeDragArea =
187 DirectionalDragArea *edgeDragArea =
131 view->rootObject()->findChild<DirectionalDragArea*>(dragAreaObjectName);
188 view->rootObject()->findChild<DirectionalDragArea*>(dragAreaObjectName);
132 QVERIFY(edgeDragArea != 0);
189 QVERIFY(edgeDragArea != 0);
190+ edgeDragArea->setRecognitionTimer(fakeTimer);
191+ edgeDragArea->setAxisVelocityCalculator(velCalcWithFakeTiming);
133
192
134 QSignalSpy draggingSpy(edgeDragArea, SIGNAL(draggingChanged(bool)));
193 QSignalSpy draggingSpy(edgeDragArea, SIGNAL(draggingChanged(bool)));
135
194
@@ -230,17 +289,11 @@ void tst_DirectionalDragArea::edgeDrag_data()
230 */
289 */
231void tst_DirectionalDragArea::dragWithShortDirectionChange()
290void tst_DirectionalDragArea::dragWithShortDirectionChange()
232{
291{
233− QQuickView *view = createView();
234− QScopedPointer<QQuickView> scope(view);
235− view->setSource(QUrl::fromLocalFile("edgeDragExample.qml"));
236− view->show();
237− QVERIFY(QTest::qWaitForWindowExposed(view));
238− QVERIFY(view->rootObject() != 0);
239− qApp->processEvents();
240−
241 DirectionalDragArea *edgeDragArea =
292 DirectionalDragArea *edgeDragArea =
242 view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
293 view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
243 QVERIFY(edgeDragArea != 0);
294 QVERIFY(edgeDragArea != 0);
295+ edgeDragArea->setRecognitionTimer(fakeTimer);
296+ edgeDragArea->setAxisVelocityCalculator(velCalcWithFakeTiming);
244
297
245 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, view);
298 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, view);
246 QPointF touchPoint = initialTouchPos;
299 QPointF touchPoint = initialTouchPos;
@@ -272,6 +325,140 @@ void tst_DirectionalDragArea::dragWithShortDirectionChange()
272 QTest::touchEvent(view, device).release(0, touchPoint.toPoint());
325 QTest::touchEvent(view, device).release(0, touchPoint.toPoint());
273}
326}
274
327
328+/*
329+ Checks that a gesture will be rejected if it's slower than minSpeed while
330+ status is Undecided.
331+ */
332+void tst_DirectionalDragArea::minSpeed()
333+{
334+ QFETCH(int, minSpeedMsecsDeviation);
335+ QFETCH(int, expectedStatusAfterSpeedCheck);
336+
337+ DirectionalDragArea *edgeDragArea =
338+ view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
339+ QVERIFY(edgeDragArea != 0);
340+ edgeDragArea->setRecognitionTimer(fakeTimer);
341+ edgeDragArea->setAxisVelocityCalculator(velCalcWithFakeTiming);
342+
343+ QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, view);
344+ QPointF touchPoint = initialTouchPos;
345+
346+ QPointF dragDirectionVector(1.0, 0.0);
347+ qreal distanceStep = edgeDragArea->distanceThreshold() * 0.1f;
348+ QPointF touchMovement = dragDirectionVector * distanceStep;
349+ qreal minSpeedMsecs = edgeDragArea->minSpeed() / 1000.0;
350+ qint64 timeStepMsecs = qFloor(distanceStep / minSpeedMsecs) + minSpeedMsecsDeviation;
351+
352+ // if it fails it means the params set in the QML file are not in harmony with the values
353+ // used in this test
354+ Q_ASSERT(timeStepMsecs > 0);
355+
356+ fakeTimeSource->m_msecsSinceReference = 0;
357+ QTest::touchEvent(view, device).press(0, touchPoint.toPoint());
358+
359+ // Move a bit in the proper direction
360+ for (int i=0; i < 4; ++i) {
361+ touchPoint += touchMovement;
362+ fakeTimeSource->m_msecsSinceReference += timeStepMsecs;
363+ QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
364+ }
365+
366+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
367+
368+ // Force the periodic speed check.
369+ fakeTimer->emitTimeout();
370+
371+ QCOMPARE((int)edgeDragArea->status(), expectedStatusAfterSpeedCheck);
372+}
373+
374+void tst_DirectionalDragArea::minSpeed_data()
375+{
376+ QTest::addColumn<int>("minSpeedMsecsDeviation");
377+ QTest::addColumn<int>("expectedStatusAfterSpeedCheck");
378+
379+ QTest::newRow("slower than minSpeed") << 20 << (int)DirectionalDragArea::Rejected;
380+ QTest::newRow("faster than minSpeed") << -20 << (int)DirectionalDragArea::Undecided;
381+}
382+
383+/*
384+ Checks that the recognition timer is started and stopped appropriately.
385+ I.e., check that it's running only while gesture recognition is taking place
386+ (status == Undecided)
387+ */
388+void tst_DirectionalDragArea::recognitionTimerUsage()
389+{
390+ DirectionalDragArea *edgeDragArea =
391+ view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
392+ QVERIFY(edgeDragArea != 0);
393+ edgeDragArea->setRecognitionTimer(fakeTimer);
394+ edgeDragArea->setAxisVelocityCalculator(velCalcWithFakeTiming);
395+
396+ QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, view);
397+ QPointF touchPoint = initialTouchPos;
398+
399+ QPointF dragDirectionVector(1.0, 0.0);
400+ QPointF touchMovement = dragDirectionVector * (edgeDragArea->distanceThreshold() * 0.2f);
401+
402+ QVERIFY(!fakeTimer->isRunning());
403+
404+ QTest::touchEvent(view, device).press(0, touchPoint.toPoint());
405+
406+ QVERIFY(fakeTimer->isRunning());
407+
408+ // Move a bit in the proper direction
409+ for (int i=0; i < 3; ++i) {
410+ touchPoint += touchMovement;
411+ QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
412+ }
413+
414+ QVERIFY(fakeTimer->isRunning());
415+
416+ // Move beyond distance threshold
417+ touchPoint += 3*touchMovement;
418+ QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
419+
420+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
421+
422+ QVERIFY(!fakeTimer->isRunning());
423+}
424+
425+/*
426+ A gesture should be rejected if too much time has passed without any new input
427+ events from it.
428+ */
429+void tst_DirectionalDragArea::maxSilenceTime()
430+{
431+ DirectionalDragArea *edgeDragArea =
432+ view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
433+ QVERIFY(edgeDragArea != 0);
434+ edgeDragArea->setRecognitionTimer(fakeTimer);
435+ edgeDragArea->setAxisVelocityCalculator(velCalcWithFakeTiming);
436+
437+ QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, view);
438+ QPointF touchPoint = initialTouchPos;
439+
440+ fakeTimeSource->m_msecsSinceReference = 0;
441+ QTest::touchEvent(view, device).press(0, touchPoint.toPoint());
442+
443+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
444+
445+ int neededTimeouts = qCeil((qreal)edgeDragArea->maxSilenceTime() / (qreal)fakeTimer->interval());
446+ if ((edgeDragArea->maxSilenceTime() % fakeTimer->interval()) == 0) {
447+ ++neededTimeouts;
448+ }
449+
450+ // Force the periodic speed check.
451+ for (int i = 0; i < neededTimeouts; ++i) {
452+ fakeTimer->emitTimeout();
453+
454+ if (i < neededTimeouts - 1) {
455+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
456+ }
457+ }
458+
459+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Rejected);
460+}
461+
275QTEST_MAIN(tst_DirectionalDragArea)
462QTEST_MAIN(tst_DirectionalDragArea)
276
463
277#include "tst_DirectionalDragArea.moc"
464#include "tst_DirectionalDragArea.moc"