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/qquickanimatorcontroller_p.h>
24#include <private/qquickwindow_p.h>
25#include <UbuntuGestures/private/ucswipearea_p_p.h>
27#include <AxisVelocityCalculator.h>
28#include <UbuntuGestures/private/timer_p.h>
35class tst_DragHandle: public QObject
39 tst_DragHandle() : m_device(0) { }
41 void initTestCase(); // will be called before the first test function is executed
42 void cleanupTestCase(); // will be called after the last test function was executed.
44 void init(); // called right before each and every test function is executed
45 void cleanup(); // called right after each and every test function is executed
47 void dragThreshold_horizontal();
48 void dragThreshold_horizontal_data();
49 void dragThreshold_vertical();
50 void dragThreshold_vertical_data();
51 void stretch_horizontal();
52 void stretch_vertical();
53 void hintingAnimation();
54 void hintingAnimation_dontRestartAfterFinishedAndStillPressed();
57 void flickAndHold(QQuickItem *dragHandle, qreal distance);
58 void drag(QPointF &touchPoint, const QPointF& direction, qreal distance,
59 int numSteps, qint64 timeMs = 500);
60 QQuickItem *fetchAndSetupDragHandle(const char *objectName);
61 qreal fetchDragThreshold(QQuickItem *dragHandle);
62 void tryCompare(std::function<qreal ()> actualFunc, qreal expectedValue);
64 QQuickView *createView();
66 QTouchDevice *m_device;
67 FakeTimer *m_fakeTimer;
68 QSharedPointer<FakeTimeSource> m_fakeTimeSource;
72void tst_DragHandle::initTestCase()
75 m_device = new QTouchDevice;
76 m_device->setType(QTouchDevice::TouchScreen);
77 QWindowSystemInterface::registerTouchDevice(m_device);
83void tst_DragHandle::cleanupTestCase()
87void tst_DragHandle::init()
89 m_view = createView();
90 m_view->setSource(QUrl::fromLocalFile(testDataDir() + "/" TEST_DIR "/tst_DragHandle.qml"));
92 QVERIFY(QTest::qWaitForWindowExposed(m_view));
93 QVERIFY(m_view->rootObject() != 0);
94 qApp->processEvents();
96 // Hide the controls to ensure we don't hit them accidentally
97 QQuickItem *controls = m_view->rootObject()->findChild<QQuickItem*>("controls");
98 controls->setVisible(false);
100 m_fakeTimeSource.reset(new FakeTimeSource);
101 m_fakeTimer = new FakeTimer(m_fakeTimeSource);
104void tst_DragHandle::cleanup()
112 m_fakeTimeSource.reset();
115QQuickView *tst_DragHandle::createView()
117 QQuickView *window = new QQuickView(0);
118 window->setResizeMode(QQuickView::SizeRootObjectToView);
119 window->engine()->addImportPath(testDataDir() + "/" TEST_DIR);
124void tst_DragHandle::tryCompare(std::function<qreal ()> actualFunc,
128 while (actualFunc() != expectedValue && waitCount < 100) {
132 QCOMPARE(actualFunc(), expectedValue);
136QPointF calculateDirectionVector(QQuickItem *edgeDragArea)
138 QPointF localOrigin(0., 0.);
139 QPointF localDirection;
140 switch (edgeDragArea->property("direction").toInt()) {
141 case Direction::Upwards:
142 localDirection.rx() = 0.;
143 localDirection.ry() = -1.;
145 case Direction::Downwards:
146 localDirection.rx() = 0.;
147 localDirection.ry() = 1;
149 case Direction::Leftwards:
150 localDirection.rx() = -1.;
151 localDirection.ry() = 0.;
153 default: // Direction::Rightwards:
154 localDirection.rx() = 1.;
155 localDirection.ry() = 0.;
158 QPointF sceneOrigin = edgeDragArea->mapToScene(localOrigin);
159 QPointF sceneDirection = edgeDragArea->mapToScene(localDirection);
160 return sceneDirection - sceneOrigin;
164void tst_DragHandle::flickAndHold(QQuickItem *dragHandle,
167 QPointF initialTouchPos = dragHandle->mapToScene(
168 QPointF(dragHandle->width() / 2.0, dragHandle->height() / 2.0));
169 QPointF touchPoint = initialTouchPos;
171 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
174 QPointF dragDirectionVector = calculateDirectionVector(dragHandle);
175 drag(touchPoint, dragDirectionVector, distance, numSteps);
177 // Wait for quite a bit before finally releasing to make a very low flick/release
179 m_fakeTimeSource->m_msecsSinceReference += 5000;
180 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
182 QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(m_view);
183 if (windowPrivate->delayedTouch) {
184 windowPrivate->deliverDelayedTouchEvent();
186 // Touch events which constantly start animations (such as a behavior tracking
187 // the mouse point) need animations to start.
188 QQmlAnimationTimer *ut = QQmlAnimationTimer::instance();
189 if (ut && ut->hasStartAnimationPending())
190 ut->startAnimations();
194void tst_DragHandle::drag(QPointF &touchPoint, const QPointF& direction, qreal distance,
195 int numSteps, qint64 timeMs)
197 QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(m_view);
199 qint64 timeStep = timeMs / numSteps;
200 QPointF touchMovement = direction * (distance / (qreal)numSteps);
201 for (int i = 0; i < numSteps; ++i) {
202 touchPoint += touchMovement;
203 m_fakeTimeSource->m_msecsSinceReference += timeStep;
204 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
206 if (windowPrivate->delayedTouch) {
207 windowPrivate->deliverDelayedTouchEvent();
209 // Touch events which constantly start animations (such as a behavior tracking
210 // the mouse point) need animations to start.
211 QQmlAnimationTimer *ut = QQmlAnimationTimer::instance();
212 if (ut && ut->hasStartAnimationPending())
213 ut->startAnimations();
218QQuickItem *tst_DragHandle::fetchAndSetupDragHandle(const char *objectName)
220 QQuickItem *dragHandle =
221 m_view->rootObject()->findChild<QQuickItem*>(objectName);
222 Q_ASSERT(dragHandle != 0);
223 UCSwipeArea *swipeArea = dynamic_cast<UCSwipeArea*>(dragHandle);
225 UCSwipeAreaPrivate *swipeAreaPrivate = dynamic_cast<UCSwipeAreaPrivate *>(QQuickItemPrivate::get(swipeArea));
226 swipeAreaPrivate->setRecognitionTimer(m_fakeTimer);
227 swipeAreaPrivate->setTimeSource(m_fakeTimeSource);
230 AxisVelocityCalculator *edgeDragEvaluator =
231 dragHandle->findChild<AxisVelocityCalculator*>("edgeDragEvaluator");
232 Q_ASSERT(edgeDragEvaluator != 0);
233 edgeDragEvaluator->setTimeSource(m_fakeTimeSource);
238qreal tst_DragHandle::fetchDragThreshold(QQuickItem *dragHandle)
240 AxisVelocityCalculator *edgeDragEvaluator =
241 dragHandle->findChild<AxisVelocityCalculator*>("edgeDragEvaluator");
242 Q_ASSERT(edgeDragEvaluator != 0);
244 return edgeDragEvaluator->property("dragThreshold").toReal();
248 Checks that ending a low-speed drag before dragThreshold results in the
249 Showable getting back to its original position, whereas ending after dragThreshold
250 results in Showable continuing until reaching its new states (shown or hidden)
252void tst_DragHandle::dragThreshold_horizontal()
254 QFETCH(qreal, rotation);
256 QQuickItem *baseItem = m_view->rootObject()->findChild<QQuickItem*>("baseItem");
257 baseItem->setRotation(rotation);
259 QQuickItem *dragHandle = fetchAndSetupDragHandle("rightwardsDragHandle");
260 QQuickItem *parentItem = dragHandle->parentItem();
262 qreal dragThreshold = fetchDragThreshold(dragHandle);
264 // end before the threshold
265 flickAndHold(dragHandle, dragThreshold * 0.7);
268 tryCompare([&](){ return parentItem->x(); }, -parentItem->width());
269 QCOMPARE(parentItem->property("shown").toBool(), false);
271 // end after the threshold
272 flickAndHold(dragHandle, dragThreshold * 1.2);
274 // should keep going until completion
275 tryCompare([&](){ return parentItem->x(); }, 0);
276 QCOMPARE(parentItem->property("shown").toBool(), true);
278 dragHandle = fetchAndSetupDragHandle("leftwardsDragHandle");
280 dragThreshold = fetchDragThreshold(dragHandle);
282 // end before the threshold
283 flickAndHold(dragHandle, dragThreshold * 0.7);
286 tryCompare([&](){ return parentItem->x(); }, 0);
287 QCOMPARE(parentItem->property("shown").toBool(), true);
289 // end after the threshold
290 flickAndHold(dragHandle, dragThreshold * 1.2);
292 // should keep going until completion
293 tryCompare([&](){ return parentItem->x(); }, -parentItem->width());
294 QCOMPARE(parentItem->property("shown").toBool(), false);
297void tst_DragHandle::dragThreshold_horizontal_data()
299 QTest::addColumn<qreal>("rotation");
301 QTest::newRow("not rotated") << 0.;
302 QTest::newRow("rotated 90") << 90.;
305void tst_DragHandle::dragThreshold_vertical()
307 QFETCH(qreal, rotation);
309 QQuickItem *baseItem = m_view->rootObject()->findChild<QQuickItem*>("baseItem");
310 baseItem->setRotation(rotation);
312 QQuickItem *dragHandle = fetchAndSetupDragHandle("topEdgeShowDragHandle");
314 qreal dragThreshold = fetchDragThreshold(dragHandle);
316 // end before the threshold
317 flickAndHold(dragHandle, dragThreshold * 0.7);
320 QQuickItem *parentItem = dragHandle->parentItem();
321 tryCompare([&](){ return parentItem->y(); }, -parentItem->height());
322 QCOMPARE(parentItem->property("shown").toBool(), false);
324 // end after the threshold
325 flickAndHold(dragHandle, dragThreshold * 1.2);
327 // should keep going until completion
328 tryCompare([&](){ return parentItem->y(); }, 0);
329 QCOMPARE(parentItem->property("shown").toBool(), true);
331 dragHandle = fetchAndSetupDragHandle("topEdgeHideDragHandle");
333 dragThreshold = fetchDragThreshold(dragHandle);
335 // end before the threshold
336 flickAndHold(dragHandle, dragThreshold * 0.7);
339 tryCompare([&](){ return parentItem->y(); }, 0);
340 QCOMPARE(parentItem->property("shown").toBool(), true);
342 // end after the threshold
343 flickAndHold(dragHandle, dragThreshold * 1.2);
345 // should keep going until completion
346 tryCompare([&](){ return parentItem->y(); }, -parentItem->height());
347 QCOMPARE(parentItem->property("shown").toBool(), false);
350void tst_DragHandle::dragThreshold_vertical_data()
352 QTest::addColumn<qreal>("rotation");
354 QTest::newRow("not rotated") << 0.;
355 QTest::newRow("rotated 90") << 90.;
359 Checks that when the stretch property is true, dragging the DragHandle increases
360 the width or height (depending on its direction) of its parent Showable
362void tst_DragHandle::stretch_horizontal()
364 QQuickItem *dragHandle = fetchAndSetupDragHandle("rightwardsDragHandle");
365 qreal totalDragDistance = dragHandle->property("maxTotalDragDistance").toReal();
366 QQuickItem *parentItem = dragHandle->parentItem();
368 // enable strech mode
369 m_view->rootObject()->setProperty("stretch", QVariant(true));
371 QCOMPARE(parentItem->width(), 0.0);
374 flickAndHold(dragHandle, totalDragDistance);
376 // should keep going until completion
377 // Parent item should now have its full height
378 tryCompare([&](){ return parentItem->width(); }, totalDragDistance);
379 QCOMPARE(parentItem->property("shown").toBool(), true);
381 dragHandle = fetchAndSetupDragHandle("leftwardsDragHandle");
384 flickAndHold(dragHandle, totalDragDistance);
386 // should keep going until completion
387 // Parent item should now have its full height
388 tryCompare([&](){ return parentItem->width(); }, 0.0);
389 QCOMPARE(parentItem->property("shown").toBool(), false);
392void tst_DragHandle::stretch_vertical()
394 QQuickItem *dragHandle = fetchAndSetupDragHandle("topEdgeShowDragHandle");
395 qreal totalDragDistance = dragHandle->property("maxTotalDragDistance").toReal();
396 QQuickItem *parentItem = dragHandle->parentItem();
398 // enable strech mode
399 m_view->rootObject()->setProperty("stretch", QVariant(true));
401 QCOMPARE(parentItem->height(), 0.0);
404 flickAndHold(dragHandle, totalDragDistance);
406 // should keep going until completion
407 // Parent item should now have its full height
408 tryCompare([&](){ return parentItem->height(); }, totalDragDistance);
409 QCOMPARE(parentItem->property("shown").toBool(), true);
411 dragHandle = fetchAndSetupDragHandle("topEdgeHideDragHandle");
414 flickAndHold(dragHandle, totalDragDistance);
416 // should keep going until completion
417 // Parent item should now have its full height
418 tryCompare([&](){ return parentItem->height(); }, 0.0);
419 QCOMPARE(parentItem->property("shown").toBool(), false);
423 Set DragHandle.hintDisplacement to a value bigger than zero.
424 Then lay a finger on the DragHandle.
425 The expected behavior is that it will move or strech its parent Showable
426 by hintDisplacement pixels.
428void tst_DragHandle::hintingAnimation()
430 QQuickItem *dragHandle = fetchAndSetupDragHandle("topEdgeShowDragHandle");
431 QQuickItem *parentItem = dragHandle->parentItem();
432 qreal hintDisplacement = 100.0;
434 // enable hinting animations and stretch mode
435 m_view->rootObject()->setProperty("hintDisplacement", QVariant(hintDisplacement));
436 m_view->rootObject()->setProperty("stretch", QVariant(true));
438 QCOMPARE(parentItem->height(), 0.0);
440 QPointF initialTouchPos = dragHandle->mapToScene(
441 QPointF(dragHandle->width() / 2.0, dragHandle->height() / 2.0));
442 QPointF touchPoint = initialTouchPos;
444 // Pressing causes the Showable to be stretched by hintDisplacement pixels
445 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
446 tryCompare([&](){ return parentItem->height(); }, hintDisplacement);
448 // Releasing causes the Showable to shrink back to 0 pixels.
449 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
450 tryCompare([&](){ return parentItem->height(); }, 0.0);
452 QCOMPARE(parentItem->property("shown").toBool(), false);
456 Regression test for LP#1269022: https://bugs.launchpad.net/unity8/+bug/1269022
459 2) wait for hint portion to appear
460 3) slowly drag handle, only a few pixels
466 Handle will momentarily move back to zero position, then back down to the
467 hint displacement location.
469void tst_DragHandle::hintingAnimation_dontRestartAfterFinishedAndStillPressed()
471 QQuickItem *dragHandle = fetchAndSetupDragHandle("topEdgeShowDragHandle");
472 QQuickItem *parentItem = dragHandle->parentItem();
473 qreal hintDisplacement = 100.0;
475 // enable hinting animations
476 m_view->rootObject()->setProperty("hintDisplacement", QVariant(hintDisplacement));
477 m_view->rootObject()->setProperty("stretch", QVariant(true));
479 QCOMPARE(parentItem->height(), 0.0);
481 QPointF initialTouchPos = dragHandle->mapToScene(
482 QPointF(dragHandle->width() / 2.0, dragHandle->height() / 2.0));
483 QPointF touchPoint = initialTouchPos;
485 // Pressing causes the Showable to be stretched by hintDisplacement pixels
486 const int touchId = 0;
487 QTest::touchEvent(m_view, m_device).press(touchId, touchPoint.toPoint());
488 tryCompare([&](){ return parentItem->height(); }, hintDisplacement);
491 QSignalSpy parentHeightChangedSpy(parentItem, &QQuickItem::heightChanged);
493 drag(touchPoint, QPointF(0.0, -1.0) /*dragDirectionVector*/, 15 /*distance*/, 3 /*numSteps*/);
495 // Give some time for animations to run, if any
498 // parentItem height shouldn't have changed at all
499 QVERIFY(parentHeightChangedSpy.isEmpty());
502QTEST_MAIN(tst_DragHandle)
504#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