aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/quick/handlers/qquickpinchhandler.cpp103
-rw-r--r--src/quick/handlers/qquickpinchhandler_p.h10
-rw-r--r--tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/pinchDragMPTA.qml2
-rw-r--r--tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp13
-rw-r--r--tests/manual/pointer/map.qml2
5 files changed, 121 insertions, 9 deletions
diff --git a/src/quick/handlers/qquickpinchhandler.cpp b/src/quick/handlers/qquickpinchhandler.cpp
index 75a2847aa0..1c3dbf9902 100644
--- a/src/quick/handlers/qquickpinchhandler.cpp
+++ b/src/quick/handlers/qquickpinchhandler.cpp
@@ -47,6 +47,7 @@
#include <QMouseEvent>
#include <QDebug>
#include <qpa/qplatformnativeinterface.h>
+#include <math.h>
QT_BEGIN_NAMESPACE
@@ -334,7 +335,7 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event)
} else
#endif // QT_CONFIG(gestures)
{
- bool containsReleasedPoints = event->isReleaseEvent();
+ const bool containsReleasedPoints = event->isReleaseEvent();
QVector<QQuickEventPoint *> chosenPoints;
for (const QQuickHandlerPoint &p : m_currentPoints) {
QQuickEventPoint *ep = event->pointById(p.id());
@@ -342,10 +343,65 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event)
}
if (!active()) {
// Verify that at least one of the points has moved beyond threshold needed to activate the handler
+ int numberOfPointsDraggedOverThreshold = 0;
+ QVector2D accumulatedDrag;
+ const QVector2D currentCentroid(m_centroid.scenePosition());
+ const QVector2D pressCentroid(m_centroid.scenePressPosition());
+
+ QStyleHints *styleHints = QGuiApplication::styleHints();
+ const int dragThreshold = styleHints->startDragDistance();
+ const int dragThresholdSquared = dragThreshold * dragThreshold;
+
+ double accumulatedCentroidDistance = 0; // Used to detect scale
+ if (event->isPressEvent())
+ m_accumulatedStartCentroidDistance = 0; // Used to detect scale
+
+ float accumulatedMovementMagnitude = 0;
+
for (QQuickEventPoint *point : qAsConst(chosenPoints)) {
- if (!containsReleasedPoints && QQuickWindowPrivate::dragOverThreshold(point) && grabPoints(chosenPoints)) {
- setActive(true);
- break;
+ if (!containsReleasedPoints) {
+ accumulatedDrag += QVector2D(point->scenePressPosition() - point->scenePosition());
+ /*
+ In order to detect a drag, we want to check if all points have moved more or
+ less in the same direction.
+
+ We then take each point, and convert the point to a local coordinate system where
+ the centroid is the origin. This is done both for the press positions and the
+ current positions. We will then have two positions:
+
+ - pressCentroidRelativePosition
+ is the start point relative to the press centroid
+ - currentCentroidRelativePosition
+ is the current point relative to the current centroid
+
+ If those two points are far enough apart, it might not be considered as a drag
+ anymore. (Note that the threshold will matched to the average of the relative
+ movement of all the points). Therefore, a big relative movement will make a big
+ contribution to the average relative movement.
+
+ The algorithm then can be described as:
+ For each point:
+ - Calculate vector pressCentroidRelativePosition (from the press centroid to the press position)
+ - Calculate vector currentCentroidRelativePosition (from the current centroid to the current position)
+ - Calculate the relative movement vector:
+
+ centroidRelativeMovement = currentCentroidRelativePosition - pressCentroidRelativePosition
+
+ and measure its magnitude. Add the magnitude to the accumulatedMovementMagnitude.
+
+ Finally, if the accumulatedMovementMagnitude is below some threshold, it means
+ that the points were stationary or they were moved in parallel (e.g. the hand
+ was moved, but the relative position between each finger remained very much
+ the same). This is then used to rule out if there is a rotation or scale.
+ */
+ QVector2D pressCentroidRelativePosition = QVector2D(point->scenePosition()) - currentCentroid;
+ QVector2D currentCentroidRelativePosition = QVector2D(point->scenePressPosition()) - pressCentroid;
+ QVector2D centroidRelativeMovement = currentCentroidRelativePosition - pressCentroidRelativePosition;
+ accumulatedMovementMagnitude += centroidRelativeMovement.length();
+
+ accumulatedCentroidDistance += pressCentroidRelativePosition.length();
+ if (event->isPressEvent())
+ m_accumulatedStartCentroidDistance += (QVector2D(point->scenePressPosition()) - pressCentroid).length();
} else {
setPassiveGrab(point);
}
@@ -353,11 +409,44 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event)
point->setAccepted(false); // don't stop propagation
setPassiveGrab(point);
}
+ if (QQuickWindowPrivate::dragOverThreshold(point))
+ ++numberOfPointsDraggedOverThreshold;
+ }
+
+ const bool requiredNumberOfPointsDraggedOverThreshold = numberOfPointsDraggedOverThreshold >= minimumPointCount() && numberOfPointsDraggedOverThreshold <= maximumPointCount();
+ accumulatedMovementMagnitude /= m_currentPoints.count();
+
+ QVector2D avgDrag = accumulatedDrag / m_currentPoints.count();
+ if (!xAxis()->enabled())
+ avgDrag.setX(0);
+ if (!yAxis()->enabled())
+ avgDrag.setY(0);
+
+ const qreal centroidMovementDelta = (currentCentroid - pressCentroid).length();
+
+ qreal distanceToCentroidDelta = qAbs(accumulatedCentroidDistance - m_accumulatedStartCentroidDistance); // Used to detect scale
+ if (numberOfPointsDraggedOverThreshold >= 1) {
+ if (requiredNumberOfPointsDraggedOverThreshold && avgDrag.lengthSquared() >= dragThresholdSquared && accumulatedMovementMagnitude < dragThreshold) {
+ // Drag
+ if (grabPoints(chosenPoints))
+ setActive(true);
+ } else if (distanceToCentroidDelta > dragThreshold) { // all points should in accumulation have been moved beyond threshold (?)
+ // Scale
+ if (grabPoints(chosenPoints))
+ setActive(true);
+ } else if (distanceToCentroidDelta < dragThreshold && (centroidMovementDelta < dragThreshold)) {
+ // Rotate
+ // Since it wasn't a scale and if we exceeded the dragthreshold, and the
+ // centroid didn't moved much, the points must have been moved around the centroid.
+ if (grabPoints(chosenPoints))
+ setActive(true);
+ }
}
if (!active())
return;
}
// TODO check m_pinchOrigin: right now it acts like it's set to PinchCenter
+
// avoid mapping the minima and maxima, as they might have unmappable values
// such as -inf/+inf. Because of this we perform the bounding to min/max in local coords.
// 1. scale
@@ -376,7 +465,7 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event)
}
QPointF centroidParentPos;
- QRectF bounds(m_minimumX, m_minimumY, m_maximumX - m_minimumX, m_maximumY - m_minimumY);
+ QRectF bounds(QPointF(xAxis()->minimum(), yAxis()->minimum()), QPointF(xAxis()->maximum(), yAxis()->maximum()) );
if (target() && target()->parentItem()) {
centroidParentPos = target()->parentItem()->mapFromScene(m_centroid.scenePosition());
centroidParentPos = QPointF(qBound(bounds.left(), centroidParentPos.x(), bounds.right()),
@@ -391,6 +480,10 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event)
// 3. Drag/translate
const QPointF centroidStartParentPos = target()->parentItem()->mapFromScene(m_centroid.sceneGrabPosition());
m_activeTranslation = QVector2D(centroidParentPos - centroidStartParentPos);
+ if (!xAxis()->enabled())
+ m_activeTranslation.setX(0);
+ if (!yAxis()->enabled())
+ m_activeTranslation.setY(0);
// apply rotation + scaling around the centroid - then apply translation.
QMatrix4x4 mat;
diff --git a/src/quick/handlers/qquickpinchhandler_p.h b/src/quick/handlers/qquickpinchhandler_p.h
index 305802b6cf..6ec119ba72 100644
--- a/src/quick/handlers/qquickpinchhandler_p.h
+++ b/src/quick/handlers/qquickpinchhandler_p.h
@@ -55,6 +55,7 @@
#include "qevent.h"
#include "qquickmultipointhandler_p.h"
#include <private/qquicktranslate_p.h>
+#include "qquickdragaxis_p.h"
QT_BEGIN_NAMESPACE
@@ -74,6 +75,8 @@ class Q_AUTOTEST_EXPORT QQuickPinchHandler : public QQuickMultiPointHandler
Q_PROPERTY(qreal maximumX READ maximumX WRITE setMaximumX NOTIFY maximumXChanged)
Q_PROPERTY(qreal minimumY READ minimumY WRITE setMinimumY NOTIFY minimumYChanged)
Q_PROPERTY(qreal maximumY READ maximumY WRITE setMaximumY NOTIFY maximumYChanged)
+ Q_PROPERTY(QQuickDragAxis * xAxis READ xAxis CONSTANT)
+ Q_PROPERTY(QQuickDragAxis * yAxis READ yAxis CONSTANT)
public:
enum PinchOrigin {
@@ -111,6 +114,9 @@ public:
qreal maximumY() const { return m_maximumY; }
void setMaximumY(qreal maxY);
+ QQuickDragAxis *xAxis() { return &m_xAxis; }
+ QQuickDragAxis *yAxis() { return &m_yAxis; }
+
signals:
void minimumScaleChanged();
void maximumScaleChanged();
@@ -145,6 +151,8 @@ private:
qreal m_maximumX = qInf();
qreal m_minimumY = -qInf();
qreal m_maximumY = qInf();
+ QQuickDragAxis m_xAxis;
+ QQuickDragAxis m_yAxis;
PinchOrigin m_pinchOrigin = PinchOrigin::PinchCenter;
@@ -153,11 +161,13 @@ private:
qreal m_startRotation = 0;
qreal m_startDistance = 0;
QPointF m_startPos;
+ qreal m_accumulatedStartCentroidDistance = 0;
QVector<PointData> m_startAngles;
QMatrix4x4 m_startMatrix;
QQuickMatrix4x4 m_transform;
+
};
QT_END_NAMESPACE
diff --git a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/pinchDragMPTA.qml b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/pinchDragMPTA.qml
index f841b45431..f1591a412e 100644
--- a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/pinchDragMPTA.qml
+++ b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/pinchDragMPTA.qml
@@ -93,6 +93,7 @@ Rectangle {
id: dragHandler
objectName: "DragHandler"
target: container
+ grabPermissions: PointerHandler.CanTakeOverFromItems
}
PinchHandler {
id: pinch3
@@ -101,6 +102,7 @@ Rectangle {
minimumPointCount: 3
minimumScale: 0.1
maximumScale: 10
+ grabPermissions: PointerHandler.CanTakeOverFromItems
}
}
}
diff --git a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp
index 5ea5a42044..1c95bb57b4 100644
--- a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp
+++ b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp
@@ -188,10 +188,15 @@ void tst_MptaInterop::touchesThenPinch()
// Start moving: PinchHandler steals the exclusive grab from MPTA as soon as dragThreshold is exceeded
int pinchStoleGrab = 0;
- for (int i = 0; i < 8; ++i) {
- p1 += QPoint(dragThreshold / 2, dragThreshold / 2);
- p2 += QPoint(dragThreshold / 2, dragThreshold / 2);
- p3 += QPoint(-dragThreshold / 2, dragThreshold / 2);
+
+ const QPointF c = (p1 + p2 + p3)/3; // centroid of p1,p2,p3
+ QTransform xform; // transform to rotate around the centroid
+ xform.translate(c.x(), c.y()).rotate(1).translate(-c.x(), -c.y());
+
+ for (int i = 0; i < 16; ++i) {
+ p1 = xform.map(p1);
+ p2 = xform.map(p2);
+ p3 = xform.map(p3);
touch.move(1, p1).move(2, p2).move(3, p3).commit();
QQuickTouchUtils::flush(window);
if (!pinchStoleGrab && pointerEvent->point(0)->exclusiveGrabber() == pinch) {
diff --git a/tests/manual/pointer/map.qml b/tests/manual/pointer/map.qml
index d5de9eaa39..c400874d58 100644
--- a/tests/manual/pointer/map.qml
+++ b/tests/manual/pointer/map.qml
@@ -77,6 +77,8 @@ Item {
target: map
minimumScale: 0.1
maximumScale: 10
+ xAxis.enabled: false
+ yAxis.enabled: false
onActiveChanged: if (!active) reRenderIfNecessary()
grabPermissions: PinchHandler.TakeOverForbidden // don't allow takeover if pinch has started
}