2 * Copyright (C) 2013-2014 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/>.
18#include <QtTest/QtTest>
19#include <QtCore/QObject>
20#include <qpa/qwindowsysteminterface.h>
21#include <QtQuick/QQuickView>
22#include <QtQml/QQmlEngine>
23#include <private/qquickwindow_p.h>
25#include <AxisVelocityCalculator.h>
26#include <DirectionalDragArea.h>
27#include <DirectionalDragArea_p.h>
29#include <TouchRegistry.h>
31using namespace UbuntuGestures;
33class tst_DragHandle: public QObject
37 tst_DragHandle() : m_device(0) { }
39 void initTestCase(); // will be called before the first test function is executed
40 void cleanupTestCase(); // will be called after the last test function was executed.
42 void init(); // called right before each and every test function is executed
43 void cleanup(); // called right after each and every test function is executed
45 void dragThreshold_horizontal();
46 void dragThreshold_horizontal_data();
47 void dragThreshold_vertical();
48 void dragThreshold_vertical_data();
49 void stretch_horizontal();
50 void stretch_vertical();
51 void hintingAnimation();
52 void hintingAnimation_dontRestartAfterFinishedAndStillPressed();
55 void flickAndHold(DirectionalDragArea *dragHandle, qreal distance);
56 void drag(QPointF &touchPoint, const QPointF& direction, qreal distance,
57 int numSteps, qint64 timeMs = 500);
58 DirectionalDragArea *fetchAndSetupDragHandle(const char *objectName);
59 qreal fetchDragThreshold(DirectionalDragArea *dragHandle);
60 void tryCompare(std::function<qreal ()> actualFunc, qreal expectedValue);
62 QQuickView *createView();
64 TouchRegistry *m_touchRegistry;
65 QTouchDevice *m_device;
66 FakeTimer *m_fakeTimer;
67 QSharedPointer<FakeTimeSource> m_fakeTimeSource;
71void tst_DragHandle::initTestCase()
74 m_device = new QTouchDevice;
75 m_device->setType(QTouchDevice::TouchScreen);
76 QWindowSystemInterface::registerTouchDevice(m_device);
82void tst_DragHandle::cleanupTestCase()
86void tst_DragHandle::init()
88 m_touchRegistry = new TouchRegistry;
90 m_view = createView();
91 m_view->installEventFilter(m_touchRegistry);
92 m_view->setSource(QUrl::fromLocalFile(TEST_DIR"/tst_DragHandle.qml"));
94 QVERIFY(QTest::qWaitForWindowExposed(m_view));
95 QVERIFY(m_view->rootObject() != 0);
96 qApp->processEvents();
98 // Hide the controls to ensure we don't hit them accidentally
99 QQuickItem *controls = m_view->rootObject()->findChild<QQuickItem*>("controls");
100 controls->setVisible(false);
102 m_fakeTimeSource.reset(new FakeTimeSource);
103 m_fakeTimer = new FakeTimer(m_fakeTimeSource);
106void tst_DragHandle::cleanup()
111 delete m_touchRegistry;
117 m_fakeTimeSource.reset();
120QQuickView *tst_DragHandle::createView()
122 QQuickView *window = new QQuickView(0);
123 window->setResizeMode(QQuickView::SizeRootObjectToView);
124 window->engine()->addImportPath(QLatin1String(UBUNTU_GESTURES_PLUGIN_DIR));
125 window->engine()->addImportPath(QLatin1String(TEST_DIR));
126 window->engine()->addImportPath(QStringLiteral(TESTS_UTILS_MODULES_DIR));
131void tst_DragHandle::tryCompare(std::function<qreal ()> actualFunc,
135 while (actualFunc() != expectedValue && waitCount < 100) {
139 QCOMPARE(actualFunc(), expectedValue);
143QPointF calculateDirectionVector(DirectionalDragArea *edgeDragArea)
145 QPointF localOrigin(0., 0.);
146 QPointF localDirection;
147 switch (edgeDragArea->direction()) {
148 case Direction::Upwards:
149 localDirection.rx() = 0.;
150 localDirection.ry() = -1.;
152 case Direction::Downwards:
153 localDirection.rx() = 0.;
154 localDirection.ry() = 1;
156 case Direction::Leftwards:
157 localDirection.rx() = -1.;
158 localDirection.ry() = 0.;
160 default: // Direction::Rightwards:
161 localDirection.rx() = 1.;
162 localDirection.ry() = 0.;
165 QPointF sceneOrigin = edgeDragArea->mapToScene(localOrigin);
166 QPointF sceneDirection = edgeDragArea->mapToScene(localDirection);
167 return sceneDirection - sceneOrigin;
171void tst_DragHandle::flickAndHold(DirectionalDragArea *dragHandle,
174 QPointF initialTouchPos = dragHandle->mapToScene(
175 QPointF(dragHandle->width() / 2.0, dragHandle->height() / 2.0));
176 QPointF touchPoint = initialTouchPos;
178 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
181 QPointF dragDirectionVector = calculateDirectionVector(dragHandle);
182 drag(touchPoint, dragDirectionVector, distance, numSteps);
184 // Wait for quite a bit before finally releasing to make a very low flick/release
186 m_fakeTimeSource->m_msecsSinceReference += 5000;
187 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
189 QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(m_view);
190 windowPrivate->flushDelayedTouchEvent();
193void tst_DragHandle::drag(QPointF &touchPoint, const QPointF& direction, qreal distance,
194 int numSteps, qint64 timeMs)
196 QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(m_view);
198 qint64 timeStep = timeMs / numSteps;
199 QPointF touchMovement = direction * (distance / (qreal)numSteps);
200 for (int i = 0; i < numSteps; ++i) {
201 touchPoint += touchMovement;
202 m_fakeTimeSource->m_msecsSinceReference += timeStep;
203 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
205 windowPrivate->flushDelayedTouchEvent();
209DirectionalDragArea *tst_DragHandle::fetchAndSetupDragHandle(const char *objectName)
211 DirectionalDragArea *dragHandle =
212 m_view->rootObject()->findChild<DirectionalDragArea*>(objectName);
213 Q_ASSERT(dragHandle != 0);
214 dragHandle->d->setRecognitionTimer(m_fakeTimer);
215 dragHandle->d->setTimeSource(m_fakeTimeSource);
217 AxisVelocityCalculator *edgeDragEvaluator =
218 dragHandle->findChild<AxisVelocityCalculator*>("edgeDragEvaluator");
219 Q_ASSERT(edgeDragEvaluator != 0);
220 edgeDragEvaluator->setTimeSource(m_fakeTimeSource);
225qreal tst_DragHandle::fetchDragThreshold(DirectionalDragArea *dragHandle)
227 AxisVelocityCalculator *edgeDragEvaluator =
228 dragHandle->findChild<AxisVelocityCalculator*>("edgeDragEvaluator");
229 Q_ASSERT(edgeDragEvaluator != 0);
231 return edgeDragEvaluator->property("dragThreshold").toReal();
235 Checks that ending a low-speed drag before dragThreshold results in the
236 Showable getting back to its original position, whereas ending after dragThreshold
237 results in Showable continuing until reaching its new states (shown or hidden)
239void tst_DragHandle::dragThreshold_horizontal()
241 QFETCH(qreal, rotation);
243 QQuickItem *baseItem = m_view->rootObject()->findChild<QQuickItem*>("baseItem");
244 baseItem->setRotation(rotation);
246 DirectionalDragArea *dragHandle = fetchAndSetupDragHandle("rightwardsDragHandle");
247 QQuickItem *parentItem = dragHandle->parentItem();
249 qreal dragThreshold = fetchDragThreshold(dragHandle);
251 // end before the threshold
252 flickAndHold(dragHandle, dragThreshold * 0.7);
255 tryCompare([&](){ return parentItem->x(); }, -parentItem->width());
256 QCOMPARE(parentItem->property("shown").toBool(), false);
258 // end after the threshold
259 flickAndHold(dragHandle, dragThreshold * 1.2);
261 // should keep going until completion
262 tryCompare([&](){ return parentItem->x(); }, 0);
263 QCOMPARE(parentItem->property("shown").toBool(), true);
265 dragHandle = fetchAndSetupDragHandle("leftwardsDragHandle");
267 dragThreshold = fetchDragThreshold(dragHandle);
269 // end before the threshold
270 flickAndHold(dragHandle, dragThreshold * 0.7);
273 tryCompare([&](){ return parentItem->x(); }, 0);
274 QCOMPARE(parentItem->property("shown").toBool(), true);
276 // end after the threshold
277 flickAndHold(dragHandle, dragThreshold * 1.2);
279 // should keep going until completion
280 tryCompare([&](){ return parentItem->x(); }, -parentItem->width());
281 QCOMPARE(parentItem->property("shown").toBool(), false);
284void tst_DragHandle::dragThreshold_horizontal_data()
286 QTest::addColumn<qreal>("rotation");
288 QTest::newRow("not rotated") << 0.;
289 QTest::newRow("rotated 90") << 90.;
292void tst_DragHandle::dragThreshold_vertical()
294 QFETCH(qreal, rotation);
296 QQuickItem *baseItem = m_view->rootObject()->findChild<QQuickItem*>("baseItem");
297 baseItem->setRotation(rotation);
299 DirectionalDragArea *dragHandle = fetchAndSetupDragHandle("downwardsDragHandle");
301 qreal dragThreshold = fetchDragThreshold(dragHandle);
303 // end before the threshold
304 flickAndHold(dragHandle, dragThreshold * 0.7);
307 QQuickItem *parentItem = dragHandle->parentItem();
308 tryCompare([&](){ return parentItem->y(); }, -parentItem->height());
309 QCOMPARE(parentItem->property("shown").toBool(), false);
311 // end after the threshold
312 flickAndHold(dragHandle, dragThreshold * 1.2);
314 // should keep going until completion
315 tryCompare([&](){ return parentItem->y(); }, 0);
316 QCOMPARE(parentItem->property("shown").toBool(), true);
318 dragHandle = fetchAndSetupDragHandle("upwardsDragHandle");
320 dragThreshold = fetchDragThreshold(dragHandle);
322 // end before the threshold
323 flickAndHold(dragHandle, dragThreshold * 0.7);
326 tryCompare([&](){ return parentItem->y(); }, 0);
327 QCOMPARE(parentItem->property("shown").toBool(), true);
329 // end after the threshold
330 flickAndHold(dragHandle, dragThreshold * 1.2);
332 // should keep going until completion
333 tryCompare([&](){ return parentItem->y(); }, -parentItem->height());
334 QCOMPARE(parentItem->property("shown").toBool(), false);
337void tst_DragHandle::dragThreshold_vertical_data()
339 QTest::addColumn<qreal>("rotation");
341 QTest::newRow("not rotated") << 0.;
342 QTest::newRow("rotated 90") << 90.;
346 Checks that when the stretch property is true, dragging the DragHandle increases
347 the width or height (depending on its direction) of its parent Showable
349void tst_DragHandle::stretch_horizontal()
351 DirectionalDragArea *dragHandle = fetchAndSetupDragHandle("rightwardsDragHandle");
352 qreal totalDragDistance = dragHandle->property("maxTotalDragDistance").toReal();
353 QQuickItem *parentItem = dragHandle->parentItem();
355 // enable strech mode
356 m_view->rootObject()->setProperty("stretch", QVariant(true));
358 QCOMPARE(parentItem->width(), 0.0);
361 flickAndHold(dragHandle, totalDragDistance);
363 // should keep going until completion
364 // Parent item should now have its full height
365 tryCompare([&](){ return parentItem->width(); }, totalDragDistance);
366 QCOMPARE(parentItem->property("shown").toBool(), true);
368 dragHandle = fetchAndSetupDragHandle("leftwardsDragHandle");
371 flickAndHold(dragHandle, totalDragDistance);
373 // should keep going until completion
374 // Parent item should now have its full height
375 tryCompare([&](){ return parentItem->width(); }, 0.0);
376 QCOMPARE(parentItem->property("shown").toBool(), false);
379void tst_DragHandle::stretch_vertical()
381 DirectionalDragArea *dragHandle = fetchAndSetupDragHandle("downwardsDragHandle");
382 qreal totalDragDistance = dragHandle->property("maxTotalDragDistance").toReal();
383 QQuickItem *parentItem = dragHandle->parentItem();
385 // enable strech mode
386 m_view->rootObject()->setProperty("stretch", QVariant(true));
388 QCOMPARE(parentItem->height(), 0.0);
391 flickAndHold(dragHandle, totalDragDistance);
393 // should keep going until completion
394 // Parent item should now have its full height
395 tryCompare([&](){ return parentItem->height(); }, totalDragDistance);
396 QCOMPARE(parentItem->property("shown").toBool(), true);
398 dragHandle = fetchAndSetupDragHandle("upwardsDragHandle");
401 flickAndHold(dragHandle, totalDragDistance);
403 // should keep going until completion
404 // Parent item should now have its full height
405 tryCompare([&](){ return parentItem->height(); }, 0.0);
406 QCOMPARE(parentItem->property("shown").toBool(), false);
410 Set DragHandle.hintDisplacement to a value bigger than zero.
411 Then lay a finger on the DragHandle.
412 The expected behavior is that it will move or strech its parent Showable
413 by hintDisplacement pixels.
415void tst_DragHandle::hintingAnimation()
417 DirectionalDragArea *dragHandle = fetchAndSetupDragHandle("downwardsDragHandle");
418 QQuickItem *parentItem = dragHandle->parentItem();
419 qreal hintDisplacement = 100.0;
421 // enable hinting animations and stretch mode
422 m_view->rootObject()->setProperty("hintDisplacement", QVariant(hintDisplacement));
423 m_view->rootObject()->setProperty("stretch", QVariant(true));
425 QCOMPARE(parentItem->height(), 0.0);
427 QPointF initialTouchPos = dragHandle->mapToScene(
428 QPointF(dragHandle->width() / 2.0, dragHandle->height() / 2.0));
429 QPointF touchPoint = initialTouchPos;
431 // Pressing causes the Showable to be stretched by hintDisplacement pixels
432 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
433 tryCompare([&](){ return parentItem->height(); }, hintDisplacement);
435 // Releasing causes the Showable to shrink back to 0 pixels.
436 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
437 tryCompare([&](){ return parentItem->height(); }, 0.0);
439 QCOMPARE(parentItem->property("shown").toBool(), false);
443 Regression test for LP#1269022: https://bugs.launchpad.net/unity8/+bug/1269022
446 2) wait for hint portion to appear
447 3) slowly drag handle, only a few pixels
453 Handle will momentarily move back to zero position, then back down to the
454 hint displacement location.
456void tst_DragHandle::hintingAnimation_dontRestartAfterFinishedAndStillPressed()
458 DirectionalDragArea *dragHandle = fetchAndSetupDragHandle("downwardsDragHandle");
459 QQuickItem *parentItem = dragHandle->parentItem();
460 qreal hintDisplacement = 100.0;
462 // enable hinting animations
463 m_view->rootObject()->setProperty("hintDisplacement", QVariant(hintDisplacement));
464 m_view->rootObject()->setProperty("stretch", QVariant(true));
466 QCOMPARE(parentItem->height(), 0.0);
468 QPointF initialTouchPos = dragHandle->mapToScene(
469 QPointF(dragHandle->width() / 2.0, dragHandle->height() / 2.0));
470 QPointF touchPoint = initialTouchPos;
472 // Pressing causes the Showable to be stretched by hintDisplacement pixels
473 const int touchId = 0;
474 QTest::touchEvent(m_view, m_device).press(touchId, touchPoint.toPoint());
475 tryCompare([&](){ return parentItem->height(); }, hintDisplacement);
478 QSignalSpy parentHeightChangedSpy(parentItem, SIGNAL(heightChanged()));
480 drag(touchPoint, QPointF(0.0, -1.0) /*dragDirectionVector*/, 15 /*distance*/, 3 /*numSteps*/);
482 // Give some time for animations to run, if any
485 // parentItem height shouldn't have changed at all
486 QVERIFY(parentHeightChangedSpy.isEmpty());
489QTEST_MAIN(tst_DragHandle)
491#include "tst_DragHandle.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