Expose drag threshold in MouseArea
authorJens Bache-Wiig <jens.bache-wiig@digia.com>
Thu, 22 Aug 2013 15:20:52 +0000 (17:20 +0200)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Fri, 13 Sep 2013 15:51:48 +0000 (17:51 +0200)
In several cases such as for creating a Slider, it is useful to  have a
0 pixel threshold in order to initiate a drag operation. We have
previously hardcoded this to a platform dependent (usually 10 pixel)
value for MouseArea.

Note that we have no way of indicating the version/revision number
in a grouped property for documentation at the moment.

In addition we deliberately had to remove the REVISION from the
property because it is blocked by QTBUG-33179. However, since
this is not a user-creatable type it should not cause any
issues in practice.

This patch adds MouseArea.drag.threshold in order to improve on this.

Change-Id: Ia4871e64fab39e30c4494f00be99ad38cdd630df
Reviewed-by: J-P Nurmi <jpnurmi@digia.com>
src/quick/items/qquickdrag.cpp
src/quick/items/qquickdrag_p.h
src/quick/items/qquickmousearea.cpp
src/quick/items/qquickwindow.cpp
src/quick/items/qquickwindow_p.h
tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp

index 0ae26cb..166b40a 100644 (file)
 #include <QtQuick/private/qquickevents_p_p.h>
 #include <private/qquickitemchangelistener_p.h>
 #include <private/qv8engine_p.h>
-#include <QtCore/qcoreapplication.h>
 #include <QtCore/qmimedata.h>
 #include <QtQml/qqmlinfo.h>
 #include <QtGui/qdrag.h>
 #include <QtGui/qevent.h>
+#include <QtGui/qstylehints.h>
+#include <QtGui/qguiapplication.h>
 
 #ifndef QT_NO_DRAGANDDROP
 
@@ -785,7 +786,8 @@ void QQuickDragAttached::startDrag(QQmlV4Function *args)
 
 QQuickDrag::QQuickDrag(QObject *parent)
 : QObject(parent), _target(0), _axis(XAndYAxis), _xmin(-FLT_MAX),
-_xmax(FLT_MAX), _ymin(-FLT_MAX), _ymax(FLT_MAX), _active(false), _filterChildren(false)
+_xmax(FLT_MAX), _ymin(-FLT_MAX), _ymax(FLT_MAX), _active(false), _filterChildren(false),
+  _threshold(qApp->styleHints()->startDragDistance())
 {
 }
 
@@ -879,6 +881,25 @@ void QQuickDrag::setYmax(qreal m)
     emit maximumYChanged();
 }
 
+
+qreal QQuickDrag::threshold() const
+{
+    return _threshold;
+}
+
+void QQuickDrag::setThreshold(qreal value)
+{
+    if (_threshold != value) {
+        _threshold = value;
+        emit thresholdChanged();
+    }
+}
+
+void QQuickDrag::resetThreshold()
+{
+    setThreshold(qApp->styleHints()->startDragDistance());
+}
+
 bool QQuickDrag::active() const
 {
     return _active;
index 098fcc6..2282935 100644 (file)
@@ -157,6 +157,9 @@ class Q_AUTOTEST_EXPORT QQuickDrag : public QObject
     Q_PROPERTY(qreal maximumY READ ymax WRITE setYmax NOTIFY maximumYChanged)
     Q_PROPERTY(bool active READ active NOTIFY activeChanged)
     Q_PROPERTY(bool filterChildren READ filterChildren WRITE setFilterChildren NOTIFY filterChildrenChanged)
+    // Note, threshold was added in QtQuick 2.2 but REVISION is not supported (or needed) for grouped
+    // properties See QTBUG-33179
+    Q_PROPERTY(qreal threshold READ threshold WRITE setThreshold NOTIFY thresholdChanged RESET resetThreshold)
     //### consider drag and drop
 
 public:
@@ -182,6 +185,10 @@ public:
     qreal ymax() const;
     void setYmax(qreal);
 
+    qreal threshold() const;
+    void setThreshold(qreal);
+    void resetThreshold();
+
     bool active() const;
     void setActive(bool);
 
@@ -199,6 +206,7 @@ Q_SIGNALS:
     void maximumYChanged();
     void activeChanged();
     void filterChildrenChanged();
+    void thresholdChanged();
 
 private:
     QQuickItem *_target;
@@ -209,6 +217,7 @@ private:
     qreal _ymax;
     bool _active : 1;
     bool _filterChildren: 1;
+    qreal _threshold;
     Q_DISABLE_COPY(QQuickDrag)
 };
 
index 8a73a5d..39f07f2 100644 (file)
 #include <private/qqmldata_p.h>
 
 #include <QtGui/private/qguiapplication_p.h>
-
 #include <QtGui/qevent.h>
-#include <QtGui/qguiapplication.h>
-#include <QtGui/qstylehints.h>
 
 #include <float.h>
 
@@ -695,8 +692,8 @@ void QQuickMouseArea::mouseMoveEvent(QMouseEvent *event)
             d->drag->target()->setPosition(dragPos);
 
         if (!keepMouseGrab()
-                && (QQuickWindowPrivate::dragOverThreshold(dragPos.x() - startPos.x(), Qt::XAxis, event)
-                || QQuickWindowPrivate::dragOverThreshold(dragPos.y() - startPos.y(), Qt::YAxis, event))) {
+                && (QQuickWindowPrivate::dragOverThreshold(dragPos.x() - startPos.x(), Qt::XAxis, event, d->drag->threshold())
+                || QQuickWindowPrivate::dragOverThreshold(dragPos.y() - startPos.y(), Qt::YAxis, event, d->drag->threshold()))) {
             setKeepMouseGrab(true);
             d->stealMouse = true;
             d->startScene = event->windowPos();
@@ -1189,6 +1186,7 @@ void QQuickMouseArea::setCursorShape(Qt::CursorShape shape)
     \qmlproperty real QtQuick2::MouseArea::drag.minimumY
     \qmlproperty real QtQuick2::MouseArea::drag.maximumY
     \qmlproperty bool QtQuick2::MouseArea::drag.filterChildren
+    \qmlproperty real QtQuick2::MouseArea::drag.threshold
 
     \c drag provides a convenient way to make an item draggable.
 
@@ -1213,6 +1211,10 @@ void QQuickMouseArea::setCursorShape(Qt::CursorShape shape)
     If \c drag.filterChildren is set to true, a drag can override descendant MouseAreas.  This
     enables a parent MouseArea to handle drags, for example, while descendants handle clicks:
 
+    \c drag.threshold determines the threshold in pixels of when the drag operation should
+    start. By default this is bound to a platform dependent value. This property was added in
+    Qt Quick 2.2.
+
     \snippet qml/mousearea/mouseareadragfilter.qml dragfilter
 
 */
index c8bc026..513c3da 100644 (file)
@@ -2077,13 +2077,13 @@ bool QQuickWindowPrivate::sendFilteredMouseEvent(QQuickItem *target, QQuickItem
     return false;
 }
 
-bool QQuickWindowPrivate::dragOverThreshold(qreal d, Qt::Axis axis, QMouseEvent *event)
+bool QQuickWindowPrivate::dragOverThreshold(qreal d, Qt::Axis axis, QMouseEvent *event, int startDragThreshold)
 {
     QStyleHints *styleHints = qApp->styleHints();
     int caps = QGuiApplicationPrivate::mouseEventCaps(event);
     bool dragVelocityLimitAvailable = (caps & QTouchDevice::Velocity)
         && styleHints->startDragVelocity();
-    bool overThreshold = qAbs(d) > styleHints->startDragDistance();
+    bool overThreshold = qAbs(d) > (startDragThreshold >= 0 ? startDragThreshold : styleHints->startDragDistance());
     if (dragVelocityLimitAvailable) {
         QVector2D velocityVec = QGuiApplicationPrivate::mouseEventVelocity(event);
         qreal velocity = axis == Qt::XAxis ? velocityVec.x() : velocityVec.y();
index 12925cb..e29ceb5 100644 (file)
@@ -221,7 +221,7 @@ public:
 
     static bool defaultAlphaBuffer;
 
-    static bool dragOverThreshold(qreal d, Qt::Axis axis, QMouseEvent *event);
+    static bool dragOverThreshold(qreal d, Qt::Axis axis, QMouseEvent *event, int startDragThreshold = -1);
 
     // data property
     static void data_append(QQmlListProperty<QObject> *, QObject *);
index de1c120..0c0b51d 100644 (file)
@@ -86,6 +86,7 @@ private slots:
     void resetDrag();
     void dragging_data() { acceptedButton_data(); }
     void dragging();
+    void dragThreshold();
     void invalidDrag_data() { rejectedButton_data(); }
     void invalidDrag();
     void setDragOnPressed();
@@ -235,6 +236,20 @@ void tst_QQuickMouseArea::dragProperties()
 
     drag->setFilterChildren(true);
     QCOMPARE(filterChildrenSpy.count(), 1);
+
+    // threshold
+    QCOMPARE(int(drag->threshold()), qApp->styleHints()->startDragDistance());
+    QSignalSpy thresholdSpy(drag, SIGNAL(thresholdChanged()));
+    drag->setThreshold(0.0);
+    QCOMPARE(drag->threshold(), 0.0);
+    QCOMPARE(thresholdSpy.count(), 1);
+    drag->setThreshold(99);
+    QCOMPARE(thresholdSpy.count(), 2);
+    drag->setThreshold(99);
+    QCOMPARE(thresholdSpy.count(), 2);
+    drag->resetThreshold();
+    QCOMPARE(int(drag->threshold()), qApp->styleHints()->startDragDistance());
+    QCOMPARE(thresholdSpy.count(), 3);
 }
 
 void tst_QQuickMouseArea::resetDrag()
@@ -318,6 +333,61 @@ void tst_QQuickMouseArea::dragging()
     QCOMPARE(blackRect->y(), 61.0);
 }
 
+
+void tst_QQuickMouseArea::dragThreshold()
+{
+    QQuickView window;
+    QByteArray errorMessage;
+    QVERIFY2(initView(window, testFileUrl("dragging.qml"), true, &errorMessage), errorMessage.constData());
+
+    window.show();
+    QVERIFY(QTest::qWaitForWindowExposed(&window));
+    QVERIFY(window.rootObject() != 0);
+
+    QQuickMouseArea *mouseRegion = window.rootObject()->findChild<QQuickMouseArea*>("mouseregion");
+    QQuickDrag *drag = mouseRegion->drag();
+
+    drag->setThreshold(5);
+
+    mouseRegion->setAcceptedButtons(Qt::LeftButton);
+    QQuickItem *blackRect = window.rootObject()->findChild<QQuickItem*>("blackrect");
+    QVERIFY(blackRect != 0);
+    QVERIFY(blackRect == drag->target());
+    QVERIFY(!drag->active());
+    QTest::mousePress(&window, Qt::LeftButton, 0, QPoint(100,100));
+    QVERIFY(!drag->active());
+    QCOMPARE(blackRect->x(), 50.0);
+    QCOMPARE(blackRect->y(), 50.0);
+    QTest::mouseMove(&window, QPoint(100, 102), 50);
+    QVERIFY(!drag->active());
+    QTest::mouseMove(&window, QPoint(100, 100), 50);
+    QVERIFY(!drag->active());
+    QTest::mouseMove(&window, QPoint(100, 104), 50);
+    QTest::mouseMove(&window, QPoint(100, 105), 50);
+    QVERIFY(!drag->active());
+    QTest::mouseMove(&window, QPoint(100, 106), 50);
+    QTest::mouseMove(&window, QPoint(100, 108), 50);
+    QVERIFY(drag->active());
+    QTest::mouseMove(&window, QPoint(100, 116), 50);
+    QTest::mouseMove(&window, QPoint(100, 122), 50);
+    QTRY_VERIFY(drag->active());
+    QTRY_COMPARE(blackRect->x(), 50.0);
+    QTRY_COMPARE(blackRect->y(), 66.0);
+    QTest::mouseRelease(&window, Qt::LeftButton, 0, QPoint(122,122));
+    QTRY_VERIFY(!drag->active());
+
+    // Immediate drag threshold
+    drag->setThreshold(0);
+    QTest::mousePress(&window, Qt::LeftButton, 0, QPoint(100,100));
+    QTest::mouseMove(&window, QPoint(100, 122), 50);
+    QVERIFY(!drag->active());
+    QTest::mouseMove(&window, QPoint(100, 123), 50);
+    QVERIFY(drag->active());
+    QTest::mouseMove(&window, QPoint(100, 124), 50);
+    QTest::mouseRelease(&window, Qt::LeftButton, 0, QPoint(100, 124));
+    QTRY_VERIFY(!drag->active());
+    drag->resetThreshold();
+}
 void tst_QQuickMouseArea::invalidDrag()
 {
     QFETCH(Qt::MouseButtons, acceptedButtons);