diff options
-rw-r--r-- | src/quick/items/qquickflickable.cpp | 110 | ||||
-rw-r--r-- | src/quick/items/qquickflickable_p.h | 11 | ||||
-rw-r--r-- | src/quick/items/qquickflickable_p_p.h | 1 | ||||
-rw-r--r-- | src/quick/items/qquickitemsmodule.cpp | 2 | ||||
-rw-r--r-- | tests/auto/quick/qquickflickable/tst_qquickflickable.cpp | 143 |
5 files changed, 201 insertions, 66 deletions
diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp index 27d7072f03..f4dce3ea47 100644 --- a/src/quick/items/qquickflickable.cpp +++ b/src/quick/items/qquickflickable.cpp @@ -254,6 +254,7 @@ QQuickFlickablePrivate::QQuickFlickablePrivate() , flickBoost(1.0), fixupMode(Normal), vTime(0), visibleArea(0) , flickableDirection(QQuickFlickable::AutoFlickDirection) , boundsBehavior(QQuickFlickable::DragAndOvershootBounds) + , boundsMovement(QQuickFlickable::FollowBoundsBehavior) , rebound(0) { } @@ -1573,16 +1574,18 @@ void QQuickFlickablePrivate::replayDelayedPress() void QQuickFlickablePrivate::setViewportX(qreal x) { Q_Q(QQuickFlickable); - if (pixelAligned) - x = -Round(-x); - - contentItem->setX(x); - if (contentItem->x() != x) - return; // reentered + qreal effectiveX = pixelAligned ? -Round(-x) : x; const qreal maxX = q->maxXExtent(); const qreal minX = q->minXExtent(); + if (boundsMovement == int(QQuickFlickable::StopAtBounds)) + effectiveX = qBound(maxX, effectiveX, minX); + + contentItem->setX(effectiveX); + if (contentItem->x() != effectiveX) + return; // reentered + qreal overshoot = 0.0; if (x <= maxX) overshoot = maxX - x; @@ -1598,16 +1601,18 @@ void QQuickFlickablePrivate::setViewportX(qreal x) void QQuickFlickablePrivate::setViewportY(qreal y) { Q_Q(QQuickFlickable); - if (pixelAligned) - y = -Round(-y); - - contentItem->setY(y); - if (contentItem->y() != y) - return; // reentered + qreal effectiveY = pixelAligned ? -Round(-y) : y; const qreal maxY = q->maxYExtent(); const qreal minY = q->minYExtent(); + if (boundsMovement == int(QQuickFlickable::StopAtBounds)) + effectiveY = qBound(maxY, effectiveY, minY); + + contentItem->setY(effectiveY); + if (contentItem->y() != effectiveY) + return; // reentered + qreal overshoot = 0.0; if (y <= maxY) overshoot = maxY - y; @@ -1867,8 +1872,9 @@ QQmlListProperty<QQuickItem> QQuickFlickable::flickableChildren() beyond the Flickable's boundaries, or overshoot the Flickable's boundaries when flicked. - This enables the feeling that the edges of the view are soft, - rather than a hard physical boundary. + When the \l boundsMovement is \c Flickable.FollowBoundsBehavior, a value + other than \c Flickable.StopAtBounds will give a feeling that the edges of + the view are soft, rather than a hard physical boundary. The \c boundsBehavior can be one of: @@ -1884,7 +1890,7 @@ QQmlListProperty<QQuickItem> QQuickFlickable::flickableChildren() boundary when flicked. \endlist - \sa horizontalOvershoot, verticalOvershoot + \sa horizontalOvershoot, verticalOvershoot, boundsMovement */ QQuickFlickable::BoundsBehavior QQuickFlickable::boundsBehavior() const { @@ -2695,7 +2701,11 @@ void QQuickFlickablePrivate::updateVelocity() The value is negative when the content is dragged or flicked beyond the beginning, and positive when beyond the end; \c 0.0 otherwise. - \sa verticalOvershoot, boundsBehavior + Whether the values are reported for dragging and/or flicking is determined by + \l boundsBehavior. The overshoot distance is reported even when \l boundsMovement + is \c Flickable.StopAtBounds. + + \sa verticalOvershoot, boundsBehavior, boundsMovement */ qreal QQuickFlickable::horizontalOvershoot() const { @@ -2712,7 +2722,11 @@ qreal QQuickFlickable::horizontalOvershoot() const The value is negative when the content is dragged or flicked beyond the beginning, and positive when beyond the end; \c 0.0 otherwise. - \sa horizontalOvershoot, boundsBehavior + Whether the values are reported for dragging and/or flicking is determined by + \l boundsBehavior. The overshoot distance is reported even when \l boundsMovement + is \c Flickable.StopAtBounds. + + \sa horizontalOvershoot, boundsBehavior, boundsMovement */ qreal QQuickFlickable::verticalOvershoot() const { @@ -2720,4 +2734,66 @@ qreal QQuickFlickable::verticalOvershoot() const return d->vData.overshoot; } +/*! + \qmlproperty enumeration QtQuick::Flickable::boundsMovement + \since 5.10 + + This property holds whether the flickable will give a feeling that the edges of the + view are soft, rather than a hard physical boundary. + + The \c boundsMovement can be one of: + + \list + \li Flickable.StopAtBounds - this allows implementing custom edge effects where the + contents do not follow drags or flicks beyond the bounds of the flickable. The values + of \l horizontalOvershoot and \l verticalOvershoot can be utilized to implement custom + edge effects. + \li Flickable.FollowBoundsBehavior (default) - whether the contents follow drags or + flicks beyond the bounds of the flickable is determined by \l boundsBehavior. + \endlist + + The following example keeps the contents within bounds and instead applies a flip + effect when flicked over horizontal bounds: + \code + Flickable { + id: flickable + boundsMovement: Flickable.StopAtBounds + boundsBehavior: Flickable.DragAndOvershootBounds + transform: Rotation { + axis { x: 0; y: 1; z: 0 } + origin.x: flickable.width / 2 + origin.y: flickable.height / 2 + angle: Math.min(30, Math.max(-30, flickable.horizontalOvershoot)) + } + } + \endcode + + The following example keeps the contents within bounds and instead applies an opacity + effect when dragged over vertical bounds: + \code + Flickable { + boundsMovement: Flickable.StopAtBounds + boundsBehavior: Flickable.DragOverBounds + opacity: Math.max(0.5, 1.0 - Math.abs(verticalOvershoot) / height) + } + \endcode + + \sa boundsBehavior, verticalOvershoot, horizontalOvershoot +*/ +QQuickFlickable::BoundsMovement QQuickFlickable::boundsMovement() const +{ + Q_D(const QQuickFlickable); + return d->boundsMovement; +} + +void QQuickFlickable::setBoundsMovement(BoundsMovement movement) +{ + Q_D(QQuickFlickable); + if (d->boundsMovement == movement) + return; + + d->boundsMovement = movement; + emit boundsMovementChanged(); +} + QT_END_NAMESPACE diff --git a/src/quick/items/qquickflickable_p.h b/src/quick/items/qquickflickable_p.h index 52cade1472..7558ee7df8 100644 --- a/src/quick/items/qquickflickable_p.h +++ b/src/quick/items/qquickflickable_p.h @@ -80,6 +80,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickFlickable : public QQuickItem Q_PROPERTY(qreal verticalVelocity READ verticalVelocity NOTIFY verticalVelocityChanged) Q_PROPERTY(BoundsBehavior boundsBehavior READ boundsBehavior WRITE setBoundsBehavior NOTIFY boundsBehaviorChanged) + Q_PROPERTY(BoundsMovement boundsMovement READ boundsMovement WRITE setBoundsMovement NOTIFY boundsMovementChanged REVISION 10) Q_PROPERTY(QQuickTransition *rebound READ rebound WRITE setRebound NOTIFY reboundChanged) Q_PROPERTY(qreal maximumFlickVelocity READ maximumFlickVelocity WRITE setMaximumFlickVelocity NOTIFY maximumFlickVelocityChanged) Q_PROPERTY(qreal flickDeceleration READ flickDeceleration WRITE setFlickDeceleration NOTIFY flickDecelerationChanged) @@ -132,6 +133,15 @@ public: BoundsBehavior boundsBehavior() const; void setBoundsBehavior(BoundsBehavior); + enum BoundsMovement { + // StopAtBounds = 0x0, + FollowBoundsBehavior = 0x1 + }; + Q_ENUM(BoundsMovement) + + BoundsMovement boundsMovement() const; + void setBoundsMovement(BoundsMovement movement); + QQuickTransition *rebound() const; void setRebound(QQuickTransition *transition); @@ -237,6 +247,7 @@ Q_SIGNALS: void flickableDirectionChanged(); void interactiveChanged(); void boundsBehaviorChanged(); + Q_REVISION(10) void boundsMovementChanged(); void reboundChanged(); void maximumFlickVelocityChanged(); void flickDecelerationChanged(); diff --git a/src/quick/items/qquickflickable_p_p.h b/src/quick/items/qquickflickable_p_p.h index 1ceff22dfc..8609a15fcd 100644 --- a/src/quick/items/qquickflickable_p_p.h +++ b/src/quick/items/qquickflickable_p_p.h @@ -247,6 +247,7 @@ public: QQuickFlickableVisibleArea *visibleArea; QQuickFlickable::FlickableDirection flickableDirection; QQuickFlickable::BoundsBehavior boundsBehavior; + QQuickFlickable::BoundsMovement boundsMovement; QQuickTransition *rebound; void viewportAxisMoved(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp index 4d6ae0c3e2..13f23c918a 100644 --- a/src/quick/items/qquickitemsmodule.cpp +++ b/src/quick/items/qquickitemsmodule.cpp @@ -389,6 +389,8 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor) #if QT_CONFIG(quick_shadereffect) qmlRegisterType<QQuickShaderEffectSource, 2>(uri, 2, 9, "ShaderEffectSource"); #endif + + qmlRegisterType<QQuickFlickable, 10>(uri, 2, 10, "Flickable"); } static void initResources() diff --git a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp index f8277c6895..ef6e444580 100644 --- a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp +++ b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp @@ -2216,6 +2216,7 @@ Q_DECLARE_METATYPE(QQuickFlickable::BoundsBehavior) void tst_qquickflickable::overshoot() { QFETCH(QQuickFlickable::BoundsBehavior, boundsBehavior); + QFETCH(int, boundsMovement); QScopedPointer<QQuickView> window(new QQuickView); window->setSource(testFileUrl("overshoot.qml")); @@ -2232,6 +2233,7 @@ void tst_qquickflickable::overshoot() QCOMPARE(flickable->contentHeight(), 400.0); flickable->setBoundsBehavior(boundsBehavior); + flickable->setBoundsMovement(QQuickFlickable::BoundsMovement(boundsMovement)); // drag past the beginning QTest::mousePress(window.data(), Qt::LeftButton, 0, QPoint(10, 10)); @@ -2240,23 +2242,30 @@ void tst_qquickflickable::overshoot() QTest::mouseMove(window.data(), QPoint(40, 40)); QTest::mouseRelease(window.data(), Qt::LeftButton, 0, QPoint(50, 50)); + if ((boundsMovement == QQuickFlickable::FollowBoundsBehavior) && (boundsBehavior & QQuickFlickable::DragOverBounds)) { + QVERIFY(flickable->property("minContentX").toReal() < 0.0); + QVERIFY(flickable->property("minContentY").toReal() < 0.0); + } else { + QCOMPARE(flickable->property("minContentX").toReal(), 0.0); + QCOMPARE(flickable->property("minContentY").toReal(), 0.0); + } if (boundsBehavior & QQuickFlickable::DragOverBounds) { - QVERIFY(flickable->property("minVerticalOvershoot").toReal() < 0.0); QVERIFY(flickable->property("minHorizontalOvershoot").toReal() < 0.0); - QCOMPARE(flickable->property("minContentY").toReal(), - flickable->property("minVerticalOvershoot").toReal()); - QCOMPARE(flickable->property("minContentX").toReal(), - flickable->property("minHorizontalOvershoot").toReal()); + QVERIFY(flickable->property("minVerticalOvershoot").toReal() < 0.0); } else { - QCOMPARE(flickable->property("minContentY").toReal(), 0.0); - QCOMPARE(flickable->property("minContentX").toReal(), 0.0); - QCOMPARE(flickable->property("minVerticalOvershoot").toReal(), 0.0); QCOMPARE(flickable->property("minHorizontalOvershoot").toReal(), 0.0); + QCOMPARE(flickable->property("minVerticalOvershoot").toReal(), 0.0); + } + if (bool(boundsMovement == QQuickFlickable::FollowBoundsBehavior) == bool(boundsBehavior & QQuickFlickable::DragOverBounds)) { + QCOMPARE(flickable->property("minContentX").toReal(), + flickable->property("minHorizontalOvershoot").toReal()); + QCOMPARE(flickable->property("minContentY").toReal(), + flickable->property("minVerticalOvershoot").toReal()); } - QCOMPARE(flickable->property("maxContentY").toReal(), 0.0); QCOMPARE(flickable->property("maxContentX").toReal(), 0.0); - QCOMPARE(flickable->property("maxVerticalOvershoot").toReal(), 0.0); + QCOMPARE(flickable->property("maxContentY").toReal(), 0.0); QCOMPARE(flickable->property("maxHorizontalOvershoot").toReal(), 0.0); + QCOMPARE(flickable->property("maxVerticalOvershoot").toReal(), 0.0); flickable->setContentX(20.0); flickable->setContentY(20.0); @@ -2266,23 +2275,30 @@ void tst_qquickflickable::overshoot() flick(window.data(), QPoint(10, 10), QPoint(50, 50), 100); QTRY_VERIFY(!flickable->property("flicking").toBool()); + if ((boundsMovement == QQuickFlickable::FollowBoundsBehavior) && (boundsBehavior & QQuickFlickable::OvershootBounds)) { + QVERIFY(flickable->property("minContentX").toReal() < 0.0); + QVERIFY(flickable->property("minContentY").toReal() < 0.0); + } else { + QCOMPARE(flickable->property("minContentX").toReal(), 0.0); + QCOMPARE(flickable->property("minContentY").toReal(), 0.0); + } if (boundsBehavior & QQuickFlickable::OvershootBounds) { - QVERIFY(flickable->property("minVerticalOvershoot").toReal() < 0.0); QVERIFY(flickable->property("minHorizontalOvershoot").toReal() < 0.0); - QCOMPARE(flickable->property("minContentY").toReal(), - flickable->property("minVerticalOvershoot").toReal()); - QCOMPARE(flickable->property("minContentX").toReal(), - flickable->property("minHorizontalOvershoot").toReal()); + QVERIFY(flickable->property("minVerticalOvershoot").toReal() < 0.0); } else { - QCOMPARE(flickable->property("minContentY").toReal(), 0.0); - QCOMPARE(flickable->property("minContentX").toReal(), 0.0); - QCOMPARE(flickable->property("minVerticalOvershoot").toReal(), 0.0); QCOMPARE(flickable->property("minHorizontalOvershoot").toReal(), 0.0); + QCOMPARE(flickable->property("minVerticalOvershoot").toReal(), 0.0); + } + if ((boundsMovement == QQuickFlickable::FollowBoundsBehavior) == (boundsBehavior & QQuickFlickable::OvershootBounds)) { + QCOMPARE(flickable->property("minContentX").toReal(), + flickable->property("minHorizontalOvershoot").toReal()); + QCOMPARE(flickable->property("minContentY").toReal(), + flickable->property("minVerticalOvershoot").toReal()); } - QCOMPARE(flickable->property("maxContentY").toReal(), 20.0); QCOMPARE(flickable->property("maxContentX").toReal(), 20.0); - QCOMPARE(flickable->property("maxVerticalOvershoot").toReal(), 0.0); + QCOMPARE(flickable->property("maxContentY").toReal(), 20.0); QCOMPARE(flickable->property("maxHorizontalOvershoot").toReal(), 0.0); + QCOMPARE(flickable->property("maxVerticalOvershoot").toReal(), 0.0); flickable->setContentX(200.0); flickable->setContentY(200.0); @@ -2295,23 +2311,30 @@ void tst_qquickflickable::overshoot() QTest::mouseMove(window.data(), QPoint(20, 20)); QTest::mouseRelease(window.data(), Qt::LeftButton, 0, QPoint(10, 10)); + if ((boundsMovement == QQuickFlickable::FollowBoundsBehavior) && (boundsBehavior & QQuickFlickable::DragOverBounds)) { + QVERIFY(flickable->property("maxContentX").toReal() > 200.0); + QVERIFY(flickable->property("maxContentX").toReal() > 200.0); + } else { + QCOMPARE(flickable->property("maxContentX").toReal(), 200.0); + QCOMPARE(flickable->property("maxContentY").toReal(), 200.0); + } if (boundsBehavior & QQuickFlickable::DragOverBounds) { - QVERIFY(flickable->property("maxVerticalOvershoot").toReal() > 0.0); QVERIFY(flickable->property("maxHorizontalOvershoot").toReal() > 0.0); - QCOMPARE(flickable->property("maxContentY").toReal() - 200.0, - flickable->property("maxVerticalOvershoot").toReal()); - QCOMPARE(flickable->property("maxContentX").toReal() - 200.0, - flickable->property("maxHorizontalOvershoot").toReal()); + QVERIFY(flickable->property("maxVerticalOvershoot").toReal() > 0.0); } else { - QCOMPARE(flickable->property("maxContentY").toReal(), 200.0); - QCOMPARE(flickable->property("maxContentX").toReal(), 200.0); - QCOMPARE(flickable->property("maxVerticalOvershoot").toReal(), 0.0); QCOMPARE(flickable->property("maxHorizontalOvershoot").toReal(), 0.0); + QCOMPARE(flickable->property("maxVerticalOvershoot").toReal(), 0.0); + } + if ((boundsMovement == QQuickFlickable::FollowBoundsBehavior) == (boundsBehavior & QQuickFlickable::DragOverBounds)) { + QCOMPARE(flickable->property("maxContentX").toReal() - 200.0, + flickable->property("maxHorizontalOvershoot").toReal()); + QCOMPARE(flickable->property("maxContentY").toReal() - 200.0, + flickable->property("maxVerticalOvershoot").toReal()); } - QCOMPARE(flickable->property("minContentY").toReal(), 200.0); QCOMPARE(flickable->property("minContentX").toReal(), 200.0); - QCOMPARE(flickable->property("minVerticalOvershoot").toReal(), 0.0); + QCOMPARE(flickable->property("minContentY").toReal(), 200.0); QCOMPARE(flickable->property("minHorizontalOvershoot").toReal(), 0.0); + QCOMPARE(flickable->property("minVerticalOvershoot").toReal(), 0.0); flickable->setContentX(180.0); flickable->setContentY(180.0); @@ -2321,37 +2344,59 @@ void tst_qquickflickable::overshoot() flick(window.data(), QPoint(50, 50), QPoint(10, 10), 100); QTRY_VERIFY(!flickable->property("flicking").toBool()); + if ((boundsMovement == QQuickFlickable::FollowBoundsBehavior) && (boundsBehavior & QQuickFlickable::OvershootBounds)) { + QVERIFY(flickable->property("maxContentX").toReal() > 200.0); + QVERIFY(flickable->property("maxContentY").toReal() > 200.0); + } else { + QCOMPARE(flickable->property("maxContentX").toReal(), 200.0); + QCOMPARE(flickable->property("maxContentY").toReal(), 200.0); + } if (boundsBehavior & QQuickFlickable::OvershootBounds) { - QVERIFY(flickable->property("maxVerticalOvershoot").toReal() > 0.0); QVERIFY(flickable->property("maxHorizontalOvershoot").toReal() > 0.0); - QCOMPARE(flickable->property("maxContentY").toReal() - 200.0, - flickable->property("maxVerticalOvershoot").toReal()); - QCOMPARE(flickable->property("maxContentX").toReal() - 200.0, - flickable->property("maxHorizontalOvershoot").toReal()); + QVERIFY(flickable->property("maxVerticalOvershoot").toReal() > 0.0); } else { - QCOMPARE(flickable->property("maxContentY").toReal(), 200.0); - QCOMPARE(flickable->property("maxContentX").toReal(), 200.0); - QCOMPARE(flickable->property("maxVerticalOvershoot").toReal(), 0.0); QCOMPARE(flickable->property("maxHorizontalOvershoot").toReal(), 0.0); + QCOMPARE(flickable->property("maxVerticalOvershoot").toReal(), 0.0); + } + if ((boundsMovement == QQuickFlickable::FollowBoundsBehavior) == (boundsBehavior & QQuickFlickable::OvershootBounds)) { + QCOMPARE(flickable->property("maxContentX").toReal() - 200.0, + flickable->property("maxHorizontalOvershoot").toReal()); + QCOMPARE(flickable->property("maxContentY").toReal() - 200.0, + flickable->property("maxVerticalOvershoot").toReal()); } - QCOMPARE(flickable->property("minContentY").toReal(), 180.0); QCOMPARE(flickable->property("minContentX").toReal(), 180.0); - QCOMPARE(flickable->property("minVerticalOvershoot").toReal(), 0.0); + QCOMPARE(flickable->property("minContentY").toReal(), 180.0); QCOMPARE(flickable->property("minHorizontalOvershoot").toReal(), 0.0); + QCOMPARE(flickable->property("minVerticalOvershoot").toReal(), 0.0); } void tst_qquickflickable::overshoot_data() { QTest::addColumn<QQuickFlickable::BoundsBehavior>("boundsBehavior"); - - QTest::newRow("StopAtBounds") - << QQuickFlickable::BoundsBehavior(QQuickFlickable::StopAtBounds); - QTest::newRow("DragOverBounds") - << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragOverBounds); - QTest::newRow("OvershootBounds") - << QQuickFlickable::BoundsBehavior(QQuickFlickable::OvershootBounds); - QTest::newRow("DragAndOvershootBounds") - << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragAndOvershootBounds); + QTest::addColumn<int>("boundsMovement"); + + QTest::newRow("StopAtBounds,FollowBoundsBehavior") + << QQuickFlickable::BoundsBehavior(QQuickFlickable::StopAtBounds) + << int(QQuickFlickable::FollowBoundsBehavior); + QTest::newRow("DragOverBounds,FollowBoundsBehavior") + << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragOverBounds) + << int(QQuickFlickable::FollowBoundsBehavior); + QTest::newRow("OvershootBounds,FollowBoundsBehavior") + << QQuickFlickable::BoundsBehavior(QQuickFlickable::OvershootBounds) + << int(QQuickFlickable::FollowBoundsBehavior); + QTest::newRow("DragAndOvershootBounds,FollowBoundsBehavior") + << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragAndOvershootBounds) + << int(QQuickFlickable::FollowBoundsBehavior); + + QTest::newRow("DragOverBounds,StopAtBounds") + << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragOverBounds) + << int(QQuickFlickable::StopAtBounds); + QTest::newRow("OvershootBounds,StopAtBounds") + << QQuickFlickable::BoundsBehavior(QQuickFlickable::OvershootBounds) + << int(QQuickFlickable::StopAtBounds); + QTest::newRow("DragAndOvershootBounds,StopAtBounds") + << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragAndOvershootBounds) + << int(QQuickFlickable::StopAtBounds); } void tst_qquickflickable::overshoot_reentrant() |