diff options
author | Jan Arve Saether <jan-arve.saether@qt.io> | 2016-12-13 11:38:23 +0100 |
---|---|---|
committer | Shawn Rutledge <shawn.rutledge@qt.io> | 2016-12-27 11:54:46 +0000 |
commit | 3eeffae835b3474c8a3ca62125cb8ec24acdaa84 (patch) | |
tree | 9dc883768516931acb4f17d93b09bd09087c52a2 | |
parent | 4f69895cf15b9ff76b9a4404709a28153d34de5e (diff) |
Let pinchhandler operate on QQuickItem properties
This requires coordinate system mapping that varies with the
transformOrigin.
The properties exposed from PinchHandler (rotation, scale and translation)
are currently relative to the point when the pinch became active.
(Therefore, rotation, will reset back to 0 when a new pinch is activated).
Its still unclear how the properties that limits the transform should
influence. With this patch, they are like this:
* {min,max}imumRotation applies to the actual rotation of the item.
* {min,max}imumScale applies to the actual scale of the item.
* {min,max}imum{X,Y} applies to the actual position of the item. (This has
some unfortunate side-effects when the item is scaled or rotated, since
the items actual position will change as it rotates)
In addition, the behavior described above means that the limits won't
have any effect if there is no target item.
Change-Id: I279fb03667cd75324e8337039ae2594658265d13
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
-rw-r--r-- | src/quick/handlers/qquickmultipointerhandler.cpp | 2 | ||||
-rw-r--r-- | src/quick/handlers/qquickpinchhandler.cpp | 101 | ||||
-rw-r--r-- | src/quick/handlers/qquickpinchhandler_p.h | 15 | ||||
-rw-r--r-- | tests/manual/pointer/pinchHandler.qml | 41 |
4 files changed, 88 insertions, 71 deletions
diff --git a/src/quick/handlers/qquickmultipointerhandler.cpp b/src/quick/handlers/qquickmultipointerhandler.cpp index 746d33a12c..c2060ca3c5 100644 --- a/src/quick/handlers/qquickmultipointerhandler.cpp +++ b/src/quick/handlers/qquickmultipointerhandler.cpp @@ -167,7 +167,7 @@ QVector<QQuickMultiPointerHandler::PointData> QQuickMultiPointerHandler::angles( angles.reserve(m_currentPoints.count()); for (QQuickEventPoint *point : qAsConst(m_currentPoints)) { qreal angle = QLineF(ref, point->scenePos()).angle(); - angles.append(PointData(point->pointId(), angle)); + angles.append(PointData(point->pointId(), -angle)); // convert to clockwise, to be consistent with QQuickItem::rotation } return angles; } diff --git a/src/quick/handlers/qquickpinchhandler.cpp b/src/quick/handlers/qquickpinchhandler.cpp index 8ee8e2c6b6..45d8235dff 100644 --- a/src/quick/handlers/qquickpinchhandler.cpp +++ b/src/quick/handlers/qquickpinchhandler.cpp @@ -64,9 +64,9 @@ Q_LOGGING_CATEGORY(lcPinchHandler, "qt.quick.handler.pinch") QQuickPinchHandler::QQuickPinchHandler(QObject *parent) : QQuickMultiPointerHandler(parent, 2) - , m_scale(1) - , m_rotation(0) - , m_translation(0,0) + , m_activeScale(1) + , m_activeRotation(0) + , m_activeTranslation(0,0) , m_minimumScale(-qInf()) , m_maximumScale(qInf()) , m_minimumRotation(-qInf()) @@ -79,7 +79,6 @@ QQuickPinchHandler::QQuickPinchHandler(QObject *parent) , m_startScale(1) , m_startRotation(0) { - connect(this, &QQuickPinchHandler::targetChanged, this, &QQuickPinchHandler::onTargetChanged); } QQuickPinchHandler::~QQuickPinchHandler() @@ -199,27 +198,26 @@ void QQuickPinchHandler::setMaximumY(qreal maxY) void QQuickPinchHandler::onActiveChanged() { if (active()) { - m_startScale = m_scale; // TODO incompatible with independent x/y scaling - m_startRotation = m_rotation; - m_startCentroid = touchPointCentroid(); - m_startAngles = angles(m_startCentroid); - m_startDistance = averageTouchPointDistance(m_startCentroid); - m_activeRotation = 0; - m_startMatrix = m_transform.matrix(); - qCInfo(lcPinchHandler) << "activated with starting scale" << m_startScale << "rotation" << m_startRotation; - grabPoints(m_currentPoints); + if (const QQuickItem *t = target()) { + m_startScale = t->scale(); // TODO incompatible with independent x/y scaling + m_startRotation = t->rotation(); + m_startCentroid = touchPointCentroid(); + m_startAngles = angles(m_startCentroid); + m_startDistance = averageTouchPointDistance(m_startCentroid); + QVector3D xformOrigin(t->transformOriginPoint()); + m_startMatrix = QMatrix4x4(); + m_startMatrix.translate(t->x(), t->y()); + m_startMatrix.translate(xformOrigin); + m_startMatrix.scale(m_startScale); + m_startMatrix.rotate(m_startRotation, 0, 0, -1); + m_startMatrix.translate(-xformOrigin); + m_activeRotation = 0; + m_activeTranslation = QPointF(0,0); + qCInfo(lcPinchHandler) << "activated with starting scale" << m_startScale << "rotation" << m_startRotation; + grabPoints(m_currentPoints); + } } else { - qCInfo(lcPinchHandler) << "deactivated with scale" << m_scale << "rotation" << m_rotation; - } -} - -void QQuickPinchHandler::onTargetChanged() -{ - if (target()) { - // TODO if m_target was previously set differently, - // does prepending to the new target remove it from the old one? - // If not, should we fix that there, or here? - m_transform.prependToItem(target()); + qCInfo(lcPinchHandler) << "deactivated with scale" << m_activeScale << "rotation" << m_activeRotation; } } @@ -237,51 +235,60 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event) QRectF bounds(m_minimumX, m_minimumY, m_maximumX, m_maximumY); // 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. - QPointF centroidLocalPos; + QPointF centroidParentPos; if (target() && target()->parentItem()) { - centroidLocalPos = target()->parentItem()->mapFromScene(m_centroid); - centroidLocalPos = QPointF(qBound(bounds.left(), centroidLocalPos.x(), bounds.right()), - qBound(bounds.top(), centroidLocalPos.y(), bounds.bottom())); + centroidParentPos = target()->parentItem()->mapFromScene(m_centroid); + centroidParentPos = QPointF(qBound(bounds.left(), centroidParentPos.x(), bounds.right()), + qBound(bounds.top(), centroidParentPos.y(), bounds.bottom())); } // 1. scale - qreal dist = averageTouchPointDistance(m_centroid); - qreal activeScale = dist / m_startDistance; - activeScale = qBound(m_minimumScale/m_startScale, activeScale, m_maximumScale/m_startScale); - m_scale = m_startScale * activeScale; + const qreal dist = averageTouchPointDistance(m_centroid); + m_activeScale = dist / m_startDistance; + m_activeScale = qBound(m_minimumScale/m_startScale, m_activeScale, m_maximumScale/m_startScale); + const qreal scale = m_startScale * m_activeScale; // 2. rotate QVector<PointData> newAngles = angles(m_centroid); const qreal angleDelta = averageAngleDelta(m_startAngles, newAngles); m_activeRotation += angleDelta; const qreal totalRotation = m_startRotation + m_activeRotation; - m_rotation = qBound(m_minimumRotation, totalRotation, m_maximumRotation); - m_activeRotation += (m_rotation - totalRotation); //adjust for the potential bounding above + const qreal rotation = qBound(m_minimumRotation, totalRotation, m_maximumRotation); + m_activeRotation += (rotation - totalRotation); //adjust for the potential bounding above m_startAngles = std::move(newAngles); if (target() && target()->parentItem()) { // 3. Drag/translate - QPointF activeTranslation(centroidLocalPos - target()->parentItem()->mapFromScene(m_startCentroid)); + const QPointF centroidStartParentPos = target()->parentItem()->mapFromScene(m_startCentroid); + m_activeTranslation = centroidParentPos - centroidStartParentPos; // apply rotation + scaling around the centroid - then apply translation. QMatrix4x4 mat; - QVector3D xlatOrigin(centroidLocalPos - target()->position()); - mat.translate(xlatOrigin); - mat.rotate(m_activeRotation, 0, 0, -1); - mat.scale(activeScale); - mat.translate(-xlatOrigin); - mat.translate(QVector3D(activeTranslation)); + + const QVector3D centroidParentVector(centroidParentPos); + mat.translate(centroidParentVector); + mat.rotate(m_activeRotation, 0, 0, 1); + mat.scale(m_activeScale); + mat.translate(-centroidParentVector); + mat.translate(QVector3D(m_activeTranslation)); + + mat = mat * m_startMatrix; + + QPointF xformOriginPoint = target()->transformOriginPoint(); + QPointF pos = mat * xformOriginPoint; + pos -= xformOriginPoint; + + target()->setPosition(pos); + target()->setRotation(rotation); + target()->setScale(scale); + // TODO some translation inadvertently happens; try to hold the chosen pinch origin in place qCDebug(lcPinchHandler) << "centroid" << m_startCentroid << "->" << m_centroid << ", distance" << m_startDistance << "->" << dist - << ", startScale" << m_startScale << "->" << m_scale + << ", startScale" << m_startScale << "->" << scale << ", activeRotation" << m_activeRotation - << ", rotation" << m_rotation; - - mat = mat * m_startMatrix; - m_transform.setMatrix(mat); - m_translation = QPointF(mat.constData()[12], mat.constData()[13]); + << ", rotation" << rotation; } acceptPoints(m_currentPoints); diff --git a/src/quick/handlers/qquickpinchhandler_p.h b/src/quick/handlers/qquickpinchhandler_p.h index 83d6447b15..0861368682 100644 --- a/src/quick/handlers/qquickpinchhandler_p.h +++ b/src/quick/handlers/qquickpinchhandler_p.h @@ -99,9 +99,9 @@ public: PinchOrigin pinchOrigin() const { return m_pinchOrigin; } void setPinchOrigin(PinchOrigin pinchOrigin); - QPointF translation() const { return m_translation; } - qreal scale() const { return m_scale; } - qreal rotation() const { return m_rotation; } + QPointF translation() const { return m_activeTranslation; } + qreal scale() const { return m_activeScale; } + qreal rotation() const { return m_activeRotation; } QPointF centroid() const { return m_centroid; } qreal minimumX() const { return m_minimumX; } @@ -128,14 +128,13 @@ signals: protected: void onActiveChanged() override; - void onTargetChanged(); void handlePointerEventImpl(QQuickPointerEvent *event) override; private: // properties - qreal m_scale; - qreal m_rotation; - QPointF m_translation; + qreal m_activeScale; + qreal m_activeRotation; + QPointF m_activeTranslation; QPointF m_centroid; qreal m_minimumScale; @@ -154,9 +153,9 @@ private: // internal qreal m_startScale; qreal m_startRotation; - qreal m_activeRotation; QPointF m_startCentroid; qreal m_startDistance; + QPointF m_startPos; QVector<PointData> m_startAngles; QMatrix4x4 m_startMatrix; diff --git a/tests/manual/pointer/pinchHandler.qml b/tests/manual/pointer/pinchHandler.qml index 3ab196a10a..f317f361e2 100644 --- a/tests/manual/pointer/pinchHandler.qml +++ b/tests/manual/pointer/pinchHandler.qml @@ -44,25 +44,38 @@ import Qt.labs.handlers 1.0 Rectangle { width: 1024; height: 600 color: "#eee" + + function getTransformationDetails(item, pinchhandler) { + return "\n\npinch.scale:" + pinchhandler.scale.toFixed(2) + + "\npinch.rotation:" + pinchhandler.rotation.toFixed(2) + + "\npinch.translation:" + "(" + pinchhandler.translation.x.toFixed(2) + "," + pinchhandler.translation.y.toFixed(2) + ")" + + "\nrect.scale: " + item.scale.toFixed(2) + + "\nrect.rotation: " + item.rotation.toFixed(2) + + "\nrect.position: " + "(" + item.x.toFixed(2) + "," + item.y.toFixed(2) + ")" + } + Rectangle { - id: root - color: "black" - width: 900 - height: 600 - x: 100 + // Purpose of this item is just to make sure the rectangles are transformed into + // a coordinate system that is different from the scene coordinate system. + anchors.fill: parent + anchors.margins: 50 + color: "#ffe0e0e0" Rectangle { + id: rect2 width: 400 height: 300 color: "lightsteelblue" antialiasing: true + x: 100 + y: 200 + rotation: 30 + transformOrigin: Item.TopRight Text { anchors.centerIn: parent text: "Pinch with 2 fingers to scale, rotate and translate" - + "\ncurrent rotation: " + pinch2.rotation.toFixed(1) - + "\nscale: " + pinch2.scale.toFixed(1) - + "\ntranslation: " + pinch2.translation + + getTransformationDetails(rect2, pinch2) } PinchHandler { @@ -74,11 +87,12 @@ Rectangle { maximumScale: 3 minimumX: 0 maximumX: 600 - pointDistanceThreshold: 150 + pointDistanceThreshold: 0 } } Rectangle { + id: rect3 x: 500 width: 400 height: 300 @@ -88,9 +102,7 @@ Rectangle { Text { anchors.centerIn: parent text: "Pinch with 3 fingers to scale, rotate and translate\nDrag with 1 finger" - + "\ncurrent rotation " + pinch3.rotation.toFixed(1) - + "\nscale: " + pinch3.scale.toFixed(1) - + "\ntranslation: " + pinch3.translation + + getTransformationDetails(rect3, pinch3) } DragHandler { objectName: "DragHandler" } @@ -102,13 +114,12 @@ Rectangle { maximumScale: 10 } } - } Rectangle { id: centroidIndicator property QtObject pincher: pinch2.active ? pinch2 : pinch3 - x: pincher.centroid.x - y: pincher.centroid.y + x: pincher.centroid.x - radius + y: pincher.centroid.y - radius z: 1 visible: pincher.active radius: width / 2 |