diff options
author | Shawn Rutledge <shawn.rutledge@qt.io> | 2018-05-16 14:52:32 +0200 |
---|---|---|
committer | Shawn Rutledge <shawn.rutledge@qt.io> | 2018-07-12 20:25:04 +0000 |
commit | 1bf1a1c460fcfb1555026cbb1127cfc6daee7ca0 (patch) | |
tree | 7135a86b43680bbbbef24b327f8811c69a78b027 | |
parent | 8092c5617092195d7df02775f423f5e420eb764f (diff) |
Add Flickable.synchronousDrag property
When it is set true, Flickable begins dragging by making the content
jump to the position where it would have been if there was no drag
threshold: that is, the content moves exactly in sync with the mouse
cursor or finger (as long as it's not hitting the bounds).
[ChangeLog][QtQuick][Flickable] Added a synchronousDrag property that
makes the content jump to the position it would have had if there was
no drag threshold, as soon as dragging begins.
Task-number: QTBUG-62902
Change-Id: I5f3b530956363172167896b0f19aec4a41bf82b3
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
-rw-r--r-- | src/quick/items/qquickflickable.cpp | 32 | ||||
-rw-r--r-- | src/quick/items/qquickflickable_p.h | 5 | ||||
-rw-r--r-- | src/quick/items/qquickflickable_p_p.h | 1 | ||||
-rw-r--r-- | src/quick/items/qquickitemsmodule.cpp | 1 | ||||
-rw-r--r-- | tests/auto/quick/qquickflickable/tst_qquickflickable.cpp | 66 | ||||
-rw-r--r-- | tests/manual/touch/flicktext.qml | 8 |
6 files changed, 110 insertions, 3 deletions
diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp index d892e3b490..5a3db5e1eb 100644 --- a/src/quick/items/qquickflickable.cpp +++ b/src/quick/items/qquickflickable.cpp @@ -248,6 +248,7 @@ QQuickFlickablePrivate::QQuickFlickablePrivate() , stealMouse(false), pressed(false) , scrollingPhase(false), interactive(true), calcVelocity(false) , pixelAligned(false) + , syncDrag(false) , lastPosTime(-1) , lastPressTime(0) , deceleration(QML_FLICK_DEFAULTDECELERATION) @@ -959,6 +960,33 @@ void QQuickFlickable::setPixelAligned(bool align) } } +/*! + \qmlproperty bool QtQuick::Flickable::synchronousDrag + \since 5.12 + + If this property is set to true, then when the mouse or touchpoint moves + far enough to begin dragging the content, the content will jump, such that + the content pixel which was under the cursor or touchpoint when pressed + remains under that point. + + The default is \c false, which provides a smoother experience (no jump) + at the cost that some of the drag distance is "lost" at the beginning. +*/ +bool QQuickFlickable::synchronousDrag() const +{ + Q_D(const QQuickFlickable); + return d->syncDrag; +} + +void QQuickFlickable::setSynchronousDrag(bool v) +{ + Q_D(QQuickFlickable); + if (v != d->syncDrag) { + d->syncDrag = v; + emit synchronousDragChanged(); + } +} + qint64 QQuickFlickablePrivate::computeCurrentTime(QInputEvent *event) const { if (0 != event->timestamp()) @@ -1075,7 +1103,7 @@ void QQuickFlickablePrivate::drag(qint64 currentTimestamp, QEvent::Type eventTyp if (overThreshold || elapsedSincePress > 200) { if (!vMoved) vData.dragStartOffset = dy; - qreal newY = dy + vData.pressPos - vData.dragStartOffset; + qreal newY = dy + vData.pressPos - (syncDrag ? 0 : vData.dragStartOffset); // Recalculate bounds in case margins have changed, but use the content // size estimate taken at the start of the drag in case the drag causes // the estimate to be altered @@ -1151,7 +1179,7 @@ void QQuickFlickablePrivate::drag(qint64 currentTimestamp, QEvent::Type eventTyp if (overThreshold || elapsedSincePress > 200) { if (!hMoved) hData.dragStartOffset = dx; - qreal newX = dx + hData.pressPos - hData.dragStartOffset; + qreal newX = dx + hData.pressPos - (syncDrag ? 0 : hData.dragStartOffset); const qreal minX = hData.dragMinBound + hData.startMargin; const qreal maxX = hData.dragMaxBound - hData.endMargin; if (!(boundsBehavior & QQuickFlickable::DragOverBounds)) { diff --git a/src/quick/items/qquickflickable_p.h b/src/quick/items/qquickflickable_p.h index 939e3af698..45921c8b86 100644 --- a/src/quick/items/qquickflickable_p.h +++ b/src/quick/items/qquickflickable_p.h @@ -106,6 +106,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickFlickable : public QQuickItem Q_PROPERTY(QQuickFlickableVisibleArea *visibleArea READ visibleArea CONSTANT) Q_PROPERTY(bool pixelAligned READ pixelAligned WRITE setPixelAligned NOTIFY pixelAlignedChanged) + Q_PROPERTY(bool synchronousDrag READ synchronousDrag WRITE setSynchronousDrag NOTIFY synchronousDragChanged REVISION 12) Q_PROPERTY(qreal horizontalOvershoot READ horizontalOvershoot NOTIFY horizontalOvershootChanged REVISION 9) Q_PROPERTY(qreal verticalOvershoot READ verticalOvershoot NOTIFY verticalOvershootChanged REVISION 9) @@ -213,6 +214,9 @@ public: bool pixelAligned() const; void setPixelAligned(bool align); + bool synchronousDrag() const; + void setSynchronousDrag(bool v); + qreal horizontalOvershoot() const; qreal verticalOvershoot() const; @@ -259,6 +263,7 @@ Q_SIGNALS: void dragStarted(); void dragEnded(); void pixelAlignedChanged(); + Q_REVISION(12) void synchronousDragChanged(); Q_REVISION(9) void horizontalOvershootChanged(); Q_REVISION(9) void verticalOvershootChanged(); diff --git a/src/quick/items/qquickflickable_p_p.h b/src/quick/items/qquickflickable_p_p.h index 08f069e830..dfb943d710 100644 --- a/src/quick/items/qquickflickable_p_p.h +++ b/src/quick/items/qquickflickable_p_p.h @@ -221,6 +221,7 @@ public: bool interactive : 1; bool calcVelocity : 1; bool pixelAligned : 1; + bool syncDrag : 1; QElapsedTimer timer; QBasicTimer movementEndingTimer; qint64 lastPosTime; diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp index 96927dd37a..806fd9e465 100644 --- a/src/quick/items/qquickitemsmodule.cpp +++ b/src/quick/items/qquickitemsmodule.cpp @@ -425,6 +425,7 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor) qmlRegisterType<QQuickAnimatedSprite, 12>("QtQuick", 2, 12, "AnimatedSprite"); qmlRegisterType<QQuickGradient, 12>(uri, 2, 12, "Gradient"); + qmlRegisterType<QQuickFlickable, 12>(uri, 2, 12, "Flickable"); } static void initResources() diff --git a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp index 248f8447e0..ba266824e6 100644 --- a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp +++ b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp @@ -203,6 +203,8 @@ private slots: void overshoot(); void overshoot_data(); void overshoot_reentrant(); + void synchronousDrag_data(); + void synchronousDrag(); private: void flickWithTouch(QQuickWindow *window, const QPoint &from, const QPoint &to); @@ -2458,6 +2460,70 @@ void tst_qquickflickable::overshoot_reentrant() QCOMPARE(flickable->verticalOvershoot(), 25.0); } +void tst_qquickflickable::synchronousDrag_data() +{ + QTest::addColumn<bool>("synchronousDrag"); + + QTest::newRow("default") << false; + QTest::newRow("synch") << true; +} + +void tst_qquickflickable::synchronousDrag() +{ + QFETCH(bool, synchronousDrag); + + QScopedPointer<QQuickView> scopedWindow(new QQuickView); + QQuickView *window = scopedWindow.data(); + window->setSource(testFileUrl("longList.qml")); + QTRY_COMPARE(window->status(), QQuickView::Ready); + QQuickViewTestUtil::centerOnScreen(window); + QQuickViewTestUtil::moveMouseAway(window); + window->show(); + QVERIFY(window->rootObject() != nullptr); + + QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(window->rootObject()); + QVERIFY(flickable != nullptr); + QCOMPARE(flickable->synchronousDrag(), false); + flickable->setSynchronousDrag(synchronousDrag); + + QPoint p1(100, 100); + QPoint p2(95, 95); + QPoint p3(70, 70); + QPoint p4(50, 50); + QPoint p5(30, 30); + QCOMPARE(flickable->contentY(), 0.0f); + + // Drag via mouse + moveAndPress(window, p1); + QTest::mouseMove(window, p2); + QTest::mouseMove(window, p3); + QTest::mouseMove(window, p4); + QCOMPARE(flickable->contentY(), synchronousDrag ? 50.0f : 0.0f); + QTest::mouseMove(window, p5); + if (!synchronousDrag) + QVERIFY(flickable->contentY() < 50.0f); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p5); + + // Reset to initial condition + flickable->setContentY(0); + + // Drag via touch + QTest::touchEvent(window, touchDevice).press(0, p1, window); + QQuickTouchUtils::flush(window); + QTest::touchEvent(window, touchDevice).move(0, p2, window); + QQuickTouchUtils::flush(window); + QTest::touchEvent(window, touchDevice).move(0, p3, window); + QQuickTouchUtils::flush(window); + QTest::touchEvent(window, touchDevice).move(0, p4, window); + QQuickTouchUtils::flush(window); + QCOMPARE(flickable->contentY(), synchronousDrag ? 50.0f : 0.0f); + QTest::touchEvent(window, touchDevice).move(0, p5, window); + QQuickTouchUtils::flush(window); + if (!synchronousDrag) + QVERIFY(flickable->contentY() < 50.0f); + QTest::touchEvent(window, touchDevice).release(0, p5, window); +} + QTEST_MAIN(tst_qquickflickable) #include "tst_qquickflickable.moc" diff --git a/tests/manual/touch/flicktext.qml b/tests/manual/touch/flicktext.qml index 6e8dcfded5..9e84261687 100644 --- a/tests/manual/touch/flicktext.qml +++ b/tests/manual/touch/flicktext.qml @@ -48,7 +48,7 @@ ** ****************************************************************************/ -import QtQuick 2.4 +import QtQuick 2.12 import "qrc:/quick/shared/" as Examples Rectangle { @@ -67,6 +67,7 @@ Rectangle { contentWidth: text.implicitWidth contentHeight: text.implicitHeight pixelAligned: pxAlignCB.checked + synchronousDrag: syncDragCB.checked Text { id: text text: "foo bar" @@ -366,10 +367,15 @@ Rectangle { } Column { + spacing: 2 Examples.CheckBox { id: pxAlignCB text: "pixel aligned" } + Examples.CheckBox { + id: syncDragCB + text: "synchronous drag" + } Text { text: "content X " + flick.contentX.toFixed(2) + " Y " + flick.contentY.toFixed(2) } |