1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28#include <qtest.h>
29#include <QtTest/QSignalSpy>
30#include <QtGui/QStyleHints>
31#include <QtQml/qqmlengine.h>
32#include <QtQml/qqmlcomponent.h>
33#include <QtQuick/qquickview.h>
34#include <private/qquickflickable_p.h>
35#include <private/qquickflickable_p_p.h>
36#include <private/qquickmousearea_p.h>
37#include <private/qquicktransition_p.h>
38#include <private/qqmlvaluetype_p.h>
39#include <math.h>
40#include "../../shared/util.h"
41#include "../shared/geometrytestutil.h"
42#include "../shared/viewtestutil.h"
43#include "../shared/visualtestutil.h"
44
45#include <qpa/qwindowsysteminterface.h>
46
47using namespace QQuickViewTestUtil;
48using namespace QQuickVisualTestUtil;
49
50// an abstract Slider which only handles touch events
51class TouchDragArea : public QQuickItem
52{
53 Q_OBJECT
54 Q_PROPERTY(QPointF pos READ pos NOTIFY posChanged)
55 Q_PROPERTY(bool active READ active NOTIFY activeChanged)
56 Q_PROPERTY(bool keepMouseGrab READ keepMouseGrab WRITE setKeepMouseGrab NOTIFY keepMouseGrabChanged)
57 Q_PROPERTY(bool keepTouchGrab READ keepTouchGrab WRITE setKeepTouchGrab NOTIFY keepTouchGrabChanged)
58
59public:
60 TouchDragArea(QQuickItem *parent = nullptr)
61 : QQuickItem(parent)
62 , touchEvents(0)
63 , touchUpdates(0)
64 , touchReleases(0)
65 , ungrabs(0)
66 , m_active(false)
67 {
68#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
69 setAcceptTouchEvents(true);
70#else
71 setAcceptedMouseButtons(Qt::LeftButton); // not really, but we want touch events
72#endif
73 }
74
75 QPointF pos() const { return m_pos; }
76
77 bool active() const { return m_active; }
78
79 void setKeepMouseGrab(bool keepMouseGrab)
80 {
81 QQuickItem::setKeepMouseGrab(keepMouseGrab);
82 emit keepMouseGrabChanged();
83 }
84
85 void setKeepTouchGrab(bool keepTouchGrab)
86 {
87 QQuickItem::setKeepTouchGrab(keepTouchGrab);
88 emit keepTouchGrabChanged();
89 }
90
91 int touchEvents;
92 int touchUpdates;
93 int touchReleases;
94 int ungrabs;
95 QVector<Qt::TouchPointState> touchPointStates;
96
97protected:
98 void touchEvent(QTouchEvent *ev) override
99 {
100 QCOMPARE(ev->touchPoints().count(), 1);
101 auto touchpoint = ev->touchPoints().first();
102 switch (touchpoint.state()) {
103 case Qt::TouchPointPressed:
104 QVERIFY(!m_active);
105 m_active = true;
106 emit activeChanged();
107 grabTouchPoints(ids: QVector<int>() << touchpoint.id());
108 break;
109 case Qt::TouchPointMoved:
110 ++touchUpdates;
111 break;
112 case Qt::TouchPointReleased:
113 QVERIFY(m_active);
114 m_active = false;
115 ++touchReleases;
116 emit activeChanged();
117 case Qt::TouchPointStationary:
118 break;
119 }
120 touchPointStates << touchpoint.state();
121 ++touchEvents;
122 m_pos = touchpoint.pos();
123 emit posChanged();
124 }
125
126 void touchUngrabEvent() override
127 {
128 ++ungrabs;
129 QVERIFY(m_active);
130 emit ungrabbed();
131 m_active = false;
132 emit activeChanged();
133 }
134
135signals:
136 void ungrabbed();
137 void posChanged();
138 void keepMouseGrabChanged();
139 void keepTouchGrabChanged();
140 void activeChanged();
141
142private:
143 QPointF m_pos;
144 bool m_active;
145};
146
147class tst_qquickflickable : public QQmlDataTest
148{
149 Q_OBJECT
150public:
151 tst_qquickflickable()
152 : touchDevice(QTest::createTouchDevice())
153 {}
154
155private slots:
156 void initTestCase() override;
157 void create();
158 void horizontalViewportSize();
159 void verticalViewportSize();
160 void visibleAreaRatiosUpdate();
161 void properties();
162 void boundsBehavior();
163 void rebound();
164 void maximumFlickVelocity();
165 void flickDeceleration();
166 void pressDelay();
167 void nestedPressDelay();
168 void filterReplayedPress();
169 void nestedClickThenFlick();
170 void flickableDirection();
171 void resizeContent();
172 void returnToBounds();
173 void returnToBounds_data();
174 void wheel();
175 void trackpad();
176 void movingAndFlicking();
177 void movingAndFlicking_data();
178 void movingAndDragging();
179 void movingAndDragging_data();
180 void flickOnRelease();
181 void pressWhileFlicking();
182 void disabled();
183 void flickVelocity();
184 void margins();
185 void cancelOnHide();
186 void cancelOnMouseGrab();
187 void clickAndDragWhenTransformed();
188 void flickTwiceUsingTouches();
189 void nestedStopAtBounds();
190 void nestedStopAtBounds_data();
191 void stopAtBounds();
192 void stopAtBounds_data();
193 void nestedMouseAreaUsingTouch();
194 void nestedSliderUsingTouch();
195 void nestedSliderUsingTouch_data();
196 void pressDelayWithLoader();
197 void movementFromProgrammaticFlick();
198 void cleanup();
199 void contentSize();
200 void ratios_smallContent();
201 void contentXYNotTruncatedToInt();
202 void keepGrab();
203 void overshoot();
204 void overshoot_data();
205 void overshoot_reentrant();
206 void synchronousDrag_data();
207 void synchronousDrag();
208 void visibleAreaBinding();
209
210private:
211 void flickWithTouch(QQuickWindow *window, const QPoint &from, const QPoint &to);
212 QTouchDevice *touchDevice;
213};
214
215void tst_qquickflickable::initTestCase()
216{
217 QQmlDataTest::initTestCase();
218 qmlRegisterType<TouchDragArea>(uri: "Test",versionMajor: 1,versionMinor: 0,qmlName: "TouchDragArea");
219}
220
221void tst_qquickflickable::cleanup()
222{
223 QVERIFY(QGuiApplication::topLevelWindows().isEmpty());
224}
225
226void tst_qquickflickable::create()
227{
228 QQmlEngine engine;
229 QQmlComponent c(&engine, testFileUrl(fileName: "flickable01.qml"));
230 QQuickFlickable *obj = qobject_cast<QQuickFlickable*>(object: c.createWithInitialProperties(initialProperties: {{"setRebound", false}}));
231
232 QVERIFY(obj != nullptr);
233 QCOMPARE(obj->isAtXBeginning(), true);
234 QCOMPARE(obj->isAtXEnd(), false);
235 QCOMPARE(obj->isAtYBeginning(), true);
236 QCOMPARE(obj->isAtYEnd(), false);
237 QCOMPARE(obj->contentX(), 0.);
238 QCOMPARE(obj->contentY(), 0.);
239
240 QCOMPARE(obj->horizontalVelocity(), 0.);
241 QCOMPARE(obj->verticalVelocity(), 0.);
242
243 QCOMPARE(obj->isInteractive(), true);
244 QCOMPARE(obj->boundsBehavior(), QQuickFlickable::DragAndOvershootBounds);
245 QCOMPARE(obj->pressDelay(), 0);
246 QCOMPARE(obj->maximumFlickVelocity(), 2500.);
247
248 delete obj;
249}
250
251void tst_qquickflickable::horizontalViewportSize()
252{
253 QQmlEngine engine;
254 QQmlComponent c(&engine, testFileUrl(fileName: "flickable02.qml"));
255 QQuickFlickable *obj = qobject_cast<QQuickFlickable*>(object: c.create());
256
257 QVERIFY(obj != nullptr);
258 QCOMPARE(obj->contentWidth(), 800.);
259 QCOMPARE(obj->contentHeight(), 300.);
260 QCOMPARE(obj->isAtXBeginning(), true);
261 QCOMPARE(obj->isAtXEnd(), false);
262 QCOMPARE(obj->isAtYBeginning(), true);
263 QCOMPARE(obj->isAtYEnd(), false);
264
265 delete obj;
266}
267
268void tst_qquickflickable::verticalViewportSize()
269{
270 QQmlEngine engine;
271 QQmlComponent c(&engine, testFileUrl(fileName: "flickable03.qml"));
272 QQuickFlickable *obj = qobject_cast<QQuickFlickable*>(object: c.create());
273
274 QVERIFY(obj != nullptr);
275 QCOMPARE(obj->contentWidth(), 200.);
276 QCOMPARE(obj->contentHeight(), 6000.);
277 QCOMPARE(obj->isAtXBeginning(), true);
278 QCOMPARE(obj->isAtXEnd(), false);
279 QCOMPARE(obj->isAtYBeginning(), true);
280 QCOMPARE(obj->isAtYEnd(), false);
281
282 delete obj;
283}
284
285void tst_qquickflickable::visibleAreaRatiosUpdate()
286{
287 QQmlEngine engine;
288 QQmlComponent c(&engine, testFileUrl(fileName: "ratios.qml"));
289 QQuickItem *obj = qobject_cast<QQuickItem*>(object: c.create());
290
291 QVERIFY(obj != nullptr);
292 // check initial ratio values
293 QCOMPARE(obj->property("heightRatioIs").toDouble(), obj->property("heightRatioShould").toDouble());
294 QCOMPARE(obj->property("widthRatioIs").toDouble(), obj->property("widthRatioShould").toDouble());
295 // change flickable geometry so that flicking is enabled (content size > flickable size)
296 obj->setProperty(name: "forceNoFlicking", value: false);
297 QCOMPARE(obj->property("heightRatioIs").toDouble(), obj->property("heightRatioShould").toDouble());
298 QCOMPARE(obj->property("widthRatioIs").toDouble(), obj->property("widthRatioShould").toDouble());
299 // change flickable geometry so that flicking is disabled (content size == flickable size)
300 obj->setProperty(name: "forceNoFlicking", value: true);
301 QCOMPARE(obj->property("heightRatioIs").toDouble(), obj->property("heightRatioShould").toDouble());
302 QCOMPARE(obj->property("widthRatioIs").toDouble(), obj->property("widthRatioShould").toDouble());
303
304 delete obj;
305}
306
307void tst_qquickflickable::properties()
308{
309 QQmlEngine engine;
310 QQmlComponent c(&engine, testFileUrl(fileName: "flickable04.qml"));
311 QQuickFlickable *obj = qobject_cast<QQuickFlickable*>(object: c.create());
312
313 QVERIFY(obj != nullptr);
314 QCOMPARE(obj->isInteractive(), false);
315 QCOMPARE(obj->boundsBehavior(), QQuickFlickable::StopAtBounds);
316 QCOMPARE(obj->pressDelay(), 200);
317 QCOMPARE(obj->maximumFlickVelocity(), 2000.);
318
319 QVERIFY(!obj->property("ok").toBool());
320 QMetaObject::invokeMethod(obj, member: "check");
321 QVERIFY(obj->property("ok").toBool());
322
323 delete obj;
324}
325
326void tst_qquickflickable::boundsBehavior()
327{
328 QQmlEngine engine;
329 QQmlComponent component(&engine);
330 component.setData("import QtQuick 2.0; Flickable { boundsBehavior: Flickable.StopAtBounds }", baseUrl: QUrl::fromLocalFile(localfile: ""));
331 QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(object: component.create());
332 QSignalSpy spy(flickable, SIGNAL(boundsBehaviorChanged()));
333
334 QVERIFY(flickable);
335 QCOMPARE(flickable->boundsBehavior(), QQuickFlickable::StopAtBounds);
336
337 flickable->setBoundsBehavior(QQuickFlickable::DragAndOvershootBounds);
338 QCOMPARE(flickable->boundsBehavior(), QQuickFlickable::DragAndOvershootBounds);
339 QCOMPARE(spy.count(),1);
340 flickable->setBoundsBehavior(QQuickFlickable::DragAndOvershootBounds);
341 QCOMPARE(spy.count(),1);
342
343 flickable->setBoundsBehavior(QQuickFlickable::DragOverBounds);
344 QCOMPARE(flickable->boundsBehavior(), QQuickFlickable::DragOverBounds);
345 QCOMPARE(spy.count(),2);
346 flickable->setBoundsBehavior(QQuickFlickable::DragOverBounds);
347 QCOMPARE(spy.count(),2);
348
349 flickable->setBoundsBehavior(QQuickFlickable::StopAtBounds);
350 QCOMPARE(flickable->boundsBehavior(), QQuickFlickable::StopAtBounds);
351 QCOMPARE(spy.count(),3);
352 flickable->setBoundsBehavior(QQuickFlickable::StopAtBounds);
353 QCOMPARE(spy.count(),3);
354
355 flickable->setBoundsBehavior(QQuickFlickable::OvershootBounds);
356 QCOMPARE(flickable->boundsBehavior(), QQuickFlickable::OvershootBounds);
357 QCOMPARE(spy.count(),4);
358 flickable->setBoundsBehavior(QQuickFlickable::OvershootBounds);
359 QCOMPARE(spy.count(),4);
360
361 delete flickable;
362}
363
364void tst_qquickflickable::rebound()
365{
366 QScopedPointer<QQuickView> window(new QQuickView);
367 window->setSource(testFileUrl(fileName: "rebound.qml"));
368 QTRY_COMPARE(window->status(), QQuickView::Ready);
369 QQuickViewTestUtil::centerOnScreen(window: window.data());
370 QQuickViewTestUtil::moveMouseAway(window: window.data());
371
372 window->show();
373 QVERIFY(QTest::qWaitForWindowActive(window.data()));
374 QVERIFY(window->rootObject() != nullptr);
375
376 QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(object: window->rootObject());
377 QVERIFY(flickable != nullptr);
378
379 QQuickTransition *rebound = window->rootObject()->findChild<QQuickTransition*>(aName: "rebound");
380 QVERIFY(rebound);
381 QSignalSpy reboundSpy(rebound, SIGNAL(runningChanged()));
382
383 QSignalSpy movementStartedSpy(flickable, SIGNAL(movementStarted()));
384 QSignalSpy movementEndedSpy(flickable, SIGNAL(movementEnded()));
385 QSignalSpy vMoveSpy(flickable, SIGNAL(movingVerticallyChanged()));
386 QSignalSpy hMoveSpy(flickable, SIGNAL(movingHorizontallyChanged()));
387
388 // flick and test the transition is run
389 flick(window: window.data(), from: QPoint(20,20), to: QPoint(120,120), duration: 200);
390
391 QTRY_COMPARE(window->rootObject()->property("transitionsStarted").toInt(), 2);
392 QCOMPARE(hMoveSpy.count(), 1);
393 QCOMPARE(vMoveSpy.count(), 1);
394 QCOMPARE(movementStartedSpy.count(), 1);
395 QCOMPARE(movementEndedSpy.count(), 0);
396 QVERIFY(rebound->running());
397
398 QTRY_VERIFY(!flickable->isMoving());
399 QCOMPARE(flickable->contentX(), 0.0);
400 QCOMPARE(flickable->contentY(), 0.0);
401
402 QCOMPARE(hMoveSpy.count(), 2);
403 QCOMPARE(vMoveSpy.count(), 2);
404 QCOMPARE(movementStartedSpy.count(), 1);
405 QCOMPARE(movementEndedSpy.count(), 1);
406 QCOMPARE(window->rootObject()->property("transitionsStarted").toInt(), 2);
407 QVERIFY(!rebound->running());
408 QCOMPARE(reboundSpy.count(), 2);
409
410 hMoveSpy.clear();
411 vMoveSpy.clear();
412 movementStartedSpy.clear();
413 movementEndedSpy.clear();
414 window->rootObject()->setProperty(name: "transitionsStarted", value: 0);
415 window->rootObject()->setProperty(name: "transitionsFinished", value: 0);
416
417 // flick and trigger the transition multiple times
418 // (moving signals are emitted as soon as the first transition starts)
419 flick(window: window.data(), from: QPoint(20,20), to: QPoint(120,120), duration: 50); // both x and y will bounce back
420 flick(window: window.data(), from: QPoint(20,120), to: QPoint(120,20), duration: 50); // only x will bounce back
421
422 QVERIFY(flickable->isMoving());
423 QTRY_VERIFY(window->rootObject()->property("transitionsStarted").toInt() >= 1);
424 QCOMPARE(hMoveSpy.count(), 1);
425 QCOMPARE(vMoveSpy.count(), 1);
426 QCOMPARE(movementStartedSpy.count(), 1);
427
428 QTRY_VERIFY(!flickable->isMoving());
429 QCOMPARE(flickable->contentX(), 0.0);
430
431 // moving started/stopped signals should only have been emitted once,
432 // and when they are, all transitions should have finished
433 QCOMPARE(hMoveSpy.count(), 2);
434 QCOMPARE(vMoveSpy.count(), 2);
435 QCOMPARE(movementStartedSpy.count(), 1);
436 QCOMPARE(movementEndedSpy.count(), 1);
437
438 hMoveSpy.clear();
439 vMoveSpy.clear();
440 movementStartedSpy.clear();
441 movementEndedSpy.clear();
442 window->rootObject()->setProperty(name: "transitionsStarted", value: 0);
443 window->rootObject()->setProperty(name: "transitionsFinished", value: 0);
444
445 // disable and the default transition should run
446 // (i.e. moving but transition->running = false)
447 window->rootObject()->setProperty(name: "transitionEnabled", value: false);
448
449 flick(window: window.data(), from: QPoint(20,20), to: QPoint(120,120), duration: 200);
450 QCOMPARE(window->rootObject()->property("transitionsStarted").toInt(), 0);
451 QCOMPARE(hMoveSpy.count(), 1);
452 QCOMPARE(vMoveSpy.count(), 1);
453 QCOMPARE(movementStartedSpy.count(), 1);
454 QCOMPARE(movementEndedSpy.count(), 0);
455
456 QTRY_VERIFY(!flickable->isMoving());
457 QCOMPARE(hMoveSpy.count(), 2);
458 QCOMPARE(vMoveSpy.count(), 2);
459 QCOMPARE(movementStartedSpy.count(), 1);
460 QCOMPARE(movementEndedSpy.count(), 1);
461 QCOMPARE(window->rootObject()->property("transitionsStarted").toInt(), 0);
462}
463
464void tst_qquickflickable::maximumFlickVelocity()
465{
466 QQmlEngine engine;
467 QQmlComponent component(&engine);
468 component.setData("import QtQuick 2.0; Flickable { maximumFlickVelocity: 1.0; }", baseUrl: QUrl::fromLocalFile(localfile: ""));
469 QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(object: component.create());
470 QSignalSpy spy(flickable, SIGNAL(maximumFlickVelocityChanged()));
471
472 QVERIFY(flickable);
473 QCOMPARE(flickable->maximumFlickVelocity(), 1.0);
474
475 flickable->setMaximumFlickVelocity(2.0);
476 QCOMPARE(flickable->maximumFlickVelocity(), 2.0);
477 QCOMPARE(spy.count(),1);
478 flickable->setMaximumFlickVelocity(2.0);
479 QCOMPARE(spy.count(),1);
480
481 delete flickable;
482}
483
484void tst_qquickflickable::flickDeceleration()
485{
486 QQmlEngine engine;
487 QQmlComponent component(&engine);
488 component.setData("import QtQuick 2.0; Flickable { flickDeceleration: 1.0; }", baseUrl: QUrl::fromLocalFile(localfile: ""));
489 QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(object: component.create());
490 QSignalSpy spy(flickable, SIGNAL(flickDecelerationChanged()));
491
492 QVERIFY(flickable);
493 QCOMPARE(flickable->flickDeceleration(), 1.0);
494
495 flickable->setFlickDeceleration(2.0);
496 QCOMPARE(flickable->flickDeceleration(), 2.0);
497 QCOMPARE(spy.count(),1);
498 flickable->setFlickDeceleration(2.0);
499 QCOMPARE(spy.count(),1);
500
501 delete flickable;
502}
503
504void tst_qquickflickable::pressDelay()
505{
506 QScopedPointer<QQuickView> window(new QQuickView);
507 window->setSource(testFileUrl(fileName: "pressDelay.qml"));
508 QTRY_COMPARE(window->status(), QQuickView::Ready);
509 QQuickViewTestUtil::centerOnScreen(window: window.data());
510 QQuickViewTestUtil::moveMouseAway(window: window.data());
511 window->show();
512 QVERIFY(QTest::qWaitForWindowActive(window.data()));
513 QVERIFY(window->rootObject() != nullptr);
514
515 QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(object: window->rootObject());
516 QSignalSpy spy(flickable, SIGNAL(pressDelayChanged()));
517
518 QVERIFY(flickable);
519 QCOMPARE(flickable->pressDelay(), 100);
520
521 flickable->setPressDelay(200);
522 QCOMPARE(flickable->pressDelay(), 200);
523 QCOMPARE(spy.count(),1);
524 flickable->setPressDelay(200);
525 QCOMPARE(spy.count(),1);
526
527 QQuickItem *mouseArea = window->rootObject()->findChild<QQuickItem*>(aName: "mouseArea");
528 QSignalSpy clickedSpy(mouseArea, SIGNAL(clicked(QQuickMouseEvent*)));
529
530 moveAndPress(window: window.data(), position: QPoint(150, 150));
531
532 // The press should not occur immediately
533 QVERIFY(!mouseArea->property("pressed").toBool());
534
535 // But, it should occur eventually
536 QTRY_VERIFY(mouseArea->property("pressed").toBool());
537
538 QCOMPARE(clickedSpy.count(),0);
539
540 // On release the clicked signal should be emitted
541 QTest::mouseRelease(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(150, 150));
542 QCOMPARE(clickedSpy.count(),1);
543
544 // Press and release position should match
545 QCOMPARE(flickable->property("pressX").toReal(), flickable->property("releaseX").toReal());
546 QCOMPARE(flickable->property("pressY").toReal(), flickable->property("releaseY").toReal());
547
548
549 // Test a quick tap within the pressDelay timeout
550 clickedSpy.clear();
551 moveAndPress(window: window.data(), position: QPoint(180, 180));
552
553 // The press should not occur immediately
554 QVERIFY(!mouseArea->property("pressed").toBool());
555
556 QCOMPARE(clickedSpy.count(),0);
557
558 // On release the press, release and clicked signal should be emitted
559 QTest::mouseRelease(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(180, 180));
560 QCOMPARE(clickedSpy.count(),1);
561
562 // Press and release position should match
563 QCOMPARE(flickable->property("pressX").toReal(), flickable->property("releaseX").toReal());
564 QCOMPARE(flickable->property("pressY").toReal(), flickable->property("releaseY").toReal());
565
566
567 // QTBUG-31168
568 moveAndPress(window: window.data(), position: QPoint(150, 110));
569
570 // The press should not occur immediately
571 QVERIFY(!mouseArea->property("pressed").toBool());
572
573 QTest::mouseMove(window: window.data(), pos: QPoint(150, 190));
574
575 // As we moved pass the drag threshold, we should never receive the press
576 QVERIFY(!mouseArea->property("pressed").toBool());
577 QTRY_VERIFY(!mouseArea->property("pressed").toBool());
578
579 // On release the clicked signal should *not* be emitted
580 QTest::mouseRelease(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(150, 190));
581 QCOMPARE(clickedSpy.count(),1);
582}
583
584// QTBUG-17361
585void tst_qquickflickable::nestedPressDelay()
586{
587 QScopedPointer<QQuickView> window(new QQuickView);
588 window->setSource(testFileUrl(fileName: "nestedPressDelay.qml"));
589 QTRY_COMPARE(window->status(), QQuickView::Ready);
590 QQuickViewTestUtil::centerOnScreen(window: window.data());
591 QQuickViewTestUtil::moveMouseAway(window: window.data());
592 window->show();
593 QVERIFY(QTest::qWaitForWindowActive(window.data()));
594 QVERIFY(window->rootObject() != nullptr);
595
596 QQuickFlickable *outer = qobject_cast<QQuickFlickable*>(object: window->rootObject());
597 QVERIFY(outer != nullptr);
598
599 QQuickFlickable *inner = window->rootObject()->findChild<QQuickFlickable*>(aName: "innerFlickable");
600 QVERIFY(inner != nullptr);
601
602 moveAndPress(window: window.data(), position: QPoint(150, 150));
603 // the MouseArea is not pressed immediately
604 QVERIFY(!outer->property("pressed").toBool());
605 QVERIFY(!inner->property("pressed").toBool());
606
607 // The inner pressDelay will prevail (50ms, vs. 10sec)
608 // QTRY_VERIFY() has 5sec timeout, so will timeout well within 10sec.
609 QTRY_VERIFY(outer->property("pressed").toBool());
610
611 QTest::mouseMove(window: window.data(), pos: QPoint(130, 150));
612 QTest::mouseMove(window: window.data(), pos: QPoint(110, 150));
613 QTest::mouseMove(window: window.data(), pos: QPoint(90, 150));
614
615 QVERIFY(!outer->property("moving").toBool());
616 QVERIFY(!outer->property("dragging").toBool());
617 QVERIFY(inner->property("moving").toBool());
618 QVERIFY(inner->property("dragging").toBool());
619
620 QTest::mouseRelease(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(150, 150));
621
622 QVERIFY(!inner->property("dragging").toBool());
623 QTRY_VERIFY(!inner->property("moving").toBool());
624
625 // Dragging inner Flickable should work
626 moveAndPress(window: window.data(), position: QPoint(80, 150));
627 // the MouseArea is not pressed immediately
628 QVERIFY(!outer->property("pressed").toBool());
629 QVERIFY(!inner->property("pressed").toBool());
630
631 QTest::mouseMove(window: window.data(), pos: QPoint(60, 150));
632 QTest::mouseMove(window: window.data(), pos: QPoint(40, 150));
633 QTest::mouseMove(window: window.data(), pos: QPoint(20, 150));
634
635 QVERIFY(inner->property("moving").toBool());
636 QVERIFY(inner->property("dragging").toBool());
637 QVERIFY(!outer->property("moving").toBool());
638 QVERIFY(!outer->property("dragging").toBool());
639
640 QTest::mouseRelease(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(20, 150));
641
642 QVERIFY(!inner->property("dragging").toBool());
643 QTRY_VERIFY(!inner->property("moving").toBool());
644
645 // Dragging the MouseArea in the inner Flickable should move the inner Flickable
646 moveAndPress(window: window.data(), position: QPoint(150, 150));
647 // the MouseArea is not pressed immediately
648 QVERIFY(!outer->property("pressed").toBool());
649
650 QTest::mouseMove(window: window.data(), pos: QPoint(130, 150));
651 QTest::mouseMove(window: window.data(), pos: QPoint(110, 150));
652 QTest::mouseMove(window: window.data(), pos: QPoint(90, 150));
653
654 QVERIFY(!outer->property("moving").toBool());
655 QVERIFY(!outer->property("dragging").toBool());
656 QVERIFY(inner->property("moving").toBool());
657 QVERIFY(inner->property("dragging").toBool());
658
659 QTest::mouseRelease(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(90, 150));
660
661 QVERIFY(!inner->property("dragging").toBool());
662 QTRY_VERIFY(!inner->property("moving").toBool());
663}
664
665void tst_qquickflickable::filterReplayedPress()
666{
667 QScopedPointer<QQuickView> window(new QQuickView);
668 window->setSource(testFileUrl(fileName: "nestedPressDelay.qml"));
669 QTRY_COMPARE(window->status(), QQuickView::Ready);
670 QQuickViewTestUtil::centerOnScreen(window: window.data());
671 QQuickViewTestUtil::moveMouseAway(window: window.data());
672 window->show();
673 QVERIFY(QTest::qWaitForWindowActive(window.data()));
674 QVERIFY(window->rootObject() != nullptr);
675
676 QQuickFlickable *outer = qobject_cast<QQuickFlickable*>(object: window->rootObject());
677 QVERIFY(outer != nullptr);
678
679 QQuickFlickable *inner = window->rootObject()->findChild<QQuickFlickable*>(aName: "innerFlickable");
680 QVERIFY(inner != nullptr);
681
682 QQuickItem *filteringMouseArea = outer->findChild<QQuickItem *>(aName: "filteringMouseArea");
683 QVERIFY(filteringMouseArea);
684
685 moveAndPress(window: window.data(), position: QPoint(150, 150));
686 // the MouseArea filtering the Flickable is pressed immediately.
687 QCOMPARE(filteringMouseArea->property("pressed").toBool(), true);
688
689 // Some event causes the mouse area to set keepMouseGrab.
690 filteringMouseArea->setKeepMouseGrab(true);
691 QCOMPARE(filteringMouseArea->keepMouseGrab(), true);
692
693 // The inner pressDelay will prevail (50ms, vs. 10sec)
694 // QTRY_VERIFY() has 5sec timeout, so will timeout well within 10sec.
695 QTRY_VERIFY(outer->property("pressed").toBool());
696
697 // The replayed press event isn't delivered to parent items of the
698 // flickable with the press delay, and the state of the parent mouse
699 // area is therefore unaffected.
700 QCOMPARE(filteringMouseArea->property("pressed").toBool(), true);
701 QCOMPARE(filteringMouseArea->keepMouseGrab(), true);
702
703 QTest::mouseRelease(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(150, 150));
704}
705
706
707// QTBUG-37316
708void tst_qquickflickable::nestedClickThenFlick()
709{
710 QScopedPointer<QQuickView> window(new QQuickView);
711 window->setSource(testFileUrl(fileName: "nestedClickThenFlick.qml"));
712 QTRY_COMPARE(window->status(), QQuickView::Ready);
713 QQuickViewTestUtil::centerOnScreen(window: window.data());
714 QQuickViewTestUtil::moveMouseAway(window: window.data());
715 window->show();
716 QVERIFY(QTest::qWaitForWindowActive(window.data()));
717 QVERIFY(window->rootObject() != nullptr);
718
719 QQuickFlickable *outer = qobject_cast<QQuickFlickable*>(object: window->rootObject());
720 QVERIFY(outer != nullptr);
721
722 QQuickFlickable *inner = window->rootObject()->findChild<QQuickFlickable*>(aName: "innerFlickable");
723 QVERIFY(inner != nullptr);
724
725 moveAndPress(window: window.data(), position: QPoint(150, 150));
726
727 // the MouseArea is not pressed immediately
728 QVERIFY(!outer->property("pressed").toBool());
729 QTRY_VERIFY(outer->property("pressed").toBool());
730
731 QTest::mouseRelease(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(150, 150));
732
733 QVERIFY(!outer->property("pressed").toBool());
734
735 // Dragging inner Flickable should work
736 moveAndPress(window: window.data(), position: QPoint(80, 150));
737 // the MouseArea is not pressed immediately
738
739 QVERIFY(!outer->property("pressed").toBool());
740
741 QTest::mouseMove(window: window.data(), pos: QPoint(80, 148));
742 QTest::mouseMove(window: window.data(), pos: QPoint(80, 140));
743 QTest::mouseMove(window: window.data(), pos: QPoint(80, 120));
744 QTest::mouseMove(window: window.data(), pos: QPoint(80, 100));
745
746 QVERIFY(!outer->property("moving").toBool());
747 QVERIFY(inner->property("moving").toBool());
748
749 QTest::mouseRelease(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(80, 100));
750}
751
752void tst_qquickflickable::flickableDirection()
753{
754 QQmlEngine engine;
755 QQmlComponent component(&engine);
756 component.setData("import QtQuick 2.0; Flickable { flickableDirection: Flickable.VerticalFlick; }", baseUrl: QUrl::fromLocalFile(localfile: ""));
757 QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(object: component.create());
758 QSignalSpy spy(flickable, SIGNAL(flickableDirectionChanged()));
759
760 QVERIFY(flickable);
761 QCOMPARE(flickable->flickableDirection(), QQuickFlickable::VerticalFlick);
762
763 flickable->setFlickableDirection(QQuickFlickable::HorizontalAndVerticalFlick);
764 QCOMPARE(flickable->flickableDirection(), QQuickFlickable::HorizontalAndVerticalFlick);
765 QCOMPARE(spy.count(),1);
766
767 flickable->setFlickableDirection(QQuickFlickable::AutoFlickDirection);
768 QCOMPARE(flickable->flickableDirection(), QQuickFlickable::AutoFlickDirection);
769 QCOMPARE(spy.count(),2);
770
771 flickable->setFlickableDirection(QQuickFlickable::HorizontalFlick);
772 QCOMPARE(flickable->flickableDirection(), QQuickFlickable::HorizontalFlick);
773 QCOMPARE(spy.count(),3);
774
775 flickable->setFlickableDirection(QQuickFlickable::HorizontalFlick);
776 QCOMPARE(flickable->flickableDirection(), QQuickFlickable::HorizontalFlick);
777 QCOMPARE(spy.count(),3);
778
779 delete flickable;
780}
781
782// QtQuick 1.1
783void tst_qquickflickable::resizeContent()
784{
785 QQmlEngine engine;
786 QQmlComponent c(&engine, testFileUrl(fileName: "resize.qml"));
787 QQuickItem *root = qobject_cast<QQuickItem*>(object: c.createWithInitialProperties(initialProperties: {{"setRebound", false}}));
788 QQuickFlickable *obj = findItem<QQuickFlickable>(parent: root, objectName: "flick");
789
790 QVERIFY(obj != nullptr);
791 QCOMPARE(obj->contentX(), 0.);
792 QCOMPARE(obj->contentY(), 0.);
793 QCOMPARE(obj->contentWidth(), 300.);
794 QCOMPARE(obj->contentHeight(), 300.);
795
796 QQuickFlickablePrivate *fp = QQuickFlickablePrivate::get(o: obj);
797 QSizeChangeListener sizeListener(fp->contentItem);
798
799 QMetaObject::invokeMethod(obj: root, member: "resizeContent");
800 for (const QSize sizeOnGeometryChanged : sizeListener) {
801 // Check that we have the correct size on all signals
802 QCOMPARE(sizeOnGeometryChanged, QSize(600, 600));
803 }
804
805 QCOMPARE(obj->contentX(), 100.);
806 QCOMPARE(obj->contentY(), 100.);
807 QCOMPARE(obj->contentWidth(), 600.);
808 QCOMPARE(obj->contentHeight(), 600.);
809
810 delete root;
811}
812
813void tst_qquickflickable::returnToBounds()
814{
815 QFETCH(bool, setRebound);
816
817 QScopedPointer<QQuickView> window(new QQuickView);
818
819 window->setInitialProperties({{"setRebound", setRebound}});
820 window->setSource(testFileUrl(fileName: "resize.qml"));
821 window->show();
822 QVERIFY(QTest::qWaitForWindowActive(window.data()));
823 QVERIFY(window->rootObject() != nullptr);
824 QQuickFlickable *obj = findItem<QQuickFlickable>(parent: window->rootObject(), objectName: "flick");
825
826 QQuickTransition *rebound = window->rootObject()->findChild<QQuickTransition*>(aName: "rebound");
827 QVERIFY(rebound);
828 QSignalSpy reboundSpy(rebound, SIGNAL(runningChanged()));
829
830 QVERIFY(obj != nullptr);
831 QCOMPARE(obj->contentX(), 0.);
832 QCOMPARE(obj->contentY(), 0.);
833 QCOMPARE(obj->contentWidth(), 300.);
834 QCOMPARE(obj->contentHeight(), 300.);
835
836 obj->setContentX(100);
837 obj->setContentY(400);
838 QTRY_COMPARE(obj->contentX(), 100.);
839 QTRY_COMPARE(obj->contentY(), 400.);
840
841 QMetaObject::invokeMethod(obj: window->rootObject(), member: "returnToBounds");
842
843 if (setRebound)
844 QTRY_VERIFY(rebound->running());
845
846 QTRY_COMPARE(obj->contentX(), 0.);
847 QTRY_COMPARE(obj->contentY(), 0.);
848
849 QVERIFY(!rebound->running());
850 QCOMPARE(reboundSpy.count(), setRebound ? 2 : 0);
851}
852
853void tst_qquickflickable::returnToBounds_data()
854{
855 QTest::addColumn<bool>(name: "setRebound");
856
857 QTest::newRow(dataTag: "with bounds transition") << true;
858 QTest::newRow(dataTag: "with bounds transition") << false;
859}
860
861void tst_qquickflickable::wheel()
862{
863 QScopedPointer<QQuickView> window(new QQuickView);
864 window->setSource(testFileUrl(fileName: "wheel.qml"));
865 window->show();
866 QVERIFY(QTest::qWaitForWindowActive(window.data()));
867 QVERIFY(window->rootObject() != nullptr);
868
869 QQuickFlickable *flick = window->rootObject()->findChild<QQuickFlickable*>(aName: "flick");
870 QVERIFY(flick != nullptr);
871 QQuickFlickablePrivate *fp = QQuickFlickablePrivate::get(o: flick);
872 QSignalSpy moveEndSpy(flick, SIGNAL(movementEnded()));
873 quint64 timestamp = 10;
874
875 // test a vertical flick
876 {
877 QPoint pos(200, 200);
878 QWheelEvent event(pos, window->mapToGlobal(pos), QPoint(), QPoint(0,-120),
879 Qt::NoButton, Qt::NoModifier, Qt::NoScrollPhase, false);
880 event.setAccepted(false);
881 event.setTimestamp(timestamp);
882 QGuiApplication::sendEvent(receiver: window.data(), event: &event);
883 }
884
885 QTRY_VERIFY(flick->contentY() > 0);
886 QCOMPARE(flick->contentX(), qreal(0));
887
888 QTRY_COMPARE(moveEndSpy.count(), 1);
889 QCOMPARE(fp->velocityTimeline.isActive(), false);
890 QCOMPARE(fp->timeline.isActive(), false);
891 QTest::qWait(ms: 50); // make sure that onContentYChanged won't sneak in again
892 timestamp += 50;
893 QCOMPARE(flick->property("movementsAfterEnd").value<int>(), 0); // QTBUG-55886
894
895 // get ready to test horizontal flick
896 flick->setContentY(0); // which triggers movementEnded again
897 flick->setProperty(name: "movementsAfterEnd", value: 0);
898 flick->setProperty(name: "ended", value: false);
899 QCOMPARE(flick->contentY(), qreal(0));
900
901 // test a horizontal flick
902 {
903 QPoint pos(200, 200);
904 QWheelEvent event(pos, window->mapToGlobal(pos), QPoint(), QPoint(-120,0),
905 Qt::NoButton, Qt::NoModifier, Qt::NoScrollPhase, false);
906 event.setAccepted(false);
907 event.setTimestamp(timestamp);
908 QGuiApplication::sendEvent(receiver: window.data(), event: &event);
909 }
910
911 QTRY_VERIFY(flick->contentX() > 0);
912 QCOMPARE(flick->contentY(), qreal(0));
913 QTRY_COMPARE(moveEndSpy.count(), 2);
914 QCOMPARE(fp->velocityTimeline.isActive(), false);
915 QCOMPARE(fp->timeline.isActive(), false);
916 QTest::qWait(ms: 50); // make sure that onContentXChanged won't sneak in again
917 QCOMPARE(flick->property("movementsAfterEnd").value<int>(), 0); // QTBUG-55886
918}
919
920void tst_qquickflickable::trackpad()
921{
922 QScopedPointer<QQuickView> window(new QQuickView);
923 window->setSource(testFileUrl(fileName: "wheel.qml"));
924 window->show();
925 QVERIFY(QTest::qWaitForWindowActive(window.data()));
926 QVERIFY(window->rootObject() != nullptr);
927
928 QQuickFlickable *flick = window->rootObject()->findChild<QQuickFlickable*>(aName: "flick");
929 QVERIFY(flick != nullptr);
930 QSignalSpy moveEndSpy(flick, SIGNAL(movementEnded()));
931 QPoint pos(200, 200);
932 quint64 timestamp = 10;
933
934 {
935 QWheelEvent event(pos, window->mapToGlobal(pos), QPoint(0,-100), QPoint(0,-120),
936 Qt::NoButton, Qt::NoModifier, Qt::ScrollBegin, false);
937 event.setAccepted(false);
938 event.setTimestamp(timestamp++);
939 QGuiApplication::sendEvent(receiver: window.data(), event: &event);
940 }
941
942 QTRY_VERIFY(flick->contentY() > 0);
943 QCOMPARE(flick->contentX(), qreal(0));
944
945 flick->setContentY(0);
946 QCOMPARE(flick->contentY(), qreal(0));
947
948 {
949 QWheelEvent event(pos, window->mapToGlobal(pos), QPoint(-100,0), QPoint(-120,0),
950 Qt::NoButton, Qt::NoModifier, Qt::ScrollUpdate, false);
951 event.setAccepted(false);
952 event.setTimestamp(timestamp++);
953 QGuiApplication::sendEvent(receiver: window.data(), event: &event);
954 }
955
956 QTRY_VERIFY(flick->contentX() > 0);
957 QCOMPARE(flick->contentY(), qreal(0));
958
959 {
960 QWheelEvent event(pos, window->mapToGlobal(pos), QPoint(0,0), QPoint(0,0),
961 Qt::NoButton, Qt::NoModifier, Qt::ScrollEnd, false);
962 event.setAccepted(false);
963 event.setTimestamp(timestamp++);
964 QGuiApplication::sendEvent(receiver: window.data(), event: &event);
965 }
966
967 QTRY_COMPARE(moveEndSpy.count(), 1); // QTBUG-55871
968 QCOMPARE(flick->property("movementsAfterEnd").value<int>(), 0); // QTBUG-55886
969}
970
971void tst_qquickflickable::movingAndFlicking_data()
972{
973 QTest::addColumn<bool>(name: "verticalEnabled");
974 QTest::addColumn<bool>(name: "horizontalEnabled");
975 QTest::addColumn<QPoint>(name: "flickToWithoutSnapBack");
976 QTest::addColumn<QPoint>(name: "flickToWithSnapBack");
977
978 QTest::newRow(dataTag: "vertical")
979 << true << false
980 << QPoint(50, 100)
981 << QPoint(50, 300);
982
983 QTest::newRow(dataTag: "horizontal")
984 << false << true
985 << QPoint(-50, 200)
986 << QPoint(150, 200);
987
988 QTest::newRow(dataTag: "both")
989 << true << true
990 << QPoint(-50, 100)
991 << QPoint(150, 300);
992}
993
994void tst_qquickflickable::movingAndFlicking()
995{
996 QFETCH(bool, verticalEnabled);
997 QFETCH(bool, horizontalEnabled);
998 QFETCH(QPoint, flickToWithoutSnapBack);
999 QFETCH(QPoint, flickToWithSnapBack);
1000
1001 const QPoint flickFrom(50, 200); // centre
1002
1003 QScopedPointer<QQuickView> window(new QQuickView);
1004 window->setSource(testFileUrl(fileName: "flickable03.qml"));
1005 QTRY_COMPARE(window->status(), QQuickView::Ready);
1006 QQuickViewTestUtil::centerOnScreen(window: window.data());
1007 QQuickViewTestUtil::moveMouseAway(window: window.data());
1008 window->show();
1009 QVERIFY(QTest::qWaitForWindowActive(window.data()));
1010 QVERIFY(window->rootObject() != nullptr);
1011
1012 QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(object: window->rootObject());
1013 QVERIFY(flickable != nullptr);
1014
1015 QSignalSpy vMoveSpy(flickable, SIGNAL(movingVerticallyChanged()));
1016 QSignalSpy hMoveSpy(flickable, SIGNAL(movingHorizontallyChanged()));
1017 QSignalSpy moveSpy(flickable, SIGNAL(movingChanged()));
1018 QSignalSpy vFlickSpy(flickable, SIGNAL(flickingVerticallyChanged()));
1019 QSignalSpy hFlickSpy(flickable, SIGNAL(flickingHorizontallyChanged()));
1020 QSignalSpy flickSpy(flickable, SIGNAL(flickingChanged()));
1021
1022 QSignalSpy moveStartSpy(flickable, SIGNAL(movementStarted()));
1023 QSignalSpy moveEndSpy(flickable, SIGNAL(movementEnded()));
1024 QSignalSpy flickStartSpy(flickable, SIGNAL(flickStarted()));
1025 QSignalSpy flickEndSpy(flickable, SIGNAL(flickEnded()));
1026
1027 // do a flick that keeps the view within the bounds
1028 flick(window: window.data(), from: flickFrom, to: flickToWithoutSnapBack, duration: 200);
1029
1030 QTRY_VERIFY(flickable->isMoving());
1031 QCOMPARE(flickable->isMovingHorizontally(), horizontalEnabled);
1032 QCOMPARE(flickable->isMovingVertically(), verticalEnabled);
1033 QVERIFY(flickable->isFlicking());
1034 QCOMPARE(flickable->isFlickingHorizontally(), horizontalEnabled);
1035 QCOMPARE(flickable->isFlickingVertically(), verticalEnabled);
1036 // contentX/contentY are either unchanged, or moving is true when the value changed.
1037 QCOMPARE(flickable->property("movingInContentX").value<bool>(), true);
1038 QCOMPARE(flickable->property("movingInContentY").value<bool>(), true);
1039
1040 QCOMPARE(moveSpy.count(), 1);
1041 QCOMPARE(vMoveSpy.count(), verticalEnabled ? 1 : 0);
1042 QCOMPARE(hMoveSpy.count(), horizontalEnabled ? 1 : 0);
1043 QCOMPARE(flickSpy.count(), 1);
1044 QCOMPARE(vFlickSpy.count(), verticalEnabled ? 1 : 0);
1045 QCOMPARE(hFlickSpy.count(), horizontalEnabled ? 1 : 0);
1046
1047 QCOMPARE(moveStartSpy.count(), 1);
1048 QCOMPARE(flickStartSpy.count(), 1);
1049
1050 // wait for any motion to end
1051 QTRY_VERIFY(!flickable->isMoving());
1052
1053 QVERIFY(!flickable->isMovingHorizontally());
1054 QVERIFY(!flickable->isMovingVertically());
1055 QVERIFY(!flickable->isFlicking());
1056 QVERIFY(!flickable->isFlickingHorizontally());
1057 QVERIFY(!flickable->isFlickingVertically());
1058
1059 QCOMPARE(moveSpy.count(), 2);
1060 QCOMPARE(vMoveSpy.count(), verticalEnabled ? 2 : 0);
1061 QCOMPARE(hMoveSpy.count(), horizontalEnabled ? 2 : 0);
1062 QCOMPARE(flickSpy.count(), 2);
1063 QCOMPARE(vFlickSpy.count(), verticalEnabled ? 2 : 0);
1064 QCOMPARE(hFlickSpy.count(), horizontalEnabled ? 2 : 0);
1065
1066 QCOMPARE(moveStartSpy.count(), 1);
1067 QCOMPARE(moveEndSpy.count(), 1);
1068 QCOMPARE(flickStartSpy.count(), 1);
1069 QCOMPARE(flickEndSpy.count(), 1);
1070
1071 // Stop on a full pixel after user interaction
1072 if (verticalEnabled)
1073 QCOMPARE(flickable->contentY(), (qreal)qRound(flickable->contentY()));
1074 if (horizontalEnabled)
1075 QCOMPARE(flickable->contentX(), (qreal)qRound(flickable->contentX()));
1076
1077 // clear for next flick
1078 vMoveSpy.clear(); hMoveSpy.clear(); moveSpy.clear();
1079 vFlickSpy.clear(); hFlickSpy.clear(); flickSpy.clear();
1080 moveStartSpy.clear(); moveEndSpy.clear();
1081 flickStartSpy.clear(); flickEndSpy.clear();
1082
1083 // do a flick that flicks the view out of bounds
1084 flickable->setContentX(0);
1085 flickable->setContentY(0);
1086 QTRY_VERIFY(!flickable->isMoving());
1087 flick(window: window.data(), from: flickFrom, to: flickToWithSnapBack, duration: 10);
1088
1089 QTRY_VERIFY(flickable->isMoving());
1090 QCOMPARE(flickable->isMovingHorizontally(), horizontalEnabled);
1091 QCOMPARE(flickable->isMovingVertically(), verticalEnabled);
1092 QVERIFY(flickable->isFlicking());
1093 QCOMPARE(flickable->isFlickingHorizontally(), horizontalEnabled);
1094 QCOMPARE(flickable->isFlickingVertically(), verticalEnabled);
1095
1096 QCOMPARE(moveSpy.count(), 1);
1097 QCOMPARE(vMoveSpy.count(), verticalEnabled ? 1 : 0);
1098 QCOMPARE(hMoveSpy.count(), horizontalEnabled ? 1 : 0);
1099 QCOMPARE(flickSpy.count(), 1);
1100 QCOMPARE(vFlickSpy.count(), verticalEnabled ? 1 : 0);
1101 QCOMPARE(hFlickSpy.count(), horizontalEnabled ? 1 : 0);
1102
1103 QCOMPARE(moveStartSpy.count(), 1);
1104 QCOMPARE(moveEndSpy.count(), 0);
1105 QCOMPARE(flickStartSpy.count(), 1);
1106 QCOMPARE(flickEndSpy.count(), 0);
1107
1108 // wait for any motion to end
1109 QTRY_VERIFY(!flickable->isMoving());
1110
1111 QVERIFY(!flickable->isMovingHorizontally());
1112 QVERIFY(!flickable->isMovingVertically());
1113 QVERIFY(!flickable->isFlicking());
1114 QVERIFY(!flickable->isFlickingHorizontally());
1115 QVERIFY(!flickable->isFlickingVertically());
1116
1117 QCOMPARE(moveSpy.count(), 2);
1118 QCOMPARE(vMoveSpy.count(), verticalEnabled ? 2 : 0);
1119 QCOMPARE(hMoveSpy.count(), horizontalEnabled ? 2 : 0);
1120 QCOMPARE(flickSpy.count(), 2);
1121 QCOMPARE(vFlickSpy.count(), verticalEnabled ? 2 : 0);
1122 QCOMPARE(hFlickSpy.count(), horizontalEnabled ? 2 : 0);
1123
1124 QCOMPARE(moveStartSpy.count(), 1);
1125 QCOMPARE(moveEndSpy.count(), 1);
1126 QCOMPARE(flickStartSpy.count(), 1);
1127 QCOMPARE(flickEndSpy.count(), 1);
1128
1129 QCOMPARE(flickable->contentX(), 0.0);
1130 QCOMPARE(flickable->contentY(), 0.0);
1131}
1132
1133
1134void tst_qquickflickable::movingAndDragging_data()
1135{
1136 QTest::addColumn<bool>(name: "verticalEnabled");
1137 QTest::addColumn<bool>(name: "horizontalEnabled");
1138 QTest::addColumn<QPoint>(name: "moveByWithoutSnapBack");
1139 QTest::addColumn<QPoint>(name: "moveByWithSnapBack");
1140
1141 QTest::newRow(dataTag: "vertical")
1142 << true << false
1143 << QPoint(0, -10)
1144 << QPoint(0, 20);
1145
1146 QTest::newRow(dataTag: "horizontal")
1147 << false << true
1148 << QPoint(-10, 0)
1149 << QPoint(20, 0);
1150
1151 QTest::newRow(dataTag: "both")
1152 << true << true
1153 << QPoint(-10, -10)
1154 << QPoint(20, 20);
1155}
1156
1157void tst_qquickflickable::movingAndDragging()
1158{
1159 QFETCH(bool, verticalEnabled);
1160 QFETCH(bool, horizontalEnabled);
1161 QFETCH(QPoint, moveByWithoutSnapBack);
1162 QFETCH(QPoint, moveByWithSnapBack);
1163
1164 const QPoint moveFrom(50, 200); // centre
1165
1166 QScopedPointer<QQuickView> window(new QQuickView);
1167 window->setSource(testFileUrl(fileName: "flickable03.qml"));
1168 QTRY_COMPARE(window->status(), QQuickView::Ready);
1169 QQuickViewTestUtil::centerOnScreen(window: window.data());
1170 QQuickViewTestUtil::moveMouseAway(window: window.data());
1171 window->show();
1172 QVERIFY(QTest::qWaitForWindowActive(window.data()));
1173 QVERIFY(window->rootObject() != nullptr);
1174
1175 QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(object: window->rootObject());
1176 QVERIFY(flickable != nullptr);
1177
1178 QSignalSpy vDragSpy(flickable, SIGNAL(draggingVerticallyChanged()));
1179 QSignalSpy hDragSpy(flickable, SIGNAL(draggingHorizontallyChanged()));
1180 QSignalSpy dragSpy(flickable, SIGNAL(draggingChanged()));
1181 QSignalSpy vMoveSpy(flickable, SIGNAL(movingVerticallyChanged()));
1182 QSignalSpy hMoveSpy(flickable, SIGNAL(movingHorizontallyChanged()));
1183 QSignalSpy moveSpy(flickable, SIGNAL(movingChanged()));
1184
1185 QSignalSpy dragStartSpy(flickable, SIGNAL(dragStarted()));
1186 QSignalSpy dragEndSpy(flickable, SIGNAL(dragEnded()));
1187 QSignalSpy moveStartSpy(flickable, SIGNAL(movementStarted()));
1188 QSignalSpy moveEndSpy(flickable, SIGNAL(movementEnded()));
1189
1190 // start the drag
1191 moveAndPress(window: window.data(), position: moveFrom);
1192 QTest::mouseMove(window: window.data(), pos: moveFrom + moveByWithoutSnapBack);
1193 QTest::mouseMove(window: window.data(), pos: moveFrom + moveByWithoutSnapBack*2);
1194 QTest::mouseMove(window: window.data(), pos: moveFrom + moveByWithoutSnapBack*3);
1195
1196 QTRY_VERIFY(flickable->isMoving());
1197 QCOMPARE(flickable->isMovingHorizontally(), horizontalEnabled);
1198 QCOMPARE(flickable->isMovingVertically(), verticalEnabled);
1199 QVERIFY(flickable->isDragging());
1200 QCOMPARE(flickable->isDraggingHorizontally(), horizontalEnabled);
1201 QCOMPARE(flickable->isDraggingVertically(), verticalEnabled);
1202 // contentX/contentY are either unchanged, or moving and dragging are true when the value changes.
1203 QCOMPARE(flickable->property("movingInContentX").value<bool>(), true);
1204 QCOMPARE(flickable->property("movingInContentY").value<bool>(), true);
1205 QCOMPARE(flickable->property("draggingInContentX").value<bool>(), true);
1206 QCOMPARE(flickable->property("draggingInContentY").value<bool>(), true);
1207
1208 QCOMPARE(moveSpy.count(), 1);
1209 QCOMPARE(vMoveSpy.count(), verticalEnabled ? 1 : 0);
1210 QCOMPARE(hMoveSpy.count(), horizontalEnabled ? 1 : 0);
1211 QCOMPARE(dragSpy.count(), 1);
1212 QCOMPARE(vDragSpy.count(), verticalEnabled ? 1 : 0);
1213 QCOMPARE(hDragSpy.count(), horizontalEnabled ? 1 : 0);
1214
1215 QCOMPARE(moveStartSpy.count(), 1);
1216 QCOMPARE(dragStartSpy.count(), 1);
1217
1218 QTest::mouseRelease(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: moveFrom + moveByWithoutSnapBack*3);
1219
1220 QVERIFY(!flickable->isDragging());
1221 QVERIFY(!flickable->isDraggingHorizontally());
1222 QVERIFY(!flickable->isDraggingVertically());
1223 QCOMPARE(dragSpy.count(), 2);
1224 QCOMPARE(vDragSpy.count(), verticalEnabled ? 2 : 0);
1225 QCOMPARE(hDragSpy.count(), horizontalEnabled ? 2 : 0);
1226 QCOMPARE(dragStartSpy.count(), 1);
1227 QCOMPARE(dragEndSpy.count(), 1);
1228 // Don't test whether moving finished because a flick could occur
1229
1230 // wait for any motion to end
1231 QTRY_VERIFY(!flickable->isMoving());
1232
1233 QVERIFY(!flickable->isMovingHorizontally());
1234 QVERIFY(!flickable->isMovingVertically());
1235 QVERIFY(!flickable->isDragging());
1236 QVERIFY(!flickable->isDraggingHorizontally());
1237 QVERIFY(!flickable->isDraggingVertically());
1238
1239 QCOMPARE(dragSpy.count(), 2);
1240 QCOMPARE(vDragSpy.count(), verticalEnabled ? 2 : 0);
1241 QCOMPARE(hDragSpy.count(), horizontalEnabled ? 2 : 0);
1242 QCOMPARE(moveSpy.count(), 2);
1243 QCOMPARE(vMoveSpy.count(), verticalEnabled ? 2 : 0);
1244 QCOMPARE(hMoveSpy.count(), horizontalEnabled ? 2 : 0);
1245
1246 QCOMPARE(dragStartSpy.count(), 1);
1247 QCOMPARE(dragEndSpy.count(), 1);
1248 QCOMPARE(moveStartSpy.count(), 1);
1249 QCOMPARE(moveEndSpy.count(), 1);
1250
1251 // Stop on a full pixel after user interaction
1252 if (verticalEnabled)
1253 QCOMPARE(flickable->contentY(), (qreal)qRound(flickable->contentY()));
1254 if (horizontalEnabled)
1255 QCOMPARE(flickable->contentX(), (qreal)qRound(flickable->contentX()));
1256
1257 // clear for next drag
1258 vMoveSpy.clear(); hMoveSpy.clear(); moveSpy.clear();
1259 vDragSpy.clear(); hDragSpy.clear(); dragSpy.clear();
1260 moveStartSpy.clear(); moveEndSpy.clear();
1261 dragStartSpy.clear(); dragEndSpy.clear();
1262
1263 // do a drag that drags the view out of bounds
1264 flickable->setContentX(0);
1265 flickable->setContentY(0);
1266 QTRY_VERIFY(!flickable->isMoving());
1267 moveAndPress(window: window.data(), position: moveFrom);
1268 QTest::mouseMove(window: window.data(), pos: moveFrom + moveByWithSnapBack);
1269 QTest::mouseMove(window: window.data(), pos: moveFrom + moveByWithSnapBack*2);
1270 QTest::mouseMove(window: window.data(), pos: moveFrom + moveByWithSnapBack*3);
1271
1272 QVERIFY(flickable->isMoving());
1273 QCOMPARE(flickable->isMovingHorizontally(), horizontalEnabled);
1274 QCOMPARE(flickable->isMovingVertically(), verticalEnabled);
1275 QVERIFY(flickable->isDragging());
1276 QCOMPARE(flickable->isDraggingHorizontally(), horizontalEnabled);
1277 QCOMPARE(flickable->isDraggingVertically(), verticalEnabled);
1278
1279 QCOMPARE(moveSpy.count(), 1);
1280 QCOMPARE(vMoveSpy.count(), verticalEnabled ? 1 : 0);
1281 QCOMPARE(hMoveSpy.count(), horizontalEnabled ? 1 : 0);
1282 QCOMPARE(dragSpy.count(), 1);
1283 QCOMPARE(vDragSpy.count(), verticalEnabled ? 1 : 0);
1284 QCOMPARE(hDragSpy.count(), horizontalEnabled ? 1 : 0);
1285
1286 QCOMPARE(moveStartSpy.count(), 1);
1287 QCOMPARE(moveEndSpy.count(), 0);
1288 QCOMPARE(dragStartSpy.count(), 1);
1289 QCOMPARE(dragEndSpy.count(), 0);
1290
1291 QTest::mouseRelease(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: moveFrom + moveByWithSnapBack*3);
1292
1293 // should now start snapping back to bounds (moving but not dragging)
1294 QVERIFY(flickable->isMoving());
1295 QCOMPARE(flickable->isMovingHorizontally(), horizontalEnabled);
1296 QCOMPARE(flickable->isMovingVertically(), verticalEnabled);
1297 QVERIFY(!flickable->isDragging());
1298 QVERIFY(!flickable->isDraggingHorizontally());
1299 QVERIFY(!flickable->isDraggingVertically());
1300
1301 QCOMPARE(moveSpy.count(), 1);
1302 QCOMPARE(vMoveSpy.count(), verticalEnabled ? 1 : 0);
1303 QCOMPARE(hMoveSpy.count(), horizontalEnabled ? 1 : 0);
1304 QCOMPARE(dragSpy.count(), 2);
1305 QCOMPARE(vDragSpy.count(), verticalEnabled ? 2 : 0);
1306 QCOMPARE(hDragSpy.count(), horizontalEnabled ? 2 : 0);
1307
1308 QCOMPARE(moveStartSpy.count(), 1);
1309 QCOMPARE(moveEndSpy.count(), 0);
1310
1311 // wait for any motion to end
1312 QTRY_VERIFY(!flickable->isMoving());
1313
1314 QVERIFY(!flickable->isMovingHorizontally());
1315 QVERIFY(!flickable->isMovingVertically());
1316 QVERIFY(!flickable->isDragging());
1317 QVERIFY(!flickable->isDraggingHorizontally());
1318 QVERIFY(!flickable->isDraggingVertically());
1319
1320 QCOMPARE(moveSpy.count(), 2);
1321 QCOMPARE(vMoveSpy.count(), verticalEnabled ? 2 : 0);
1322 QCOMPARE(hMoveSpy.count(), horizontalEnabled ? 2 : 0);
1323 QCOMPARE(dragSpy.count(), 2);
1324 QCOMPARE(vDragSpy.count(), verticalEnabled ? 2 : 0);
1325 QCOMPARE(hDragSpy.count(), horizontalEnabled ? 2 : 0);
1326
1327 QCOMPARE(moveStartSpy.count(), 1);
1328 QCOMPARE(moveEndSpy.count(), 1);
1329 QCOMPARE(dragStartSpy.count(), 1);
1330 QCOMPARE(dragEndSpy.count(), 1);
1331
1332 QCOMPARE(flickable->contentX(), 0.0);
1333 QCOMPARE(flickable->contentY(), 0.0);
1334}
1335
1336void tst_qquickflickable::flickOnRelease()
1337{
1338 QScopedPointer<QQuickView> window(new QQuickView);
1339 window->setSource(testFileUrl(fileName: "flickable03.qml"));
1340 window->show();
1341 QVERIFY(QTest::qWaitForWindowActive(window.data()));
1342 QVERIFY(window->rootObject() != nullptr);
1343
1344 QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(object: window->rootObject());
1345 QVERIFY(flickable != nullptr);
1346
1347 // Vertical with a quick press-move-release: should cause a flick in release.
1348 QSignalSpy vFlickSpy(flickable, SIGNAL(flickingVerticallyChanged()));
1349 // Use something that generates a huge velocity just to make it testable.
1350 // In practice this feature matters on touchscreen devices where the
1351 // underlying drivers will hopefully provide a pre-calculated velocity
1352 // (based on more data than what the UI gets), thus making this use case
1353 // working even with small movements.
1354 moveAndPress(window: window.data(), position: QPoint(50, 300));
1355 QTest::mouseMove(window: window.data(), pos: QPoint(50, 10), delay: 10);
1356 QTest::mouseRelease(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(50, 10), delay: 10);
1357
1358 QCOMPARE(vFlickSpy.count(), 1);
1359
1360 // wait for any motion to end
1361 QTRY_VERIFY(!flickable->isMoving());
1362
1363 // Stop on a full pixel after user interaction
1364 QCOMPARE(flickable->contentY(), (qreal)qRound(flickable->contentY()));
1365}
1366
1367void tst_qquickflickable::pressWhileFlicking()
1368{
1369 QScopedPointer<QQuickView> window(new QQuickView);
1370 window->setSource(testFileUrl(fileName: "flickable03.qml"));
1371 QTRY_COMPARE(window->status(), QQuickView::Ready);
1372 QQuickViewTestUtil::centerOnScreen(window: window.data());
1373 QQuickViewTestUtil::moveMouseAway(window: window.data());
1374 window->show();
1375 QVERIFY(QTest::qWaitForWindowActive(window.data()));
1376 QVERIFY(window->rootObject() != nullptr);
1377
1378 QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(object: window->rootObject());
1379 QVERIFY(flickable != nullptr);
1380
1381 QSignalSpy vMoveSpy(flickable, SIGNAL(movingVerticallyChanged()));
1382 QSignalSpy hMoveSpy(flickable, SIGNAL(movingHorizontallyChanged()));
1383 QSignalSpy moveSpy(flickable, SIGNAL(movingChanged()));
1384 QSignalSpy hFlickSpy(flickable, SIGNAL(flickingHorizontallyChanged()));
1385 QSignalSpy vFlickSpy(flickable, SIGNAL(flickingVerticallyChanged()));
1386 QSignalSpy flickSpy(flickable, SIGNAL(flickingChanged()));
1387
1388 // flick then press while it is still moving
1389 // flicking == false, moving == true;
1390 flick(window: window.data(), from: QPoint(20,190), to: QPoint(20, 50), duration: 200);
1391 QVERIFY(flickable->verticalVelocity() > 0.0);
1392 QTRY_VERIFY(flickable->isFlicking());
1393 QVERIFY(flickable->isFlickingVertically());
1394 QVERIFY(!flickable->isFlickingHorizontally());
1395 QVERIFY(flickable->isMoving());
1396 QVERIFY(flickable->isMovingVertically());
1397 QVERIFY(!flickable->isMovingHorizontally());
1398 QCOMPARE(vMoveSpy.count(), 1);
1399 QCOMPARE(hMoveSpy.count(), 0);
1400 QCOMPARE(moveSpy.count(), 1);
1401 QCOMPARE(vFlickSpy.count(), 1);
1402 QCOMPARE(hFlickSpy.count(), 0);
1403 QCOMPARE(flickSpy.count(), 1);
1404
1405 QTest::mousePress(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(20, 50));
1406 QTRY_VERIFY(!flickable->isFlicking());
1407 QVERIFY(!flickable->isFlickingVertically());
1408 QVERIFY(flickable->isMoving());
1409 QVERIFY(flickable->isMovingVertically());
1410
1411 QTest::mouseRelease(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(20,50));
1412 QVERIFY(!flickable->isFlicking());
1413 QVERIFY(!flickable->isFlickingVertically());
1414 QTRY_VERIFY(!flickable->isMoving());
1415 QVERIFY(!flickable->isMovingVertically());
1416 // Stop on a full pixel after user interaction
1417 QCOMPARE(flickable->contentX(), (qreal)qRound(flickable->contentX()));
1418}
1419
1420void tst_qquickflickable::disabled()
1421{
1422 QScopedPointer<QQuickView> window(new QQuickView);
1423 window->setSource(testFileUrl(fileName: "disabled.qml"));
1424 window->show();
1425 QVERIFY(QTest::qWaitForWindowActive(window.data()));
1426 QVERIFY(window->rootObject() != nullptr);
1427
1428 QQuickFlickable *flick = window->rootObject()->findChild<QQuickFlickable*>(aName: "flickable");
1429 QVERIFY(flick != nullptr);
1430
1431 moveAndPress(window: window.data(), position: QPoint(50, 90));
1432
1433 QTest::mouseMove(window: window.data(), pos: QPoint(50, 80));
1434 QTest::mouseMove(window: window.data(), pos: QPoint(50, 70));
1435 QTest::mouseMove(window: window.data(), pos: QPoint(50, 60));
1436
1437 QVERIFY(!flick->isMoving());
1438
1439 QTest::mouseRelease(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(50, 60));
1440
1441 // verify that mouse clicks on other elements still work (QTBUG-20584)
1442 moveAndPress(window: window.data(), position: QPoint(50, 10));
1443 QTest::mouseRelease(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(50, 10));
1444
1445 QTRY_VERIFY(window->rootObject()->property("clicked").toBool());
1446}
1447
1448void tst_qquickflickable::flickVelocity()
1449{
1450 QScopedPointer<QQuickView> window(new QQuickView);
1451 window->setSource(testFileUrl(fileName: "flickable03.qml"));
1452 QTRY_COMPARE(window->status(), QQuickView::Ready);
1453 QQuickViewTestUtil::centerOnScreen(window: window.data());
1454 QQuickViewTestUtil::moveMouseAway(window: window.data());
1455 window->show();
1456 QVERIFY(QTest::qWaitForWindowActive(window.data()));
1457 QVERIFY(window->rootObject() != nullptr);
1458
1459 QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(object: window->rootObject());
1460 QVERIFY(flickable != nullptr);
1461
1462 // flick up
1463 flick(window: window.data(), from: QPoint(20,190), to: QPoint(20, 50), duration: 200);
1464 QVERIFY(flickable->verticalVelocity() > 0.0);
1465 QTRY_COMPARE(flickable->verticalVelocity(), 0.0);
1466
1467 // flick down
1468 flick(window: window.data(), from: QPoint(20,10), to: QPoint(20, 140), duration: 200);
1469 QTRY_VERIFY(flickable->verticalVelocity() < 0.0);
1470 QTRY_COMPARE(flickable->verticalVelocity(), 0.0);
1471
1472#ifdef Q_OS_MAC
1473 QSKIP("boost doesn't work on OS X");
1474 return;
1475#endif
1476
1477 // Flick multiple times and verify that flick acceleration is applied.
1478 QQuickFlickablePrivate *fp = QQuickFlickablePrivate::get(o: flickable);
1479 bool boosted = false;
1480 for (int i = 0; i < 6; ++i) {
1481 flick(window: window.data(), from: QPoint(20,390), to: QPoint(20, 50), duration: 100);
1482 boosted |= fp->flickBoost > 1.0;
1483 }
1484 QVERIFY(boosted);
1485
1486 // Flick in opposite direction -> boost cancelled.
1487 flick(window: window.data(), from: QPoint(20,10), to: QPoint(20, 340), duration: 200);
1488 QTRY_VERIFY(flickable->verticalVelocity() < 0.0);
1489 QCOMPARE(fp->flickBoost, 1.0);
1490}
1491
1492void tst_qquickflickable::margins()
1493{
1494 QScopedPointer<QQuickView> window(new QQuickView);
1495 window->setSource(testFileUrl(fileName: "margins.qml"));
1496 QTRY_COMPARE(window->status(), QQuickView::Ready);
1497 QQuickViewTestUtil::centerOnScreen(window: window.data());
1498 QQuickViewTestUtil::moveMouseAway(window: window.data());
1499 window->setTitle(QTest::currentTestFunction());
1500 window->show();
1501 QVERIFY(QTest::qWaitForWindowActive(window.data()));
1502 QQuickItem *root = window->rootObject();
1503 QVERIFY(root);
1504 QQuickFlickable *obj = qobject_cast<QQuickFlickable*>(object: root);
1505 QVERIFY(obj != nullptr);
1506
1507 // starting state
1508 QCOMPARE(obj->contentX(), -40.);
1509 QCOMPARE(obj->contentY(), -20.);
1510 QCOMPARE(obj->contentWidth(), 1600.);
1511 QCOMPARE(obj->contentHeight(), 600.);
1512 QCOMPARE(obj->originX(), 0.);
1513 QCOMPARE(obj->originY(), 0.);
1514
1515 // Reduce left margin
1516 obj->setLeftMargin(30);
1517 QTRY_COMPARE(obj->contentX(), -30.);
1518
1519 // Reduce top margin
1520 obj->setTopMargin(20);
1521 QTRY_COMPARE(obj->contentY(), -20.);
1522
1523 // position to the far right, including margin
1524 obj->setContentX(1600 + 50 - obj->width());
1525 obj->returnToBounds();
1526 QTRY_COMPARE(obj->contentX(), 1600. + 50. - obj->width());
1527
1528 // position beyond the far right, including margin
1529 obj->setContentX(1600 + 50 - obj->width() + 1.);
1530 obj->returnToBounds();
1531 QTRY_COMPARE(obj->contentX(), 1600. + 50. - obj->width());
1532
1533 // Reduce right margin
1534 obj->setRightMargin(40);
1535 QTRY_COMPARE(obj->contentX(), 1600. + 40. - obj->width());
1536 QCOMPARE(obj->contentWidth(), 1600.);
1537
1538 // position to the far bottom, including margin
1539 obj->setContentY(600 + 30 - obj->height());
1540 obj->returnToBounds();
1541 QTRY_COMPARE(obj->contentY(), 600. + 30. - obj->height());
1542
1543 // position beyond the far bottom, including margin
1544 obj->setContentY(600 + 30 - obj->height() + 1.);
1545 obj->returnToBounds();
1546 QTRY_COMPARE(obj->contentY(), 600. + 30. - obj->height());
1547
1548 // Reduce bottom margin
1549 obj->setBottomMargin(20);
1550 QTRY_COMPARE(obj->contentY(), 600. + 20. - obj->height());
1551 QCOMPARE(obj->contentHeight(), 600.);
1552
1553 delete root;
1554}
1555
1556void tst_qquickflickable::cancelOnHide()
1557{
1558 QScopedPointer<QQuickView> window(new QQuickView);
1559 window->setSource(testFileUrl(fileName: "hide.qml"));
1560 QTRY_COMPARE(window->status(), QQuickView::Ready);
1561 QQuickViewTestUtil::centerOnScreen(window: window.data());
1562 QQuickViewTestUtil::moveMouseAway(window: window.data());
1563 window->show();
1564 QVERIFY(QTest::qWaitForWindowActive(window.data()));
1565 QVERIFY(window->rootObject());
1566
1567 QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(object: window->rootObject());
1568 QVERIFY(flickable);
1569
1570 QTest::mouseDClick(window: window.data(), button: Qt::LeftButton);
1571 QVERIFY(!flickable->isVisible());
1572 QVERIFY(!QQuickFlickablePrivate::get(flickable)->pressed);
1573}
1574
1575void tst_qquickflickable::cancelOnMouseGrab()
1576{
1577 QScopedPointer<QQuickView> window(new QQuickView);
1578 window->setSource(testFileUrl(fileName: "cancel.qml"));
1579 QTRY_COMPARE(window->status(), QQuickView::Ready);
1580 QQuickViewTestUtil::centerOnScreen(window: window.data());
1581 QQuickViewTestUtil::moveMouseAway(window: window.data());
1582 window->show();
1583 QVERIFY(QTest::qWaitForWindowActive(window.data()));
1584 QVERIFY(window->rootObject() != nullptr);
1585
1586 QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(object: window->rootObject());
1587 QVERIFY(flickable != nullptr);
1588
1589 moveAndPress(window: window.data(), position: QPoint(10, 10));
1590 // drag out of bounds
1591 QTest::mouseMove(window: window.data(), pos: QPoint(50, 50));
1592 QTest::mouseMove(window: window.data(), pos: QPoint(100, 100));
1593 QTest::mouseMove(window: window.data(), pos: QPoint(150, 150));
1594
1595 QVERIFY(flickable->contentX() != 0);
1596 QVERIFY(flickable->contentY() != 0);
1597 QVERIFY(flickable->isMoving());
1598 QVERIFY(flickable->isDragging());
1599
1600 // grabbing mouse will cancel flickable interaction.
1601 QQuickItem *item = window->rootObject()->findChild<QQuickItem*>(aName: "row");
1602 item->grabMouse();
1603
1604 QTRY_COMPARE(flickable->contentX(), 0.);
1605 QTRY_COMPARE(flickable->contentY(), 0.);
1606 QTRY_VERIFY(!flickable->isMoving());
1607 QTRY_VERIFY(!flickable->isDragging());
1608
1609 moveAndRelease(window: window.data(), position: QPoint(50, 10));
1610
1611}
1612
1613void tst_qquickflickable::clickAndDragWhenTransformed()
1614{
1615 QScopedPointer<QQuickView> view(new QQuickView);
1616 view->setSource(testFileUrl(fileName: "transformedFlickable.qml"));
1617 QTRY_COMPARE(view->status(), QQuickView::Ready);
1618 QQuickViewTestUtil::centerOnScreen(window: view.data());
1619 QQuickViewTestUtil::moveMouseAway(window: view.data());
1620 view->show();
1621 QVERIFY(QTest::qWaitForWindowActive(view.data()));
1622 QVERIFY(view->rootObject() != nullptr);
1623
1624 QQuickFlickable *flickable = view->rootObject()->findChild<QQuickFlickable*>(aName: "flickable");
1625 QVERIFY(flickable != nullptr);
1626
1627 // click outside child rect
1628 moveAndPress(window: view.data(), position: QPoint(190, 190));
1629 QTRY_COMPARE(flickable->property("itemPressed").toBool(), false);
1630 QTest::mouseRelease(window: view.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(190, 190));
1631
1632 // click inside child rect
1633 moveAndPress(window: view.data(), position: QPoint(200, 200));
1634 QTRY_COMPARE(flickable->property("itemPressed").toBool(), true);
1635 QTest::mouseRelease(window: view.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(200, 200));
1636
1637 const int threshold = qApp->styleHints()->startDragDistance();
1638
1639 // drag outside bounds
1640 moveAndPress(window: view.data(), position: QPoint(160, 160));
1641 QTest::qWait(ms: 10);
1642 QTest::mouseMove(window: view.data(), pos: QPoint(160 + threshold * 2, 160));
1643 QTest::mouseMove(window: view.data(), pos: QPoint(160 + threshold * 3, 160));
1644 QCOMPARE(flickable->isDragging(), false);
1645 QCOMPARE(flickable->property("itemPressed").toBool(), false);
1646 moveAndRelease(window: view.data(), position: QPoint(180, 160));
1647
1648 // drag inside bounds
1649 moveAndPress(window: view.data(), position: QPoint(200, 140));
1650 QTest::qWait(ms: 10);
1651 QTest::mouseMove(window: view.data(), pos: QPoint(200 + threshold * 2, 140));
1652 QTest::mouseMove(window: view.data(), pos: QPoint(200 + threshold * 3, 140));
1653 QCOMPARE(flickable->isDragging(), true);
1654 QCOMPARE(flickable->property("itemPressed").toBool(), false);
1655 moveAndRelease(window: view.data(), position: QPoint(220, 140));
1656}
1657
1658void tst_qquickflickable::flickTwiceUsingTouches()
1659{
1660 QScopedPointer<QQuickView> window(new QQuickView);
1661 window->setSource(testFileUrl(fileName: "longList.qml"));
1662 QTRY_COMPARE(window->status(), QQuickView::Ready);
1663 QQuickViewTestUtil::centerOnScreen(window: window.data());
1664 QQuickViewTestUtil::moveMouseAway(window: window.data());
1665 window->show();
1666 QVERIFY(window->rootObject() != nullptr);
1667 QVERIFY(QTest::qWaitForWindowActive(window.data()));
1668
1669 QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(object: window->rootObject());
1670 QVERIFY(flickable != nullptr);
1671
1672 QCOMPARE(flickable->contentY(), 0.0f);
1673 flickWithTouch(window: window.data(), from: QPoint(100, 400), to: QPoint(100, 240));
1674
1675 qreal contentYAfterFirstFlick = flickable->contentY();
1676 qDebug() << "contentYAfterFirstFlick " << contentYAfterFirstFlick;
1677 QVERIFY(contentYAfterFirstFlick > 50.0f);
1678 // Wait until view stops moving
1679 QTRY_VERIFY(!flickable->isMoving());
1680
1681 flickWithTouch(window: window.data(), from: QPoint(100, 400), to: QPoint(100, 240));
1682
1683 // In the original bug, that second flick would cause Flickable to halt immediately
1684 qreal contentYAfterSecondFlick = flickable->contentY();
1685 qDebug() << "contentYAfterSecondFlick " << contentYAfterSecondFlick;
1686 QTRY_VERIFY(contentYAfterSecondFlick > (contentYAfterFirstFlick + 80.0f));
1687}
1688
1689void tst_qquickflickable::flickWithTouch(QQuickWindow *window, const QPoint &from, const QPoint &to)
1690{
1691 QTest::touchEvent(window, device: touchDevice).press(touchId: 0, pt: from, window);
1692 QQuickTouchUtils::flush(window);
1693
1694 QPoint diff = to - from;
1695 for (int i = 1; i <= 8; ++i) {
1696 QTest::touchEvent(window, device: touchDevice).move(touchId: 0, pt: from + i*diff/8, window);
1697 QQuickTouchUtils::flush(window);
1698 }
1699 QTest::touchEvent(window, device: touchDevice).release(touchId: 0, pt: to, window);
1700 QQuickTouchUtils::flush(window);
1701}
1702
1703void tst_qquickflickable::nestedStopAtBounds_data()
1704{
1705 QTest::addColumn<bool>(name: "transpose");
1706 QTest::addColumn<bool>(name: "invert");
1707 QTest::addColumn<int>(name: "boundsBehavior");
1708 QTest::addColumn<qreal>(name: "margin");
1709 QTest::addColumn<bool>(name: "innerFiltering");
1710 QTest::addColumn<int>(name: "pressDelay");
1711 QTest::addColumn<bool>(name: "waitForPressDelay");
1712
1713 QTest::newRow(dataTag: "left,stop") << false << false << int(QQuickFlickable::StopAtBounds) << qreal(0) << false << 0 << false;
1714 QTest::newRow(dataTag: "right,stop") << false << true << int(QQuickFlickable::StopAtBounds) << qreal(0) << false << 0 << false;
1715 QTest::newRow(dataTag: "top,stop") << true << false << int(QQuickFlickable::StopAtBounds) << qreal(0) << false << 0 << false;
1716 QTest::newRow(dataTag: "bottom,stop") << true << true << int(QQuickFlickable::StopAtBounds) << qreal(0) << false << 0 << false;
1717 QTest::newRow(dataTag: "left,over") << false << false << int(QQuickFlickable::DragOverBounds) << qreal(0) << false << 0 << false;
1718 QTest::newRow(dataTag: "right,over") << false << true << int(QQuickFlickable::DragOverBounds) << qreal(0) << false << 0 << false;
1719 QTest::newRow(dataTag: "top,over") << true << false << int(QQuickFlickable::DragOverBounds) << qreal(0) << false << 0 << false;
1720 QTest::newRow(dataTag: "bottom,over") << true << true << int(QQuickFlickable::DragOverBounds) << qreal(0) << false << 0 << false;
1721
1722 QTest::newRow(dataTag: "left,stop,margin") << false << false << int(QQuickFlickable::StopAtBounds) << qreal(20) << false << 0 << false;
1723 QTest::newRow(dataTag: "right,stop,margin") << false << true << int(QQuickFlickable::StopAtBounds) << qreal(20) << false << 0 << false;
1724 QTest::newRow(dataTag: "top,stop,margin") << true << false << int(QQuickFlickable::StopAtBounds) << qreal(20) << false << 0 << false;
1725 QTest::newRow(dataTag: "bottom,stop,margin") << true << true << int(QQuickFlickable::StopAtBounds) << qreal(20) << false << 0 << false;
1726
1727 QTest::newRow(dataTag: "left,stop,after press delay") << false << false << int(QQuickFlickable::StopAtBounds) << qreal(0) << true << 50 << true;
1728 QTest::newRow(dataTag: "left,stop,before press delay") << false << false << int(QQuickFlickable::StopAtBounds) << qreal(0) << true << 50 << false;
1729}
1730
1731void tst_qquickflickable::nestedStopAtBounds()
1732{
1733 QFETCH(bool, transpose);
1734 QFETCH(bool, invert);
1735 QFETCH(int, boundsBehavior);
1736 QFETCH(qreal, margin);
1737 QFETCH(bool, innerFiltering);
1738 QFETCH(int, pressDelay);
1739 QFETCH(bool, waitForPressDelay);
1740
1741 QQuickView view;
1742 view.setSource(testFileUrl(fileName: "nestedStopAtBounds.qml"));
1743 QTRY_COMPARE(view.status(), QQuickView::Ready);
1744 QQuickViewTestUtil::centerOnScreen(window: &view);
1745 QQuickViewTestUtil::moveMouseAway(window: &view);
1746 view.show();
1747 view.requestActivate();
1748 QVERIFY(QTest::qWaitForWindowActive(&view));
1749 QVERIFY(view.rootObject());
1750
1751 QQuickFlickable *outer = qobject_cast<QQuickFlickable*>(object: view.rootObject());
1752 QVERIFY(outer);
1753
1754 QQuickFlickable *inner = outer->findChild<QQuickFlickable*>(aName: "innerFlickable");
1755 QVERIFY(inner);
1756 inner->setFlickableDirection(transpose ? QQuickFlickable::VerticalFlick : QQuickFlickable::HorizontalFlick);
1757 inner->setBoundsBehavior(QQuickFlickable::BoundsBehavior(boundsBehavior));
1758
1759 invert ? inner->setRightMargin(margin) : inner->setLeftMargin(margin);
1760 invert ? inner->setBottomMargin(margin) : inner->setTopMargin(margin);
1761
1762 inner->setContentX(invert ? -margin : 100 - margin);
1763 inner->setContentY(invert ? -margin : 100 - margin);
1764 inner->setContentWidth(400 - margin);
1765 inner->setContentHeight(400 - margin);
1766
1767 QCOMPARE(inner->isAtXBeginning(), invert);
1768 QCOMPARE(inner->isAtXEnd(), !invert);
1769 QCOMPARE(inner->isAtYBeginning(), invert);
1770 QCOMPARE(inner->isAtYEnd(), !invert);
1771
1772 inner->setPressDelay(pressDelay);
1773
1774 QQuickMouseArea *mouseArea = inner->findChild<QQuickMouseArea *>(aName: "mouseArea");
1775 QVERIFY(mouseArea);
1776 mouseArea->setEnabled(innerFiltering);
1777
1778 const int threshold = qApp->styleHints()->startDragDistance();
1779
1780 QPoint position(200, 200);
1781 int &axis = transpose ? position.ry() : position.rx();
1782
1783 // drag toward the aligned boundary. Outer flickable dragged.
1784 moveAndPress(window: &view, position);
1785 if (waitForPressDelay) {
1786 QVERIFY(innerFiltering); // isPressed will never be true if the mouse area isn't enabled.
1787 QTRY_VERIFY(mouseArea->pressed());
1788 }
1789
1790 axis += invert ? threshold * 2 : -threshold * 2;
1791 QTest::mouseMove(window: &view, pos: position);
1792 axis += invert ? threshold : -threshold;
1793 QTest::mouseMove(window: &view, pos: position);
1794 QCOMPARE(outer->isDragging(), true);
1795 QCOMPARE(outer->isMoving(), true);
1796 QCOMPARE(inner->isDragging(), false);
1797 QCOMPARE(inner->isMoving(), false);
1798 QTest::mouseRelease(window: &view, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: position);
1799
1800 QVERIFY(!outer->isDragging());
1801 QTRY_VERIFY(!outer->isMoving());
1802 QVERIFY(!inner->isDragging());
1803 QVERIFY(!inner->isMoving());
1804
1805 axis = 200;
1806 outer->setContentX(50);
1807 outer->setContentY(50);
1808
1809 // drag away from the aligned boundary. Inner flickable dragged.
1810 moveAndPress(window: &view, position);
1811 QTest::qWait(ms: 10);
1812 axis += invert ? -threshold * 2 : threshold * 2;
1813 QTest::mouseMove(window: &view, pos: position);
1814 axis += invert ? -threshold : threshold;
1815 QTest::mouseMove(window: &view, pos: position);
1816 QCOMPARE(outer->isDragging(), false);
1817 QCOMPARE(outer->isMoving(), false);
1818 QCOMPARE(inner->isDragging(), true);
1819 QCOMPARE(inner->isMoving(), true);
1820 QTest::mouseRelease(window: &view, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: position);
1821
1822 QVERIFY(!inner->isDragging());
1823 QTRY_VERIFY(!inner->isMoving());
1824 QVERIFY(!outer->isDragging());
1825 QVERIFY(!outer->isMoving());
1826
1827 axis = 200;
1828 inner->setContentX(-margin);
1829 inner->setContentY(-margin);
1830 inner->setContentWidth(inner->width() - margin);
1831 inner->setContentHeight(inner->height() - margin);
1832
1833 // Drag inner with equal size and contentSize
1834 moveAndPress(window: &view, position);
1835 QTest::qWait(ms: 10);
1836 axis += invert ? -threshold * 2 : threshold * 2;
1837 QTest::mouseMove(window: &view, pos: position);
1838 axis += invert ? -threshold : threshold;
1839 QTest::mouseMove(window: &view, pos: position);
1840 QCOMPARE(outer->isDragging(), true);
1841 QCOMPARE(outer->isMoving(), true);
1842 QCOMPARE(inner->isDragging(), false);
1843 QCOMPARE(inner->isMoving(), false);
1844 QTest::mouseRelease(window: &view, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: position);
1845
1846 QVERIFY(!outer->isDragging());
1847 QTRY_VERIFY(!outer->isMoving());
1848 QVERIFY(!inner->isDragging());
1849 QVERIFY(!inner->isMoving());
1850
1851 axis = 200;
1852 inner->setContentX(-margin);
1853 inner->setContentY(-margin);
1854 inner->setContentWidth(inner->width() - 100);
1855 inner->setContentHeight(inner->height() - 100);
1856
1857 // Drag inner with size greater than contentSize
1858 moveAndPress(window: &view, position);
1859 QTest::qWait(ms: 10);
1860 axis += invert ? -threshold * 2 : threshold * 2;
1861 QTest::mouseMove(window: &view, pos: position);
1862 axis += invert ? -threshold : threshold;
1863 QTest::mouseMove(window: &view, pos: position);
1864 QCOMPARE(outer->isDragging(), true);
1865 QCOMPARE(outer->isMoving(), true);
1866 QCOMPARE(inner->isDragging(), false);
1867 QCOMPARE(inner->isMoving(), false);
1868 QTest::mouseRelease(window: &view, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: position);
1869
1870 QVERIFY(!outer->isDragging());
1871 QTRY_VERIFY(!outer->isMoving());
1872 QVERIFY(!inner->isDragging());
1873 QVERIFY(!inner->isMoving());
1874}
1875
1876void tst_qquickflickable::stopAtBounds_data()
1877{
1878 QTest::addColumn<bool>(name: "transpose");
1879 QTest::addColumn<bool>(name: "invert");
1880 QTest::addColumn<bool>(name: "pixelAligned");
1881
1882 QTest::newRow(dataTag: "left") << false << false << false;
1883 QTest::newRow(dataTag: "right") << false << true << false;
1884 QTest::newRow(dataTag: "top") << true << false << false;
1885 QTest::newRow(dataTag: "bottom") << true << true << false;
1886 QTest::newRow(dataTag: "left,pixelAligned") << false << false << true;
1887 QTest::newRow(dataTag: "right,pixelAligned") << false << true << true;
1888 QTest::newRow(dataTag: "top,pixelAligned") << true << false << true;
1889 QTest::newRow(dataTag: "bottom,pixelAligned") << true << true << true;
1890}
1891
1892void tst_qquickflickable::stopAtBounds()
1893{
1894 QFETCH(bool, transpose);
1895 QFETCH(bool, invert);
1896 QFETCH(bool, pixelAligned);
1897
1898 QQuickView view;
1899 view.setSource(testFileUrl(fileName: "stopAtBounds.qml"));
1900 QTRY_COMPARE(view.status(), QQuickView::Ready);
1901 QQuickViewTestUtil::centerOnScreen(window: &view);
1902 QQuickViewTestUtil::moveMouseAway(window: &view);
1903 view.show();
1904 view.requestActivate();
1905 QVERIFY(QTest::qWaitForWindowActive(&view));
1906 QVERIFY(view.rootObject());
1907
1908 QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(object: view.rootObject());
1909 QVERIFY(flickable);
1910
1911 if (transpose)
1912 flickable->setContentY(invert ? 100 : 0);
1913 else
1914 flickable->setContentX(invert ? 100 : 0);
1915 flickable->setPixelAligned(pixelAligned);
1916
1917 const int threshold = qApp->styleHints()->startDragDistance();
1918
1919 QPoint position(200, 200);
1920 int &axis = transpose ? position.ry() : position.rx();
1921
1922 // drag away from the aligned boundary. View should not move
1923 moveAndPress(window: &view, position);
1924 QTest::qWait(ms: 10);
1925 for (int i = 0; i < 3; ++i) {
1926 axis += invert ? -threshold : threshold;
1927 QTest::mouseMove(window: &view, pos: position);
1928 }
1929 QCOMPARE(flickable->isDragging(), false);
1930 if (invert)
1931 QCOMPARE(transpose ? flickable->isAtYEnd() : flickable->isAtXEnd(), true);
1932 else
1933 QCOMPARE(transpose ? flickable->isAtYBeginning() : flickable->isAtXBeginning(), true);
1934
1935 QSignalSpy atXBeginningChangedSpy(flickable, &QQuickFlickable::atXBeginningChanged);
1936 QSignalSpy atYBeginningChangedSpy(flickable, &QQuickFlickable::atYBeginningChanged);
1937 QSignalSpy atXEndChangedSpy(flickable, &QQuickFlickable::atXEndChanged);
1938 QSignalSpy atYEndChangedSpy(flickable, &QQuickFlickable::atYEndChanged);
1939 // drag back towards boundary
1940 for (int i = 0; i < 24; ++i) {
1941 axis += invert ? threshold / 3 : -threshold / 3;
1942 QTest::mouseMove(window: &view, pos: position);
1943 }
1944 QTRY_COMPARE(flickable->isDragging(), true);
1945 if (invert)
1946 QCOMPARE(transpose ? flickable->isAtYEnd() : flickable->isAtXEnd(), false);
1947 else
1948 QCOMPARE(transpose ? flickable->isAtYBeginning() : flickable->isAtXBeginning(), false);
1949
1950 QCOMPARE(atXBeginningChangedSpy.count(), (!transpose && !invert) ? 1 : 0);
1951 QCOMPARE(atYBeginningChangedSpy.count(), ( transpose && !invert) ? 1 : 0);
1952 QCOMPARE(atXEndChangedSpy.count(), (!transpose && invert) ? 1 : 0);
1953 QCOMPARE(atYEndChangedSpy.count(), ( transpose && invert) ? 1 : 0);
1954
1955 // Drag away from the aligned boundary again.
1956 // None of the mouse movements will position the view at the boundary exactly,
1957 // but the view should end up aligned on the boundary
1958 for (int i = 0; i < 5; ++i) {
1959 axis += invert ? -threshold * 2 : threshold * 2;
1960 QTest::mouseMove(window: &view, pos: position);
1961 }
1962 QCOMPARE(flickable->isDragging(), true);
1963
1964 // we should have hit the boundary and stopped
1965 if (invert) {
1966 QCOMPARE(transpose ? flickable->isAtYEnd() : flickable->isAtXEnd(), true);
1967 QCOMPARE(transpose ? flickable->contentY() : flickable->contentX(), 100.0);
1968 } else {
1969 QCOMPARE(transpose ? flickable->isAtYBeginning() : flickable->isAtXBeginning(), true);
1970 QCOMPARE(transpose ? flickable->contentY() : flickable->contentX(), 0.0);
1971 }
1972
1973 QTest::mouseRelease(window: &view, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: position);
1974
1975 if (transpose) {
1976 flickable->setContentY(invert ? 100 : 0);
1977 } else {
1978 flickable->setContentX(invert ? 100 : 0);
1979 }
1980
1981 QSignalSpy flickSignal(flickable, SIGNAL(flickingChanged()));
1982 if (invert)
1983 flick(window: &view, from: QPoint(20,20), to: QPoint(120,120), duration: 100);
1984 else
1985 flick(window: &view, from: QPoint(120,120), to: QPoint(20,20), duration: 100);
1986
1987 QVERIFY(flickSignal.count() > 0);
1988 if (transpose) {
1989 if (invert)
1990 QTRY_COMPARE(flickable->isAtYBeginning(), true);
1991 else
1992 QTRY_COMPARE(flickable->isAtYEnd(), true);
1993 } else {
1994 if (invert)
1995 QTRY_COMPARE(flickable->isAtXBeginning(), true);
1996 else
1997 QTRY_COMPARE(flickable->isAtXEnd(), true);
1998 }
1999}
2000
2001void tst_qquickflickable::nestedMouseAreaUsingTouch()
2002{
2003 QScopedPointer<QQuickView> window(new QQuickView);
2004 window->setSource(testFileUrl(fileName: "nestedmousearea.qml"));
2005 QTRY_COMPARE(window->status(), QQuickView::Ready);
2006 QQuickViewTestUtil::centerOnScreen(window: window.data());
2007 QQuickViewTestUtil::moveMouseAway(window: window.data());
2008 window->show();
2009 QVERIFY(window->rootObject() != nullptr);
2010 QVERIFY(QTest::qWaitForWindowActive(window.data()));
2011
2012 QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(object: window->rootObject());
2013 QVERIFY(flickable != nullptr);
2014
2015 QCOMPARE(flickable->contentY(), 50.0f);
2016 flickWithTouch(window: window.data(), from: QPoint(100, 300), to: QPoint(100, 200));
2017
2018 // flickable should not have moved
2019 QCOMPARE(flickable->contentY(), 50.0);
2020
2021 // draggable item should have moved up
2022 QQuickItem *nested = window->rootObject()->findChild<QQuickItem*>(aName: "nested");
2023 QVERIFY(nested->y() < 100.0);
2024}
2025
2026void tst_qquickflickable::nestedSliderUsingTouch_data()
2027{
2028 QTest::addColumn<bool>(name: "keepMouseGrab");
2029 QTest::addColumn<bool>(name: "keepTouchGrab");
2030 QTest::addColumn<int>(name: "minUpdates");
2031 QTest::addColumn<int>(name: "releases");
2032 QTest::addColumn<int>(name: "ungrabs");
2033
2034 QTest::newRow(dataTag: "keepBoth") << true << true << 8 << 1 << 0;
2035 QTest::newRow(dataTag: "keepMouse") << true << false << 8 << 1 << 0;
2036 QTest::newRow(dataTag: "keepTouch") << false << true << 8 << 1 << 0;
2037 QTest::newRow(dataTag: "keepNeither") << false << false << 5 << 0 << 1;
2038}
2039
2040void tst_qquickflickable::nestedSliderUsingTouch()
2041{
2042 QFETCH(bool, keepMouseGrab);
2043 QFETCH(bool, keepTouchGrab);
2044 QFETCH(int, minUpdates);
2045 QFETCH(int, releases);
2046 QFETCH(int, ungrabs);
2047
2048 QQuickView *window = new QQuickView;
2049 QScopedPointer<QQuickView> windowPtr(window);
2050 windowPtr->setSource(testFileUrl(fileName: "nestedSlider.qml"));
2051 QTRY_COMPARE(window->status(), QQuickView::Ready);
2052 QQuickViewTestUtil::centerOnScreen(window);
2053 QQuickViewTestUtil::moveMouseAway(window);
2054 window->show();
2055 QVERIFY(QTest::qWaitForWindowActive(window));
2056 QVERIFY(window->rootObject() != nullptr);
2057
2058 QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(object: window->rootObject());
2059 QVERIFY(flickable);
2060
2061 TouchDragArea *tda = flickable->findChild<TouchDragArea*>(aName: "drag");
2062 QVERIFY(tda);
2063
2064 // Drag down and a little to the right: flickable will steal the grab only if tda allows it
2065 const int dragThreshold = qApp->styleHints()->startDragDistance();
2066 tda->setKeepMouseGrab(keepMouseGrab);
2067 tda->setKeepTouchGrab(keepTouchGrab);
2068 QPoint p0 = tda->mapToScene(point: QPoint(20, 20)).toPoint();
2069 QTest::touchEvent(window, device: touchDevice).press(touchId: 0, pt: p0, window);
2070 QQuickTouchUtils::flush(window);
2071 for (int i = 0; i < 8; ++i) {
2072 p0 += QPoint(dragThreshold / 6, dragThreshold / 4);
2073 QTest::touchEvent(window, device: touchDevice).move(touchId: 0, pt: p0, window);
2074 QQuickTouchUtils::flush(window);
2075 }
2076 QCOMPARE(tda->active(), !ungrabs);
2077 QTest::touchEvent(window, device: touchDevice).release(touchId: 0, pt: p0, window);
2078 QQuickTouchUtils::flush(window);
2079 QTRY_COMPARE(tda->touchPointStates.first(), Qt::TouchPointPressed);
2080 QTRY_VERIFY(tda->touchUpdates >= minUpdates);
2081 QTRY_COMPARE(tda->touchReleases, releases);
2082 QTRY_COMPARE(tda->ungrabs, ungrabs);
2083}
2084
2085// QTBUG-31328
2086void tst_qquickflickable::pressDelayWithLoader()
2087{
2088 QScopedPointer<QQuickView> window(new QQuickView);
2089 window->setSource(testFileUrl(fileName: "pressDelayWithLoader.qml"));
2090 QTRY_COMPARE(window->status(), QQuickView::Ready);
2091 QQuickViewTestUtil::centerOnScreen(window: window.data());
2092 QQuickViewTestUtil::moveMouseAway(window: window.data());
2093 window->show();
2094 QVERIFY(QTest::qWaitForWindowActive(window.data()));
2095 QVERIFY(window->rootObject() != nullptr);
2096
2097 // do not crash
2098 moveAndPress(window: window.data(), position: QPoint(150, 150));
2099 QTest::mouseRelease(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(150, 150));
2100}
2101
2102// QTBUG-34507
2103void tst_qquickflickable::movementFromProgrammaticFlick()
2104{
2105 QScopedPointer<QQuickView> window(new QQuickView);
2106 window->setSource(testFileUrl(fileName: "movementSignals.qml"));
2107 QTRY_COMPARE(window->status(), QQuickView::Ready);
2108 QQuickViewTestUtil::centerOnScreen(window: window.data());
2109 QQuickViewTestUtil::moveMouseAway(window: window.data());
2110 window->show();
2111 QVERIFY(QTest::qWaitForWindowActive(window.data()));
2112
2113 QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(object: window->rootObject());
2114 QVERIFY(flickable != nullptr);
2115
2116 // verify that the signals for movement and flicking are called in the right order
2117 flickable->flick(xVelocity: 0, yVelocity: -1000);
2118 QTRY_COMPARE(flickable->property("signalString").toString(), QString("msfsfeme"));
2119}
2120
2121// QTBUG_35038
2122void tst_qquickflickable::contentSize()
2123{
2124 QQuickFlickable flickable;
2125 QCOMPARE(flickable.contentWidth(), qreal(-1));
2126 QCOMPARE(flickable.contentHeight(), qreal(-1));
2127
2128 QSignalSpy cwspy(&flickable, SIGNAL(contentWidthChanged()));
2129 QVERIFY(cwspy.isValid());
2130
2131 QSignalSpy chspy(&flickable, SIGNAL(contentHeightChanged()));
2132 QVERIFY(chspy.isValid());
2133
2134 flickable.setWidth(100);
2135 QCOMPARE(flickable.width(), qreal(100));
2136 QCOMPARE(flickable.contentWidth(), qreal(-1.0));
2137 QCOMPARE(cwspy.count(), 0);
2138
2139 flickable.setContentWidth(10);
2140 QCOMPARE(flickable.width(), qreal(100));
2141 QCOMPARE(flickable.contentWidth(), qreal(10));
2142 QCOMPARE(cwspy.count(), 1);
2143
2144 flickable.setHeight(100);
2145 QCOMPARE(flickable.height(), qreal(100));
2146 QCOMPARE(flickable.contentHeight(), qreal(-1.0));
2147 QCOMPARE(chspy.count(), 0);
2148
2149 flickable.setContentHeight(10);
2150 QCOMPARE(flickable.height(), qreal(100));
2151 QCOMPARE(flickable.contentHeight(), qreal(10));
2152 QCOMPARE(chspy.count(), 1);
2153}
2154
2155// QTBUG-53726
2156void tst_qquickflickable::ratios_smallContent()
2157{
2158 QScopedPointer<QQuickView> window(new QQuickView);
2159 window->setSource(testFileUrl(fileName: "ratios_smallContent.qml"));
2160 QTRY_COMPARE(window->status(), QQuickView::Ready);
2161 QQuickViewTestUtil::centerOnScreen(window: window.data());
2162 QQuickViewTestUtil::moveMouseAway(window: window.data());
2163 window->setTitle(QTest::currentTestFunction());
2164 window->show();
2165 QVERIFY(QTest::qWaitForWindowExposed(window.data()));
2166 QQuickItem *root = window->rootObject();
2167 QVERIFY(root);
2168 QQuickFlickable *obj = qobject_cast<QQuickFlickable*>(object: root);
2169 QVERIFY(obj != nullptr);
2170
2171 //doublecheck the item, as specified by contentWidth/Height, fits in the view
2172 //use tryCompare to allow a bit of stabilization in component's properties
2173 QTRY_COMPARE(obj->leftMargin() + obj->contentWidth() + obj->rightMargin() <= obj->width(), true);
2174 QTRY_COMPARE(obj->topMargin() + obj->contentHeight() + obj->bottomMargin() <= obj->height(), true);
2175
2176 //the whole item fits in the flickable, heightRatio should be 1
2177 QCOMPARE(obj->property("heightRatioIs").toDouble(), 1.);
2178 QCOMPARE(obj->property("widthRatioIs").toDouble(), 1.);
2179}
2180
2181// QTBUG-48018
2182void tst_qquickflickable::contentXYNotTruncatedToInt()
2183{
2184 QScopedPointer<QQuickView> window(new QQuickView);
2185 window->setSource(testFileUrl(fileName: "contentXY.qml"));
2186 QTRY_COMPARE(window->status(), QQuickView::Ready);
2187 QQuickViewTestUtil::centerOnScreen(window: window.data());
2188 QQuickViewTestUtil::moveMouseAway(window: window.data());
2189 window->show();
2190 QVERIFY(QTest::qWaitForWindowActive(window.data()));
2191
2192 QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(object: window->rootObject());
2193 QVERIFY(flickable);
2194
2195 flickable->setContentX(1e10);
2196 flick(window: window.data(), from: QPoint(200, 100), to: QPoint(100, 100), duration: 50);
2197
2198 // make sure we are not clipped at 2^31
2199 QVERIFY(flickable->contentX() > qreal(1e10));
2200}
2201
2202void tst_qquickflickable::keepGrab()
2203{
2204 QScopedPointer<QQuickView> window(new QQuickView);
2205 window->setSource(testFileUrl(fileName: "keepGrab.qml"));
2206 QTRY_COMPARE(window->status(), QQuickView::Ready);
2207 QQuickViewTestUtil::centerOnScreen(window: window.data());
2208 QQuickViewTestUtil::moveMouseAway(window: window.data());
2209 window->show();
2210 QVERIFY(QTest::qWaitForWindowActive(window.data()));
2211
2212 QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(object: window->rootObject());
2213 QVERIFY(flickable);
2214
2215 QQuickMouseArea *ma = flickable->findChild<QQuickMouseArea*>(aName: "ma");
2216 QVERIFY(ma);
2217 ma->setPreventStealing(true);
2218
2219 QPoint pos(250, 250);
2220 moveAndPress(window: window.data(), position: pos);
2221 for (int i = 0; i < 6; ++i) {
2222 pos += QPoint(10, 10);
2223 QTest::mouseMove(window: window.data(), pos);
2224 QTest::qWait(ms: 10);
2225 }
2226 QTest::mouseRelease(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(310, 310));
2227 QTest::qWait(ms: 10);
2228
2229 QCOMPARE(flickable->contentX(), 0.0);
2230 QCOMPARE(flickable->contentY(), 0.0);
2231
2232 ma->setPreventStealing(false);
2233
2234 pos = QPoint(250, 250);
2235 moveAndPress(window: window.data(), position: pos);
2236 for (int i = 0; i < 6; ++i) {
2237 pos += QPoint(10, 10);
2238 QTest::mouseMove(window: window.data(), pos);
2239 QTest::qWait(ms: 10);
2240 }
2241 QTest::mouseRelease(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(310, 310));
2242 QTest::qWait(ms: 10);
2243
2244 QVERIFY(flickable->contentX() != 0.0);
2245 QVERIFY(flickable->contentY() != 0.0);
2246}
2247
2248Q_DECLARE_METATYPE(QQuickFlickable::BoundsBehavior)
2249
2250void tst_qquickflickable::overshoot()
2251{
2252 QFETCH(QQuickFlickable::BoundsBehavior, boundsBehavior);
2253 QFETCH(int, boundsMovement);
2254
2255 QScopedPointer<QQuickView> window(new QQuickView);
2256 window->setSource(testFileUrl(fileName: "overshoot.qml"));
2257 window->show();
2258
2259 QVERIFY(QTest::qWaitForWindowExposed(window.data()));
2260
2261 QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(object: window->rootObject());
2262 QVERIFY(flickable);
2263
2264 QCOMPARE(flickable->width(), 200.0);
2265 QCOMPARE(flickable->height(), 200.0);
2266 QCOMPARE(flickable->contentWidth(), 400.0);
2267 QCOMPARE(flickable->contentHeight(), 400.0);
2268
2269 flickable->setBoundsBehavior(boundsBehavior);
2270 flickable->setBoundsMovement(QQuickFlickable::BoundsMovement(boundsMovement));
2271
2272 // drag past the beginning
2273 QTest::mousePress(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(10, 10));
2274 QTest::mouseMove(window: window.data(), pos: QPoint(20, 20));
2275 QTest::mouseMove(window: window.data(), pos: QPoint(30, 30));
2276 QTest::mouseMove(window: window.data(), pos: QPoint(40, 40));
2277 QTest::mouseRelease(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(50, 50));
2278
2279 if ((boundsMovement == QQuickFlickable::FollowBoundsBehavior) && (boundsBehavior & QQuickFlickable::DragOverBounds)) {
2280 QVERIFY(flickable->property("minContentX").toReal() < 0.0);
2281 QVERIFY(flickable->property("minContentY").toReal() < 0.0);
2282 } else {
2283 QCOMPARE(flickable->property("minContentX").toReal(), 0.0);
2284 QCOMPARE(flickable->property("minContentY").toReal(), 0.0);
2285 }
2286 if (boundsBehavior & QQuickFlickable::DragOverBounds) {
2287 QVERIFY(flickable->property("minHorizontalOvershoot").toReal() < 0.0);
2288 QVERIFY(flickable->property("minVerticalOvershoot").toReal() < 0.0);
2289 } else {
2290 QCOMPARE(flickable->property("minHorizontalOvershoot").toReal(), 0.0);
2291 QCOMPARE(flickable->property("minVerticalOvershoot").toReal(), 0.0);
2292 }
2293 if (bool(boundsMovement == QQuickFlickable::FollowBoundsBehavior) == bool(boundsBehavior & QQuickFlickable::DragOverBounds)) {
2294 QCOMPARE(flickable->property("minContentX").toReal(),
2295 flickable->property("minHorizontalOvershoot").toReal());
2296 QCOMPARE(flickable->property("minContentY").toReal(),
2297 flickable->property("minVerticalOvershoot").toReal());
2298 }
2299 QCOMPARE(flickable->property("maxContentX").toReal(), 0.0);
2300 QCOMPARE(flickable->property("maxContentY").toReal(), 0.0);
2301 QCOMPARE(flickable->property("maxHorizontalOvershoot").toReal(), 0.0);
2302 QCOMPARE(flickable->property("maxVerticalOvershoot").toReal(), 0.0);
2303
2304 flickable->setContentX(20.0);
2305 flickable->setContentY(20.0);
2306 QMetaObject::invokeMethod(obj: flickable, member: "reset");
2307
2308 // flick past the beginning
2309 flick(window: window.data(), from: QPoint(10, 10), to: QPoint(50, 50), duration: 100);
2310 QTRY_VERIFY(!flickable->property("flicking").toBool());
2311
2312 if ((boundsMovement == QQuickFlickable::FollowBoundsBehavior) && (boundsBehavior & QQuickFlickable::OvershootBounds)) {
2313 QVERIFY(flickable->property("minContentX").toReal() < 0.0);
2314 QVERIFY(flickable->property("minContentY").toReal() < 0.0);
2315 } else {
2316 QCOMPARE(flickable->property("minContentX").toReal(), 0.0);
2317 QCOMPARE(flickable->property("minContentY").toReal(), 0.0);
2318 }
2319 if (boundsBehavior & QQuickFlickable::OvershootBounds) {
2320 QVERIFY(flickable->property("minHorizontalOvershoot").toReal() < 0.0);
2321 QVERIFY(flickable->property("minVerticalOvershoot").toReal() < 0.0);
2322 } else {
2323 QCOMPARE(flickable->property("minHorizontalOvershoot").toReal(), 0.0);
2324 QCOMPARE(flickable->property("minVerticalOvershoot").toReal(), 0.0);
2325 }
2326 if ((boundsMovement == QQuickFlickable::FollowBoundsBehavior) == (boundsBehavior & QQuickFlickable::OvershootBounds)) {
2327 QCOMPARE(flickable->property("minContentX").toReal(),
2328 flickable->property("minHorizontalOvershoot").toReal());
2329 QCOMPARE(flickable->property("minContentY").toReal(),
2330 flickable->property("minVerticalOvershoot").toReal());
2331 }
2332 QCOMPARE(flickable->property("maxContentX").toReal(), 20.0);
2333 QCOMPARE(flickable->property("maxContentY").toReal(), 20.0);
2334 QCOMPARE(flickable->property("maxHorizontalOvershoot").toReal(), 0.0);
2335 QCOMPARE(flickable->property("maxVerticalOvershoot").toReal(), 0.0);
2336
2337 flickable->setContentX(200.0);
2338 flickable->setContentY(200.0);
2339 QMetaObject::invokeMethod(obj: flickable, member: "reset");
2340
2341 // drag past the end
2342 QTest::mousePress(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(50, 50));
2343 QTest::mouseMove(window: window.data(), pos: QPoint(40, 40));
2344 QTest::mouseMove(window: window.data(), pos: QPoint(30, 30));
2345 QTest::mouseMove(window: window.data(), pos: QPoint(20, 20));
2346 QTest::mouseRelease(window: window.data(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(10, 10));
2347
2348 if ((boundsMovement == QQuickFlickable::FollowBoundsBehavior) && (boundsBehavior & QQuickFlickable::DragOverBounds)) {
2349 QVERIFY(flickable->property("maxContentX").toReal() > 200.0);
2350 QVERIFY(flickable->property("maxContentX").toReal() > 200.0);
2351 } else {
2352 QCOMPARE(flickable->property("maxContentX").toReal(), 200.0);
2353 QCOMPARE(flickable->property("maxContentY").toReal(), 200.0);
2354 }
2355 if (boundsBehavior & QQuickFlickable::DragOverBounds) {
2356 QVERIFY(flickable->property("maxHorizontalOvershoot").toReal() > 0.0);
2357 QVERIFY(flickable->property("maxVerticalOvershoot").toReal() > 0.0);
2358 } else {
2359 QCOMPARE(flickable->property("maxHorizontalOvershoot").toReal(), 0.0);
2360 QCOMPARE(flickable->property("maxVerticalOvershoot").toReal(), 0.0);
2361 }
2362 if ((boundsMovement == QQuickFlickable::FollowBoundsBehavior) == (boundsBehavior & QQuickFlickable::DragOverBounds)) {
2363 QCOMPARE(flickable->property("maxContentX").toReal() - 200.0,
2364 flickable->property("maxHorizontalOvershoot").toReal());
2365 QCOMPARE(flickable->property("maxContentY").toReal() - 200.0,
2366 flickable->property("maxVerticalOvershoot").toReal());
2367 }
2368 QCOMPARE(flickable->property("minContentX").toReal(), 200.0);
2369 QCOMPARE(flickable->property("minContentY").toReal(), 200.0);
2370 QCOMPARE(flickable->property("minHorizontalOvershoot").toReal(), 0.0);
2371 QCOMPARE(flickable->property("minVerticalOvershoot").toReal(), 0.0);
2372
2373 flickable->setContentX(180.0);
2374 flickable->setContentY(180.0);
2375 QMetaObject::invokeMethod(obj: flickable, member: "reset");
2376
2377 // flick past the end
2378 flick(window: window.data(), from: QPoint(50, 50), to: QPoint(10, 10), duration: 100);
2379 QTRY_VERIFY(!flickable->property("flicking").toBool());
2380
2381 if ((boundsMovement == QQuickFlickable::FollowBoundsBehavior) && (boundsBehavior & QQuickFlickable::OvershootBounds)) {
2382 QVERIFY(flickable->property("maxContentX").toReal() > 200.0);
2383 QVERIFY(flickable->property("maxContentY").toReal() > 200.0);
2384 } else {
2385 QCOMPARE(flickable->property("maxContentX").toReal(), 200.0);
2386 QCOMPARE(flickable->property("maxContentY").toReal(), 200.0);
2387 }
2388 if (boundsBehavior & QQuickFlickable::OvershootBounds) {
2389 QVERIFY(flickable->property("maxHorizontalOvershoot").toReal() > 0.0);
2390 QVERIFY(flickable->property("maxVerticalOvershoot").toReal() > 0.0);
2391 } else {
2392 QCOMPARE(flickable->property("maxHorizontalOvershoot").toReal(), 0.0);
2393 QCOMPARE(flickable->property("maxVerticalOvershoot").toReal(), 0.0);
2394 }
2395 if ((boundsMovement == QQuickFlickable::FollowBoundsBehavior) == (boundsBehavior & QQuickFlickable::OvershootBounds)) {
2396 QCOMPARE(flickable->property("maxContentX").toReal() - 200.0,
2397 flickable->property("maxHorizontalOvershoot").toReal());
2398 QCOMPARE(flickable->property("maxContentY").toReal() - 200.0,
2399 flickable->property("maxVerticalOvershoot").toReal());
2400 }
2401 QCOMPARE(flickable->property("minContentX").toReal(), 180.0);
2402 QCOMPARE(flickable->property("minContentY").toReal(), 180.0);
2403 QCOMPARE(flickable->property("minHorizontalOvershoot").toReal(), 0.0);
2404 QCOMPARE(flickable->property("minVerticalOvershoot").toReal(), 0.0);
2405}
2406
2407void tst_qquickflickable::overshoot_data()
2408{
2409 QTest::addColumn<QQuickFlickable::BoundsBehavior>(name: "boundsBehavior");
2410 QTest::addColumn<int>(name: "boundsMovement");
2411
2412 QTest::newRow(dataTag: "StopAtBounds,FollowBoundsBehavior")
2413 << QQuickFlickable::BoundsBehavior(QQuickFlickable::StopAtBounds)
2414 << int(QQuickFlickable::FollowBoundsBehavior);
2415 QTest::newRow(dataTag: "DragOverBounds,FollowBoundsBehavior")
2416 << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragOverBounds)
2417 << int(QQuickFlickable::FollowBoundsBehavior);
2418 QTest::newRow(dataTag: "OvershootBounds,FollowBoundsBehavior")
2419 << QQuickFlickable::BoundsBehavior(QQuickFlickable::OvershootBounds)
2420 << int(QQuickFlickable::FollowBoundsBehavior);
2421 QTest::newRow(dataTag: "DragAndOvershootBounds,FollowBoundsBehavior")
2422 << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragAndOvershootBounds)
2423 << int(QQuickFlickable::FollowBoundsBehavior);
2424
2425 QTest::newRow(dataTag: "DragOverBounds,StopAtBounds")
2426 << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragOverBounds)
2427 << int(QQuickFlickable::StopAtBounds);
2428 QTest::newRow(dataTag: "OvershootBounds,StopAtBounds")
2429 << QQuickFlickable::BoundsBehavior(QQuickFlickable::OvershootBounds)
2430 << int(QQuickFlickable::StopAtBounds);
2431 QTest::newRow(dataTag: "DragAndOvershootBounds,StopAtBounds")
2432 << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragAndOvershootBounds)
2433 << int(QQuickFlickable::StopAtBounds);
2434}
2435
2436void tst_qquickflickable::overshoot_reentrant()
2437{
2438 QScopedPointer<QQuickView> window(new QQuickView);
2439 window->setSource(testFileUrl(fileName: "overshoot_reentrant.qml"));
2440 window->show();
2441
2442 QVERIFY(QTest::qWaitForWindowExposed(window.data()));
2443
2444 QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(object: window->rootObject());
2445 QVERIFY(flickable);
2446
2447 // horizontal
2448 flickable->setContentX(-10.0);
2449 QCOMPARE(flickable->contentX(), -10.0);
2450 QCOMPARE(flickable->horizontalOvershoot(), -10.0);
2451
2452 flickable->setProperty(name: "contentPosAdjustment", value: -5.0);
2453 flickable->setContentX(-20.0);
2454 QCOMPARE(flickable->contentX(), -25.0);
2455 QCOMPARE(flickable->horizontalOvershoot(), -25.0);
2456
2457 flickable->setContentX(210);
2458 QCOMPARE(flickable->contentX(), 210.0);
2459 QCOMPARE(flickable->horizontalOvershoot(), 10.0);
2460
2461 flickable->setProperty(name: "contentPosAdjustment", value: 5.0);
2462 flickable->setContentX(220.0);
2463 QCOMPARE(flickable->contentX(), 225.0);
2464 QCOMPARE(flickable->horizontalOvershoot(), 25.0);
2465
2466 // vertical
2467 flickable->setContentY(-10.0);
2468 QCOMPARE(flickable->contentY(), -10.0);
2469 QCOMPARE(flickable->verticalOvershoot(), -10.0);
2470
2471 flickable->setProperty(name: "contentPosAdjustment", value: -5.0);
2472 flickable->setContentY(-20.0);
2473 QCOMPARE(flickable->contentY(), -25.0);
2474 QCOMPARE(flickable->verticalOvershoot(), -25.0);
2475
2476 flickable->setContentY(210);
2477 QCOMPARE(flickable->contentY(), 210.0);
2478 QCOMPARE(flickable->verticalOvershoot(), 10.0);
2479
2480 flickable->setProperty(name: "contentPosAdjustment", value: 5.0);
2481 flickable->setContentY(220.0);
2482 QCOMPARE(flickable->contentY(), 225.0);
2483 QCOMPARE(flickable->verticalOvershoot(), 25.0);
2484}
2485
2486void tst_qquickflickable::synchronousDrag_data()
2487{
2488 QTest::addColumn<bool>(name: "synchronousDrag");
2489
2490 QTest::newRow(dataTag: "default") << false;
2491 QTest::newRow(dataTag: "synch") << true;
2492}
2493
2494void tst_qquickflickable::synchronousDrag()
2495{
2496 QFETCH(bool, synchronousDrag);
2497
2498 QScopedPointer<QQuickView> scopedWindow(new QQuickView);
2499 QQuickView *window = scopedWindow.data();
2500 window->setSource(testFileUrl(fileName: "longList.qml"));
2501 QTRY_COMPARE(window->status(), QQuickView::Ready);
2502 QQuickViewTestUtil::centerOnScreen(window);
2503 QQuickViewTestUtil::moveMouseAway(window);
2504 window->show();
2505 QVERIFY(window->rootObject() != nullptr);
2506 QVERIFY(QTest::qWaitForWindowActive(window));
2507
2508 QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(object: window->rootObject());
2509 QVERIFY(flickable != nullptr);
2510 QCOMPARE(flickable->synchronousDrag(), false);
2511 flickable->setSynchronousDrag(synchronousDrag);
2512
2513 QPoint p1(100, 100);
2514 QPoint p2(95, 95);
2515 QPoint p3(70, 70);
2516 QPoint p4(50, 50);
2517 QPoint p5(30, 30);
2518 QCOMPARE(flickable->contentY(), 0.0f);
2519
2520 // Drag via mouse
2521 moveAndPress(window, position: p1);
2522 QTest::mouseMove(window, pos: p2);
2523 QTest::mouseMove(window, pos: p3);
2524 QTest::mouseMove(window, pos: p4);
2525 QCOMPARE(flickable->contentY(), synchronousDrag ? 50.0f : 0.0f);
2526 QTest::mouseMove(window, pos: p5);
2527 if (!synchronousDrag)
2528 QVERIFY(flickable->contentY() < 50.0f);
2529 QTest::mouseRelease(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p5);
2530
2531 // Reset to initial condition
2532 flickable->setContentY(0);
2533
2534 // Drag via touch
2535 QTest::touchEvent(window, device: touchDevice).press(touchId: 0, pt: p1, window);
2536 QQuickTouchUtils::flush(window);
2537 QTest::touchEvent(window, device: touchDevice).move(touchId: 0, pt: p2, window);
2538 QQuickTouchUtils::flush(window);
2539 QTest::touchEvent(window, device: touchDevice).move(touchId: 0, pt: p3, window);
2540 QQuickTouchUtils::flush(window);
2541 QTest::touchEvent(window, device: touchDevice).move(touchId: 0, pt: p4, window);
2542 QQuickTouchUtils::flush(window);
2543 QCOMPARE(flickable->contentY(), synchronousDrag ? 50.0f : 0.0f);
2544 QTest::touchEvent(window, device: touchDevice).move(touchId: 0, pt: p5, window);
2545 QQuickTouchUtils::flush(window);
2546 if (!synchronousDrag)
2547 QVERIFY(flickable->contentY() < 50.0f);
2548 QTest::touchEvent(window, device: touchDevice).release(touchId: 0, pt: p5, window);
2549}
2550
2551// QTBUG-81098: tests that a binding to visibleArea doesn't result
2552// in a division-by-zero exception (when exceptions are enabled).
2553void tst_qquickflickable::visibleAreaBinding()
2554{
2555 QScopedPointer<QQuickView> window(new QQuickView);
2556 window->setSource(testFileUrl(fileName: "visibleAreaBinding.qml"));
2557 QTRY_COMPARE(window->status(), QQuickView::Ready);
2558 // Shouldn't crash.
2559}
2560
2561QTEST_MAIN(tst_qquickflickable)
2562
2563#include "tst_qquickflickable.moc"
2564

source code of qtdeclarative/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp