on xenial help sign in
Tests for DragHandle + some refactoring to make it possible
commit6bfa4a0ebe009cc89d4dbc690d717585a3ef66b5 authorDaniel d'Andrada <daniel.dandrada@canonical.com> dateWed 19 Jun 2013, 05:30 (13 years ago) parent9fe18e8
14 files changed+674−75
Components/DragHandle.qml+3 Components/EdgeDragEvaluator.qml+7−2 plugins/Ubuntu/Gestures/AxisVelocityCalculator.cpp+13−10 plugins/Ubuntu/Gestures/AxisVelocityCalculator.h+19−17 plugins/Ubuntu/Gestures/DirectionalDragArea.cpp+2−5 plugins/Ubuntu/Gestures/DirectionalDragArea.h+2−2 tests/plugins/Ubuntu/Gestures/tst_AxisVelocityCalculator.cpp+40−28 tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.cpp+7−11 tests/qmltests/CMakeLists.txt+2 tests/qmltests/Components/CMakeLists.txt+23 tests/qmltests/Components/tst_DragHandle.cpp+296 tests/qmltests/Components/tst_DragHandle.qml+110 tests/qmltests/Components/tst_DragHandle/HorizontalShowable.qml+75 tests/qmltests/Components/tst_DragHandle/VerticalShowable.qml+75
Components/DragHandle.qml+3
@@ -59,7 +59,10 @@ DirectionalDragArea {
5959 property var __previousStatus
6060 property real __totalDragDistance: 0
6161
62+ property alias edgeDragEvaluator: dragEvaluator
63+
6264 EdgeDragEvaluator {
65+ objectName: "edgeDragEvaluator"
6366 id: dragEvaluator
6467 trackedPosition: Direction.isHorizontal(direction) ? parent.x + touchX : parent.y + touchY
6568 maxDragDistance: maxTotalDragDistance
Components/EdgeDragEvaluator.qml+7−2
@@ -47,15 +47,20 @@ AxisVelocityCalculator {
4747
4848 property int direction
4949
50+ property real velocity
51+ property real minVelocity
52+
5053 // Returns whether the drag should continue by itself until completed.
5154 function shouldAutoComplete() {
5255 var deltaPos = trackedPosition - __startPosition
5356 if (Math.abs(deltaPos) < minDragDistance) {
57+ velocity = 0
58+ minVelocity = 0
5459 return false
5560 }
5661
57− var minVelocity = __calculateMinimumVelocityForAutoCompletion(deltaPos)
58− var velocity = calculate()
62+ minVelocity = __calculateMinimumVelocityForAutoCompletion(deltaPos)
63+ velocity = calculate()
5964
6065 if (Direction.isPositive(direction) && (velocity >= minVelocity)
6166 || !Direction.isPositive(direction) && (velocity <= minVelocity))
plugins/Ubuntu/Gestures/AxisVelocityCalculator.cpp+13−10
@@ -21,9 +21,10 @@
2121#include "AxisVelocityCalculator.h"
2222#include <QtCore/QElapsedTimer>
2323
24−class RealTimeSource : public AxisVelocityCalculator::TimeSource {
24+class RealTimeSource : public UbuntuGestures::TimeSource {
2525public:
26− RealTimeSource() {
26+ RealTimeSource()
27+ : UbuntuGestures::TimeSource() {
2728 m_timer.start();
2829 }
2930 virtual qint64 msecsSinceReference() {
@@ -33,23 +34,17 @@ private:
3334 QElapsedTimer m_timer;
3435};
3536
36−
3737AxisVelocityCalculator::AxisVelocityCalculator(QObject *parent)
38− : AxisVelocityCalculator(new RealTimeSource, parent)
39−{
40−}
41−
42−AxisVelocityCalculator::AxisVelocityCalculator(TimeSource *timeSource, QObject *parent)
4338 : QObject(parent)
44− , m_timeSource(timeSource)
39+ , m_timeSource(new RealTimeSource)
4540 , m_trackedPosition(0.0)
4641{
42+ m_timeSource->setParent(this);
4743 reset();
4844}
4945
5046AxisVelocityCalculator::~AxisVelocityCalculator()
5147{
52− delete m_timeSource;
5348}
5449
5550qreal AxisVelocityCalculator::trackedPosition() const
@@ -145,3 +140,11 @@ int AxisVelocityCalculator::numSamples() const
145140 }
146141 }
147142}
143+
144+void AxisVelocityCalculator::setTimeSource(UbuntuGestures::TimeSource *timeSource)
145+{
146+ if (m_timeSource && m_timeSource->parent() == this)
147+ delete m_timeSource;
148+
149+ m_timeSource = timeSource;
150+}
plugins/Ubuntu/Gestures/AxisVelocityCalculator.h+19−17
@@ -25,6 +25,19 @@
2525#include <stdint.h>
2626#include <QtCore/QObject>
2727
28+namespace UbuntuGestures {
29+/*
30+ Interface for a time source.
31+ */
32+class UBUNTUGESTURES_EXPORT TimeSource : public QObject {
33+ Q_OBJECT
34+public:
35+ virtual ~TimeSource() {}
36+ /* Returns the current time in milliseconds since some reference time in the past. */
37+ virtual qint64 msecsSinceReference() = 0;
38+};
39+}
40+
2841/*
2942 Estimates the current velocity of a finger based on recent movement along an axis
3043
@@ -59,27 +72,11 @@ class UBUNTUGESTURES_EXPORT AxisVelocityCalculator : public QObject
5972 NOTIFY trackedPositionChanged)
6073public:
6174
62− /*
63− Interface for a time source.
64− */
65− class TimeSource {
66− public:
67− virtual ~TimeSource() {}
68− /* Returns the current time in milliseconds since some reference time in the past. */
69− virtual qint64 msecsSinceReference() = 0;
70− };
71−
7275 /*
7376 Regular constructor
7477 */
7578 AxisVelocityCalculator(QObject *parent = 0);
7679
77− /*
78− Extended constructor useful for testing purposes where you can inject
79− a fake time source implementation.
80− */
81− AxisVelocityCalculator(TimeSource *timeSource, QObject *parent = 0);
82−
8380 virtual ~AxisVelocityCalculator();
8481
8582 qreal trackedPosition() const;
@@ -105,6 +102,11 @@ public:
105102
106103 int numSamples() const;
107104
105+ /*
106+ Replaces the TimeSource with the given one. Useful for testing purposes.
107+ */
108+ void setTimeSource(UbuntuGestures::TimeSource *timeSource);
109+
108110 /*
109111 The minimum amount of samples needed for a velocity calculation.
110112 */
@@ -142,7 +144,7 @@ private:
142144 int m_samplesRead; /* index of the oldest sample available. -1 if buffer is empty */
143145 int m_samplesWrite; /* index where the next sample will be written */
144146
145− TimeSource *m_timeSource;
147+ UbuntuGestures::TimeSource *m_timeSource;
146148
147149 qreal m_trackedPosition;
148150};
plugins/Ubuntu/Gestures/DirectionalDragArea.cpp+2−5
@@ -141,12 +141,9 @@ void DirectionalDragArea::setRecognitionTimer(UbuntuGestures::AbstractTimer *tim
141141 }
142142}
143143
144−void DirectionalDragArea::setAxisVelocityCalculator(AxisVelocityCalculator *newVelCalc)
144+void DirectionalDragArea::setTimeSource(UbuntuGestures::TimeSource *timeSource)
145145{
146− if (m_velocityCalculator->parent() == this)
147− delete m_velocityCalculator;
148−
149− m_velocityCalculator = newVelCalc;
146+ m_velocityCalculator->setTimeSource(timeSource);
150147}
151148
152149qreal DirectionalDragArea::distance() const
plugins/Ubuntu/Gestures/DirectionalDragArea.h+2−2
@@ -172,8 +172,8 @@ public:
172172 // Useful for providing a fake timer when testing.
173173 void setRecognitionTimer(UbuntuGestures::AbstractTimer *timer);
174174
175− // Useful for testing, where a calculator with a fake time source can be supplied
176− void setAxisVelocityCalculator(AxisVelocityCalculator *velCalc);
175+ // Useful for testing, where a fake time source can be supplied
176+ void setTimeSource(UbuntuGestures::TimeSource *timeSource);
177177
178178Q_SIGNALS:
179179 void directionChanged(Direction::Type direction);
tests/plugins/Ubuntu/Gestures/tst_AxisVelocityCalculator.cpp+40−28
@@ -20,7 +20,7 @@
2020
2121#include <AxisVelocityCalculator.h>
2222
23−class FakeTimeSource : public AxisVelocityCalculator::TimeSource {
23+class FakeTimeSource : public UbuntuGestures::TimeSource {
2424public:
2525 FakeTimeSource() : m_value(0) {}
2626
@@ -44,71 +44,85 @@ class tst_AxisVelocityCalculator : public QObject
4444{
4545 Q_OBJECT
4646private Q_SLOTS:
47+ void init(); // called right before each and every test function is executed
48+ void cleanup(); // called right after each and every test function is executed
49+
4750 void simpleSamples();
4851 void noSamples();
4952 void overflowSamples();
5053 void average();
54+
55+private:
56+ AxisVelocityCalculator *velCalc;
57+ FakeTimeSource *fakeTimeSource;
5158};
5259
60+void tst_AxisVelocityCalculator::init()
61+{
62+ fakeTimeSource = new FakeTimeSource;
63+
64+ velCalc = new AxisVelocityCalculator;
65+ velCalc->setTimeSource(fakeTimeSource);
66+}
67+
68+void tst_AxisVelocityCalculator::cleanup()
69+{
70+ delete velCalc;
71+ delete fakeTimeSource;
72+}
73+
5374void tst_AxisVelocityCalculator::simpleSamples()
5475{
55− FakeTimeSource *fakeTimeSource = new FakeTimeSource;
56− AxisVelocityCalculator velCalc(fakeTimeSource);
5776 qreal pos = 0;
5877
59− velCalc.setTrackedPosition(pos);
60− velCalc.reset();
78+ velCalc->setTrackedPosition(pos);
79+ velCalc->reset();
6180
6281 fakeTimeSource->setMsecsSinceReference(10);
6382 pos += 20;
64− velCalc.setTrackedPosition(pos);
83+ velCalc->setTrackedPosition(pos);
6584
6685 fakeTimeSource->setMsecsSinceReference(20);
6786 pos += 20;
68− velCalc.setTrackedPosition(pos);
87+ velCalc->setTrackedPosition(pos);
6988
7089 fakeTimeSource->setMsecsSinceReference(30);
7190 pos += 20;
72− velCalc.setTrackedPosition(pos);
91+ velCalc->setTrackedPosition(pos);
7392
74− qreal velocity = velCalc.calculate();
93+ qreal velocity = velCalc->calculate();
7594
7695 QCOMPARE(velocity, 2.0f);
7796}
7897
7998void tst_AxisVelocityCalculator::noSamples()
8099{
81− FakeTimeSource *fakeTimeSource = new FakeTimeSource;
82− AxisVelocityCalculator velCalc(fakeTimeSource);
83−
84− float velocity = velCalc.calculate();
100+ float velocity = velCalc->calculate();
85101
86102 QCOMPARE(velocity, 0.0f);
87103}
88104
89105void tst_AxisVelocityCalculator::overflowSamples()
90106{
91− FakeTimeSource *fakeTimeSource = new FakeTimeSource;
92− AxisVelocityCalculator velCalc(fakeTimeSource);
93107 qreal pos = 0;
94108
95− velCalc.setTrackedPosition(pos);
96− velCalc.reset();
109+ velCalc->setTrackedPosition(pos);
110+ velCalc->reset();
97111
98112 for (int i = 0; i < 1000; ++i) {
99113 fakeTimeSource->increaseMsecsSinceReference(10);
100114 pos += 20;
101− velCalc.setTrackedPosition(pos);
115+ velCalc->setTrackedPosition(pos);
102116 }
103117
104118 /* overwrite all existing samples with faster ones */
105119 for (int i = 0; i < AxisVelocityCalculator::MAX_SAMPLES; ++i) {
106120 fakeTimeSource->increaseMsecsSinceReference(10);
107121 pos += 40;
108− velCalc.setTrackedPosition(pos);
122+ velCalc->setTrackedPosition(pos);
109123 }
110124
111− float velocity = velCalc.calculate();
125+ float velocity = velCalc->calculate();
112126
113127 /* check that the calculated velocity correspond to the latest, faster, samples */
114128 QCOMPARE(velocity, 4.0f);
@@ -116,28 +130,26 @@ void tst_AxisVelocityCalculator::overflowSamples()
116130
117131void tst_AxisVelocityCalculator::average()
118132{
119− FakeTimeSource *fakeTimeSource = new FakeTimeSource;
120− AxisVelocityCalculator velCalc(fakeTimeSource);
121133 qreal pos = 0;
122134
123− velCalc.setTrackedPosition(pos);
124− velCalc.reset();
135+ velCalc->setTrackedPosition(pos);
136+ velCalc->reset();
125137
126138 fakeTimeSource->increaseMsecsSinceReference(10);
127139 pos += 20;
128− velCalc.setTrackedPosition(pos);
140+ velCalc->setTrackedPosition(pos);
129141
130142 fakeTimeSource->increaseMsecsSinceReference(10);
131143 pos += 20;
132− velCalc.setTrackedPosition(pos);
144+ velCalc->setTrackedPosition(pos);
133145
134146 /* the last sample is an erratic one and would yield a big velocity if
135147 considered isolatedly */
136148 fakeTimeSource->increaseMsecsSinceReference(10);
137149 pos += 100;
138− velCalc.setTrackedPosition(pos);
150+ velCalc->setTrackedPosition(pos);
139151
140− float velocity = velCalc.calculate();
152+ float velocity = velCalc->calculate();
141153
142154 /* calculated velocity is lower than the one from the last sample */
143155 QVERIFY(velocity < 9.0f);
tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.cpp+7−11
@@ -38,7 +38,7 @@ private:
3838 int m_duration;
3939};
4040
41−class FakeTimeSource : public AxisVelocityCalculator::TimeSource
41+class FakeTimeSource : public UbuntuGestures::TimeSource
4242{
4343public:
4444 FakeTimeSource() { m_msecsSinceReference = 0; }
@@ -72,7 +72,6 @@ private:
7272 QTouchDevice *device;
7373 FakeTimer *fakeTimer;
7474 FakeTimeSource *fakeTimeSource;
75− AxisVelocityCalculator *velCalcWithFakeTiming;
7675};
7776
7877void tst_DirectionalDragArea::initTestCase()
@@ -101,7 +100,6 @@ void tst_DirectionalDragArea::init()
101100
102101 fakeTimer = new FakeTimer;
103102 fakeTimeSource = new FakeTimeSource;
104− velCalcWithFakeTiming = new AxisVelocityCalculator(fakeTimeSource);
105103}
106104
107105void tst_DirectionalDragArea::cleanup()
@@ -112,10 +110,8 @@ void tst_DirectionalDragArea::cleanup()
112110 delete fakeTimer;
113111 fakeTimer = 0;
114112
115− // will bring fakeTimeSource down with it
116− delete velCalcWithFakeTiming;
113+ delete fakeTimeSource;
117114 fakeTimeSource = 0;
118− velCalcWithFakeTiming = 0;
119115}
120116
121117QQuickView *tst_DirectionalDragArea::createView()
@@ -187,7 +183,7 @@ void tst_DirectionalDragArea::edgeDrag()
187183 view->rootObject()->findChild<DirectionalDragArea*>(dragAreaObjectName);
188184 QVERIFY(edgeDragArea != 0);
189185 edgeDragArea->setRecognitionTimer(fakeTimer);
190− edgeDragArea->setAxisVelocityCalculator(velCalcWithFakeTiming);
186+ edgeDragArea->setTimeSource(fakeTimeSource);
191187
192188 QSignalSpy draggingSpy(edgeDragArea, SIGNAL(draggingChanged(bool)));
193189
@@ -292,7 +288,7 @@ void tst_DirectionalDragArea::dragWithShortDirectionChange()
292288 view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
293289 QVERIFY(edgeDragArea != 0);
294290 edgeDragArea->setRecognitionTimer(fakeTimer);
295− edgeDragArea->setAxisVelocityCalculator(velCalcWithFakeTiming);
291+ edgeDragArea->setTimeSource(fakeTimeSource);
296292
297293 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, view);
298294 QPointF touchPoint = initialTouchPos;
@@ -337,7 +333,7 @@ void tst_DirectionalDragArea::minSpeed()
337333 view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
338334 QVERIFY(edgeDragArea != 0);
339335 edgeDragArea->setRecognitionTimer(fakeTimer);
340− edgeDragArea->setAxisVelocityCalculator(velCalcWithFakeTiming);
336+ edgeDragArea->setTimeSource(fakeTimeSource);
341337
342338 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, view);
343339 QPointF touchPoint = initialTouchPos;
@@ -390,7 +386,7 @@ void tst_DirectionalDragArea::recognitionTimerUsage()
390386 view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
391387 QVERIFY(edgeDragArea != 0);
392388 edgeDragArea->setRecognitionTimer(fakeTimer);
393− edgeDragArea->setAxisVelocityCalculator(velCalcWithFakeTiming);
389+ edgeDragArea->setTimeSource(fakeTimeSource);
394390
395391 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, view);
396392 QPointF touchPoint = initialTouchPos;
@@ -431,7 +427,7 @@ void tst_DirectionalDragArea::maxSilenceTime()
431427 view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
432428 QVERIFY(edgeDragArea != 0);
433429 edgeDragArea->setRecognitionTimer(fakeTimer);
434− edgeDragArea->setAxisVelocityCalculator(velCalcWithFakeTiming);
430+ edgeDragArea->setTimeSource(fakeTimeSource);
435431
436432 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, view);
437433 QPointF touchPoint = initialTouchPos;
tests/qmltests/CMakeLists.txt+2
@@ -2,6 +2,7 @@
22include(QmlTest)
33
44add_subdirectory(plugins)
5+add_subdirectory(Components)
56
67set(qmltest_DEFAULT_IMPORT_PATHS
78 ${CMAKE_CURRENT_BINARY_DIR}/plugins
@@ -25,6 +26,7 @@ add_qml_test(utils/Unity/Test UnityTest)
2526set(qmltest_DEFAULT_TARGETS qmluitests)
2627set(qmltest_DEFAULT_NO_ADD_TEST TRUE)
2728set(qmltest_DEFAULT_PROPERTIES "")
29+add_manual_qml_test(Components DragHandle IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
2830add_qml_test(Components DraggingArea)
2931add_qml_test(Components FilterGrid IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/plugins)
3032add_qml_test(Components ListViewWithPageHeader)
tests/qmltests/Components/CMakeLists.txtnew file+23
@@ -0,0 +1,23 @@
1+########## tst_DragHandle
2+
3+include_directories(
4+ ${CMAKE_SOURCE_DIR}/plugins/Ubuntu/Gestures
5+ ${CMAKE_CURRENT_BINARY_DIR}
6+ )
7+
8+set(testCommand
9+ LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/plugins/Ubuntu/Gestures
10+ ${CMAKE_CURRENT_BINARY_DIR}/DragHandleTestExec
11+ -o ${CMAKE_BINARY_DIR}/DragHandleTest.xml,xunitxml
12+ -o -,txt)
13+
14+add_custom_target(testDragHandle ${testCommand})
15+add_dependencies(qmluitests testDragHandle)
16+add_dependencies(testDragHandle DragHandleTestExec)
17+
18+add_executable(DragHandleTestExec tst_DragHandle.cpp)
19+qt5_use_modules(DragHandleTestExec Test Core Qml Gui Quick)
20+target_link_libraries(DragHandleTestExec UbuntuGestureQml)
21+
22+add_definitions(-DUBUNTU_GESTURES_PLUGIN_DIR="${CMAKE_BINARY_DIR}/plugins")
23+add_definitions(-DTEST_DIR="${CMAKE_CURRENT_LIST_DIR}")
tests/qmltests/Components/tst_DragHandle.cppnew file+296
@@ -0,0 +1,296 @@
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+ */
16+
17+#include <functional>
18+#include <QtTest/QtTest>
19+#include <QtCore/QObject>
20+#include <qpa/qwindowsysteminterface.h>
21+#include <QtQuick/QQuickView>
22+#include <QtQml/QQmlEngine>
23+
24+#include <DirectionalDragArea.h>
25+
26+class FakeTimer : public UbuntuGestures::AbstractTimer
27+{
28+ Q_OBJECT
29+public:
30+ FakeTimer(QObject *parent = 0)
31+ : UbuntuGestures::AbstractTimer(parent)
32+ {}
33+
34+ virtual int interval() const { return m_duration; }
35+ virtual void setInterval(int msecs) { m_duration = msecs; }
36+private:
37+ int m_duration;
38+};
39+
40+class FakeTimeSource : public UbuntuGestures::TimeSource
41+{
42+public:
43+ FakeTimeSource() { m_msecsSinceReference = 0; }
44+ virtual qint64 msecsSinceReference() {return m_msecsSinceReference;}
45+ qint64 m_msecsSinceReference;
46+};
47+
48+class tst_DragHandle: public QObject
49+{
50+ Q_OBJECT
51+public:
52+ tst_DragHandle() : device(0) { }
53+private Q_SLOTS:
54+ void initTestCase(); // will be called before the first test function is executed
55+ void cleanupTestCase(); // will be called after the last test function was executed.
56+
57+ void init(); // called right before each and every test function is executed
58+ void cleanup(); // called right after each and every test function is executed
59+
60+ void dragThreshold_horizontal();
61+ void dragThreshold_vertical();
62+
63+private:
64+ void flickAndHold(DirectionalDragArea *dragHandle, qreal distance);
65+ DirectionalDragArea *fetchAndSetupDragHandle(const char *objectName);
66+ qreal fetchDragThreshold(DirectionalDragArea *dragHandle);
67+ void tryCompare(std::function<qreal ()> actualFunc, qreal expectedValue);
68+
69+ QQuickView *createView();
70+ QQuickView *view;
71+ QTouchDevice *device;
72+ FakeTimer *fakeTimer;
73+ FakeTimeSource *fakeTimeSource;
74+};
75+
76+
77+void tst_DragHandle::initTestCase()
78+{
79+ if (!device) {
80+ device = new QTouchDevice;
81+ device->setType(QTouchDevice::TouchScreen);
82+ QWindowSystemInterface::registerTouchDevice(device);
83+ }
84+
85+ view = 0;
86+}
87+
88+void tst_DragHandle::cleanupTestCase()
89+{
90+}
91+
92+void tst_DragHandle::init()
93+{
94+ view = createView();
95+ view->setSource(QUrl::fromLocalFile(TEST_DIR"/tst_DragHandle.qml"));
96+ view->show();
97+ QVERIFY(QTest::qWaitForWindowExposed(view));
98+ QVERIFY(view->rootObject() != 0);
99+ qApp->processEvents();
100+
101+ fakeTimer = new FakeTimer;
102+ fakeTimeSource = new FakeTimeSource;
103+}
104+
105+void tst_DragHandle::cleanup()
106+{
107+ delete view;
108+ view = 0;
109+
110+ delete fakeTimer;
111+ fakeTimer = 0;
112+
113+ delete fakeTimeSource;
114+ fakeTimeSource = 0;
115+}
116+
117+QQuickView *tst_DragHandle::createView()
118+{
119+ QQuickView *window = new QQuickView(0);
120+ window->setResizeMode(QQuickView::SizeRootObjectToView);
121+ window->resize(600, 600);
122+ window->engine()->addImportPath(QLatin1String(UBUNTU_GESTURES_PLUGIN_DIR));
123+ window->engine()->addImportPath(QLatin1String(TEST_DIR));
124+
125+ return window;
126+}
127+
128+void tst_DragHandle::tryCompare(std::function<qreal ()> actualFunc,
129+ qreal expectedValue)
130+{
131+ int waitCount = 0;
132+ while (actualFunc() != expectedValue && waitCount < 100) {
133+ QTest::qWait(50);
134+ ++waitCount;
135+ }
136+ QCOMPARE(actualFunc(), expectedValue);
137+}
138+
139+namespace {
140+QPointF calculateDirectionVector(DirectionalDragArea *edgeDragArea)
141+{
142+
143+ switch (edgeDragArea->direction()) {
144+ case Direction::Upwards:
145+ return QPointF(0, -1);
146+ case Direction::Downwards:
147+ return QPointF(0, 1);
148+ case Direction::Leftwards:
149+ return QPointF(-1, 0);
150+ default: // Direction::Rightwards:
151+ return QPointF(1, 0);
152+ }
153+}
154+}
155+
156+void tst_DragHandle::flickAndHold(DirectionalDragArea *dragHandle,
157+ qreal distance)
158+{
159+ QPointF initialTouchPos = dragHandle->mapToScene(
160+ QPointF(dragHandle->width() / 2.0, dragHandle->height() / 2.0));
161+ QPointF touchPoint = initialTouchPos;
162+ int numSteps = 10;
163+ qint64 flickTimeMs = 500;
164+ qint64 timeStep = flickTimeMs / numSteps;
165+
166+ QPointF dragDirectionVector = calculateDirectionVector(dragHandle);
167+ QPointF touchMovement = dragDirectionVector * (distance / (qreal)numSteps);
168+
169+ QTest::touchEvent(view, device).press(0, touchPoint.toPoint());
170+
171+ for (int i = 0; i < numSteps; ++i) {
172+ touchPoint += touchMovement;
173+ fakeTimeSource->m_msecsSinceReference += timeStep;
174+ QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
175+ }
176+
177+ // Wait for quite a bit before finally releasing to make a very low flick/release
178+ // speed.
179+ fakeTimeSource->m_msecsSinceReference += 5000;
180+ QTest::touchEvent(view, device).release(0, touchPoint.toPoint());
181+}
182+
183+DirectionalDragArea *tst_DragHandle::fetchAndSetupDragHandle(const char *objectName)
184+{
185+ DirectionalDragArea *dragHandle =
186+ view->rootObject()->findChild<DirectionalDragArea*>(objectName);
187+ Q_ASSERT(dragHandle != 0);
188+ dragHandle->setRecognitionTimer(fakeTimer);
189+ dragHandle->setTimeSource(fakeTimeSource);
190+
191+ AxisVelocityCalculator *edgeDragEvaluator =
192+ dragHandle->findChild<AxisVelocityCalculator*>("edgeDragEvaluator");
193+ Q_ASSERT(edgeDragEvaluator != 0);
194+ edgeDragEvaluator->setTimeSource(fakeTimeSource);
195+
196+ return dragHandle;
197+}
198+
199+qreal tst_DragHandle::fetchDragThreshold(DirectionalDragArea *dragHandle)
200+{
201+ AxisVelocityCalculator *edgeDragEvaluator =
202+ dragHandle->findChild<AxisVelocityCalculator*>("edgeDragEvaluator");
203+ Q_ASSERT(edgeDragEvaluator != 0);
204+ edgeDragEvaluator->setTimeSource(fakeTimeSource);
205+
206+ return edgeDragEvaluator->property("dragThreshold").toReal();
207+}
208+
209+/*
210+ Checks that ending a low-speed drag before dragThreshold results in the
211+ Showable getting back to its original position, whereas ending after dragThreshold
212+ results in Showable continuing until reaching its new states (shown or hidden)
213+ */
214+void tst_DragHandle::dragThreshold_horizontal()
215+{
216+ DirectionalDragArea *dragHandle = fetchAndSetupDragHandle("rightwardsDragHandle");
217+
218+ qreal dragThreshold = fetchDragThreshold(dragHandle);
219+
220+ // end before the threshold
221+ flickAndHold(dragHandle, dragThreshold * 0.7);
222+
223+ // should rollback
224+ QQuickItem *parentItem = dragHandle->parentItem();
225+ tryCompare([&](){ return parentItem->x(); }, -parentItem->width());
226+ QCOMPARE(parentItem->property("shown").toBool(), false);
227+
228+ // end after the threshold
229+ flickAndHold(dragHandle, dragThreshold * 1.2);
230+
231+ // should keep going until completion
232+ tryCompare([&](){ return parentItem->x(); }, 0);
233+ QCOMPARE(parentItem->property("shown").toBool(), true);
234+
235+ dragHandle = fetchAndSetupDragHandle("leftwardsDragHandle");
236+
237+ dragThreshold = fetchDragThreshold(dragHandle);
238+
239+ // end before the threshold
240+ flickAndHold(dragHandle, dragThreshold * 0.7);
241+
242+ // should rollback
243+ tryCompare([&](){ return parentItem->x(); }, 0);
244+ QCOMPARE(parentItem->property("shown").toBool(), true);
245+
246+ // end after the threshold
247+ flickAndHold(dragHandle, dragThreshold * 1.2);
248+
249+ // should keep going until completion
250+ tryCompare([&](){ return parentItem->x(); }, -parentItem->width());
251+ QCOMPARE(parentItem->property("shown").toBool(), false);
252+}
253+
254+void tst_DragHandle::dragThreshold_vertical()
255+{
256+ DirectionalDragArea *dragHandle = fetchAndSetupDragHandle("downwardsDragHandle");
257+
258+ qreal dragThreshold = fetchDragThreshold(dragHandle);
259+
260+ // end before the threshold
261+ flickAndHold(dragHandle, dragThreshold * 0.7);
262+
263+ // should rollback
264+ QQuickItem *parentItem = dragHandle->parentItem();
265+ tryCompare([&](){ return parentItem->y(); }, -parentItem->height());
266+ QCOMPARE(parentItem->property("shown").toBool(), false);
267+
268+ // end after the threshold
269+ flickAndHold(dragHandle, dragThreshold * 1.2);
270+
271+ // should keep going until completion
272+ tryCompare([&](){ return parentItem->y(); }, 0);
273+ QCOMPARE(parentItem->property("shown").toBool(), true);
274+
275+ dragHandle = fetchAndSetupDragHandle("upwardsDragHandle");
276+
277+ dragThreshold = fetchDragThreshold(dragHandle);
278+
279+ // end before the threshold
280+ flickAndHold(dragHandle, dragThreshold * 0.7);
281+
282+ // should rollback
283+ tryCompare([&](){ return parentItem->y(); }, 0);
284+ QCOMPARE(parentItem->property("shown").toBool(), true);
285+
286+ // end after the threshold
287+ flickAndHold(dragHandle, dragThreshold * 1.2);
288+
289+ // should keep going until completion
290+ tryCompare([&](){ return parentItem->y(); }, -parentItem->height());
291+ QCOMPARE(parentItem->property("shown").toBool(), false);
292+}
293+
294+QTEST_MAIN(tst_DragHandle)
295+
296+#include "tst_DragHandle.moc"
tests/qmltests/Components/tst_DragHandle.qmlnew file+110
@@ -0,0 +1,110 @@
1+/*
2+ * Copyright 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+ */
16+
17+import QtQuick 2.0
18+import "../../../Components"
19+import "tst_DragHandle"
20+import Ubuntu.Components 0.1
21+import Ubuntu.Gestures 0.1
22+
23+/*
24+ Two blue fullscreen Showables (a vertical and a horizontal one) with red handles you
25+ can use to drag them away and green handles to drag them in.
26+
27+ A black vertical line marks the point where a drag needs no forward velocity to
28+ achieve auto-completion (i.e., get Showable.show() called after touch release).
29+ */
30+Item {
31+ id: root
32+ width: 700
33+ height: 500
34+
35+ property var dragHandle
36+
37+ VerticalShowable {
38+ onDragHandleRecognizedGesture: { root.dragHandle = dragHandle }
39+ }
40+
41+ HorizontalShowable {
42+ onDragHandleRecognizedGesture: { root.dragHandle = dragHandle }
43+ }
44+
45+ // Visually mark drag threshold
46+ Rectangle {
47+ color: "black";
48+ width: 2
49+ height: parent.height
50+
51+ visible: dragHandle !== undefined && Direction.isHorizontal(dragHandle.direction)
52+
53+ x: {
54+ if (dragHandle) {
55+ if (dragHandle.direction === Direction.Rightwards)
56+ dragHandle.edgeDragEvaluator.dragThreshold
57+ else
58+ parent.width - dragHandle.edgeDragEvaluator.dragThreshold
59+ } else {
60+ 0
61+ }
62+ }
63+ }
64+ Rectangle {
65+ color: "black";
66+ height: 2
67+ width: parent.width
68+
69+ visible: dragHandle !== undefined && Direction.isVertical(dragHandle.direction)
70+
71+ y: {
72+ if (dragHandle) {
73+ if (dragHandle.direction === Direction.Downwards)
74+ dragHandle.edgeDragEvaluator.dragThreshold
75+ else
76+ parent.height - dragHandle.edgeDragEvaluator.dragThreshold
77+ } else {
78+ 0
79+ }
80+ }
81+ }
82+
83+ // Display velocities
84+ Rectangle {
85+ width: childrenRect.width
86+ height: childrenRect.height
87+ color: "white"
88+ opacity: 0.4
89+ Text {
90+ id: velocityText
91+ font.pixelSize: units.gu(2)
92+ text: {
93+ if (dragHandle !== undefined)
94+ "Velocity: " + (dragHandle.edgeDragEvaluator.velocity * 1000)
95+ else
96+ "Velocity: -"
97+ }
98+ }
99+ Text {
100+ anchors.top: velocityText.bottom
101+ font.pixelSize: units.gu(2)
102+ text: {
103+ if (dragHandle !== undefined)
104+ "Minimum velocity: " + (dragHandle.edgeDragEvaluator.minVelocity * 1000)
105+ else
106+ "Minimum velocity: -"
107+ }
108+ }
109+ }
110+}
tests/qmltests/Components/tst_DragHandle/HorizontalShowable.qmlnew file+75
@@ -0,0 +1,75 @@
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+ */
16+
17+import QtQuick 2.0
18+import "../../../../Components"
19+import Ubuntu.Components 0.1
20+import Ubuntu.Gestures 0.1
21+
22+Showable {
23+ id: showable
24+ x: -width
25+ width: parent.width
26+ height: parent.height
27+
28+ shown: false
29+
30+ signal dragHandleRecognizedGesture(var dragHandle)
31+
32+ showAnimation: StandardAnimation { property: "x"; to: 0 }
33+ hideAnimation: StandardAnimation { property: "x"; to: -width }
34+
35+ Rectangle { color: "blue"; anchors.fill: parent }
36+
37+ DragHandle {
38+ objectName: "leftwardsDragHandle"
39+ id: leftwardsDragHandle
40+ anchors.top: parent.top
41+ anchors.bottom: parent.bottom
42+ anchors.right: parent.right
43+
44+ width: units.gu(2)
45+
46+ direction: Direction.Leftwards
47+
48+ onStatusChanged: {
49+ if (status === DirectionalDragArea.Recognized)
50+ dragHandleRecognizedGesture(leftwardsDragHandle)
51+ }
52+
53+ Rectangle { color: "red"; anchors.fill: parent }
54+ }
55+
56+ DragHandle {
57+ objectName: "rightwardsDragHandle"
58+ id: rightwardsDragHandle
59+ anchors.top: parent.top
60+ anchors.bottom: parent.bottom
61+ anchors.left: parent.right
62+
63+ width: units.gu(2)
64+
65+ direction: Direction.Rightwards
66+
67+ onStatusChanged: {
68+ if (status === DirectionalDragArea.Recognized)
69+ dragHandleRecognizedGesture(rightwardsDragHandle)
70+ }
71+
72+ Rectangle { color: "green"; anchors.fill: parent }
73+ }
74+
75+}
tests/qmltests/Components/tst_DragHandle/VerticalShowable.qmlnew file+75
@@ -0,0 +1,75 @@
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+ */
16+
17+import QtQuick 2.0
18+import "../../../../Components"
19+import Ubuntu.Components 0.1
20+import Ubuntu.Gestures 0.1
21+
22+Showable {
23+ id: showable
24+ y: -height
25+ width: parent.width
26+ height: parent.height
27+
28+ shown: false
29+
30+ signal dragHandleRecognizedGesture(var dragHandle)
31+
32+ showAnimation: StandardAnimation { property: "y"; to: 0 }
33+ hideAnimation: StandardAnimation { property: "y"; to: -height }
34+
35+ Rectangle { color: "blue"; anchors.fill: parent }
36+
37+ DragHandle {
38+ objectName: "upwardsDragHandle"
39+ id: upwardsDragHandle
40+ anchors.left: parent.left
41+ anchors.right: parent.right
42+ anchors.bottom: parent.bottom
43+
44+ height: units.gu(2)
45+
46+ direction: Direction.Upwards
47+
48+ onStatusChanged: {
49+ if (status === DirectionalDragArea.Recognized)
50+ dragHandleRecognizedGesture(upwardsDragHandle)
51+ }
52+
53+ Rectangle { color: "red"; anchors.fill: parent }
54+ }
55+
56+ DragHandle {
57+ objectName: "downwardsDragHandle"
58+ id: downwardsDragHandle
59+ anchors.left: parent.left
60+ anchors.right: parent.right
61+ anchors.top: parent.bottom
62+
63+ height: units.gu(2)
64+
65+ direction: Direction.Downwards
66+
67+ onStatusChanged: {
68+ if (status === DirectionalDragArea.Recognized)
69+ dragHandleRecognizedGesture(downwardsDragHandle)
70+ }
71+
72+ Rectangle { color: "green"; anchors.fill: parent }
73+ }
74+
75+}