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 | |
47 | using namespace QQuickViewTestUtil; |
48 | using namespace QQuickVisualTestUtil; |
49 | |
50 | // an abstract Slider which only handles touch events |
51 | class 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 | |
59 | public: |
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 | |
97 | protected: |
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 | |
135 | signals: |
136 | void ungrabbed(); |
137 | void posChanged(); |
138 | void keepMouseGrabChanged(); |
139 | void keepTouchGrabChanged(); |
140 | void activeChanged(); |
141 | |
142 | private: |
143 | QPointF m_pos; |
144 | bool m_active; |
145 | }; |
146 | |
147 | class tst_qquickflickable : public QQmlDataTest |
148 | { |
149 | Q_OBJECT |
150 | public: |
151 | tst_qquickflickable() |
152 | : touchDevice(QTest::createTouchDevice()) |
153 | {} |
154 | |
155 | private 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 | |
210 | private: |
211 | void flickWithTouch(QQuickWindow *window, const QPoint &from, const QPoint &to); |
212 | QTouchDevice *touchDevice; |
213 | }; |
214 | |
215 | void tst_qquickflickable::initTestCase() |
216 | { |
217 | QQmlDataTest::initTestCase(); |
218 | qmlRegisterType<TouchDragArea>(uri: "Test" ,versionMajor: 1,versionMinor: 0,qmlName: "TouchDragArea" ); |
219 | } |
220 | |
221 | void tst_qquickflickable::cleanup() |
222 | { |
223 | QVERIFY(QGuiApplication::topLevelWindows().isEmpty()); |
224 | } |
225 | |
226 | void 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 | |
251 | void 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 | |
268 | void 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 | |
285 | void 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 | |
307 | void 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 | |
326 | void 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 | |
364 | void 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 | |
464 | void 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 | |
484 | void 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 | |
504 | void 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 |
585 | void 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 | |
665 | void 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 |
708 | void 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 | |
752 | void 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 |
783 | void 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 | |
813 | void 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 | |
853 | void 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 | |
861 | void 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 | |
920 | void 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 | |
971 | void 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 | |
994 | void 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 | |
1134 | void 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 | |
1157 | void 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 | |
1336 | void 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 | |
1367 | void 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 | |
1420 | void 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 | |
1448 | void 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 | |
1492 | void 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 | |
1556 | void 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 | |
1575 | void 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 | |
1613 | void 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 | |
1658 | void 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 | |
1689 | void 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 | |
1703 | void 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 | |
1731 | void 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 | |
1876 | void 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 | |
1892 | void 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 | |
2001 | void 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 | |
2026 | void 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 | |
2040 | void 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 |
2086 | void 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 |
2103 | void 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 |
2122 | void 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 |
2156 | void 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 |
2182 | void 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 | |
2202 | void 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 | |
2248 | Q_DECLARE_METATYPE(QQuickFlickable::BoundsBehavior) |
2249 | |
2250 | void 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 | |
2407 | void 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 | |
2436 | void 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 | |
2486 | void 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 | |
2494 | void 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). |
2553 | void 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 | |
2561 | QTEST_MAIN(tst_qquickflickable) |
2562 | |
2563 | #include "tst_qquickflickable.moc" |
2564 | |