summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/quick/handlers/qquickdraghandler.cpp37
-rw-r--r--src/quick/handlers/qquickdraghandler_p.h13
-rw-r--r--tests/auto/quick/pointerhandlers/qquickdraghandler/data/snapMode.qml86
-rw-r--r--tests/auto/quick/pointerhandlers/qquickdraghandler/qquickdraghandler.pro1
-rw-r--r--tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp74
5 files changed, 209 insertions, 2 deletions
diff --git a/src/quick/handlers/qquickdraghandler.cpp b/src/quick/handlers/qquickdraghandler.cpp
index 22fe3df4d0..61b66beff4 100644
--- a/src/quick/handlers/qquickdraghandler.cpp
+++ b/src/quick/handlers/qquickdraghandler.cpp
@@ -114,8 +114,14 @@ void QQuickDragHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEvent
QQuickMultiPointHandler::onGrabChanged(grabber, transition, point);
if (grabber == this && transition == QQuickEventPoint::GrabExclusive && target()) {
// In case the grab got handed over from another grabber, we might not get the Press.
- if (!m_pressedInsideTarget) {
- if (target() != parentItem())
+
+ auto isDescendant = [](QQuickItem *parent, QQuickItem *target) {
+ return (target != parent) && !target->isAncestorOf(parent);
+ };
+ if (m_snapMode == SnapAlways
+ || (m_snapMode == SnapIfPressedOutsideTarget && !m_pressedInsideTarget)
+ || (m_snapMode == SnapAuto && !m_pressedInsideTarget && isDescendant(parentItem(), target()))
+ ) {
m_pressTargetPos = QPointF(target()->width(), target()->height()) / 2;
} else if (m_pressTargetPos.isNull()) {
m_pressTargetPos = targetCentroidPosition();
@@ -123,6 +129,33 @@ void QQuickDragHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEvent
}
}
+/*!
+ \qmlproperty enumeration QtQuick.DragHandler::snapMode
+
+ This property holds the snap mode.
+
+ The snap mode configures snapping of the \l target item's center to the event point.
+
+ Possible values:
+ \value DragHandler.SnapNever Never snap
+ \value DragHandler.SnapAuto The \l target snaps if the event point was pressed outside of the \l target
+ item \e and the \l target is a descendant of \l parentItem (default)
+ \value DragHandler.SnapWhenPressedOutsideTarget The \l target snaps if the event point was pressed outside of the \l target
+ \value DragHandler.SnapAlways Always snap
+*/
+QQuickDragHandler::SnapMode QQuickDragHandler::snapMode() const
+{
+ return m_snapMode;
+}
+
+void QQuickDragHandler::setSnapMode(QQuickDragHandler::SnapMode mode)
+{
+ if (mode == m_snapMode)
+ return;
+ m_snapMode = mode;
+ emit snapModeChanged();
+}
+
void QQuickDragHandler::onActiveChanged()
{
QQuickMultiPointHandler::onActiveChanged();
diff --git a/src/quick/handlers/qquickdraghandler_p.h b/src/quick/handlers/qquickdraghandler_p.h
index 748026488a..18c1fd841d 100644
--- a/src/quick/handlers/qquickdraghandler_p.h
+++ b/src/quick/handlers/qquickdraghandler_p.h
@@ -62,8 +62,17 @@ class Q_QUICK_PRIVATE_EXPORT QQuickDragHandler : public QQuickMultiPointHandler
Q_PROPERTY(QQuickDragAxis * xAxis READ xAxis CONSTANT)
Q_PROPERTY(QQuickDragAxis * yAxis READ yAxis CONSTANT)
Q_PROPERTY(QVector2D translation READ translation NOTIFY translationChanged)
+ Q_PROPERTY(SnapMode snapMode READ snapMode WRITE setSnapMode NOTIFY snapModeChanged)
public:
+ enum SnapMode {
+ NoSnap = 0,
+ SnapAuto,
+ SnapIfPressedOutsideTarget,
+ SnapAlways
+ };
+ Q_ENUM(SnapMode)
+
explicit QQuickDragHandler(QQuickItem *parent = nullptr);
void handlePointerEventImpl(QQuickPointerEvent *event) override;
@@ -73,11 +82,14 @@ public:
QVector2D translation() const { return m_translation; }
void setTranslation(const QVector2D &trans);
+ QQuickDragHandler::SnapMode snapMode() const;
+ void setSnapMode(QQuickDragHandler::SnapMode mode);
void enforceConstraints();
Q_SIGNALS:
void translationChanged();
+ void snapModeChanged();
protected:
void onActiveChanged() override;
@@ -97,6 +109,7 @@ private:
QQuickDragAxis m_xAxis;
QQuickDragAxis m_yAxis;
+ QQuickDragHandler::SnapMode m_snapMode = SnapAuto;
bool m_pressedInsideTarget = false;
friend class QQuickDragAxis;
diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/snapMode.qml b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/snapMode.qml
new file mode 100644
index 0000000000..d6eb791700
--- /dev/null
+++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/snapMode.qml
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.12
+
+Item {
+ id: root
+ objectName: "snapMode"
+ width: 640
+ height: 480
+
+ Rectangle {
+ id: rect1
+ objectName: "rect1"
+ width: 90
+ height: 100
+ x: 100
+ y: 100
+ color: "teal"
+
+ Rectangle {
+ width: parent.width/2
+ height: parent.width/2
+ x: width/2
+ y: -x
+ color: dragHandler1.active ? "red" : "salmon"
+
+ DragHandler {
+ id: dragHandler1
+ objectName: "dragHandler1"
+ target: rect1
+ }
+ }
+ }
+
+
+ Rectangle {
+ id: rect2
+ objectName: "rect2"
+ width: 90
+ height: 100
+ x: 200
+ y: 100
+ color: "teal"
+
+ DragHandler {
+ id: dragHandler2
+ objectName: "dragHandler2"
+ target: rect2b
+ }
+
+ Rectangle {
+ id: rect2b
+ width: parent.width/2
+ height: parent.width/2
+ anchors.horizontalCenter: parent.horizontalCenter
+ y: -width/2
+ color: dragHandler2.active ? "red" : "salmon"
+ }
+ }
+}
diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/qquickdraghandler.pro b/tests/auto/quick/pointerhandlers/qquickdraghandler/qquickdraghandler.pro
index 42c4e46c4f..6258fb9392 100644
--- a/tests/auto/quick/pointerhandlers/qquickdraghandler/qquickdraghandler.pro
+++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/qquickdraghandler.pro
@@ -19,3 +19,4 @@ OTHER_FILES += data/DragAnywhereSlider.qml \
data/grabberstate.qml \
data/multipleSliders.qml \
data/reparenting.qml \
+ data/snapMode.qml \
diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp b/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp
index cc8c567e5c..7636bb38f1 100644
--- a/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp
+++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp
@@ -56,6 +56,8 @@ private slots:
void touchDrag();
void mouseDrag();
void dragFromMargin();
+ void snapMode_data();
+ void snapMode();
void touchDragMulti();
void touchDragMultiSliders_data();
void touchDragMultiSliders();
@@ -284,6 +286,78 @@ void tst_DragHandler::dragFromMargin() // QTBUG-74966
QCOMPARE(dragHandler->centroid().pressedButtons(), Qt::NoButton);
}
+void tst_DragHandler::snapMode_data()
+{
+ const int dragThreshold = QGuiApplication::styleHints()->startDragDistance();
+ QTest::addColumn<QString>("subTree");
+ QTest::addColumn<int>("snapMode");
+ QTest::addColumn<QPoint>("startDragPos");
+ QTest::addColumn<QPoint>("dragMovement");
+ QTest::addColumn<QPoint>("expectedMovement");
+
+ struct TestEntry {
+ const char *desc;
+ const char *subTree;
+ QQuickDragHandler::SnapMode mode;
+ QPoint startDragPos;
+ QPoint dragMovement;
+ QPoint expectedMovement;
+ };
+
+ TestEntry testdata[] = {
+ {"outside the target", "rect1", QQuickDragHandler::SnapAuto, QPoint(45, -10), QPoint(dragThreshold*2, 0), QPoint(dragThreshold*2, 0)},
+ {"inside the target", "rect1", QQuickDragHandler::SnapAuto, QPoint(45, 10), QPoint(dragThreshold*2, 0), QPoint(dragThreshold*2, 0)},
+ {"outside the target", "rect1", QQuickDragHandler::SnapAlways, QPoint(45, -10), QPoint(dragThreshold*2, 0), QPoint(dragThreshold*2, -50-10)},
+ {"outside the target", "rect1", QQuickDragHandler::NoSnap, QPoint(45, -10), QPoint(dragThreshold*2, 0), QPoint(dragThreshold*2, 0)},
+ {"outside the target", "rect1", QQuickDragHandler::SnapIfPressedOutsideTarget, QPoint(45, -10), QPoint(dragThreshold*2, 0), QPoint(dragThreshold*2, -50-10)},
+ {"inside the target", "rect1", QQuickDragHandler::SnapIfPressedOutsideTarget, QPoint(45, 10), QPoint(dragThreshold*2, 0), QPoint(dragThreshold*2, 0)},
+ //targets y pos moves from -25 to (25 + dragThreshold*2) because of snapping to center:
+ {"outside target, should snap", "rect2", QQuickDragHandler::SnapAuto, QPoint(45, 50), QPoint(0, dragThreshold*2), QPoint(0, 25 + 25 + dragThreshold*2)},
+ {"inside target, shouldn't snap", "rect2", QQuickDragHandler::SnapAuto, QPoint(45, 10), QPoint(0, dragThreshold*2), QPoint(0, dragThreshold*2)}
+ };
+
+ for (const TestEntry& e : testdata) {
+ const QMetaEnum menum = QMetaEnum::fromType<QQuickDragHandler::SnapMode>();
+ const QString dataTag = QString::fromLatin1("%1, %2, %3").arg(e.subTree).arg(menum.valueToKey(e.mode)).arg(e.desc);
+ QTest::newRow(dataTag.toUtf8().constData()) << e.subTree << (int)e.mode
+ << e.startDragPos << e.dragMovement << e.expectedMovement;
+ }
+}
+
+void tst_DragHandler::snapMode()
+{
+ QFETCH(QString, subTree);
+ QFETCH(QPoint, startDragPos);
+ QFETCH(QPoint, dragMovement);
+ QFETCH(int, snapMode);
+ QFETCH(QPoint, expectedMovement);
+
+ QScopedPointer<QQuickView> windowPtr;
+ createView(windowPtr, "snapMode.qml");
+ QQuickView * window = windowPtr.data();
+
+ QQuickItem *rect1 = window->rootObject()->findChild<QQuickItem*>(subTree);
+ QVERIFY(rect1);
+ QQuickItem *rect1b = rect1->childItems().first();
+ QVERIFY(rect1b);
+ QQuickDragHandler *dragHandler1 = rect1->findChild<QQuickDragHandler*>();
+ QVERIFY(dragHandler1);
+ dragHandler1->setSnapMode((QQuickDragHandler::SnapMode)snapMode);
+ QQuickItem *dragTarget = dragHandler1->target();
+ QPointF oldTargetPos = dragTarget->position();
+
+ QPoint p1 = rect1->mapToScene(QPointF(startDragPos)).toPoint();
+ QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1);
+ QVERIFY(!dragHandler1->active());
+ p1 += dragMovement;
+ QTest::mouseMove(window, p1);
+ QTRY_VERIFY(dragHandler1->active());
+ QCOMPARE(dragTarget->position(), oldTargetPos + expectedMovement);
+ QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1);
+ QTRY_VERIFY(!dragHandler1->active());
+ QCOMPARE(dragHandler1->centroid().pressedButtons(), Qt::NoButton);
+}
+
void tst_DragHandler::touchDragMulti()
{
const int dragThreshold = QGuiApplication::styleHints()->startDragDistance();