2 * Copyright (C) 2013-2014,2016 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>
24#include <UbuntuGestures/private/ucswipearea_p_p.h>
26#include <AxisVelocityCalculator.h>
27#include <UbuntuGestures/private/timer_p.h>
34class tst_DragHandle: public QObject
38 tst_DragHandle() : m_device(0) { }
40 void initTestCase(); // will be called before the first test function is executed
41 void cleanupTestCase(); // will be called after the last test function was executed.
43 void init(); // called right before each and every test function is executed
44 void cleanup(); // called right after each and every test function is executed
46 void dragThreshold_horizontal();
47 void dragThreshold_horizontal_data();
48 void dragThreshold_vertical();
49 void dragThreshold_vertical_data();
50 void stretch_horizontal();
51 void stretch_vertical();
52 void hintingAnimation();
53 void hintingAnimation_dontRestartAfterFinishedAndStillPressed();
56 void flickAndHold(QQuickItem *dragHandle, qreal distance);
57 void drag(QPointF &touchPoint, const QPointF& direction, qreal distance,
58 int numSteps, qint64 timeMs = 500);
59 QQuickItem *fetchAndSetupDragHandle(const char *objectName);
60 qreal fetchDragThreshold(QQuickItem *dragHandle);
61 void tryCompare(std::function<qreal ()> actualFunc, qreal expectedValue);
63 QQuickView *createView();
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_view = createView();
89 m_view->setSource(QUrl::fromLocalFile(testDataDir() + "/" TEST_DIR "/tst_DragHandle.qml"));
91 QVERIFY(QTest::qWaitForWindowExposed(m_view));
92 QVERIFY(m_view->rootObject() != 0);
93 qApp->processEvents();
95 // Hide the controls to ensure we don't hit them accidentally
96 QQuickItem *controls = m_view->rootObject()->findChild<QQuickItem*>("controls");
97 controls->setVisible(false);
99 m_fakeTimeSource.reset(new FakeTimeSource);
100 m_fakeTimer = new FakeTimer(m_fakeTimeSource);
103void tst_DragHandle::cleanup()
111 m_fakeTimeSource.reset();
114QQuickView *tst_DragHandle::createView()
116 QQuickView *window = new QQuickView(0);
117 window->setResizeMode(QQuickView::SizeRootObjectToView);
118 window->engine()->addImportPath(testDataDir() + "/" TEST_DIR);
123void tst_DragHandle::tryCompare(std::function<qreal ()> actualFunc,
127 while (actualFunc() != expectedValue && waitCount < 100) {
131 QCOMPARE(actualFunc(), expectedValue);
135QPointF calculateDirectionVector(QQuickItem *edgeDragArea)
137 QPointF localOrigin(0., 0.);
138 QPointF localDirection;
139 switch (edgeDragArea->property("direction").toInt()) {
140 case Direction::Upwards:
141 localDirection.rx() = 0.;
142 localDirection.ry() = -1.;
144 case Direction::Downwards:
145 localDirection.rx() = 0.;
146 localDirection.ry() = 1;
148 case Direction::Leftwards:
149 localDirection.rx() = -1.;
150 localDirection.ry() = 0.;
152 default: // Direction::Rightwards:
153 localDirection.rx() = 1.;
154 localDirection.ry() = 0.;
157 QPointF sceneOrigin = edgeDragArea->mapToScene(localOrigin);
158 QPointF sceneDirection = edgeDragArea->mapToScene(localDirection);
159 return sceneDirection - sceneOrigin;
163void tst_DragHandle::flickAndHold(QQuickItem *dragHandle,
166 QPointF initialTouchPos = dragHandle->mapToScene(
167 QPointF(dragHandle->width() / 2.0, dragHandle->height() / 2.0));
168 QPointF touchPoint = initialTouchPos;
170 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
173 QPointF dragDirectionVector = calculateDirectionVector(dragHandle);
174 drag(touchPoint, dragDirectionVector, distance, numSteps);
176 // Wait for quite a bit before finally releasing to make a very low flick/release
178 m_fakeTimeSource->m_msecsSinceReference += 5000;
179 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
181 QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(m_view);
182 windowPrivate->flushDelayedTouchEvent();
185void tst_DragHandle::drag(QPointF &touchPoint, const QPointF& direction, qreal distance,
186 int numSteps, qint64 timeMs)
188 QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(m_view);
190 qint64 timeStep = timeMs / numSteps;
191 QPointF touchMovement = direction * (distance / (qreal)numSteps);
192 for (int i = 0; i < numSteps; ++i) {
193 touchPoint += touchMovement;
194 m_fakeTimeSource->m_msecsSinceReference += timeStep;
195 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
197 windowPrivate->flushDelayedTouchEvent();
201QQuickItem *tst_DragHandle::fetchAndSetupDragHandle(const char *objectName)
203 QQuickItem *dragHandle =
204 m_view->rootObject()->findChild<QQuickItem*>(objectName);
205 Q_ASSERT(dragHandle != 0);
206 UCSwipeArea *swipeArea = dynamic_cast<UCSwipeArea*>(dragHandle);
208 UCSwipeAreaPrivate *swipeAreaPrivate = dynamic_cast<UCSwipeAreaPrivate *>(QQuickItemPrivate::get(swipeArea));
209 swipeAreaPrivate->setRecognitionTimer(m_fakeTimer);
210 swipeAreaPrivate->setTimeSource(m_fakeTimeSource);
213 AxisVelocityCalculator *edgeDragEvaluator =
214 dragHandle->findChild<AxisVelocityCalculator*>("edgeDragEvaluator");
215 Q_ASSERT(edgeDragEvaluator != 0);
216 edgeDragEvaluator->setTimeSource(m_fakeTimeSource);
221qreal tst_DragHandle::fetchDragThreshold(QQuickItem *dragHandle)
223 AxisVelocityCalculator *edgeDragEvaluator =
224 dragHandle->findChild<AxisVelocityCalculator*>("edgeDragEvaluator");
225 Q_ASSERT(edgeDragEvaluator != 0);
227 return edgeDragEvaluator->property("dragThreshold").toReal();
231 Checks that ending a low-speed drag before dragThreshold results in the
232 Showable getting back to its original position, whereas ending after dragThreshold
233 results in Showable continuing until reaching its new states (shown or hidden)
235void tst_DragHandle::dragThreshold_horizontal()
237 QFETCH(qreal, rotation);
239 QQuickItem *baseItem = m_view->rootObject()->findChild<QQuickItem*>("baseItem");
240 baseItem->setRotation(rotation);
242 QQuickItem *dragHandle = fetchAndSetupDragHandle("rightwardsDragHandle");
243 QQuickItem *parentItem = dragHandle->parentItem();
245 qreal dragThreshold = fetchDragThreshold(dragHandle);
247 // end before the threshold
248 flickAndHold(dragHandle, dragThreshold * 0.7);
251 tryCompare([&](){ return parentItem->x(); }, -parentItem->width());
252 QCOMPARE(parentItem->property("shown").toBool(), false);
254 // end after the threshold
255 flickAndHold(dragHandle, dragThreshold * 1.2);
257 // should keep going until completion
258 tryCompare([&](){ return parentItem->x(); }, 0);
259 QCOMPARE(parentItem->property("shown").toBool(), true);
261 dragHandle = fetchAndSetupDragHandle("leftwardsDragHandle");
263 dragThreshold = fetchDragThreshold(dragHandle);
265 // end before the threshold
266 flickAndHold(dragHandle, dragThreshold * 0.7);
269 tryCompare([&](){ return parentItem->x(); }, 0);
270 QCOMPARE(parentItem->property("shown").toBool(), true);
272 // end after the threshold
273 flickAndHold(dragHandle, dragThreshold * 1.2);
275 // should keep going until completion
276 tryCompare([&](){ return parentItem->x(); }, -parentItem->width());
277 QCOMPARE(parentItem->property("shown").toBool(), false);
280void tst_DragHandle::dragThreshold_horizontal_data()
282 QTest::addColumn<qreal>("rotation");
284 QTest::newRow("not rotated") << 0.;
285 QTest::newRow("rotated 90") << 90.;
288void tst_DragHandle::dragThreshold_vertical()
290 QFETCH(qreal, rotation);
292 QQuickItem *baseItem = m_view->rootObject()->findChild<QQuickItem*>("baseItem");
293 baseItem->setRotation(rotation);
295 QQuickItem *dragHandle = fetchAndSetupDragHandle("topEdgeShowDragHandle");
297 qreal dragThreshold = fetchDragThreshold(dragHandle);
299 // end before the threshold
300 flickAndHold(dragHandle, dragThreshold * 0.7);
303 QQuickItem *parentItem = dragHandle->parentItem();
304 tryCompare([&](){ return parentItem->y(); }, -parentItem->height());
305 QCOMPARE(parentItem->property("shown").toBool(), false);
307 // end after the threshold
308 flickAndHold(dragHandle, dragThreshold * 1.2);
310 // should keep going until completion
311 tryCompare([&](){ return parentItem->y(); }, 0);
312 QCOMPARE(parentItem->property("shown").toBool(), true);
314 dragHandle = fetchAndSetupDragHandle("topEdgeHideDragHandle");
316 dragThreshold = fetchDragThreshold(dragHandle);
318 // end before the threshold
319 flickAndHold(dragHandle, dragThreshold * 0.7);
322 tryCompare([&](){ return parentItem->y(); }, 0);
323 QCOMPARE(parentItem->property("shown").toBool(), true);
325 // end after the threshold
326 flickAndHold(dragHandle, dragThreshold * 1.2);
328 // should keep going until completion
329 tryCompare([&](){ return parentItem->y(); }, -parentItem->height());
330 QCOMPARE(parentItem->property("shown").toBool(), false);
333void tst_DragHandle::dragThreshold_vertical_data()
335 QTest::addColumn<qreal>("rotation");
337 QTest::newRow("not rotated") << 0.;
338 QTest::newRow("rotated 90") << 90.;
342 Checks that when the stretch property is true, dragging the DragHandle increases
343 the width or height (depending on its direction) of its parent Showable
345void tst_DragHandle::stretch_horizontal()
347 QQuickItem *dragHandle = fetchAndSetupDragHandle("rightwardsDragHandle");
348 qreal totalDragDistance = dragHandle->property("maxTotalDragDistance").toReal();
349 QQuickItem *parentItem = dragHandle->parentItem();
351 // enable strech mode
352 m_view->rootObject()->setProperty("stretch", QVariant(true));
354 QCOMPARE(parentItem->width(), 0.0);
357 flickAndHold(dragHandle, totalDragDistance);
359 // should keep going until completion
360 // Parent item should now have its full height
361 tryCompare([&](){ return parentItem->width(); }, totalDragDistance);
362 QCOMPARE(parentItem->property("shown").toBool(), true);
364 dragHandle = fetchAndSetupDragHandle("leftwardsDragHandle");
367 flickAndHold(dragHandle, totalDragDistance);
369 // should keep going until completion
370 // Parent item should now have its full height
371 tryCompare([&](){ return parentItem->width(); }, 0.0);
372 QCOMPARE(parentItem->property("shown").toBool(), false);
375void tst_DragHandle::stretch_vertical()
377 QQuickItem *dragHandle = fetchAndSetupDragHandle("topEdgeShowDragHandle");
378 qreal totalDragDistance = dragHandle->property("maxTotalDragDistance").toReal();
379 QQuickItem *parentItem = dragHandle->parentItem();
381 // enable strech mode
382 m_view->rootObject()->setProperty("stretch", QVariant(true));
384 QCOMPARE(parentItem->height(), 0.0);
387 flickAndHold(dragHandle, totalDragDistance);
389 // should keep going until completion
390 // Parent item should now have its full height
391 tryCompare([&](){ return parentItem->height(); }, totalDragDistance);
392 QCOMPARE(parentItem->property("shown").toBool(), true);
394 dragHandle = fetchAndSetupDragHandle("topEdgeHideDragHandle");
397 flickAndHold(dragHandle, totalDragDistance);
399 // should keep going until completion
400 // Parent item should now have its full height
401 tryCompare([&](){ return parentItem->height(); }, 0.0);
402 QCOMPARE(parentItem->property("shown").toBool(), false);
406 Set DragHandle.hintDisplacement to a value bigger than zero.
407 Then lay a finger on the DragHandle.
408 The expected behavior is that it will move or strech its parent Showable
409 by hintDisplacement pixels.
411void tst_DragHandle::hintingAnimation()
413 QQuickItem *dragHandle = fetchAndSetupDragHandle("topEdgeShowDragHandle");
414 QQuickItem *parentItem = dragHandle->parentItem();
415 qreal hintDisplacement = 100.0;
417 // enable hinting animations and stretch mode
418 m_view->rootObject()->setProperty("hintDisplacement", QVariant(hintDisplacement));
419 m_view->rootObject()->setProperty("stretch", QVariant(true));
421 QCOMPARE(parentItem->height(), 0.0);
423 QPointF initialTouchPos = dragHandle->mapToScene(
424 QPointF(dragHandle->width() / 2.0, dragHandle->height() / 2.0));
425 QPointF touchPoint = initialTouchPos;
427 // Pressing causes the Showable to be stretched by hintDisplacement pixels
428 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
429 tryCompare([&](){ return parentItem->height(); }, hintDisplacement);
431 // Releasing causes the Showable to shrink back to 0 pixels.
432 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
433 tryCompare([&](){ return parentItem->height(); }, 0.0);
435 QCOMPARE(parentItem->property("shown").toBool(), false);
439 Regression test for LP#1269022: https://bugs.launchpad.net/unity8/+bug/1269022
442 2) wait for hint portion to appear
443 3) slowly drag handle, only a few pixels
449 Handle will momentarily move back to zero position, then back down to the
450 hint displacement location.
452void tst_DragHandle::hintingAnimation_dontRestartAfterFinishedAndStillPressed()
454 QQuickItem *dragHandle = fetchAndSetupDragHandle("topEdgeShowDragHandle");
455 QQuickItem *parentItem = dragHandle->parentItem();
456 qreal hintDisplacement = 100.0;
458 // enable hinting animations
459 m_view->rootObject()->setProperty("hintDisplacement", QVariant(hintDisplacement));
460 m_view->rootObject()->setProperty("stretch", QVariant(true));
462 QCOMPARE(parentItem->height(), 0.0);
464 QPointF initialTouchPos = dragHandle->mapToScene(
465 QPointF(dragHandle->width() / 2.0, dragHandle->height() / 2.0));
466 QPointF touchPoint = initialTouchPos;
468 // Pressing causes the Showable to be stretched by hintDisplacement pixels
469 const int touchId = 0;
470 QTest::touchEvent(m_view, m_device).press(touchId, touchPoint.toPoint());
471 tryCompare([&](){ return parentItem->height(); }, hintDisplacement);
474 QSignalSpy parentHeightChangedSpy(parentItem, &QQuickItem::heightChanged);
476 drag(touchPoint, QPointF(0.0, -1.0) /*dragDirectionVector*/, 15 /*distance*/, 3 /*numSteps*/);
478 // Give some time for animations to run, if any
481 // parentItem height shouldn't have changed at all
482 QVERIFY(parentHeightChangedSpy.isEmpty());
485QTEST_MAIN(tst_DragHandle)
487#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