on 551c708 help sign in
unity8/tests/qmltests/Components/tst_DragHandle.cpp
449 lines 15 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 <functional>
18#include <QtTest/QtTest>
19#include <QtCore/QObject>
20#include <qpa/qwindowsysteminterface.h>
21#include <QtQuick/QQuickView>
22#include <QtQml/QQmlEngine>
24#include <DirectionalDragArea.h>
26class FakeTimer : public UbuntuGestures::AbstractTimer
28 Q_OBJECT
29public:
30 FakeTimer(QObject *parent = 0)
31 : UbuntuGestures::AbstractTimer(parent)
32 {}
34 virtual int interval() const { return m_duration; }
35 virtual void setInterval(int msecs) { m_duration = msecs; }
36private:
37 int m_duration;
38};
40class FakeTimeSource : public UbuntuGestures::TimeSource
42public:
43 FakeTimeSource() { m_msecsSinceReference = 0; }
44 qint64 msecsSinceReference() override {return m_msecsSinceReference;}
45 qint64 m_msecsSinceReference;
46};
48class tst_DragHandle: public QObject
50 Q_OBJECT
51public:
52 tst_DragHandle() : m_device(0) { }
53private 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.
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
60 void dragThreshold_horizontal();
61 void dragThreshold_vertical();
62 void stretch_horizontal();
63 void stretch_vertical();
64 void hintingAnimation();
65 void hintingAnimation_dontRestartAfterFinishedAndStillPressed();
68private:
69 void flickAndHold(DirectionalDragArea *dragHandle, qreal distance);
70 void drag(QPointF &touchPoint, const QPointF& direction, qreal distance,
71 int numSteps, qint64 timeMs = 500);
72 DirectionalDragArea *fetchAndSetupDragHandle(const char *objectName);
73 qreal fetchDragThreshold(DirectionalDragArea *dragHandle);
74 void tryCompare(std::function<qreal ()> actualFunc, qreal expectedValue);
76 QQuickView *createView();
77 QQuickView *m_view;
78 QTouchDevice *m_device;
79 FakeTimer *m_fakeTimer;
80 QSharedPointer<FakeTimeSource> m_fakeTimeSource;
81};
84void tst_DragHandle::initTestCase()
86 if (!m_device) {
87 m_device = new QTouchDevice;
88 m_device->setType(QTouchDevice::TouchScreen);
89 QWindowSystemInterface::registerTouchDevice(m_device);
90 }
92 m_view = 0;
95void tst_DragHandle::cleanupTestCase()
99void tst_DragHandle::init()
101 m_view = createView();
102 m_view->setSource(QUrl::fromLocalFile(TEST_DIR"/tst_DragHandle.qml"));
103 m_view->show();
104 QVERIFY(QTest::qWaitForWindowExposed(m_view));
105 QVERIFY(m_view->rootObject() != 0);
106 qApp->processEvents();
108 m_fakeTimer = new FakeTimer;
109 m_fakeTimeSource.reset(new FakeTimeSource);
112void tst_DragHandle::cleanup()
114 delete m_view;
115 m_view = 0;
117 delete m_fakeTimer;
118 m_fakeTimer = 0;
120 m_fakeTimeSource.reset();
123QQuickView *tst_DragHandle::createView()
125 QQuickView *window = new QQuickView(0);
126 window->setResizeMode(QQuickView::SizeRootObjectToView);
127 window->resize(600, 600);
128 window->engine()->addImportPath(QLatin1String(UBUNTU_GESTURES_PLUGIN_DIR));
129 window->engine()->addImportPath(QLatin1String(TEST_DIR));
131 return window;
134void tst_DragHandle::tryCompare(std::function<qreal ()> actualFunc,
135 qreal expectedValue)
137 int waitCount = 0;
138 while (actualFunc() != expectedValue && waitCount < 100) {
139 QTest::qWait(50);
140 ++waitCount;
141 }
142 QCOMPARE(actualFunc(), expectedValue);
145namespace {
146QPointF calculateDirectionVector(DirectionalDragArea *edgeDragArea)
149 switch (edgeDragArea->direction()) {
150 case Direction::Upwards:
151 return QPointF(0, -1);
152 case Direction::Downwards:
153 return QPointF(0, 1);
154 case Direction::Leftwards:
155 return QPointF(-1, 0);
156 default: // Direction::Rightwards:
157 return QPointF(1, 0);
158 }
162void tst_DragHandle::flickAndHold(DirectionalDragArea *dragHandle,
163 qreal distance)
165 QPointF initialTouchPos = dragHandle->mapToScene(
166 QPointF(dragHandle->width() / 2.0, dragHandle->height() / 2.0));
167 QPointF touchPoint = initialTouchPos;
169 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
171 int numSteps = 10;
172 QPointF dragDirectionVector = calculateDirectionVector(dragHandle);
173 drag(touchPoint, dragDirectionVector, distance, numSteps);
175 // Wait for quite a bit before finally releasing to make a very low flick/release
176 // speed.
177 m_fakeTimeSource->m_msecsSinceReference += 5000;
178 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
181void tst_DragHandle::drag(QPointF &touchPoint, const QPointF& direction, qreal distance,
182 int numSteps, qint64 timeMs)
184 qint64 timeStep = timeMs / numSteps;
185 QPointF touchMovement = direction * (distance / (qreal)numSteps);
186 for (int i = 0; i < numSteps; ++i) {
187 touchPoint += touchMovement;
188 m_fakeTimeSource->m_msecsSinceReference += timeStep;
189 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
190 }
193DirectionalDragArea *tst_DragHandle::fetchAndSetupDragHandle(const char *objectName)
195 DirectionalDragArea *dragHandle =
196 m_view->rootObject()->findChild<DirectionalDragArea*>(objectName);
197 Q_ASSERT(dragHandle != 0);
198 dragHandle->setRecognitionTimer(m_fakeTimer);
199 dragHandle->setTimeSource(m_fakeTimeSource);
201 AxisVelocityCalculator *edgeDragEvaluator =
202 dragHandle->findChild<AxisVelocityCalculator*>("edgeDragEvaluator");
203 Q_ASSERT(edgeDragEvaluator != 0);
204 edgeDragEvaluator->setTimeSource(m_fakeTimeSource);
206 return dragHandle;
209qreal tst_DragHandle::fetchDragThreshold(DirectionalDragArea *dragHandle)
211 AxisVelocityCalculator *edgeDragEvaluator =
212 dragHandle->findChild<AxisVelocityCalculator*>("edgeDragEvaluator");
213 Q_ASSERT(edgeDragEvaluator != 0);
215 return edgeDragEvaluator->property("dragThreshold").toReal();
218/*
219 Checks that ending a low-speed drag before dragThreshold results in the
220 Showable getting back to its original position, whereas ending after dragThreshold
221 results in Showable continuing until reaching its new states (shown or hidden)
222 */
223void tst_DragHandle::dragThreshold_horizontal()
225 DirectionalDragArea *dragHandle = fetchAndSetupDragHandle("rightwardsDragHandle");
227 qreal dragThreshold = fetchDragThreshold(dragHandle);
229 // end before the threshold
230 flickAndHold(dragHandle, dragThreshold * 0.7);
232 // should rollback
233 QQuickItem *parentItem = dragHandle->parentItem();
234 tryCompare([&](){ return parentItem->x(); }, -parentItem->width());
235 QCOMPARE(parentItem->property("shown").toBool(), false);
237 // end after the threshold
238 flickAndHold(dragHandle, dragThreshold * 1.2);
240 // should keep going until completion
241 tryCompare([&](){ return parentItem->x(); }, 0);
242 QCOMPARE(parentItem->property("shown").toBool(), true);
244 dragHandle = fetchAndSetupDragHandle("leftwardsDragHandle");
246 dragThreshold = fetchDragThreshold(dragHandle);
248 // end before the threshold
249 flickAndHold(dragHandle, dragThreshold * 0.7);
251 // should rollback
252 tryCompare([&](){ return parentItem->x(); }, 0);
253 QCOMPARE(parentItem->property("shown").toBool(), true);
255 // end after the threshold
256 flickAndHold(dragHandle, dragThreshold * 1.2);
258 // should keep going until completion
259 tryCompare([&](){ return parentItem->x(); }, -parentItem->width());
260 QCOMPARE(parentItem->property("shown").toBool(), false);
263void tst_DragHandle::dragThreshold_vertical()
265 DirectionalDragArea *dragHandle = fetchAndSetupDragHandle("downwardsDragHandle");
267 qreal dragThreshold = fetchDragThreshold(dragHandle);
269 // end before the threshold
270 flickAndHold(dragHandle, dragThreshold * 0.7);
272 // should rollback
273 QQuickItem *parentItem = dragHandle->parentItem();
274 tryCompare([&](){ return parentItem->y(); }, -parentItem->height());
275 QCOMPARE(parentItem->property("shown").toBool(), false);
277 // end after the threshold
278 flickAndHold(dragHandle, dragThreshold * 1.2);
280 // should keep going until completion
281 tryCompare([&](){ return parentItem->y(); }, 0);
282 QCOMPARE(parentItem->property("shown").toBool(), true);
284 dragHandle = fetchAndSetupDragHandle("upwardsDragHandle");
286 dragThreshold = fetchDragThreshold(dragHandle);
288 // end before the threshold
289 flickAndHold(dragHandle, dragThreshold * 0.7);
291 // should rollback
292 tryCompare([&](){ return parentItem->y(); }, 0);
293 QCOMPARE(parentItem->property("shown").toBool(), true);
295 // end after the threshold
296 flickAndHold(dragHandle, dragThreshold * 1.2);
298 // should keep going until completion
299 tryCompare([&](){ return parentItem->y(); }, -parentItem->height());
300 QCOMPARE(parentItem->property("shown").toBool(), false);
303/*
304 Checks that when the stretch property is true, dragging the DragHandle increases
305 the width or height (depending on its direction) of its parent Showable
306 */
307void tst_DragHandle::stretch_horizontal()
309 DirectionalDragArea *dragHandle = fetchAndSetupDragHandle("rightwardsDragHandle");
310 qreal totalDragDistance = dragHandle->property("maxTotalDragDistance").toReal();
311 QQuickItem *parentItem = dragHandle->parentItem();
313 // enable strech mode
314 m_view->rootObject()->setProperty("stretch", QVariant(true));
316 QCOMPARE(parentItem->width(), 0.0);
318 // flick all the way
319 flickAndHold(dragHandle, totalDragDistance);
321 // should keep going until completion
322 // Parent item should now have its full height
323 tryCompare([&](){ return parentItem->width(); }, totalDragDistance);
324 QCOMPARE(parentItem->property("shown").toBool(), true);
326 dragHandle = fetchAndSetupDragHandle("leftwardsDragHandle");
328 // flick all the way
329 flickAndHold(dragHandle, totalDragDistance);
331 // should keep going until completion
332 // Parent item should now have its full height
333 tryCompare([&](){ return parentItem->width(); }, 0.0);
334 QCOMPARE(parentItem->property("shown").toBool(), false);
337void tst_DragHandle::stretch_vertical()
339 DirectionalDragArea *dragHandle = fetchAndSetupDragHandle("downwardsDragHandle");
340 qreal totalDragDistance = dragHandle->property("maxTotalDragDistance").toReal();
341 QQuickItem *parentItem = dragHandle->parentItem();
343 // enable strech mode
344 m_view->rootObject()->setProperty("stretch", QVariant(true));
346 QCOMPARE(parentItem->height(), 0.0);
348 // flick all the way
349 flickAndHold(dragHandle, totalDragDistance);
351 // should keep going until completion
352 // Parent item should now have its full height
353 tryCompare([&](){ return parentItem->height(); }, totalDragDistance);
354 QCOMPARE(parentItem->property("shown").toBool(), true);
356 dragHandle = fetchAndSetupDragHandle("upwardsDragHandle");
358 // flick all the way
359 flickAndHold(dragHandle, totalDragDistance);
361 // should keep going until completion
362 // Parent item should now have its full height
363 tryCompare([&](){ return parentItem->height(); }, 0.0);
364 QCOMPARE(parentItem->property("shown").toBool(), false);
367/*
368 Set DragHandle.hintDisplacement to a value bigger than zero.
369 Then lay a finger on the DragHandle.
370 The expected behavior is that it will move or strech its parent Showable
371 by hintDisplacement pixels.
372 */
373void tst_DragHandle::hintingAnimation()
375 DirectionalDragArea *dragHandle = fetchAndSetupDragHandle("downwardsDragHandle");
376 QQuickItem *parentItem = dragHandle->parentItem();
377 qreal hintDisplacement = 100.0;
379 // enable hinting animations and stretch mode
380 m_view->rootObject()->setProperty("hintDisplacement", QVariant(hintDisplacement));
381 m_view->rootObject()->setProperty("stretch", QVariant(true));
383 QCOMPARE(parentItem->height(), 0.0);
385 QPointF initialTouchPos = dragHandle->mapToScene(
386 QPointF(dragHandle->width() / 2.0, dragHandle->height() / 2.0));
387 QPointF touchPoint = initialTouchPos;
389 // Pressing causes the Showable to be stretched by hintDisplacement pixels
390 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
391 tryCompare([&](){ return parentItem->height(); }, hintDisplacement);
393 // Releasing causes the Showable to shrink back to 0 pixels.
394 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
395 tryCompare([&](){ return parentItem->height(); }, 0.0);
397 QCOMPARE(parentItem->property("shown").toBool(), false);
400/*
401 Regression test for LP#1269022: https://bugs.launchpad.net/unity8/+bug/1269022
403 1) Click on handle.
404 2) wait for hint portion to appear
405 3) slowly drag handle, only a few pixels
407 Expected outcome:
408 Nothing happens
410 Actual outcome:
411 Handle will momentarily move back to zero position, then back down to the
412 hint displacement location.
413 */
414void tst_DragHandle::hintingAnimation_dontRestartAfterFinishedAndStillPressed()
416 DirectionalDragArea *dragHandle = fetchAndSetupDragHandle("downwardsDragHandle");
417 QQuickItem *parentItem = dragHandle->parentItem();
418 qreal hintDisplacement = 100.0;
420 // enable hinting animations
421 m_view->rootObject()->setProperty("hintDisplacement", QVariant(hintDisplacement));
422 m_view->rootObject()->setProperty("stretch", QVariant(true));
424 QCOMPARE(parentItem->height(), 0.0);
426 QPointF initialTouchPos = dragHandle->mapToScene(
427 QPointF(dragHandle->width() / 2.0, dragHandle->height() / 2.0));
428 QPointF touchPoint = initialTouchPos;
430 // Pressing causes the Showable to be stretched by hintDisplacement pixels
431 const int touchId = 0;
432 QTest::touchEvent(m_view, m_device).press(touchId, touchPoint.toPoint());
433 tryCompare([&](){ return parentItem->height(); }, hintDisplacement);
436 QSignalSpy parentHeightChangedSpy(parentItem, SIGNAL(heightChanged()));
438 drag(touchPoint, QPointF(0.0, -1.0) /*dragDirectionVector*/, 15 /*distance*/, 3 /*numSteps*/);
440 // Give some time for animations to run, if any
441 QTest::qWait(300);
443 // parentItem height shouldn't have changed at all
444 QVERIFY(parentHeightChangedSpy.isEmpty());
447QTEST_MAIN(tst_DragHandle)
449#include "tst_DragHandle.moc"