Add dragging properties to Flickable
authorMartin Jones <martin.jones@nokia.com>
Thu, 28 Jul 2011 00:49:37 +0000 (10:49 +1000)
committerQt by Nokia <qt-info@nokia.com>
Thu, 28 Jul 2011 06:39:33 +0000 (08:39 +0200)
This allows the user to determine when a movement is due to the
user dragging the view directly, i.e. excluding any flick that
occurs after the touch is released.

Change-Id: Idf4b699946f808da6fa34ec21a3d2cb2f0ec9de6
Fixes: QTBUG-19685
Reviewed-on: http://codereview.qt.nokia.com/2310
Reviewed-by: Qt Sanity Bot <qt_sanity_bot@ovi.com>
Reviewed-by: Bea Lam <bea.lam@nokia.com>
doc/src/declarative/whatsnew.qdoc
src/declarative/items/qsgflickable.cpp
src/declarative/items/qsgflickable_p.h
src/declarative/items/qsgflickable_p_p.h
tests/auto/declarative/qsgflickable/tst_qsgflickable.cpp

index 0bf0fe6..f9b6683 100644 (file)
@@ -43,6 +43,8 @@ set binding when its \e when clause becomes false.
 QDeclarativeExpression can now be directly (and more efficiently) constructed from a
 QDeclarativeScriptString.
 
+Flickable: added dragging, draggingHorizontally and draggingVerically properties.
+
 \section2 QtQuick 1 is now a separate library and module
 
 Writing C++ applications using QtQuick 1 specific API, i.e. QDeclarativeView or QDeclarativeItem
index f562a85..2c04ab0 100644 (file)
@@ -428,6 +428,34 @@ void QSGFlickablePrivate::updateBeginningEnd()
         visibleArea->updateVisible();
 }
 
+/*
+XXXTODO add docs describing moving, dragging, flicking properties, e.g.
+
+When the user starts dragging the Flickable, the dragging and moving properties
+will be true.
+
+If the velocity is sufficient when the drag is ended, flicking may begin.
+
+The moving properties will remain true until all dragging and flicking
+is finished.
+*/
+
+/*!
+    \qmlsignal QtQuick2::Flickable::onDragStarted()
+
+    This handler is called when the view starts to be dragged due to user
+    interaction.
+*/
+
+/*!
+    \qmlsignal QtQuick2::Flickable::onDragEnded()
+
+    This handler is called when the user stops dragging the view.
+
+    If the velocity of the drag is suffient at the time the
+    touch/mouse button is released then a flick will start.
+*/
+
 QSGFlickable::QSGFlickable(QSGItem *parent)
   : QSGItem(*(new QSGFlickablePrivate), parent)
 {
@@ -698,6 +726,7 @@ void QSGFlickablePrivate::handleMouseMoveEvent(QGraphicsSceneMouseEvent *event)
     }
 
     if (hMoved || vMoved) {
+        draggingStarting();
         q->movementStarting();
         q->viewportMoved();
     }
@@ -724,8 +753,6 @@ void QSGFlickablePrivate::handleMouseReleaseEvent(QGraphicsSceneMouseEvent *even
     stealMouse = false;
     q->setKeepMouseGrab(false);
     pressed = false;
-    if (!lastPosTime.isValid())
-        return;
 
     // if we drag then pause before release we should not cause a flick.
     if (QSGItemPrivate::elapsed(lastPosTime) < 100) {
@@ -736,6 +763,11 @@ void QSGFlickablePrivate::handleMouseReleaseEvent(QGraphicsSceneMouseEvent *even
         vData.velocity = 0.0;
     }
 
+    draggingEnding();
+
+    if (!lastPosTime.isValid())
+        return;
+
     vTime = timeline.time();
 
     qreal velocity = vData.velocity;
@@ -1231,6 +1263,7 @@ void QSGFlickable::mouseUngrabEvent()
         // if our mouse grab has been removed (probably by another Flickable),
         // fix our state
         d->pressed = false;
+        d->draggingEnding();
         d->stealMouse = false;
         setKeepMouseGrab(false);
     }
@@ -1375,6 +1408,68 @@ bool QSGFlickable::isFlickingVertically() const
     return d->flickingVertically;
 }
 
+/*!
+    \qmlproperty bool QtQuick2::Flickable::dragging
+    \qmlproperty bool QtQuick2::Flickable::draggingHorizontally
+    \qmlproperty bool QtQuick2::Flickable::draggingVertically
+
+    These properties describe whether the view is currently moving horizontally,
+    vertically or in either direction, due to the user dragging the view.
+*/
+bool QSGFlickable::isDragging() const
+{
+    Q_D(const QSGFlickable);
+    return d->hData.dragging ||  d->vData.dragging;
+}
+
+bool QSGFlickable::isDraggingHorizontally() const
+{
+    Q_D(const QSGFlickable);
+    return d->hData.dragging;
+}
+
+bool QSGFlickable::isDraggingVertically() const
+{
+    Q_D(const QSGFlickable);
+    return d->vData.dragging;
+}
+
+void QSGFlickablePrivate::draggingStarting()
+{
+    Q_Q(QSGFlickable);
+    bool wasDragging = hData.dragging || vData.dragging;
+    if (hMoved && !hData.dragging) {
+        hData.dragging = true;
+        emit q->draggingHorizontallyChanged();
+    }
+    if (vMoved && !vData.dragging) {
+        vData.dragging = true;
+        emit q->draggingVerticallyChanged();
+    }
+    if (!wasDragging && (hData.dragging || vData.dragging)) {
+        emit q->draggingChanged();
+        emit q->dragStarted();
+    }
+}
+
+void QSGFlickablePrivate::draggingEnding()
+{
+    Q_Q(QSGFlickable);
+    bool wasDragging = hData.dragging || vData.dragging;
+    if (hData.dragging) {
+        hData.dragging = false;
+        emit q->draggingHorizontallyChanged();
+    }
+    if (vData.dragging) {
+        vData.dragging = false;
+        emit q->draggingVerticallyChanged();
+    }
+    if (wasDragging && !hData.dragging && !vData.dragging) {
+        emit q->draggingChanged();
+        emit q->dragEnded();
+    }
+}
+
 int QSGFlickable::pressDelay() const
 {
     Q_D(const QSGFlickable);
index e934b3a..7f12e8a 100644 (file)
@@ -75,6 +75,9 @@ class Q_AUTOTEST_EXPORT QSGFlickable : public QSGItem
     Q_PROPERTY(bool flicking READ isFlicking NOTIFY flickingChanged)
     Q_PROPERTY(bool flickingHorizontally READ isFlickingHorizontally NOTIFY flickingHorizontallyChanged)
     Q_PROPERTY(bool flickingVertically READ isFlickingVertically NOTIFY flickingVerticallyChanged)
+    Q_PROPERTY(bool dragging READ isDragging NOTIFY draggingChanged)
+    Q_PROPERTY(bool draggingHorizontally READ isDraggingHorizontally NOTIFY draggingHorizontallyChanged)
+    Q_PROPERTY(bool draggingVertically READ isDraggingVertically NOTIFY draggingVerticallyChanged)
     Q_PROPERTY(FlickableDirection flickableDirection READ flickableDirection WRITE setFlickableDirection NOTIFY flickableDirectionChanged)
 
     Q_PROPERTY(bool interactive READ isInteractive WRITE setInteractive NOTIFY interactiveChanged)
@@ -123,6 +126,9 @@ public:
     bool isFlicking() const;
     bool isFlickingHorizontally() const;
     bool isFlickingVertically() const;
+    bool isDragging() const;
+    bool isDraggingHorizontally() const;
+    bool isDraggingVertically() const;
 
     int pressDelay() const;
     void setPressDelay(int delay);
@@ -164,6 +170,9 @@ Q_SIGNALS:
     void flickingChanged();
     void flickingHorizontallyChanged();
     void flickingVerticallyChanged();
+    void draggingChanged();
+    void draggingHorizontallyChanged();
+    void draggingVerticallyChanged();
     void horizontalVelocityChanged();
     void verticalVelocityChanged();
     void isAtBoundaryChanged();
@@ -177,6 +186,8 @@ Q_SIGNALS:
     void movementEnded();
     void flickStarted();
     void flickEnded();
+    void dragStarted();
+    void dragEnded();
 
 protected:
     virtual bool childMouseEventFilter(QSGItem *, QEvent *);
index fd09b0e..9e854af 100644 (file)
@@ -96,7 +96,7 @@ public:
     struct AxisData {
         AxisData(QSGFlickablePrivate *fp, void (QSGFlickablePrivate::*func)(qreal))
             : move(fp, func), viewSize(-1), smoothVelocity(fp), atEnd(false), atBeginning(true)
-            , fixingUp(false), inOvershoot(false)
+            , fixingUp(false), inOvershoot(false), dragging(false)
         {}
 
         void reset() {
@@ -123,6 +123,7 @@ public:
         bool atBeginning : 1;
         bool fixingUp : 1;
         bool inOvershoot : 1;
+        bool dragging : 1;
     };
 
     void flickX(qreal velocity);
@@ -147,6 +148,9 @@ public:
 
     void itemGeometryChanged(QSGItem *, const QRectF &, const QRectF &);
 
+    void draggingStarting();
+    void draggingEnding();
+
 public:
     QSGItem *contentItem;
 
index d100781..b7c43ce 100644 (file)
@@ -77,6 +77,7 @@ private slots:
     void resizeContent();
     void returnToBounds();
     void wheel();
+    void movingAndDragging();
 
 private:
     QDeclarativeEngine engine;
@@ -400,6 +401,116 @@ void tst_qsgflickable::wheel()
     delete canvas;
 }
 
+void tst_qsgflickable::movingAndDragging()
+{
+    QSGView *canvas = new QSGView;
+    canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/flickable03.qml"));
+    canvas->show();
+    canvas->setFocus();
+    QVERIFY(canvas->rootObject() != 0);
+
+    QSGFlickable *flickable = qobject_cast<QSGFlickable*>(canvas->rootObject());
+    QVERIFY(flickable != 0);
+
+    QSignalSpy vDragSpy(flickable, SIGNAL(draggingVerticallyChanged()));
+    QSignalSpy hDragSpy(flickable, SIGNAL(draggingHorizontallyChanged()));
+    QSignalSpy dragSpy(flickable, SIGNAL(draggingChanged()));
+    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()));
+
+    //Vertical
+    QTest::mousePress(canvas, Qt::LeftButton, 0, QPoint(50, 90));
+
+    QMouseEvent moveEvent(QEvent::MouseMove, QPoint(50, 80), Qt::LeftButton, Qt::LeftButton, 0);
+    QApplication::sendEvent(canvas, &moveEvent);
+
+    moveEvent = QMouseEvent(QEvent::MouseMove, QPoint(50, 70), Qt::LeftButton, Qt::LeftButton, 0);
+    QApplication::sendEvent(canvas, &moveEvent);
+
+    moveEvent = QMouseEvent(QEvent::MouseMove, QPoint(50, 60), Qt::LeftButton, Qt::LeftButton, 0);
+    QApplication::sendEvent(canvas, &moveEvent);
+
+    QVERIFY(!flickable->isDraggingHorizontally());
+    QVERIFY(flickable->isDraggingVertically());
+    QVERIFY(flickable->isDragging());
+    QCOMPARE(vDragSpy.count(), 1);
+    QCOMPARE(dragSpy.count(), 1);
+    QCOMPARE(hDragSpy.count(), 0);
+    QCOMPARE(dragStartSpy.count(), 1);
+    QCOMPARE(dragEndSpy.count(), 0);
+
+    QVERIFY(!flickable->isMovingHorizontally());
+    QVERIFY(flickable->isMovingVertically());
+    QVERIFY(flickable->isMoving());
+    QCOMPARE(vMoveSpy.count(), 1);
+    QCOMPARE(moveSpy.count(), 1);
+    QCOMPARE(hMoveSpy.count(), 0);
+
+    QTest::mouseRelease(canvas, Qt::LeftButton, 0, QPoint(50, 60));
+
+    QVERIFY(!flickable->isDraggingVertically());
+    QVERIFY(!flickable->isDragging());
+    QCOMPARE(vDragSpy.count(), 2);
+    QCOMPARE(dragSpy.count(), 2);
+    QCOMPARE(hDragSpy.count(), 0);
+    QCOMPARE(dragStartSpy.count(), 1);
+    QCOMPARE(dragEndSpy.count(), 1);
+
+    // Don't test moving because a flick could occur
+
+    //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));
+
+    moveEvent = QMouseEvent(QEvent::MouseMove, QPoint(80, 50), Qt::LeftButton, Qt::LeftButton, 0);
+    QApplication::sendEvent(canvas, &moveEvent);
+
+    moveEvent = QMouseEvent(QEvent::MouseMove, QPoint(70, 50), Qt::LeftButton, Qt::LeftButton, 0);
+    QApplication::sendEvent(canvas, &moveEvent);
+
+    moveEvent = QMouseEvent(QEvent::MouseMove, QPoint(60, 50), Qt::LeftButton, Qt::LeftButton, 0);
+    QApplication::sendEvent(canvas, &moveEvent);
+
+    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);
+
+    QTest::mouseRelease(canvas, Qt::LeftButton, 0, QPoint(60, 50));
+
+    QVERIFY(!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
+
+    delete canvas;
+}
 
 template<typename T>
 T *tst_qsgflickable::findItem(QSGItem *parent, const QString &objectName)