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>
32using namespace UbuntuGestures;
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(DirectionalDragArea *dragHandle, qreal distance);
57 void drag(QPointF &touchPoint, const QPointF& direction, qreal distance,
58 int numSteps, qint64 timeMs = 500);
59 DirectionalDragArea *fetchAndSetupDragHandle(const char *objectName);
60 qreal fetchDragThreshold(DirectionalDragArea *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(DirectionalDragArea *edgeDragArea)
137 QPointF localOrigin(0., 0.);
138 QPointF localDirection;
139 switch (edgeDragArea->direction()) {
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(DirectionalDragArea *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();
201DirectionalDragArea *tst_DragHandle::fetchAndSetupDragHandle(const char *objectName)
203 DirectionalDragArea *dragHandle =
204 m_view->rootObject()->findChild<DirectionalDragArea*>(objectName);
205 Q_ASSERT(dragHandle != 0);
206 dragHandle->d->setRecognitionTimer(m_fakeTimer);
207 dragHandle->d->setTimeSource(m_fakeTimeSource);
209 AxisVelocityCalculator *edgeDragEvaluator =
210 dragHandle->findChild<AxisVelocityCalculator*>("edgeDragEvaluator");
211 Q_ASSERT(edgeDragEvaluator != 0);
212 edgeDragEvaluator->setTimeSource(m_fakeTimeSource);
217qreal tst_DragHandle::fetchDragThreshold(DirectionalDragArea *dragHandle)
219 AxisVelocityCalculator *edgeDragEvaluator =
220 dragHandle->findChild<AxisVelocityCalculator*>("edgeDragEvaluator");
221 Q_ASSERT(edgeDragEvaluator != 0);
223 return edgeDragEvaluator->property("dragThreshold").toReal();
227 Checks that ending a low-speed drag before dragThreshold results in the
228 Showable getting back to its original position, whereas ending after dragThreshold
229 results in Showable continuing until reaching its new states (shown or hidden)
231void tst_DragHandle::dragThreshold_horizontal()
233 QFETCH(qreal, rotation);
235 QQuickItem *baseItem = m_view->rootObject()->findChild<QQuickItem*>("baseItem");
236 baseItem->setRotation(rotation);
238 DirectionalDragArea *dragHandle = fetchAndSetupDragHandle("rightwardsDragHandle");
239 QQuickItem *parentItem = dragHandle->parentItem();
241 qreal dragThreshold = fetchDragThreshold(dragHandle);
243 // end before the threshold
244 flickAndHold(dragHandle, dragThreshold * 0.7);
247 tryCompare([&](){ return parentItem->x(); }, -parentItem->width());
248 QCOMPARE(parentItem->property("shown").toBool(), false);
250 // end after the threshold
251 flickAndHold(dragHandle, dragThreshold * 1.2);
253 // should keep going until completion
254 tryCompare([&](){ return parentItem->x(); }, 0);
255 QCOMPARE(parentItem->property("shown").toBool(), true);
257 dragHandle = fetchAndSetupDragHandle("leftwardsDragHandle");
259 dragThreshold = fetchDragThreshold(dragHandle);
261 // end before the threshold
262 flickAndHold(dragHandle, dragThreshold * 0.7);
265 tryCompare([&](){ return parentItem->x(); }, 0);
266 QCOMPARE(parentItem->property("shown").toBool(), true);
268 // end after the threshold
269 flickAndHold(dragHandle, dragThreshold * 1.2);
271 // should keep going until completion
272 tryCompare([&](){ return parentItem->x(); }, -parentItem->width());
273 QCOMPARE(parentItem->property("shown").toBool(), false);
276void tst_DragHandle::dragThreshold_horizontal_data()
278 QTest::addColumn<qreal>("rotation");
280 QTest::newRow("not rotated") << 0.;
281 QTest::newRow("rotated 90") << 90.;
284void tst_DragHandle::dragThreshold_vertical()
286 QFETCH(qreal, rotation);
288 QQuickItem *baseItem = m_view->rootObject()->findChild<QQuickItem*>("baseItem");
289 baseItem->setRotation(rotation);
291 DirectionalDragArea *dragHandle = fetchAndSetupDragHandle("downwardsDragHandle");
293 qreal dragThreshold = fetchDragThreshold(dragHandle);
295 // end before the threshold
296 flickAndHold(dragHandle, dragThreshold * 0.7);
299 QQuickItem *parentItem = dragHandle->parentItem();
300 tryCompare([&](){ return parentItem->y(); }, -parentItem->height());
301 QCOMPARE(parentItem->property("shown").toBool(), false);
303 // end after the threshold
304 flickAndHold(dragHandle, dragThreshold * 1.2);
306 // should keep going until completion
307 tryCompare([&](){ return parentItem->y(); }, 0);
308 QCOMPARE(parentItem->property("shown").toBool(), true);
310 dragHandle = fetchAndSetupDragHandle("upwardsDragHandle");
312 dragThreshold = fetchDragThreshold(dragHandle);
314 // end before the threshold
315 flickAndHold(dragHandle, dragThreshold * 0.7);
318 tryCompare([&](){ return parentItem->y(); }, 0);
319 QCOMPARE(parentItem->property("shown").toBool(), true);
321 // end after the threshold
322 flickAndHold(dragHandle, dragThreshold * 1.2);
324 // should keep going until completion
325 tryCompare([&](){ return parentItem->y(); }, -parentItem->height());
326 QCOMPARE(parentItem->property("shown").toBool(), false);
329void tst_DragHandle::dragThreshold_vertical_data()
331 QTest::addColumn<qreal>("rotation");
333 QTest::newRow("not rotated") << 0.;
334 QTest::newRow("rotated 90") << 90.;
338 Checks that when the stretch property is true, dragging the DragHandle increases
339 the width or height (depending on its direction) of its parent Showable
341void tst_DragHandle::stretch_horizontal()
343 DirectionalDragArea *dragHandle = fetchAndSetupDragHandle("rightwardsDragHandle");
344 qreal totalDragDistance = dragHandle->property("maxTotalDragDistance").toReal();
345 QQuickItem *parentItem = dragHandle->parentItem();
347 // enable strech mode
348 m_view->rootObject()->setProperty("stretch", QVariant(true));
350 QCOMPARE(parentItem->width(), 0.0);
353 flickAndHold(dragHandle, totalDragDistance);
355 // should keep going until completion
356 // Parent item should now have its full height
357 tryCompare([&](){ return parentItem->width(); }, totalDragDistance);
358 QCOMPARE(parentItem->property("shown").toBool(), true);
360 dragHandle = fetchAndSetupDragHandle("leftwardsDragHandle");
363 flickAndHold(dragHandle, totalDragDistance);
365 // should keep going until completion
366 // Parent item should now have its full height
367 tryCompare([&](){ return parentItem->width(); }, 0.0);
368 QCOMPARE(parentItem->property("shown").toBool(), false);
371void tst_DragHandle::stretch_vertical()
373 DirectionalDragArea *dragHandle = fetchAndSetupDragHandle("downwardsDragHandle");
374 qreal totalDragDistance = dragHandle->property("maxTotalDragDistance").toReal();
375 QQuickItem *parentItem = dragHandle->parentItem();
377 // enable strech mode
378 m_view->rootObject()->setProperty("stretch", QVariant(true));
380 QCOMPARE(parentItem->height(), 0.0);
383 flickAndHold(dragHandle, totalDragDistance);
385 // should keep going until completion
386 // Parent item should now have its full height
387 tryCompare([&](){ return parentItem->height(); }, totalDragDistance);
388 QCOMPARE(parentItem->property("shown").toBool(), true);
390 dragHandle = fetchAndSetupDragHandle("upwardsDragHandle");
393 flickAndHold(dragHandle, totalDragDistance);
395 // should keep going until completion
396 // Parent item should now have its full height
397 tryCompare([&](){ return parentItem->height(); }, 0.0);
398 QCOMPARE(parentItem->property("shown").toBool(), false);
402 Set DragHandle.hintDisplacement to a value bigger than zero.
403 Then lay a finger on the DragHandle.
404 The expected behavior is that it will move or strech its parent Showable
405 by hintDisplacement pixels.
407void tst_DragHandle::hintingAnimation()
409 DirectionalDragArea *dragHandle = fetchAndSetupDragHandle("downwardsDragHandle");
410 QQuickItem *parentItem = dragHandle->parentItem();
411 qreal hintDisplacement = 100.0;
413 // enable hinting animations and stretch mode
414 m_view->rootObject()->setProperty("hintDisplacement", QVariant(hintDisplacement));
415 m_view->rootObject()->setProperty("stretch", QVariant(true));
417 QCOMPARE(parentItem->height(), 0.0);
419 QPointF initialTouchPos = dragHandle->mapToScene(
420 QPointF(dragHandle->width() / 2.0, dragHandle->height() / 2.0));
421 QPointF touchPoint = initialTouchPos;
423 // Pressing causes the Showable to be stretched by hintDisplacement pixels
424 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
425 tryCompare([&](){ return parentItem->height(); }, hintDisplacement);
427 // Releasing causes the Showable to shrink back to 0 pixels.
428 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
429 tryCompare([&](){ return parentItem->height(); }, 0.0);
431 QCOMPARE(parentItem->property("shown").toBool(), false);
435 Regression test for LP#1269022: https://bugs.launchpad.net/unity8/+bug/1269022
438 2) wait for hint portion to appear
439 3) slowly drag handle, only a few pixels
445 Handle will momentarily move back to zero position, then back down to the
446 hint displacement location.
448void tst_DragHandle::hintingAnimation_dontRestartAfterFinishedAndStillPressed()
450 DirectionalDragArea *dragHandle = fetchAndSetupDragHandle("downwardsDragHandle");
451 QQuickItem *parentItem = dragHandle->parentItem();
452 qreal hintDisplacement = 100.0;
454 // enable hinting animations
455 m_view->rootObject()->setProperty("hintDisplacement", QVariant(hintDisplacement));
456 m_view->rootObject()->setProperty("stretch", QVariant(true));
458 QCOMPARE(parentItem->height(), 0.0);
460 QPointF initialTouchPos = dragHandle->mapToScene(
461 QPointF(dragHandle->width() / 2.0, dragHandle->height() / 2.0));
462 QPointF touchPoint = initialTouchPos;
464 // Pressing causes the Showable to be stretched by hintDisplacement pixels
465 const int touchId = 0;
466 QTest::touchEvent(m_view, m_device).press(touchId, touchPoint.toPoint());
467 tryCompare([&](){ return parentItem->height(); }, hintDisplacement);
470 QSignalSpy parentHeightChangedSpy(parentItem, &QQuickItem::heightChanged);
472 drag(touchPoint, QPointF(0.0, -1.0) /*dragDirectionVector*/, 15 /*distance*/, 3 /*numSteps*/);
474 // Give some time for animations to run, if any
477 // parentItem height shouldn't have changed at all
478 QVERIFY(parentHeightChangedSpy.isEmpty());
481QTEST_MAIN(tst_DragHandle)
483#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