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>
32class tst_DragHandle: public QObject
36 tst_DragHandle() : m_device(0) { }
38 void initTestCase(); // will be called before the first test function is executed
39 void cleanupTestCase(); // will be called after the last test function was executed.
41 void init(); // called right before each and every test function is executed
42 void cleanup(); // called right after each and every test function is executed
44 void dragThreshold_horizontal();
45 void dragThreshold_horizontal_data();
46 void dragThreshold_vertical();
47 void dragThreshold_vertical_data();
48 void stretch_horizontal();
49 void stretch_vertical();
50 void hintingAnimation();
51 void hintingAnimation_dontRestartAfterFinishedAndStillPressed();
54 void flickAndHold(QQuickItem *dragHandle, qreal distance);
55 void drag(QPointF &touchPoint, const QPointF& direction, qreal distance,
56 int numSteps, qint64 timeMs = 500);
57 QQuickItem *fetchAndSetupDragHandle(const char *objectName);
58 qreal fetchDragThreshold(QQuickItem *dragHandle);
59 void tryCompare(std::function<qreal ()> actualFunc, qreal expectedValue);
61 QQuickView *createView();
63 QTouchDevice *m_device;
64 FakeTimer *m_fakeTimer;
65 QSharedPointer<FakeTimeSource> m_fakeTimeSource;
69void tst_DragHandle::initTestCase()
72 m_device = new QTouchDevice;
73 m_device->setType(QTouchDevice::TouchScreen);
74 QWindowSystemInterface::registerTouchDevice(m_device);
80void tst_DragHandle::cleanupTestCase()
84void tst_DragHandle::init()
86 m_view = createView();
87 m_view->setSource(QUrl::fromLocalFile(TEST_DIR"/tst_DragHandle.qml"));
89 QVERIFY(QTest::qWaitForWindowExposed(m_view));
90 QVERIFY(m_view->rootObject() != 0);
91 qApp->processEvents();
93 // Hide the controls to ensure we don't hit them accidentally
94 QQuickItem *controls = m_view->rootObject()->findChild<QQuickItem*>("controls");
95 controls->setVisible(false);
97 m_fakeTimeSource.reset(new FakeTimeSource);
98 m_fakeTimer = new FakeTimer(m_fakeTimeSource);
101void tst_DragHandle::cleanup()
109 m_fakeTimeSource.reset();
112QQuickView *tst_DragHandle::createView()
114 QQuickView *window = new QQuickView(0);
115 window->setResizeMode(QQuickView::SizeRootObjectToView);
116 window->engine()->addImportPath(QLatin1String(TEST_DIR));
121void tst_DragHandle::tryCompare(std::function<qreal ()> actualFunc,
125 while (actualFunc() != expectedValue && waitCount < 100) {
129 QCOMPARE(actualFunc(), expectedValue);
133QPointF calculateDirectionVector(QQuickItem *edgeDragArea)
135 QPointF localOrigin(0., 0.);
136 QPointF localDirection;
137 switch (edgeDragArea->property("direction").toInt()) {
138 case Direction::Upwards:
139 localDirection.rx() = 0.;
140 localDirection.ry() = -1.;
142 case Direction::Downwards:
143 localDirection.rx() = 0.;
144 localDirection.ry() = 1;
146 case Direction::Leftwards:
147 localDirection.rx() = -1.;
148 localDirection.ry() = 0.;
150 default: // Direction::Rightwards:
151 localDirection.rx() = 1.;
152 localDirection.ry() = 0.;
155 QPointF sceneOrigin = edgeDragArea->mapToScene(localOrigin);
156 QPointF sceneDirection = edgeDragArea->mapToScene(localDirection);
157 return sceneDirection - sceneOrigin;
161void tst_DragHandle::flickAndHold(QQuickItem *dragHandle,
164 QPointF initialTouchPos = dragHandle->mapToScene(
165 QPointF(dragHandle->width() / 2.0, dragHandle->height() / 2.0));
166 QPointF touchPoint = initialTouchPos;
168 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
171 QPointF dragDirectionVector = calculateDirectionVector(dragHandle);
172 drag(touchPoint, dragDirectionVector, distance, numSteps);
174 // Wait for quite a bit before finally releasing to make a very low flick/release
176 m_fakeTimeSource->m_msecsSinceReference += 5000;
177 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
179 QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(m_view);
180 windowPrivate->flushDelayedTouchEvent();
183void tst_DragHandle::drag(QPointF &touchPoint, const QPointF& direction, qreal distance,
184 int numSteps, qint64 timeMs)
186 QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(m_view);
188 qint64 timeStep = timeMs / numSteps;
189 QPointF touchMovement = direction * (distance / (qreal)numSteps);
190 for (int i = 0; i < numSteps; ++i) {
191 touchPoint += touchMovement;
192 m_fakeTimeSource->m_msecsSinceReference += timeStep;
193 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
195 windowPrivate->flushDelayedTouchEvent();
199QQuickItem *tst_DragHandle::fetchAndSetupDragHandle(const char *objectName)
201 QQuickItem *dragHandle =
202 m_view->rootObject()->findChild<QQuickItem*>(objectName);
203 Q_ASSERT(dragHandle != 0);
204 UCSwipeArea *swipeArea = dynamic_cast<UCSwipeArea*>(dragHandle);
206 UCSwipeAreaPrivate *swipeAreaPrivate = dynamic_cast<UCSwipeAreaPrivate *>(QQuickItemPrivate::get(swipeArea));
207 swipeAreaPrivate->setRecognitionTimer(m_fakeTimer);
208 swipeAreaPrivate->setTimeSource(m_fakeTimeSource);
211 AxisVelocityCalculator *edgeDragEvaluator =
212 dragHandle->findChild<AxisVelocityCalculator*>("edgeDragEvaluator");
213 Q_ASSERT(edgeDragEvaluator != 0);
214 edgeDragEvaluator->setTimeSource(m_fakeTimeSource);
219qreal tst_DragHandle::fetchDragThreshold(QQuickItem *dragHandle)
221 AxisVelocityCalculator *edgeDragEvaluator =
222 dragHandle->findChild<AxisVelocityCalculator*>("edgeDragEvaluator");
223 Q_ASSERT(edgeDragEvaluator != 0);
225 return edgeDragEvaluator->property("dragThreshold").toReal();
229 Checks that ending a low-speed drag before dragThreshold results in the
230 Showable getting back to its original position, whereas ending after dragThreshold
231 results in Showable continuing until reaching its new states (shown or hidden)
233void tst_DragHandle::dragThreshold_horizontal()
235 QFETCH(qreal, rotation);
237 QQuickItem *baseItem = m_view->rootObject()->findChild<QQuickItem*>("baseItem");
238 baseItem->setRotation(rotation);
240 QQuickItem *dragHandle = fetchAndSetupDragHandle("rightwardsDragHandle");
241 QQuickItem *parentItem = dragHandle->parentItem();
243 qreal dragThreshold = fetchDragThreshold(dragHandle);
245 // end before the threshold
246 flickAndHold(dragHandle, dragThreshold * 0.7);
249 tryCompare([&](){ return parentItem->x(); }, -parentItem->width());
250 QCOMPARE(parentItem->property("shown").toBool(), false);
252 // end after the threshold
253 flickAndHold(dragHandle, dragThreshold * 1.2);
255 // should keep going until completion
256 tryCompare([&](){ return parentItem->x(); }, 0);
257 QCOMPARE(parentItem->property("shown").toBool(), true);
259 dragHandle = fetchAndSetupDragHandle("leftwardsDragHandle");
261 dragThreshold = fetchDragThreshold(dragHandle);
263 // end before the threshold
264 flickAndHold(dragHandle, dragThreshold * 0.7);
267 tryCompare([&](){ return parentItem->x(); }, 0);
268 QCOMPARE(parentItem->property("shown").toBool(), true);
270 // end after the threshold
271 flickAndHold(dragHandle, dragThreshold * 1.2);
273 // should keep going until completion
274 tryCompare([&](){ return parentItem->x(); }, -parentItem->width());
275 QCOMPARE(parentItem->property("shown").toBool(), false);
278void tst_DragHandle::dragThreshold_horizontal_data()
280 QTest::addColumn<qreal>("rotation");
282 QTest::newRow("not rotated") << 0.;
283 QTest::newRow("rotated 90") << 90.;
286void tst_DragHandle::dragThreshold_vertical()
288 QFETCH(qreal, rotation);
290 QQuickItem *baseItem = m_view->rootObject()->findChild<QQuickItem*>("baseItem");
291 baseItem->setRotation(rotation);
293 QQuickItem *dragHandle = fetchAndSetupDragHandle("topEdgeShowDragHandle");
295 qreal dragThreshold = fetchDragThreshold(dragHandle);
297 // end before the threshold
298 flickAndHold(dragHandle, dragThreshold * 0.7);
301 QQuickItem *parentItem = dragHandle->parentItem();
302 tryCompare([&](){ return parentItem->y(); }, -parentItem->height());
303 QCOMPARE(parentItem->property("shown").toBool(), false);
305 // end after the threshold
306 flickAndHold(dragHandle, dragThreshold * 1.2);
308 // should keep going until completion
309 tryCompare([&](){ return parentItem->y(); }, 0);
310 QCOMPARE(parentItem->property("shown").toBool(), true);
312 dragHandle = fetchAndSetupDragHandle("topEdgeHideDragHandle");
314 dragThreshold = fetchDragThreshold(dragHandle);
316 // end before the threshold
317 flickAndHold(dragHandle, dragThreshold * 0.7);
320 tryCompare([&](){ return parentItem->y(); }, 0);
321 QCOMPARE(parentItem->property("shown").toBool(), true);
323 // end after the threshold
324 flickAndHold(dragHandle, dragThreshold * 1.2);
326 // should keep going until completion
327 tryCompare([&](){ return parentItem->y(); }, -parentItem->height());
328 QCOMPARE(parentItem->property("shown").toBool(), false);
331void tst_DragHandle::dragThreshold_vertical_data()
333 QTest::addColumn<qreal>("rotation");
335 QTest::newRow("not rotated") << 0.;
336 QTest::newRow("rotated 90") << 90.;
340 Checks that when the stretch property is true, dragging the DragHandle increases
341 the width or height (depending on its direction) of its parent Showable
343void tst_DragHandle::stretch_horizontal()
345 QQuickItem *dragHandle = fetchAndSetupDragHandle("rightwardsDragHandle");
346 qreal totalDragDistance = dragHandle->property("maxTotalDragDistance").toReal();
347 QQuickItem *parentItem = dragHandle->parentItem();
349 // enable strech mode
350 m_view->rootObject()->setProperty("stretch", QVariant(true));
352 QCOMPARE(parentItem->width(), 0.0);
355 flickAndHold(dragHandle, totalDragDistance);
357 // should keep going until completion
358 // Parent item should now have its full height
359 tryCompare([&](){ return parentItem->width(); }, totalDragDistance);
360 QCOMPARE(parentItem->property("shown").toBool(), true);
362 dragHandle = fetchAndSetupDragHandle("leftwardsDragHandle");
365 flickAndHold(dragHandle, totalDragDistance);
367 // should keep going until completion
368 // Parent item should now have its full height
369 tryCompare([&](){ return parentItem->width(); }, 0.0);
370 QCOMPARE(parentItem->property("shown").toBool(), false);
373void tst_DragHandle::stretch_vertical()
375 QQuickItem *dragHandle = fetchAndSetupDragHandle("topEdgeShowDragHandle");
376 qreal totalDragDistance = dragHandle->property("maxTotalDragDistance").toReal();
377 QQuickItem *parentItem = dragHandle->parentItem();
379 // enable strech mode
380 m_view->rootObject()->setProperty("stretch", QVariant(true));
382 QCOMPARE(parentItem->height(), 0.0);
385 flickAndHold(dragHandle, totalDragDistance);
387 // should keep going until completion
388 // Parent item should now have its full height
389 tryCompare([&](){ return parentItem->height(); }, totalDragDistance);
390 QCOMPARE(parentItem->property("shown").toBool(), true);
392 dragHandle = fetchAndSetupDragHandle("topEdgeHideDragHandle");
395 flickAndHold(dragHandle, totalDragDistance);
397 // should keep going until completion
398 // Parent item should now have its full height
399 tryCompare([&](){ return parentItem->height(); }, 0.0);
400 QCOMPARE(parentItem->property("shown").toBool(), false);
404 Set DragHandle.hintDisplacement to a value bigger than zero.
405 Then lay a finger on the DragHandle.
406 The expected behavior is that it will move or strech its parent Showable
407 by hintDisplacement pixels.
409void tst_DragHandle::hintingAnimation()
411 QQuickItem *dragHandle = fetchAndSetupDragHandle("topEdgeShowDragHandle");
412 QQuickItem *parentItem = dragHandle->parentItem();
413 qreal hintDisplacement = 100.0;
415 // enable hinting animations and stretch mode
416 m_view->rootObject()->setProperty("hintDisplacement", QVariant(hintDisplacement));
417 m_view->rootObject()->setProperty("stretch", QVariant(true));
419 QCOMPARE(parentItem->height(), 0.0);
421 QPointF initialTouchPos = dragHandle->mapToScene(
422 QPointF(dragHandle->width() / 2.0, dragHandle->height() / 2.0));
423 QPointF touchPoint = initialTouchPos;
425 // Pressing causes the Showable to be stretched by hintDisplacement pixels
426 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
427 tryCompare([&](){ return parentItem->height(); }, hintDisplacement);
429 // Releasing causes the Showable to shrink back to 0 pixels.
430 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
431 tryCompare([&](){ return parentItem->height(); }, 0.0);
433 QCOMPARE(parentItem->property("shown").toBool(), false);
437 Regression test for LP#1269022: https://bugs.launchpad.net/unity8/+bug/1269022
440 2) wait for hint portion to appear
441 3) slowly drag handle, only a few pixels
447 Handle will momentarily move back to zero position, then back down to the
448 hint displacement location.
450void tst_DragHandle::hintingAnimation_dontRestartAfterFinishedAndStillPressed()
452 QQuickItem *dragHandle = fetchAndSetupDragHandle("topEdgeShowDragHandle");
453 QQuickItem *parentItem = dragHandle->parentItem();
454 qreal hintDisplacement = 100.0;
456 // enable hinting animations
457 m_view->rootObject()->setProperty("hintDisplacement", QVariant(hintDisplacement));
458 m_view->rootObject()->setProperty("stretch", QVariant(true));
460 QCOMPARE(parentItem->height(), 0.0);
462 QPointF initialTouchPos = dragHandle->mapToScene(
463 QPointF(dragHandle->width() / 2.0, dragHandle->height() / 2.0));
464 QPointF touchPoint = initialTouchPos;
466 // Pressing causes the Showable to be stretched by hintDisplacement pixels
467 const int touchId = 0;
468 QTest::touchEvent(m_view, m_device).press(touchId, touchPoint.toPoint());
469 tryCompare([&](){ return parentItem->height(); }, hintDisplacement);
472 QSignalSpy parentHeightChangedSpy(parentItem, &QQuickItem::heightChanged);
474 drag(touchPoint, QPointF(0.0, -1.0) /*dragDirectionVector*/, 15 /*distance*/, 3 /*numSteps*/);
476 // Give some time for animations to run, if any
479 // parentItem height shouldn't have changed at all
480 QVERIFY(parentHeightChangedSpy.isEmpty());
483QTEST_MAIN(tst_DragHandle)
485#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