diff options
Diffstat (limited to 'src/quick/handlers/qquickpinchhandler.cpp')
-rw-r--r-- | src/quick/handlers/qquickpinchhandler.cpp | 540 |
1 files changed, 386 insertions, 154 deletions
diff --git a/src/quick/handlers/qquickpinchhandler.cpp b/src/quick/handlers/qquickpinchhandler.cpp index b1dca9a905..8de7390ae4 100644 --- a/src/quick/handlers/qquickpinchhandler.cpp +++ b/src/quick/handlers/qquickpinchhandler.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** 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 Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** 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-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qquickpinchhandler_p.h" #include <QtQml/qqmlinfo.h> @@ -85,73 +49,286 @@ Q_LOGGING_CATEGORY(lcPinchHandler, "qt.quick.handler.pinch") \image touchpoints-pinchhandler.png - \sa PinchArea + \note The pinch begins when the number of fingers pressed is between + \l {MultiPointHandler::minimumPointCount}{minimumPointCount} and + \l {MultiPointHandler::maximumPointCount}{maximumPointCount}, inclusive. + Until then, PinchHandler tracks the positions of any pressed fingers, + but if it's a disallowed number, it does not scale or rotate + its \l target, and the \l active property remains \c false. + + \sa PinchArea, QPointerEvent::pointCount(), QNativeGestureEvent::fingerCount(), {Qt Quick Examples - Pointer Handlers} */ QQuickPinchHandler::QQuickPinchHandler(QQuickItem *parent) : QQuickMultiPointHandler(parent, 2) { + // Tell QQuickPointerDeviceHandler::wantsPointerEvent() to ignore button state + d_func()->acceptedButtons = Qt::NoButton; } +#if QT_DEPRECATED_SINCE(6, 5) /*! \qmlproperty real QtQuick::PinchHandler::minimumScale + \deprecated [6.5] Use scaleAxis.minimum The minimum acceptable \l {Item::scale}{scale} to be applied to the \l target. */ void QQuickPinchHandler::setMinimumScale(qreal minimumScale) { - if (qFuzzyCompare(m_minimumScale, minimumScale)) + if (qFuzzyCompare(m_scaleAxis.minimum(), minimumScale)) return; - m_minimumScale = minimumScale; + m_scaleAxis.setMinimum(minimumScale); emit minimumScaleChanged(); } /*! \qmlproperty real QtQuick::PinchHandler::maximumScale + \deprecated [6.5] Use scaleAxis.maximum The maximum acceptable \l {Item::scale}{scale} to be applied to the \l target. */ void QQuickPinchHandler::setMaximumScale(qreal maximumScale) { - if (qFuzzyCompare(m_maximumScale, maximumScale)) + if (qFuzzyCompare(m_scaleAxis.maximum(), maximumScale)) return; - m_maximumScale = maximumScale; + m_scaleAxis.setMaximum(maximumScale); emit maximumScaleChanged(); } +#endif + +/*! + \readonly + \qmlproperty real QtQuick::PinchHandler::activeScale + + The scale factor while the pinch gesture is being performed. + It is 1.0 when the gesture begins, increases as the touchpoints are spread + apart, and decreases as the touchpoints are brought together. + If \l target is not null, its \l {Item::scale}{scale} will be automatically + multiplied by this value. + Otherwise, bindings can be used to do arbitrary things with this value. + + \sa QtQuick::PinchHandler::scaleAxis.activeValue +*/ + +void QQuickPinchHandler::setActiveScale(qreal scale) +{ + if (scale == activeScale()) + return; + + qreal delta = scale / m_scaleAxis.activeValue(); + m_scaleAxis.updateValue(scale, m_scaleAxis.m_startValue * scale, delta); + emit scaleChanged(delta); +} + +/*! + \qmlsignal QtQuick::PinchHandler::scaleChanged(qreal delta) + + The \c scaleChanged signal is emitted when \l activeScale (and therefore + \l persistentScale) changes. The \a delta value gives the multiplicative + change in scale. For example, if the user moves fingers to change the pinch + distance so that \c activeScale changes from 2 to 2.5, \c + scaleChanged(1.25) will be emitted. You can use that to incrementally + change the scale of an item: + + \snippet pointerHandlers/pinchHandlerScaleOrRotationChanged.qml 0 + + \note If you set the \l persistentScale property directly, \c delta is \c 1. +*/ /*! + \readonly + \qmlproperty QVector2D QtQuick::PinchHandler::scale + \deprecated [6.5] Use persistentScale +*/ + +/*! + \qmlproperty real QtQuick::PinchHandler::persistentScale + + The scale factor that will automatically be set on the \l target if it is not null. + Otherwise, bindings can be used to do arbitrary things with this value. + While the pinch gesture is being performed, it is continuously multiplied by + \l activeScale; after the gesture ends, it stays the same; and when the next + pinch gesture begins, it begins to be multiplied by activeScale again. + + It's possible to set this property, as a way of synchronizing the basis + scale with a scale that was set in some other way, for example by another + handler. If you set this property directly, \c activeScale does not change, + and \c scaleChanged(1) is emitted. +*/ + +void QQuickPinchHandler::setPersistentScale(qreal scale) +{ + if (scale == persistentScale()) + return; + + m_scaleAxis.updateValue(m_scaleAxis.activeValue(), scale); + emit scaleChanged(1); +} + +#if QT_DEPRECATED_SINCE(6, 5) +/*! \qmlproperty real QtQuick::PinchHandler::minimumRotation + \deprecated [6.5] Use rotationAxis.minimum The minimum acceptable \l {Item::rotation}{rotation} to be applied to the \l target. */ void QQuickPinchHandler::setMinimumRotation(qreal minimumRotation) { - if (qFuzzyCompare(m_minimumRotation, minimumRotation)) + if (qFuzzyCompare(m_rotationAxis.minimum(), minimumRotation)) return; - m_minimumRotation = minimumRotation; + m_rotationAxis.setMinimum(minimumRotation); emit minimumRotationChanged(); } /*! \qmlproperty real QtQuick::PinchHandler::maximumRotation + \deprecated [6.5] Use rotationAxis.maximum The maximum acceptable \l {Item::rotation}{rotation} to be applied to the \l target. */ void QQuickPinchHandler::setMaximumRotation(qreal maximumRotation) { - if (qFuzzyCompare(m_maximumRotation, maximumRotation)) + if (qFuzzyCompare(m_rotationAxis.maximum(), maximumRotation)) return; - m_maximumRotation = maximumRotation; + m_rotationAxis.setMaximum(maximumRotation); emit maximumRotationChanged(); } +#endif + +/*! + \qmlsignal QtQuick::PinchHandler::rotationChanged(qreal delta) + + The \c rotationChanged signal is emitted when \l activeRotation (and + therefore \l persistentRotation) changes. The \a delta value gives the + additive change in rotation. For example, if the user moves fingers to + change the pinch distance so that \c activeRotation changes from 10 to 30 + degrees, \c rotationChanged(20) will be emitted. You can use that to + incrementally change the rotation of an item: + + \snippet pointerHandlers/pinchHandlerScaleOrRotationChanged.qml 0 + + \note If you set the \l persistentRotation property directly, \c delta is \c 0. +*/ + +/*! + \readonly + \qmlproperty QVector2D QtQuick::PinchHandler::rotation + \deprecated [6.5] Use activeRotation +*/ + +/*! + \readonly + \qmlproperty real QtQuick::PinchHandler::activeRotation + + The rotation of the pinch gesture in degrees, with positive values clockwise. + It is \c 0 when the gesture begins. If \l target is not null, this will be + automatically added to its \l {Item::rotation}{rotation}. Otherwise, + bindings can be used to do arbitrary things with this value. + + \sa QtQuick::PinchHandler::rotationAxis.activeValue +*/ + +void QQuickPinchHandler::setActiveRotation(qreal rot) +{ + if (rot == activeRotation()) + return; + + qreal delta = rot - m_rotationAxis.activeValue(); + m_rotationAxis.updateValue(rot, m_rotationAxis.m_startValue + rot, delta); + emit rotationChanged(delta); +} + +/*! + \qmlproperty real QtQuick::PinchHandler::persistentRotation + + The rotation to be applied to the \l target if it is not null. + Otherwise, bindings can be used to do arbitrary things with this value. + While the pinch gesture is being performed, \l activeRotation is continuously + added; after the gesture ends, it stays the same; and when the next + pinch gesture begins, it begins to be modified by activeRotation again. + + It's possible to set this property, as a way of synchronizing the basis + rotation with a rotation that was set in some other way, for example by + another handler. If you set this property directly, \c activeRotation does + not change, and \c rotationChanged(0) is emitted. +*/ + +void QQuickPinchHandler::setPersistentRotation(qreal rot) +{ + if (rot == persistentRotation()) + return; + + m_rotationAxis.updateValue(m_rotationAxis.activeValue(), rot); + emit rotationChanged(0); +} + +/*! + \qmlsignal QtQuick::PinchHandler::translationChanged(QVector2D delta) + + The \c translationChanged signal is emitted when \l activeTranslation (and + therefore \l persistentTranslation) changes. The \a delta vector gives the + change in translation. You can use that to incrementally change the + position of an item: + + \snippet pointerHandlers/pinchHandlerNullTarget.qml 0 + + \note If you set the \l persistentTranslation property directly, + \c delta is \c {0, 0}. +*/ + +/*! + \readonly + \qmlproperty QVector2D QtQuick::PinchHandler::translation + \deprecated [6.5] Use activeTranslation +*/ +/*! + \readonly + \qmlproperty QPointF QtQuick::PinchHandler::activeTranslation + + The translation of the cluster of points while the pinch gesture is being + performed. It is \c {0, 0} when the gesture begins, and increases as the + \l {eventPoint}{eventPoint(s)} are dragged downward and to the right. After the gesture + ends, it stays the same; and when the next pinch gesture begins, it is + reset to \c {0, 0} again. + + \note On some touchpads, such as on a \macos trackpad, native gestures do + not generate any translation values, and this property stays at \c (0, 0). +*/ + +/*! + \qmlproperty QPointF QtQuick::PinchHandler::persistentTranslation + + The translation to be applied to the \l target if it is not \c null. + Otherwise, bindings can be used to do arbitrary things with this value. + While the pinch gesture is being performed, \l activeTranslation is + continuously added to it; after the gesture ends, it stays the same. + + It's possible to set this property, as a way of synchronizing the basis + translation with a translation that was set in some other way, for example + by another handler. If you set this property directly, \c activeTranslation + does not change, and \c translationChanged({0, 0}) is emitted. + + \note On some touchpads, such as on a \macos trackpad, native gestures do + not generate any translation values, and this property stays at \c (0, 0). +*/ + +void QQuickPinchHandler::setPersistentTranslation(const QPointF &trans) +{ + if (trans == persistentTranslation()) + return; + + m_xAxis.updateValue(m_xAxis.activeValue(), trans.x()); + m_yAxis.updateValue(m_yAxis.activeValue(), trans.y()); + emit translationChanged({}); +} bool QQuickPinchHandler::wantsPointerEvent(QPointerEvent *event) { @@ -161,7 +338,8 @@ bool QQuickPinchHandler::wantsPointerEvent(QPointerEvent *event) #if QT_CONFIG(gestures) if (event->type() == QEvent::NativeGesture) { const auto gesture = static_cast<const QNativeGestureEvent *>(event); - if (minimumPointCount() == 2) { + if (!gesture->fingerCount() || (gesture->fingerCount() >= minimumPointCount() && + gesture->fingerCount() <= maximumPointCount())) { switch (gesture->gestureType()) { case Qt::BeginNativeGesture: case Qt::EndNativeGesture: @@ -185,72 +363,144 @@ bool QQuickPinchHandler::wantsPointerEvent(QPointerEvent *event) \qmlproperty real QtQuick::PinchHandler::xAxis.minimum \qmlproperty real QtQuick::PinchHandler::xAxis.maximum \qmlproperty bool QtQuick::PinchHandler::xAxis.enabled + \qmlproperty real QtQuick::PinchHandler::xAxis.activeValue \c xAxis controls the constraints for horizontal translation of the \l target item. \c minimum is the minimum acceptable x coordinate of the translation. \c maximum is the maximum acceptable x coordinate of the translation. If \c enabled is true, horizontal dragging is allowed. - */ + + The \c activeValueChanged signal is emitted when \c activeValue changes, to + provide the increment by which it changed. + This is intended for incrementally adjusting one property via multiple handlers. + + \snippet pointerHandlers/pinchHandlerAxisValueDeltas.qml 0 + + \note The snippet is contrived: PinchHandler already knows how to move, + scale and rotate its parent item, but this code achieves different behavior + in a less-declarative way, to illustrate how to use \c activeValueChanged + in special cases. +*/ /*! \qmlpropertygroup QtQuick::PinchHandler::yAxis \qmlproperty real QtQuick::PinchHandler::yAxis.minimum \qmlproperty real QtQuick::PinchHandler::yAxis.maximum \qmlproperty bool QtQuick::PinchHandler::yAxis.enabled + \qmlproperty real QtQuick::PinchHandler::yAxis.activeValue \c yAxis controls the constraints for vertical translation of the \l target item. \c minimum is the minimum acceptable y coordinate of the translation. \c maximum is the maximum acceptable y coordinate of the translation. If \c enabled is true, vertical dragging is allowed. - */ + + The \c activeValueChanged signal is emitted when \c activeValue changes, to + provide the increment by which it changed. + This is intended for incrementally adjusting one property via multiple handlers. + + \snippet pointerHandlers/pinchHandlerAxisValueDeltas.qml 0 + + \note The snippet is contrived: PinchHandler already knows how to move, + scale and rotate its parent item, but this code achieves different behavior + in a less-declarative way, to illustrate how to use \c activeValueChanged + in special cases. +*/ /*! - \qmlproperty int QtQuick::PinchHandler::minimumTouchPoints + \qmlpropertygroup QtQuick::PinchHandler::scaleAxis + \qmlproperty real QtQuick::PinchHandler::scaleAxis.minimum + \qmlproperty real QtQuick::PinchHandler::scaleAxis.maximum + \qmlproperty bool QtQuick::PinchHandler::scaleAxis.enabled + \qmlproperty real QtQuick::PinchHandler::scaleAxis.activeValue + + \c scaleAxis controls the constraints for setting the \l {QtQuick::Item::scale}{scale} + of the \l target item according to the distance between the touchpoints. + + \c minimum is the minimum acceptable scale. + \c maximum is the maximum acceptable scale. + If \c enabled is true, scaling is allowed. + \c activeValue is the same as \l {QtQuick::PinchHandler::activeScale}. + + The \c activeValueChanged signal is emitted when \c activeValue changes, to + provide the multiplier for the incremental change. + This is intended for incrementally adjusting one property via multiple handlers. + + \snippet pointerHandlers/pinchHandlerAxisValueDeltas.qml 0 + + \note The snippet is contrived: PinchHandler already knows how to move, + scale and rotate its parent item, but this code achieves different behavior + in a less-declarative way, to illustrate how to use \c activeValueChanged + in special cases. +*/ - The pinch begins when this number of fingers are pressed. - Until then, PinchHandler tracks the positions of any pressed fingers, - but if it's an insufficient number, it does not scale or rotate - its \l target, and the \l active property will remain false. +/*! + \qmlpropertygroup QtQuick::PinchHandler::rotationAxis + \qmlproperty real QtQuick::PinchHandler::rotationAxis.minimum + \qmlproperty real QtQuick::PinchHandler::rotationAxis.maximum + \qmlproperty bool QtQuick::PinchHandler::rotationAxis.enabled + \qmlproperty real QtQuick::PinchHandler::rotationAxis.activeValue + + \c rotationAxis controls the constraints for setting the \l {QtQuick::Item::rotation}{rotation} + of the \l target item according to the rotation of the group of touchpoints. + + \c minimum is the minimum acceptable rotation. + \c maximum is the maximum acceptable rotation. + If \c enabled is true, rotation is allowed. + \c activeValue is the same as \l {QtQuick::PinchHandler::activeRotation}. + + The \c activeValueChanged signal is emitted when \c activeValue changes, to + provide the increment by which it changed. + This is intended for incrementally adjusting one property via multiple handlers. + + \snippet pointerHandlers/pinchHandlerAxisValueDeltas.qml 0 + + \note The snippet is contrived: PinchHandler already knows how to move, + scale and rotate its parent item, but this code achieves different behavior + in a less-declarative way, to illustrate how to use \c activeValueChanged + in special cases. */ /*! + \readonly \qmlproperty bool QtQuick::PinchHandler::active - This property is true when all the constraints (epecially \l minimumTouchPoints) - are satisfied and the \l target, if any, is being manipulated. + This property is \c true when all the constraints (epecially + \l {MultiPointHandler::minimumPointCount}{minimumPointCount} and + \l {MultiPointHandler::maximumPointCount}{maximumPointCount}) are satisfied + and the \l target, if any, is being manipulated. */ void QQuickPinchHandler::onActiveChanged() { QQuickMultiPointHandler::onActiveChanged(); - if (active()) { + const bool curActive = active(); + m_xAxis.onActiveChanged(curActive, 0); + m_yAxis.onActiveChanged(curActive, 0); + m_scaleAxis.onActiveChanged(curActive, 1); + m_rotationAxis.onActiveChanged(curActive, 0); + + if (curActive) { m_startAngles = angles(centroid().sceneGrabPosition()); m_startDistance = averageTouchPointDistance(centroid().sceneGrabPosition()); - m_activeRotation = 0; - m_activeTranslation = QVector2D(); - if (const QQuickItem *t = target()) { - m_startScale = t->scale(); // TODO incompatible with independent x/y scaling - m_startRotation = t->rotation(); - m_startPos = t->position(); - } else { - m_startScale = m_accumulatedScale; - m_startRotation = 0; - } - qCDebug(lcPinchHandler) << "activated with starting scale" << m_startScale << "rotation" << m_startRotation; + m_startTargetPos = target() ? target()->position() : QPointF(); + qCDebug(lcPinchHandler) << "activated with starting scale" << m_scaleAxis.m_startValue + << "rotation" << m_rotationAxis.m_startValue + << "target pos" << m_startTargetPos; } else { - qCDebug(lcPinchHandler) << "deactivated with scale" << m_activeScale << "rotation" << m_activeRotation; + m_startTargetPos = QPointF(); + qCDebug(lcPinchHandler) << "deactivated with scale" << m_scaleAxis.m_activeValue << "rotation" << m_rotationAxis.m_activeValue; } } void QQuickPinchHandler::handlePointerEventImpl(QPointerEvent *event) { + QQuickMultiPointHandler::handlePointerEventImpl(event); if (Q_UNLIKELY(lcPinchHandler().isDebugEnabled())) { for (const QQuickHandlerPoint &p : currentPoints()) qCDebug(lcPinchHandler) << Qt::hex << p.id() << p.sceneGrabPosition() << "->" << p.scenePosition(); } - QQuickMultiPointHandler::handlePointerEventImpl(event); qreal dist = 0; #if QT_CONFIG(gestures) @@ -258,30 +508,26 @@ void QQuickPinchHandler::handlePointerEventImpl(QPointerEvent *event) const auto gesture = static_cast<const QNativeGestureEvent *>(event); mutableCentroid().reset(event, event->point(0)); switch (gesture->gestureType()) { + case Qt::BeginNativeGesture: + setActive(true); + // Native gestures for 2-finger pinch do not allow dragging, so + // the centroid won't move during the gesture, and translation stays at zero + return; case Qt::EndNativeGesture: - m_activeScale = 1; - m_activeRotation = 0; - m_activeTranslation = QVector2D(); mutableCentroid().reset(); setActive(false); emit updated(); return; case Qt::ZoomNativeGesture: - m_activeScale *= 1 + gesture->value(); + setActiveScale(m_scaleAxis.activeValue() * (1 + gesture->value())); break; case Qt::RotateNativeGesture: - m_activeRotation += gesture->value(); + setActiveRotation(m_rotationAxis.activeValue() + gesture->value()); break; default: // Nothing of interest (which is unexpected, because wantsPointerEvent() should have returned false) return; } - if (!active()) { - setActive(true); - // Native gestures for 2-finger pinch do not allow dragging, so - // the centroid won't move during the gesture, and translation stays at zero - m_activeTranslation = QVector2D(); - } } else #endif // QT_CONFIG(gestures) { @@ -364,10 +610,12 @@ void QQuickPinchHandler::handlePointerEventImpl(QPointerEvent *event) ++numberOfPointsDraggedOverThreshold; } - const bool requiredNumberOfPointsDraggedOverThreshold = numberOfPointsDraggedOverThreshold >= minimumPointCount() && numberOfPointsDraggedOverThreshold <= maximumPointCount(); - accumulatedMovementMagnitude /= currentPoints().count(); + const bool requiredNumberOfPointsDraggedOverThreshold = + numberOfPointsDraggedOverThreshold >= minimumPointCount() && + numberOfPointsDraggedOverThreshold <= maximumPointCount(); + accumulatedMovementMagnitude /= currentPoints().size(); - QVector2D avgDrag = accumulatedDrag / currentPoints().count(); + QVector2D avgDrag = accumulatedDrag / currentPoints().size(); if (!xAxis()->enabled()) avgDrag.setX(0); if (!yAxis()->enabled()) @@ -377,7 +625,8 @@ void QQuickPinchHandler::handlePointerEventImpl(QPointerEvent *event) qreal distanceToCentroidDelta = qAbs(accumulatedCentroidDistance - m_accumulatedStartCentroidDistance); // Used to detect scale if (numberOfPointsDraggedOverThreshold >= 1) { - if (requiredNumberOfPointsDraggedOverThreshold && avgDrag.lengthSquared() >= dragThresholdSquared && accumulatedMovementMagnitude < dragThreshold) { + if (requiredNumberOfPointsDraggedOverThreshold && + avgDrag.lengthSquared() >= dragThresholdSquared && accumulatedMovementMagnitude < dragThreshold) { // Drag if (grabPoints(event, chosenPoints)) setActive(true); @@ -400,109 +649,92 @@ void QQuickPinchHandler::handlePointerEventImpl(QPointerEvent *event) // 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 - dist = averageTouchPointDistance(centroid().scenePosition()); - m_activeScale = dist / m_startDistance; - m_activeScale = qBound(m_minimumScale/m_startScale, m_activeScale, m_maximumScale/m_startScale); + qreal activeScale = 1; + if (m_scaleAxis.enabled()) { + dist = averageTouchPointDistance(centroid().scenePosition()); + activeScale = dist / m_startDistance; + activeScale = qBound(m_scaleAxis.minimum() / m_scaleAxis.m_startValue, activeScale, + m_scaleAxis.maximum() / m_scaleAxis.m_startValue); + setActiveScale(activeScale); + } // 2. rotate - QVector<PointData> newAngles = angles(centroid().scenePosition()); - const qreal angleDelta = averageAngleDelta(m_startAngles, newAngles); - m_activeRotation += angleDelta; - m_startAngles = std::move(newAngles); + if (m_rotationAxis.enabled()) { + QVector<PointData> newAngles = angles(centroid().scenePosition()); + const qreal angleDelta = averageAngleDelta(m_startAngles, newAngles); + setActiveRotation(m_rotationAxis.m_activeValue + angleDelta); + m_startAngles = std::move(newAngles); + } if (!containsReleasedPoints) acceptPoints(chosenPoints); } - const qreal totalRotation = m_startRotation + m_activeRotation; - const qreal rotation = qBound(m_minimumRotation, totalRotation, m_maximumRotation); - m_activeRotation += (rotation - totalRotation); //adjust for the potential bounding above - m_accumulatedScale = m_startScale * m_activeScale; if (target() && target()->parentItem()) { - const QPointF centroidParentPos = target()->parentItem()->mapFromScene(centroid().scenePosition()); + auto *t = target(); + const QPointF centroidParentPos = t->parentItem()->mapFromScene(centroid().scenePosition()); // 3. Drag/translate - const QPointF centroidStartParentPos = target()->parentItem()->mapFromScene(centroid().sceneGrabPosition()); - m_activeTranslation = QVector2D(centroidParentPos - centroidStartParentPos); + const QPointF centroidStartParentPos = t->parentItem()->mapFromScene(centroid().sceneGrabPosition()); + auto activeTranslation = centroidParentPos - centroidStartParentPos; // apply rotation + scaling around the centroid - then apply translation. - QPointF pos = QQuickItemPrivate::get(target())->adjustedPosForTransform(centroidParentPos, - m_startPos, m_activeTranslation, - m_startScale, m_activeScale, - m_startRotation, m_activeRotation); + QPointF pos = QQuickItemPrivate::get(t)->adjustedPosForTransform(centroidParentPos, + m_startTargetPos, QVector2D(activeTranslation), + t->scale(), m_scaleAxis.persistentValue() / m_scaleAxis.m_startValue, + t->rotation(), m_rotationAxis.persistentValue() - m_rotationAxis.m_startValue); if (xAxis()->enabled()) pos.setX(qBound(xAxis()->minimum(), pos.x(), xAxis()->maximum())); else - pos.rx() -= qreal(m_activeTranslation.x()); + pos.rx() -= qreal(activeTranslation.x()); if (yAxis()->enabled()) pos.setY(qBound(yAxis()->minimum(), pos.y(), yAxis()->maximum())); else - pos.ry() -= qreal(m_activeTranslation.y()); - - target()->setPosition(pos); - target()->setRotation(rotation); - target()->setScale(m_accumulatedScale); + pos.ry() -= qreal(activeTranslation.y()); + + const QVector2D delta(activeTranslation.x() - m_xAxis.activeValue(), + activeTranslation.y() - m_yAxis.activeValue()); + m_xAxis.updateValue(activeTranslation.x(), m_xAxis.persistentValue() + delta.x(), delta.x()); + m_yAxis.updateValue(activeTranslation.y(), m_yAxis.persistentValue() + delta.y(), delta.y()); + emit translationChanged(delta); + t->setPosition(pos); + t->setRotation(m_rotationAxis.persistentValue()); + t->setScale(m_scaleAxis.persistentValue()); } else { - m_activeTranslation = QVector2D(centroid().scenePosition() - centroid().scenePressPosition()); + auto activeTranslation = centroid().scenePosition() - centroid().scenePressPosition(); + auto accumulated = QPointF(m_xAxis.m_startValue, m_yAxis.m_startValue) + activeTranslation; + const QVector2D delta(activeTranslation.x() - m_xAxis.activeValue(), + activeTranslation.y() - m_yAxis.activeValue()); + m_xAxis.updateValue(activeTranslation.x(), accumulated.x(), delta.x()); + m_yAxis.updateValue(activeTranslation.y(), accumulated.y(), delta.y()); + emit translationChanged(delta); } qCDebug(lcPinchHandler) << "centroid" << centroid().scenePressPosition() << "->" << centroid().scenePosition() << ", distance" << m_startDistance << "->" << dist - << ", startScale" << m_startScale << "->" << m_accumulatedScale - << ", activeRotation" << m_activeRotation - << ", rotation" << rotation + << ", scale" << m_scaleAxis.m_startValue << "->" << m_scaleAxis.m_accumulatedValue + << ", rotation" << m_rotationAxis.m_startValue << "->" << m_rotationAxis.m_accumulatedValue + << ", translation" << persistentTranslation() << " from " << event->device()->type(); emit updated(); } /*! - \readonly - \qmlproperty QtQuick::HandlerPoint QtQuick::PinchHandler::centroid - - A point exactly in the middle of the currently-pressed touch points. - The \l target will be rotated around this point. -*/ - -/*! - \readonly - \qmlproperty real QtQuick::PinchHandler::scale + \internal + \qmlproperty flags QtQuick::PinchHandler::acceptedButtons - The scale factor that will automatically be set on the \l target if it is not null. - Otherwise, bindings can be used to do arbitrary things with this value. - While the pinch gesture is being performed, it is continuously multiplied by - \l activeScale; after the gesture ends, it stays the same; and when the next - pinch gesture begins, it begins to be multiplied by activeScale again. + This property is not used in PinchHandler. */ /*! \readonly - \qmlproperty real QtQuick::PinchHandler::activeScale + \qmlproperty QtQuick::handlerPoint QtQuick::PinchHandler::centroid - The scale factor while the pinch gesture is being performed. - It is 1.0 when the gesture begins, increases as the touchpoints are spread - apart, and decreases as the touchpoints are brought together. - If \l target is not null, its \l {Item::scale}{scale} will be automatically - multiplied by this value. - Otherwise, bindings can be used to do arbitrary things with this value. -*/ - -/*! - \readonly - \qmlproperty real QtQuick::PinchHandler::rotation - - The rotation of the pinch gesture in degrees, with positive values clockwise. - It is 0 when the gesture begins. If \l target is not null, this will be - automatically applied to its \l {Item::rotation}{rotation}. Otherwise, - bindings can be used to do arbitrary things with this value. -*/ - -/*! - \readonly - \qmlproperty QVector2D QtQuick::PinchHandler::translation - - The translation of the gesture \l centroid. It is \c (0, 0) when the - gesture begins. + A point exactly in the middle of the currently-pressed touch points. + The \l target will be rotated around this point. */ QT_END_NAMESPACE + +#include "moc_qquickpinchhandler_p.cpp" |