diff options
-rw-r--r-- | src/quick/items/qquickflickable.cpp | 163 | ||||
-rw-r--r-- | src/quick/items/qquickflickable_p.h | 3 | ||||
-rw-r--r-- | src/quick/items/qquickflickable_p_p.h | 7 | ||||
-rw-r--r-- | src/quick/items/qquickgridview.cpp | 22 | ||||
-rw-r--r-- | src/quick/items/qquicklistview.cpp | 28 | ||||
-rw-r--r-- | tests/auto/quick/qquickflickable/data/flickable03.qml | 2 | ||||
-rw-r--r-- | tests/auto/quick/qquickflickable/tst_qquickflickable.cpp | 434 |
7 files changed, 473 insertions, 186 deletions
diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp index a80f714fa7..c207c77458 100644 --- a/src/quick/items/qquickflickable.cpp +++ b/src/quick/items/qquickflickable.cpp @@ -267,19 +267,19 @@ void QQuickFlickablePrivate::itemGeometryChanged(QQuickItem *item, const QRectF } } -void QQuickFlickablePrivate::flickX(qreal velocity) +bool QQuickFlickablePrivate::flickX(qreal velocity) { Q_Q(QQuickFlickable); - flick(hData, q->minXExtent(), q->maxXExtent(), q->width(), fixupX_callback, velocity); + return flick(hData, q->minXExtent(), q->maxXExtent(), q->width(), fixupX_callback, velocity); } -void QQuickFlickablePrivate::flickY(qreal velocity) +bool QQuickFlickablePrivate::flickY(qreal velocity) { Q_Q(QQuickFlickable); - flick(vData, q->minYExtent(), q->maxYExtent(), q->height(), fixupY_callback, velocity); + return flick(vData, q->minYExtent(), q->maxYExtent(), q->height(), fixupY_callback, velocity); } -void QQuickFlickablePrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal, +bool QQuickFlickablePrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal, QQuickTimeLineCallback::Callback fixupCallback, qreal velocity) { Q_Q(QQuickFlickable); @@ -318,23 +318,16 @@ void QQuickFlickablePrivate::flick(AxisData &data, qreal minExtent, qreal maxExt else timeline.accel(data.move, v, accel, maxDistance); timeline.callback(QQuickTimeLineCallback(&data.move, fixupCallback, this)); - if (!hData.flicking && q->xflick() && (&data == &hData)) { - hData.flicking = true; - emit q->flickingChanged(); - emit q->flickingHorizontallyChanged(); - if (!vData.flicking) - emit q->flickStarted(); - } - if (!vData.flicking && q->yflick() && (&data == &vData)) { - vData.flicking = true; - emit q->flickingChanged(); - emit q->flickingVerticallyChanged(); - if (!hData.flicking) - emit q->flickStarted(); - } + + if (&data == &hData) + return !hData.flicking && q->xflick(); + else if (&data == &vData) + return !vData.flicking && q->yflick(); + return false; } else { timeline.reset(data.move); fixup(data, minExtent, maxExtent); + return false; } } @@ -641,7 +634,7 @@ void QQuickFlickable::setContentX(qreal pos) d->hData.explicitValue = true; d->timeline.reset(d->hData.move); d->vTime = d->timeline.time(); - movementXEnding(); + movementEnding(true, false); if (-pos != d->hData.move.value()) d->hData.move.setValue(-pos); } @@ -658,7 +651,7 @@ void QQuickFlickable::setContentY(qreal pos) d->vData.explicitValue = true; d->timeline.reset(d->vData.move); d->vTime = d->timeline.time(); - movementYEnding(); + movementEnding(false, true); if (-pos != d->vData.move.value()) d->vData.move.setValue(-pos); } @@ -1083,24 +1076,27 @@ void QQuickFlickablePrivate::handleMouseReleaseEvent(QMouseEvent *event) flickBoost = canBoost ? qBound(1.0, flickBoost+0.25, QML_FLICK_MULTIFLICK_MAXBOOST) : 1.0; + bool flickedV = false; vVelocity *= flickBoost; if (q->yflick() && qAbs(vVelocity) > MinimumFlickVelocity && qAbs(event->localPos().y() - pressPos.y()) > FlickThreshold) { velocityTimeline.reset(vData.smoothVelocity); vData.smoothVelocity.setValue(-vVelocity); - flickY(vVelocity); + flickedV = flickY(vVelocity); } else { fixupY(); } + bool flickedH = false; hVelocity *= flickBoost; if (q->xflick() && qAbs(hVelocity) > MinimumFlickVelocity && qAbs(event->localPos().x() - pressPos.x()) > FlickThreshold) { velocityTimeline.reset(hData.smoothVelocity); hData.smoothVelocity.setValue(-hVelocity); - flickX(hVelocity); + flickedH = flickX(hVelocity); } else { fixupX(); } + flickingStarted(flickedH, flickedV); if (!timeline.isActive()) q->movementEnding(); } @@ -1159,6 +1155,7 @@ void QQuickFlickable::wheelEvent(QWheelEvent *event) if (valid) { d->vData.flicking = false; d->flickY(d->vData.velocity); + d->flickingStarted(false, true); if (d->vData.flicking) { d->vMoved = true; movementStarting(); @@ -1177,6 +1174,7 @@ void QQuickFlickable::wheelEvent(QWheelEvent *event) if (valid) { d->hData.flicking = false; d->flickX(d->hData.velocity); + d->flickingStarted(true, false); if (d->hData.flicking) { d->hMoved = true; movementStarting(); @@ -1411,8 +1409,30 @@ void QQuickFlickable::geometryChanged(const QRectF &newGeometry, void QQuickFlickable::flick(qreal xVelocity, qreal yVelocity) { Q_D(QQuickFlickable); - d->flickX(xVelocity); - d->flickY(yVelocity); + bool flickedX = d->flickX(xVelocity); + bool flickedY = d->flickY(yVelocity); + d->flickingStarted(flickedX, flickedY); +} + +void QQuickFlickablePrivate::flickingStarted(bool flickingH, bool flickingV) +{ + Q_Q(QQuickFlickable); + if (!flickingH && !flickingV) + return; + + bool wasFlicking = hData.flicking || vData.flicking; + if (flickingH && !hData.flicking) { + hData.flicking = true; + emit q->flickingHorizontallyChanged(); + } + if (flickingV && !vData.flicking) { + vData.flicking = true; + emit q->flickingVerticallyChanged(); + } + if (!wasFlicking && (hData.flicking || vData.flicking)) { + emit q->flickingChanged(); + emit q->flickStarted(); + } } /*! @@ -2091,75 +2111,72 @@ bool QQuickFlickable::isMovingVertically() const void QQuickFlickable::movementStarting() { Q_D(QQuickFlickable); + bool wasMoving = d->hData.moving || d->vData.moving; if (d->hMoved && !d->hData.moving) { d->hData.moving = true; - emit movingChanged(); emit movingHorizontallyChanged(); - if (!d->vData.moving) - emit movementStarted(); } - else if (d->vMoved && !d->vData.moving) { + if (d->vMoved && !d->vData.moving) { d->vData.moving = true; - emit movingChanged(); emit movingVerticallyChanged(); - if (!d->hData.moving) - emit movementStarted(); + } + if (!wasMoving && (d->hData.moving || d->vData.moving)) { + emit movingChanged(); + emit movementStarted(); } } void QQuickFlickable::movementEnding() { - Q_D(QQuickFlickable); - movementXEnding(); - movementYEnding(); - d->hData.smoothVelocity.setValue(0); - d->vData.smoothVelocity.setValue(0); + movementEnding(true, true); } -void QQuickFlickable::movementXEnding() +void QQuickFlickable::movementEnding(bool hMovementEnding, bool vMovementEnding) { Q_D(QQuickFlickable); - if (d->hData.flicking) { + + // emit flicking signals + bool wasFlicking = d->hData.flicking || d->vData.flicking; + if (hMovementEnding && d->hData.flicking) { d->hData.flicking = false; - emit flickingChanged(); emit flickingHorizontallyChanged(); - if (!d->vData.flicking) - emit flickEnded(); - } - if (!d->pressed && !d->stealMouse) { - if (d->hData.moving) { - d->hData.moving = false; - d->hMoved = false; - emit movingChanged(); - emit movingHorizontallyChanged(); - if (!d->vData.moving) - emit movementEnded(); - } } - d->hData.fixingUp = false; -} - -void QQuickFlickable::movementYEnding() -{ - Q_D(QQuickFlickable); - if (d->vData.flicking) { + if (vMovementEnding && d->vData.flicking) { d->vData.flicking = false; - emit flickingChanged(); emit flickingVerticallyChanged(); - if (!d->hData.flicking) - emit flickEnded(); - } - if (!d->pressed && !d->stealMouse) { - if (d->vData.moving) { - d->vData.moving = false; - d->vMoved = false; - emit movingChanged(); - emit movingVerticallyChanged(); - if (!d->hData.moving) - emit movementEnded(); - } } - d->vData.fixingUp = false; + if (wasFlicking && (!d->hData.flicking || !d->vData.flicking)) { + emit flickingChanged(); + emit flickEnded(); + } + + // emit moving signals + bool wasMoving = d->hData.moving || d->vData.moving; + if (hMovementEnding && d->hData.moving + && (!d->pressed && !d->stealMouse)) { + d->hData.moving = false; + d->hMoved = false; + emit movingHorizontallyChanged(); + } + if (vMovementEnding && d->vData.moving + && (!d->pressed && !d->stealMouse)) { + d->vData.moving = false; + d->vMoved = false; + emit movingVerticallyChanged(); + } + if (wasMoving && (!d->hData.moving || !d->vData.moving)) { + emit movingChanged(); + emit movementEnded(); + } + + if (hMovementEnding) { + d->hData.fixingUp = false; + d->hData.smoothVelocity.setValue(0); + } + if (vMovementEnding) { + d->vData.fixingUp = false; + d->vData.smoothVelocity.setValue(0); + } } void QQuickFlickablePrivate::updateVelocity() diff --git a/src/quick/items/qquickflickable_p.h b/src/quick/items/qquickflickable_p.h index 109bca2112..08aa487951 100644 --- a/src/quick/items/qquickflickable_p.h +++ b/src/quick/items/qquickflickable_p.h @@ -237,10 +237,9 @@ protected: protected Q_SLOTS: void movementStarting(); void movementEnding(); + void movementEnding(bool hMovementEnding, bool vMovementEnding); protected: - void movementXEnding(); - void movementYEnding(); virtual qreal minXExtent() const; virtual qreal minYExtent() const; virtual qreal maxXExtent() const; diff --git a/src/quick/items/qquickflickable_p_p.h b/src/quick/items/qquickflickable_p_p.h index ac6f2fffed..ab3070e5b1 100644 --- a/src/quick/items/qquickflickable_p_p.h +++ b/src/quick/items/qquickflickable_p_p.h @@ -145,10 +145,11 @@ public: mutable bool maxExtentDirty : 1; }; - void flickX(qreal velocity); - void flickY(qreal velocity); - virtual void flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, + bool flickX(qreal velocity); + bool flickY(qreal velocity); + virtual bool flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, QQuickTimeLineCallback::Callback fixupCallback, qreal velocity); + void flickingStarted(bool flickingH, bool flickingV); void fixupX(); void fixupY(); diff --git a/src/quick/items/qquickgridview.cpp b/src/quick/items/qquickgridview.cpp index 3315deaebf..235005b758 100644 --- a/src/quick/items/qquickgridview.cpp +++ b/src/quick/items/qquickgridview.cpp @@ -229,7 +229,7 @@ public: virtual void updateViewport(); virtual void fixupPosition(); virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent); - virtual void flick(QQuickItemViewPrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, + virtual bool flick(QQuickItemViewPrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, QQuickTimeLineCallback::Callback fixupCallback, qreal velocity); QQuickGridView::Flow flow; @@ -1023,16 +1023,14 @@ void QQuickGridViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExte fixupMode = Normal; } -void QQuickGridViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, +bool QQuickGridViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, QQuickTimeLineCallback::Callback fixupCallback, qreal velocity) { - Q_Q(QQuickGridView); data.fixingUp = false; moveReason = Mouse; if ((!haveHighlightRange || highlightRange != QQuickGridView::StrictlyEnforceRange) && snapMode == QQuickGridView::NoSnap) { - QQuickItemViewPrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity); - return; + return QQuickItemViewPrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity); } qreal maxDistance = 0; qreal dataValue = isContentFlowReversed() ? -data.move.value()+size() : data.move.value(); @@ -1125,21 +1123,11 @@ void QQuickGridViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExte timeline.reset(data.move); timeline.accel(data.move, v, accel, maxDistance + overshootDist); timeline.callback(QQuickTimeLineCallback(&data.move, fixupCallback, this)); - if (!hData.flicking && q->xflick()) { - hData.flicking = true; - emit q->flickingChanged(); - emit q->flickingHorizontallyChanged(); - emit q->flickStarted(); - } - if (!vData.flicking && q->yflick()) { - vData.flicking = true; - emit q->flickingChanged(); - emit q->flickingVerticallyChanged(); - emit q->flickStarted(); - } + return true; } else { timeline.reset(data.move); fixup(data, minExtent, maxExtent); + return false; } } diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp index 48cdee8ea2..e2de19b498 100644 --- a/src/quick/items/qquicklistview.cpp +++ b/src/quick/items/qquicklistview.cpp @@ -132,7 +132,7 @@ public: void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry); virtual void fixupPosition(); virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent); - virtual void flick(QQuickItemViewPrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, + virtual bool flick(QQuickItemViewPrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, QQuickTimeLineCallback::Callback fixupCallback, qreal velocity); QQuickListView::Orientation orient; @@ -1471,17 +1471,14 @@ void QQuickListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExte fixupMode = Normal; } -void QQuickListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, +bool QQuickListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, QQuickTimeLineCallback::Callback fixupCallback, qreal velocity) { - Q_Q(QQuickListView); - data.fixingUp = false; moveReason = Mouse; if ((!haveHighlightRange || highlightRange != QQuickListView::StrictlyEnforceRange) && snapMode == QQuickListView::NoSnap) { correctFlick = true; - QQuickItemViewPrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity); - return; + return QQuickItemViewPrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity); } qreal maxDistance = 0; qreal dataValue = isContentFlowReversed() ? -data.move.value()+size() : data.move.value(); @@ -1587,19 +1584,8 @@ void QQuickListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExte timeline.reset(data.move); timeline.accel(data.move, v, accel, maxDistance + overshootDist); timeline.callback(QQuickTimeLineCallback(&data.move, fixupCallback, this)); - if (!hData.flicking && q->xflick()) { - hData.flicking = true; - emit q->flickingChanged(); - emit q->flickingHorizontallyChanged(); - emit q->flickStarted(); - } - if (!vData.flicking && q->yflick()) { - vData.flicking = true; - emit q->flickingChanged(); - emit q->flickingVerticallyChanged(); - emit q->flickStarted(); - } correctFlick = true; + return true; } else { // reevaluate the target boundary. qreal newtarget = data.flickTarget; @@ -1615,7 +1601,7 @@ void QQuickListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExte if (newtarget == data.flickTarget) { // boundary unchanged - nothing to do if (qAbs(velocity) < MinimumFlickVelocity) correctFlick = false; - return; + return false; } data.flickTarget = newtarget; qreal dist = -newtarget + data.move.value(); @@ -1623,16 +1609,18 @@ void QQuickListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExte correctFlick = false; timeline.reset(data.move); fixup(data, minExtent, maxExtent); - return; + return false; } timeline.reset(data.move); timeline.accelDistance(data.move, v, -dist); timeline.callback(QQuickTimeLineCallback(&data.move, fixupCallback, this)); + return false; } } else { correctFlick = false; timeline.reset(data.move); fixup(data, minExtent, maxExtent); + return false; } } diff --git a/tests/auto/quick/qquickflickable/data/flickable03.qml b/tests/auto/quick/qquickflickable/data/flickable03.qml index 719c682ee6..a3e9d6fd59 100644 --- a/tests/auto/quick/qquickflickable/data/flickable03.qml +++ b/tests/auto/quick/qquickflickable/data/flickable03.qml @@ -8,7 +8,7 @@ Flickable { id: column Repeater { model: 20 - Rectangle { width: 200; height: 300; color: "blue" } + Rectangle { width: 200; height: 300; border.width: 1; color: "yellow" } } } } diff --git a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp index 39d1075e53..7438320b9b 100644 --- a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp +++ b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp @@ -73,7 +73,12 @@ private slots: void resizeContent(); void returnToBounds(); void wheel(); + void movingAndFlicking(); + void movingAndFlicking_data(); void movingAndDragging(); + void movingAndDragging_data(); + void flickOnRelease(); + void pressWhileFlicking(); void disabled(); void flickVelocity(); void margins(); @@ -382,12 +387,206 @@ void tst_qquickflickable::wheel() delete canvas; } +void tst_qquickflickable::movingAndFlicking_data() +{ + QTest::addColumn<bool>("verticalEnabled"); + QTest::addColumn<bool>("horizontalEnabled"); + QTest::addColumn<QPoint>("flickToWithoutSnapBack"); + QTest::addColumn<QPoint>("flickToWithSnapBack"); + + QTest::newRow("vertical") + << true << false + << QPoint(50, 100) + << QPoint(50, 300); + + QTest::newRow("horizontal") + << false << true + << QPoint(-50, 200) + << QPoint(150, 200); + + QTest::newRow("both") + << true << true + << QPoint(-50, 100) + << QPoint(150, 300); +} + +void tst_qquickflickable::movingAndFlicking() +{ +#ifdef Q_OS_MAC + QSKIP("Producing flicks on Mac CI impossible due to timing problems"); +#endif + + QFETCH(bool, verticalEnabled); + QFETCH(bool, horizontalEnabled); + QFETCH(QPoint, flickToWithoutSnapBack); + QFETCH(QPoint, flickToWithSnapBack); + + const QPoint flickFrom(50, 200); // centre + + QQuickView *canvas = new QQuickView; + canvas->setSource(testFileUrl("flickable03.qml")); + canvas->show(); + canvas->requestActivateWindow(); + QTest::qWaitForWindowShown(canvas); + QVERIFY(canvas->rootObject() != 0); + + QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(canvas->rootObject()); + QVERIFY(flickable != 0); + + QSignalSpy vMoveSpy(flickable, SIGNAL(movingVerticallyChanged())); + QSignalSpy hMoveSpy(flickable, SIGNAL(movingHorizontallyChanged())); + QSignalSpy moveSpy(flickable, SIGNAL(movingChanged())); + QSignalSpy vFlickSpy(flickable, SIGNAL(flickingVerticallyChanged())); + QSignalSpy hFlickSpy(flickable, SIGNAL(flickingHorizontallyChanged())); + QSignalSpy flickSpy(flickable, SIGNAL(flickingChanged())); + + QSignalSpy moveStartSpy(flickable, SIGNAL(movementStarted())); + QSignalSpy moveEndSpy(flickable, SIGNAL(movementEnded())); + QSignalSpy flickStartSpy(flickable, SIGNAL(flickStarted())); + QSignalSpy flickEndSpy(flickable, SIGNAL(flickEnded())); + + // do a flick that keeps the view within the bounds + flick(canvas, flickFrom, flickToWithoutSnapBack, 200); + + QVERIFY(flickable->isMoving()); + QCOMPARE(flickable->isMovingHorizontally(), horizontalEnabled); + QCOMPARE(flickable->isMovingVertically(), verticalEnabled); + QVERIFY(flickable->isFlicking()); + QCOMPARE(flickable->isFlickingHorizontally(), horizontalEnabled); + QCOMPARE(flickable->isFlickingVertically(), verticalEnabled); + + QCOMPARE(moveSpy.count(), 1); + QCOMPARE(vMoveSpy.count(), verticalEnabled ? 1 : 0); + QCOMPARE(hMoveSpy.count(), horizontalEnabled ? 1 : 0); + QCOMPARE(flickSpy.count(), 1); + QCOMPARE(vFlickSpy.count(), verticalEnabled ? 1 : 0); + QCOMPARE(hFlickSpy.count(), horizontalEnabled ? 1 : 0); + + QCOMPARE(moveStartSpy.count(), 1); + QCOMPARE(flickStartSpy.count(), 1); + + // wait for any motion to end + QTRY_VERIFY(!flickable->isMoving()); + + QVERIFY(!flickable->isMovingHorizontally()); + QVERIFY(!flickable->isMovingVertically()); + QVERIFY(!flickable->isFlicking()); + QVERIFY(!flickable->isFlickingHorizontally()); + QVERIFY(!flickable->isFlickingVertically()); + + QCOMPARE(moveSpy.count(), 2); + QCOMPARE(vMoveSpy.count(), verticalEnabled ? 2 : 0); + QCOMPARE(hMoveSpy.count(), horizontalEnabled ? 2 : 0); + QCOMPARE(flickSpy.count(), 2); + QCOMPARE(vFlickSpy.count(), verticalEnabled ? 2 : 0); + QCOMPARE(hFlickSpy.count(), horizontalEnabled ? 2 : 0); + + QCOMPARE(moveStartSpy.count(), 1); + QCOMPARE(moveEndSpy.count(), 1); + QCOMPARE(flickStartSpy.count(), 1); + QCOMPARE(flickEndSpy.count(), 1); + + // Stop on a full pixel after user interaction + if (verticalEnabled) + QCOMPARE(flickable->contentY(), (qreal)qRound(flickable->contentY())); + if (horizontalEnabled) + QCOMPARE(flickable->contentX(), (qreal)qRound(flickable->contentX())); + + // clear for next flick + vMoveSpy.clear(); hMoveSpy.clear(); moveSpy.clear(); + vFlickSpy.clear(); hFlickSpy.clear(); flickSpy.clear(); + moveStartSpy.clear(); moveEndSpy.clear(); + flickStartSpy.clear(); flickEndSpy.clear(); + + // do a flick that flicks the view out of bounds + flickable->setContentX(0); + flickable->setContentY(0); + QTRY_VERIFY(!flickable->isMoving()); + flick(canvas, flickFrom, flickToWithSnapBack, 200); + + QVERIFY(flickable->isMoving()); + QCOMPARE(flickable->isMovingHorizontally(), horizontalEnabled); + QCOMPARE(flickable->isMovingVertically(), verticalEnabled); + QVERIFY(flickable->isFlicking()); + QCOMPARE(flickable->isFlickingHorizontally(), horizontalEnabled); + QCOMPARE(flickable->isFlickingVertically(), verticalEnabled); + + QCOMPARE(moveSpy.count(), 1); + QCOMPARE(vMoveSpy.count(), verticalEnabled ? 1 : 0); + QCOMPARE(hMoveSpy.count(), horizontalEnabled ? 1 : 0); + QCOMPARE(flickSpy.count(), 1); + QCOMPARE(vFlickSpy.count(), verticalEnabled ? 1 : 0); + QCOMPARE(hFlickSpy.count(), horizontalEnabled ? 1 : 0); + + QCOMPARE(moveStartSpy.count(), 1); + QCOMPARE(moveEndSpy.count(), 0); + QCOMPARE(flickStartSpy.count(), 1); + QCOMPARE(flickEndSpy.count(), 0); + + // wait for any motion to end + QTRY_VERIFY(!flickable->isMoving()); + + QVERIFY(!flickable->isMovingHorizontally()); + QVERIFY(!flickable->isMovingVertically()); + QVERIFY(!flickable->isFlicking()); + QVERIFY(!flickable->isFlickingHorizontally()); + QVERIFY(!flickable->isFlickingVertically()); + + QCOMPARE(moveSpy.count(), 2); + QCOMPARE(vMoveSpy.count(), verticalEnabled ? 2 : 0); + QCOMPARE(hMoveSpy.count(), horizontalEnabled ? 2 : 0); + QCOMPARE(flickSpy.count(), 2); + QCOMPARE(vFlickSpy.count(), verticalEnabled ? 2 : 0); + QCOMPARE(hFlickSpy.count(), horizontalEnabled ? 2 : 0); + + QCOMPARE(moveStartSpy.count(), 1); + QCOMPARE(moveEndSpy.count(), 1); + QCOMPARE(flickStartSpy.count(), 1); + QCOMPARE(flickEndSpy.count(), 1); + + QCOMPARE(flickable->contentX(), 0.0); + QCOMPARE(flickable->contentY(), 0.0); + + delete canvas; +} + + +void tst_qquickflickable::movingAndDragging_data() +{ + QTest::addColumn<bool>("verticalEnabled"); + QTest::addColumn<bool>("horizontalEnabled"); + QTest::addColumn<QPoint>("moveByWithoutSnapBack"); + QTest::addColumn<QPoint>("moveByWithSnapBack"); + + QTest::newRow("vertical") + << true << false + << QPoint(0, -10) + << QPoint(0, 20); + + QTest::newRow("horizontal") + << false << true + << QPoint(-10, 0) + << QPoint(20, 0); + + QTest::newRow("both") + << true << true + << QPoint(-10, -10) + << QPoint(20, 20); +} + void tst_qquickflickable::movingAndDragging() { #ifdef Q_OS_MAC QSKIP("Producing flicks on Mac CI impossible due to timing problems"); #endif + QFETCH(bool, verticalEnabled); + QFETCH(bool, horizontalEnabled); + QFETCH(QPoint, moveByWithoutSnapBack); + QFETCH(QPoint, moveByWithSnapBack); + + const QPoint moveFrom(50, 200); // centre + QQuickView *canvas = new QQuickView; canvas->setSource(testFileUrl("flickable03.qml")); canvas->show(); @@ -404,47 +603,170 @@ void tst_qquickflickable::movingAndDragging() QSignalSpy vMoveSpy(flickable, SIGNAL(movingVerticallyChanged())); QSignalSpy hMoveSpy(flickable, SIGNAL(movingHorizontallyChanged())); QSignalSpy moveSpy(flickable, SIGNAL(movingChanged())); + QSignalSpy dragStartSpy(flickable, SIGNAL(dragStarted())); QSignalSpy dragEndSpy(flickable, SIGNAL(dragEnded())); + QSignalSpy moveStartSpy(flickable, SIGNAL(movementStarted())); + QSignalSpy moveEndSpy(flickable, SIGNAL(movementEnded())); - //Vertical - QTest::mousePress(canvas, Qt::LeftButton, 0, QPoint(50, 90)); + // start the drag + QTest::mousePress(canvas, Qt::LeftButton, 0, moveFrom); + QTest::mouseMove(canvas, moveFrom + moveByWithoutSnapBack); + QTest::mouseMove(canvas, moveFrom + moveByWithoutSnapBack*2); + QTest::mouseMove(canvas, moveFrom + moveByWithoutSnapBack*3); - QTest::mouseMove(canvas, QPoint(50, 80)); - QTest::mouseMove(canvas, QPoint(50, 70)); - QTest::mouseMove(canvas, QPoint(50, 60)); - - QVERIFY(!flickable->isDraggingHorizontally()); - QVERIFY(flickable->isDraggingVertically()); + QVERIFY(flickable->isMoving()); + QCOMPARE(flickable->isMovingHorizontally(), horizontalEnabled); + QCOMPARE(flickable->isMovingVertically(), verticalEnabled); QVERIFY(flickable->isDragging()); - QCOMPARE(vDragSpy.count(), 1); - QCOMPARE(dragSpy.count(), 1); - QCOMPARE(hDragSpy.count(), 0); - QCOMPARE(dragStartSpy.count(), 1); - QCOMPARE(dragEndSpy.count(), 0); + QCOMPARE(flickable->isDraggingHorizontally(), horizontalEnabled); + QCOMPARE(flickable->isDraggingVertically(), verticalEnabled); - QVERIFY(!flickable->isMovingHorizontally()); - QVERIFY(flickable->isMovingVertically()); - QVERIFY(flickable->isMoving()); - QCOMPARE(vMoveSpy.count(), 1); QCOMPARE(moveSpy.count(), 1); - QCOMPARE(hMoveSpy.count(), 0); + QCOMPARE(vMoveSpy.count(), verticalEnabled ? 1 : 0); + QCOMPARE(hMoveSpy.count(), horizontalEnabled ? 1 : 0); + QCOMPARE(dragSpy.count(), 1); + QCOMPARE(vDragSpy.count(), verticalEnabled ? 1 : 0); + QCOMPARE(hDragSpy.count(), horizontalEnabled ? 1 : 0); - QTest::mouseRelease(canvas, Qt::LeftButton, 0, QPoint(50, 60)); + QCOMPARE(moveStartSpy.count(), 1); + QCOMPARE(dragStartSpy.count(), 1); + + QTest::mouseRelease(canvas, Qt::LeftButton, 0, moveFrom + moveByWithoutSnapBack*3); - QTRY_VERIFY(!flickable->isDraggingVertically()); QVERIFY(!flickable->isDragging()); - QCOMPARE(vDragSpy.count(), 2); + QVERIFY(!flickable->isDraggingHorizontally()); + QVERIFY(!flickable->isDraggingVertically()); QCOMPARE(dragSpy.count(), 2); - QCOMPARE(hDragSpy.count(), 0); + QCOMPARE(vDragSpy.count(), verticalEnabled ? 2 : 0); + QCOMPARE(hDragSpy.count(), horizontalEnabled ? 2 : 0); QCOMPARE(dragStartSpy.count(), 1); QCOMPARE(dragEndSpy.count(), 1); + // Don't test whether moving finished because a flick could occur // wait for any motion to end QTRY_VERIFY(flickable->isMoving() == false); + QVERIFY(!flickable->isMovingHorizontally()); + QVERIFY(!flickable->isMovingVertically()); + QVERIFY(!flickable->isDragging()); + QVERIFY(!flickable->isDraggingHorizontally()); + QVERIFY(!flickable->isDraggingVertically()); + + QCOMPARE(dragSpy.count(), 2); + QCOMPARE(vDragSpy.count(), verticalEnabled ? 2 : 0); + QCOMPARE(hDragSpy.count(), horizontalEnabled ? 2 : 0); + QCOMPARE(moveSpy.count(), 2); + QCOMPARE(vMoveSpy.count(), verticalEnabled ? 2 : 0); + QCOMPARE(hMoveSpy.count(), horizontalEnabled ? 2 : 0); + + QCOMPARE(dragStartSpy.count(), 1); + QCOMPARE(dragEndSpy.count(), 1); + QCOMPARE(moveStartSpy.count(), 1); + QCOMPARE(moveEndSpy.count(), 1); + // Stop on a full pixel after user interaction - QCOMPARE(flickable->contentY(), (qreal)qRound(flickable->contentY())); + if (verticalEnabled) + QCOMPARE(flickable->contentY(), (qreal)qRound(flickable->contentY())); + if (horizontalEnabled) + QCOMPARE(flickable->contentX(), (qreal)qRound(flickable->contentX())); + + // clear for next drag + vMoveSpy.clear(); hMoveSpy.clear(); moveSpy.clear(); + vDragSpy.clear(); hDragSpy.clear(); dragSpy.clear(); + moveStartSpy.clear(); moveEndSpy.clear(); + dragStartSpy.clear(); dragEndSpy.clear(); + + // do a drag that drags the view out of bounds + flickable->setContentX(0); + flickable->setContentY(0); + QTRY_VERIFY(!flickable->isMoving()); + QTest::mousePress(canvas, Qt::LeftButton, 0, moveFrom); + QTest::mouseMove(canvas, moveFrom + moveByWithSnapBack); + QTest::mouseMove(canvas, moveFrom + moveByWithSnapBack*2); + QTest::mouseMove(canvas, moveFrom + moveByWithSnapBack*3); + + QVERIFY(flickable->isMoving()); + QCOMPARE(flickable->isMovingHorizontally(), horizontalEnabled); + QCOMPARE(flickable->isMovingVertically(), verticalEnabled); + QVERIFY(flickable->isDragging()); + QCOMPARE(flickable->isDraggingHorizontally(), horizontalEnabled); + QCOMPARE(flickable->isDraggingVertically(), verticalEnabled); + + QCOMPARE(moveSpy.count(), 1); + QCOMPARE(vMoveSpy.count(), verticalEnabled ? 1 : 0); + QCOMPARE(hMoveSpy.count(), horizontalEnabled ? 1 : 0); + QCOMPARE(dragSpy.count(), 1); + QCOMPARE(vDragSpy.count(), verticalEnabled ? 1 : 0); + QCOMPARE(hDragSpy.count(), horizontalEnabled ? 1 : 0); + + QCOMPARE(moveStartSpy.count(), 1); + QCOMPARE(moveEndSpy.count(), 0); + QCOMPARE(dragStartSpy.count(), 1); + QCOMPARE(dragEndSpy.count(), 0); + + QTest::mouseRelease(canvas, Qt::LeftButton, 0, moveFrom + moveByWithSnapBack*3); + + // should now start snapping back to bounds (moving but not dragging) + QVERIFY(flickable->isMoving()); + QCOMPARE(flickable->isMovingHorizontally(), horizontalEnabled); + QCOMPARE(flickable->isMovingVertically(), verticalEnabled); + QVERIFY(!flickable->isDragging()); + QVERIFY(!flickable->isDraggingHorizontally()); + QVERIFY(!flickable->isDraggingVertically()); + + QCOMPARE(moveSpy.count(), 1); + QCOMPARE(vMoveSpy.count(), verticalEnabled ? 1 : 0); + QCOMPARE(hMoveSpy.count(), horizontalEnabled ? 1 : 0); + QCOMPARE(dragSpy.count(), 2); + QCOMPARE(vDragSpy.count(), verticalEnabled ? 2 : 0); + QCOMPARE(hDragSpy.count(), horizontalEnabled ? 2 : 0); + + QCOMPARE(moveStartSpy.count(), 1); + QCOMPARE(moveEndSpy.count(), 0); + + // wait for any motion to end + QTRY_VERIFY(!flickable->isMoving()); + + QVERIFY(!flickable->isMovingHorizontally()); + QVERIFY(!flickable->isMovingVertically()); + QVERIFY(!flickable->isDragging()); + QVERIFY(!flickable->isDraggingHorizontally()); + QVERIFY(!flickable->isDraggingVertically()); + + QCOMPARE(moveSpy.count(), 2); + QCOMPARE(vMoveSpy.count(), verticalEnabled ? 2 : 0); + QCOMPARE(hMoveSpy.count(), horizontalEnabled ? 2 : 0); + QCOMPARE(dragSpy.count(), 2); + QCOMPARE(vDragSpy.count(), verticalEnabled ? 2 : 0); + QCOMPARE(hDragSpy.count(), horizontalEnabled ? 2 : 0); + + QCOMPARE(moveStartSpy.count(), 1); + QCOMPARE(moveEndSpy.count(), 1); + QCOMPARE(dragStartSpy.count(), 1); + QCOMPARE(dragEndSpy.count(), 1); + + QCOMPARE(flickable->contentX(), 0.0); + QCOMPARE(flickable->contentY(), 0.0); + + delete canvas; +} + +void tst_qquickflickable::flickOnRelease() +{ +#ifdef Q_OS_MAC + QSKIP("Producing flicks on Mac CI impossible due to timing problems"); +#endif + + QQuickView *canvas = new QQuickView; + canvas->setSource(testFileUrl("flickable03.qml")); + canvas->show(); + canvas->requestActivateWindow(); + QTest::qWaitForWindowShown(canvas); + QVERIFY(canvas->rootObject() != 0); + + QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(canvas->rootObject()); + QVERIFY(flickable != 0); // Vertical with a quick press-move-release: should cause a flick in release. QSignalSpy vFlickSpy(flickable, SIGNAL(flickingVerticallyChanged())); @@ -465,58 +787,30 @@ void tst_qquickflickable::movingAndDragging() // Stop on a full pixel after user interaction QCOMPARE(flickable->contentY(), (qreal)qRound(flickable->contentY())); - //Horizontal - vDragSpy.clear(); - hDragSpy.clear(); - dragSpy.clear(); - vMoveSpy.clear(); - hMoveSpy.clear(); - moveSpy.clear(); - dragStartSpy.clear(); - dragEndSpy.clear(); - - QTest::mousePress(canvas, Qt::LeftButton, 0, QPoint(90, 50)); - - QTest::mouseMove(canvas, QPoint(80, 50)); - QTest::mouseMove(canvas, QPoint(70, 50)); - QTest::mouseMove(canvas, QPoint(60, 50)); - - QVERIFY(!flickable->isDraggingVertically()); - QVERIFY(flickable->isDraggingHorizontally()); - QVERIFY(flickable->isDragging()); - QCOMPARE(vDragSpy.count(), 0); - QCOMPARE(dragSpy.count(), 1); - QCOMPARE(hDragSpy.count(), 1); - QCOMPARE(dragStartSpy.count(), 1); - QCOMPARE(dragEndSpy.count(), 0); - - QVERIFY(!flickable->isMovingVertically()); - QVERIFY(flickable->isMovingHorizontally()); - QVERIFY(flickable->isMoving()); - QCOMPARE(vMoveSpy.count(), 0); - QCOMPARE(moveSpy.count(), 1); - QCOMPARE(hMoveSpy.count(), 1); + delete canvas; +} - QTest::mouseRelease(canvas, Qt::LeftButton, 0, QPoint(60, 50)); +void tst_qquickflickable::pressWhileFlicking() +{ +#ifdef Q_OS_MAC + QSKIP("Producing flicks on Mac CI impossible due to timing problems"); +#endif - QTRY_VERIFY(!flickable->isDraggingHorizontally()); - QVERIFY(!flickable->isDragging()); - QCOMPARE(vDragSpy.count(), 0); - QCOMPARE(dragSpy.count(), 2); - QCOMPARE(hDragSpy.count(), 2); - QCOMPARE(dragStartSpy.count(), 1); - QCOMPARE(dragEndSpy.count(), 1); - // Don't test moving because a flick could occur + QQuickView *canvas = new QQuickView; + canvas->setSource(testFileUrl("flickable03.qml")); + canvas->show(); + canvas->requestActivateWindow(); + QTest::qWaitForWindowShown(canvas); + QVERIFY(canvas->rootObject() != 0); - QTRY_VERIFY(!flickable->isMoving()); - // Stop on a full pixel after user interaction - QCOMPARE(flickable->contentX(), (qreal)qRound(flickable->contentX())); + QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(canvas->rootObject()); + QVERIFY(flickable != 0); - vMoveSpy.clear(); - hMoveSpy.clear(); - moveSpy.clear(); - vFlickSpy.clear(); + QSignalSpy vMoveSpy(flickable, SIGNAL(movingVerticallyChanged())); + QSignalSpy hMoveSpy(flickable, SIGNAL(movingHorizontallyChanged())); + QSignalSpy moveSpy(flickable, SIGNAL(movingChanged())); QSignalSpy hFlickSpy(flickable, SIGNAL(flickingHorizontallyChanged())); + QSignalSpy vFlickSpy(flickable, SIGNAL(flickingVerticallyChanged())); QSignalSpy flickSpy(flickable, SIGNAL(flickingChanged())); // flick then press while it is still moving |