diff options
author | Shawn Rutledge <shawn.rutledge@qt.io> | 2022-11-16 18:20:19 +0100 |
---|---|---|
committer | Shawn Rutledge <shawn.rutledge@qt.io> | 2022-12-10 03:13:57 +0100 |
commit | 7867a683fcb938939fb2837a26ac8e1941e3fe08 (patch) | |
tree | 271faa101a68abdd55c5c88146ee8bc223a413a8 /src/quick/handlers | |
parent | f064497bd5021e5d28266cabbb703d548f6961b0 (diff) |
Add PinchHandler.scaleAxis, rotationAxis; hold values in axes
Pointer Handlers that manipulate target item properties should now
use QQuickDragAxis consistently to:
- enforce minimum and maximum values
- hold the persistent and active values
- make those available via properties
- emit a new activeValueChanged(delta) signal when the value changes,
so that it's possible to incrementally update a target item
property in JS (onValueDelta: target.property += delta)
In the pinchHandler.qml example, you can use the PinchHandler to adjust
4 properties of one Rectangle independently (it requires coordination).
m_boundedActiveValue controls whether m_activeValue will be
kept between minimum and maximum. For rotation,
tst_QQuickPinchHandler::scaleNativeGesture() expects it to be,
although that seems questionable now, and may be addressed later.
[ChangeLog][QtQuick][Event Handlers] PinchHandler now has scaleAxis and
rotationAxis grouped properties, alongside the existing xAxis and yAxis;
and all of these now have activeValue and persistentValue properties.
The activeValueChanged signal includes a delta value, giving the
incremental change since the previous activeValue. The persistentValue
is settable, in case some target item property can be adjusted in
multiple ways: the handler's stored value can then be synced up with the
item property value after each external change. These features are
also added to DragHandler's xAxis and yAxis properties.
Task-number: QTBUG-68108
Task-number: QTBUG-76380
Task-number: QTBUG-76379
Task-number: QTBUG-94168
Change-Id: I78a5b43e9ba580448ef05054b6c4bc71b1834dd6
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
Diffstat (limited to 'src/quick/handlers')
-rw-r--r-- | src/quick/handlers/qquickdragaxis.cpp | 33 | ||||
-rw-r--r-- | src/quick/handlers/qquickdragaxis_p.h | 36 | ||||
-rw-r--r-- | src/quick/handlers/qquickdraghandler.cpp | 45 | ||||
-rw-r--r-- | src/quick/handlers/qquickdraghandler_p.h | 15 | ||||
-rw-r--r-- | src/quick/handlers/qquickpinchhandler.cpp | 229 | ||||
-rw-r--r-- | src/quick/handlers/qquickpinchhandler_p.h | 45 |
6 files changed, 288 insertions, 115 deletions
diff --git a/src/quick/handlers/qquickdragaxis.cpp b/src/quick/handlers/qquickdragaxis.cpp index c775058b86..a1fb1920e4 100644 --- a/src/quick/handlers/qquickdragaxis.cpp +++ b/src/quick/handlers/qquickdragaxis.cpp @@ -1,14 +1,18 @@ // Copyright (C) 2017 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 "qquickdragaxis_p.h" +#include "qquickpointerhandler_p.h" +#include <QtQuick/qquickitem.h> #include <limits> QT_BEGIN_NAMESPACE -QQuickDragAxis::QQuickDragAxis() - : m_minimum(-std::numeric_limits<qreal>::max()) - , m_maximum(std::numeric_limits<qreal>::max()) - , m_enabled(true) +Q_LOGGING_CATEGORY(lcDragAxis, "qt.quick.pointer.dragaxis") + +QQuickDragAxis::QQuickDragAxis(QQuickPointerHandler *handler, const QString &propertyName, + bool boundedActiveValue, qreal initValue) + : QObject(handler), m_accumulatedValue(initValue), m_propertyName(propertyName), + m_boundedActiveValue(boundedActiveValue) { } @@ -39,6 +43,27 @@ void QQuickDragAxis::setEnabled(bool enabled) emit enabledChanged(); } +void QQuickDragAxis::onActiveChanged(bool active, qreal initActiveValue) +{ + m_activeValue = initActiveValue; + m_startValue = m_accumulatedValue; + qCDebug(lcDragAxis) << parent() << m_propertyName << active << ": init active" << m_activeValue + << "target start" << m_startValue; +} + +void QQuickDragAxis::updateValue(qreal activeValue, qreal accumulatedValue, qreal delta) +{ + if (!m_enabled) + return; + + m_activeValue = m_boundedActiveValue ? qBound(m_minimum, activeValue, m_maximum) : activeValue; + m_accumulatedValue = qBound(m_minimum, accumulatedValue, m_maximum); + qCDebug(lcDragAxis) << parent() << m_propertyName << "values: active" << activeValue + << "accumulated" << m_accumulatedValue << "delta" << delta; + emit activeValueChanged(delta); + emit persistentValueChanged(); +} + QT_END_NAMESPACE #include "moc_qquickdragaxis_p.cpp" diff --git a/src/quick/handlers/qquickdragaxis_p.h b/src/quick/handlers/qquickdragaxis_p.h index 17f20f332c..f788999c0f 100644 --- a/src/quick/handlers/qquickdragaxis_p.h +++ b/src/quick/handlers/qquickdragaxis_p.h @@ -16,22 +16,29 @@ // #include <QtQml/qqml.h> +#include <QtQml/qqmlproperty.h> #include <private/qtquickglobal_p.h> QT_BEGIN_NAMESPACE +class QQuickItem; +class QQuickPointerHandler; + class Q_QUICK_PRIVATE_EXPORT QQuickDragAxis : public QObject { Q_OBJECT Q_PROPERTY(qreal minimum READ minimum WRITE setMinimum NOTIFY minimumChanged) Q_PROPERTY(qreal maximum READ maximum WRITE setMaximum NOTIFY maximumChanged) Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) + Q_PROPERTY(qreal activeValue READ activeValue NOTIFY activeValueChanged REVISION(6, 5)) + Q_PROPERTY(qreal persistentValue READ persistentValue NOTIFY persistentValueChanged REVISION(6, 5)) QML_NAMED_ELEMENT(DragAxis) QML_ADDED_IN_VERSION(2, 12) - QML_UNCREATABLE("DragAxis is only available as a grouped property of DragHandler.") + QML_UNCREATABLE("DragAxis is only available as a grouped property of DragHandler or PinchHandler.") public: - QQuickDragAxis(); + QQuickDragAxis(QQuickPointerHandler *handler, const QString &propertyName, + bool boundedActiveValue = false, qreal initValue = 0); qreal minimum() const { return m_minimum; } void setMinimum(qreal minimum); @@ -42,15 +49,34 @@ public: bool enabled() const { return m_enabled; } void setEnabled(bool enabled); + qreal activeValue() const { return m_activeValue; } + + qreal persistentValue() const { return m_accumulatedValue; } + +protected: + void onActiveChanged(bool active, qreal initActiveValue); + qreal targetValue(); + void updateValue(qreal activeValue, qreal accumulatedValue, qreal delta = 0); + Q_SIGNALS: void minimumChanged(); void maximumChanged(); void enabledChanged(); + Q_REVISION(6, 5) void activeValueChanged(qreal delta); + Q_REVISION(6, 5) void persistentValueChanged(); private: - qreal m_minimum; - qreal m_maximum; - bool m_enabled; + qreal m_minimum = std::numeric_limits<qreal>::lowest(); + qreal m_maximum = std::numeric_limits<qreal>::max(); + qreal m_startValue = 0; + qreal m_activeValue = 0; + qreal m_accumulatedValue = 0; + QString m_propertyName; + bool m_enabled = true; + bool m_boundedActiveValue = false; + + friend class QQuickDragHandler; + friend class QQuickPinchHandler; }; QT_END_NAMESPACE diff --git a/src/quick/handlers/qquickdraghandler.cpp b/src/quick/handlers/qquickdraghandler.cpp index c31eb13d98..b34ca0e7cf 100644 --- a/src/quick/handlers/qquickdraghandler.cpp +++ b/src/quick/handlers/qquickdraghandler.cpp @@ -120,7 +120,10 @@ void QQuickDragHandler::setSnapMode(QQuickDragHandler::SnapMode mode) void QQuickDragHandler::onActiveChanged() { QQuickMultiPointHandler::onActiveChanged(); - if (active()) { + const bool curActive = active(); + m_xAxis.onActiveChanged(curActive, 0); + m_yAxis.onActiveChanged(curActive, 0); + if (curActive) { if (auto parent = parentItem()) { if (QQuickDeliveryAgentPrivate::isTouchEvent(currentEvent())) parent->setKeepTouchGrab(true); @@ -129,7 +132,6 @@ void QQuickDragHandler::onActiveChanged() // mouse grab too, whenever dragging occurs in an enabled direction parent->setKeepMouseGrab(true); } - m_startTranslation = m_persistentTranslation; } else { m_pressTargetPos = QPointF(); m_pressedInsideTarget = false; @@ -283,22 +285,25 @@ void QQuickDragHandler::enforceAxisConstraints(QPointF *localPos) void QQuickDragHandler::setPersistentTranslation(const QVector2D &trans) { - if (trans == m_persistentTranslation) + if (trans == persistentTranslation()) return; - m_persistentTranslation = trans; + m_xAxis.updateValue(m_xAxis.activeValue(), trans.x()); + m_yAxis.updateValue(m_yAxis.activeValue(), trans.y()); emit translationChanged(); } void QQuickDragHandler::setActiveTranslation(const QVector2D &trans) { - if (trans == m_activeTranslation) + if (trans == activeTranslation()) return; - m_activeTranslation = trans; - m_persistentTranslation = m_startTranslation + trans; - qCDebug(lcDragHandler) << "translation: start" << m_startTranslation - << "active" << m_activeTranslation << "accumulated" << m_persistentTranslation; + const QVector2D delta = trans - activeTranslation(); + m_xAxis.updateValue(trans.x(), m_xAxis.persistentValue() + delta.x(), delta.x()); + m_yAxis.updateValue(trans.y(), m_yAxis.persistentValue() + delta.y(), delta.y()); + + qCDebug(lcDragHandler) << "translation: delta" << delta + << "active" << trans << "accumulated" << persistentTranslation(); emit translationChanged(); } @@ -307,6 +312,8 @@ void QQuickDragHandler::setActiveTranslation(const QVector2D &trans) \qmlproperty real QtQuick::DragHandler::xAxis.minimum \qmlproperty real QtQuick::DragHandler::xAxis.maximum \qmlproperty bool QtQuick::DragHandler::xAxis.enabled + \qmlproperty real QtQuick::DragHandler::xAxis.activeValue + \qmlproperty real QtQuick::DragHandler::xAxis.persistentValue \c xAxis controls the constraints for horizontal dragging. @@ -315,13 +322,21 @@ void QQuickDragHandler::setActiveTranslation(const QVector2D &trans) \c maximum is the maximum acceptable value of \l {Item::x}{x} to be applied to the \l {PointerHandler::target} {target}. If \c enabled is true, horizontal dragging is allowed. - */ + \c activeValue is the same as \l {QtQuick::DragHandler::activeTranslation}{activeTranslation.x}. + \c persistentValue is the same as \l {QtQuick::DragHandler::persistentTranslation}{persistentTranslation.x}. + + The \c activeValueChanged signal is emitted when \c activeValue (and therefore + \c persistentValue) changes, to provide the increment by which it changed. + This is intended for incrementally adjusting one property via multiple handlers. +*/ /*! \qmlpropertygroup QtQuick::DragHandler::yAxis \qmlproperty real QtQuick::DragHandler::yAxis.minimum \qmlproperty real QtQuick::DragHandler::yAxis.maximum \qmlproperty bool QtQuick::DragHandler::yAxis.enabled + \qmlproperty real QtQuick::DragHandler::yAxis.activeValue + \qmlproperty real QtQuick::DragHandler::yAxis.persistentValue \c yAxis controls the constraints for vertical dragging. @@ -330,7 +345,15 @@ void QQuickDragHandler::setActiveTranslation(const QVector2D &trans) \c maximum is the maximum acceptable value of \l {Item::y}{y} to be applied to the \l {PointerHandler::target} {target}. If \c enabled is true, vertical dragging is allowed. - */ + \c activeValue is the same as \l {QtQuick::DragHandler::activeTranslation}{activeTranslation.y}. + \c persistentValue is the same as \l {QtQuick::DragHandler::persistentTranslation}{persistentTranslation.y}. + + The \c activeValueChanged signal is emitted when \c activeValue (and therefore + \c persistentValue) changes, to provide the increment by which it changed. + This is intended for incrementally adjusting one property via multiple handlers: + + \snippet pointerHandlers/rotateViaWheelOrDrag.qml 0 +*/ /*! \readonly diff --git a/src/quick/handlers/qquickdraghandler_p.h b/src/quick/handlers/qquickdraghandler_p.h index a86ef847e2..925783ec98 100644 --- a/src/quick/handlers/qquickdraghandler_p.h +++ b/src/quick/handlers/qquickdraghandler_p.h @@ -20,6 +20,8 @@ QT_BEGIN_NAMESPACE +using namespace Qt::StringLiterals; + class Q_QUICK_PRIVATE_EXPORT QQuickDragHandler : public QQuickMultiPointHandler { Q_OBJECT @@ -51,11 +53,11 @@ public: QQuickDragAxis *yAxis() { return &m_yAxis; } #if QT_DEPRECATED_SINCE(6, 2) - QVector2D translation() const { return m_activeTranslation; } + QVector2D translation() const { return activeTranslation(); } #endif - QVector2D activeTranslation() const { return m_activeTranslation; } + QVector2D activeTranslation() const { return QVector2D(QPointF(m_xAxis.activeValue(), m_yAxis.activeValue())); } void setActiveTranslation(const QVector2D &trans); - QVector2D persistentTranslation() const { return m_persistentTranslation; } + QVector2D persistentTranslation() const { return QVector2D(QPointF(m_xAxis.persistentValue(), m_yAxis.persistentValue())); } void setPersistentTranslation(const QVector2D &trans); QQuickDragHandler::SnapMode snapMode() const; void setSnapMode(QQuickDragHandler::SnapMode mode); @@ -80,12 +82,9 @@ private: QPointF m_pressTargetPos; // We must also store the local targetPos, because we cannot deduce // the press target pos from the scene pos in case there was e.g a // flick in one of the ancestors during the drag. - QVector2D m_activeTranslation; - QVector2D m_persistentTranslation; - QVector2D m_startTranslation; - QQuickDragAxis m_xAxis; - QQuickDragAxis m_yAxis; + QQuickDragAxis m_xAxis = {this, u"x"_s}; + QQuickDragAxis m_yAxis = {this, u"y"_s}; QQuickDragHandler::SnapMode m_snapMode = SnapAuto; bool m_pressedInsideTarget = false; diff --git a/src/quick/handlers/qquickpinchhandler.cpp b/src/quick/handlers/qquickpinchhandler.cpp index 6fec0feaa1..f11d8357eb 100644 --- a/src/quick/handlers/qquickpinchhandler.cpp +++ b/src/quick/handlers/qquickpinchhandler.cpp @@ -74,10 +74,10 @@ QQuickPinchHandler::QQuickPinchHandler(QQuickItem *parent) */ 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(); } @@ -89,10 +89,10 @@ void QQuickPinchHandler::setMinimumScale(qreal minimumScale) */ 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(); } @@ -104,10 +104,10 @@ void QQuickPinchHandler::setMaximumScale(qreal maximumScale) */ 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(); } @@ -119,10 +119,10 @@ void QQuickPinchHandler::setMinimumRotation(qreal minimumRotation) */ 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(); } @@ -159,26 +159,110 @@ 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 + \qmlproperty real QtQuick::PinchHandler::xAxis.persistentValue \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 (and therefore + \c persistentValue) 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 + \qmlproperty real QtQuick::PinchHandler::yAxis.persistentValue \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 (and therefore + \c persistentValue) 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::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 + \qmlproperty real QtQuick::PinchHandler::scaleAxis.persistentValue + + \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}. + \c persistentValue is the same as \l {QtQuick::PinchHandler::scale}. + + The \c activeValueChanged signal is emitted when \c activeValue (and therefore + \c persistentValue) 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. +*/ + +/*! + \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 + \qmlproperty real QtQuick::PinchHandler::rotationAxis.persistentValue + + \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::rotation}. + \c persistentValue holds the accumulated value across multiple gestures. + + The \c activeValueChanged signal is emitted when \c activeValue (and therefore + \c persistentValue) 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 @@ -193,22 +277,25 @@ bool QQuickPinchHandler::wantsPointerEvent(QPointerEvent *event) void QQuickPinchHandler::onActiveChanged() { QQuickMultiPointHandler::onActiveChanged(); - if (active()) { + const bool curActive = active(); + if (const QQuickItem *t = target(); curActive && t) { + m_xAxis.m_accumulatedValue = t->position().x(); + m_yAxis.m_accumulatedValue = t->position().y(); + m_scaleAxis.m_accumulatedValue = t->scale(); + m_rotationAxis.m_accumulatedValue = t->rotation(); + } + 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; // TODO m_accumulatedRotation (QTBUG-94168) - } - qCDebug(lcPinchHandler) << "activated with starting scale" << m_startScale << "rotation" << m_startRotation; + qCDebug(lcPinchHandler) << "activated with starting scale" << m_scaleAxis.m_startValue + << "target scale" << m_scaleAxis.m_startValue << "rotation" << m_rotationAxis.m_startValue; } else { - qCDebug(lcPinchHandler) << "deactivated with scale" << m_activeScale << "rotation" << m_activeRotation; + qCDebug(lcPinchHandler) << "deactivated with scale" << m_scaleAxis.m_activeValue << "rotation" << m_rotationAxis.m_activeValue; } } @@ -226,31 +313,27 @@ 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(); - m_activeScale = qBound(m_minimumScale, m_activeScale, m_maximumScale); + m_scaleAxis.updateValue(1 + gesture->value(), m_scaleAxis.m_startValue * (1 + gesture->value())); break; case Qt::RotateNativeGesture: - m_activeRotation += gesture->value(); + m_rotationAxis.updateValue(m_rotationAxis.m_activeValue + gesture->value(), + m_rotationAxis.m_startValue + m_rotationAxis.m_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) { @@ -333,7 +416,9 @@ void QQuickPinchHandler::handlePointerEventImpl(QPointerEvent *event) ++numberOfPointsDraggedOverThreshold; } - const bool requiredNumberOfPointsDraggedOverThreshold = numberOfPointsDraggedOverThreshold >= minimumPointCount() && numberOfPointsDraggedOverThreshold <= maximumPointCount(); + const bool requiredNumberOfPointsDraggedOverThreshold = + numberOfPointsDraggedOverThreshold >= minimumPointCount() && + numberOfPointsDraggedOverThreshold <= maximumPointCount(); accumulatedMovementMagnitude /= currentPoints().size(); QVector2D avgDrag = accumulatedDrag / currentPoints().size(); @@ -346,7 +431,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); @@ -369,62 +455,81 @@ 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); + m_scaleAxis.updateValue(activeScale, m_scaleAxis.m_startValue * activeScale, + activeScale / m_scaleAxis.activeValue()); + } // 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); + m_rotationAxis.updateValue(m_rotationAxis.m_activeValue + angleDelta, + m_rotationAxis.m_startValue + m_rotationAxis.m_activeValue + angleDelta, + 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()); // 3. Drag/translate const QPointF centroidStartParentPos = target()->parentItem()->mapFromScene(centroid().sceneGrabPosition()); - m_activeTranslation = QVector2D(centroidParentPos - centroidStartParentPos); + auto activeTranslation = QVector2D(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); + startPos(), activeTranslation, + m_scaleAxis.m_startValue, + m_scaleAxis.persistentValue() / m_scaleAxis.m_startValue, + m_rotationAxis.m_startValue, + 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()); + pos.ry() -= qreal(activeTranslation.y()); - target()->setPosition(pos); - target()->setRotation(rotation); - target()->setScale(m_accumulatedScale); + m_xAxis.updateValue(activeTranslation.x(), pos.x(), activeTranslation.x() - m_xAxis.activeValue()); + m_yAxis.updateValue(activeTranslation.y(), pos.y(), activeTranslation.y() - m_yAxis.activeValue()); + target()->setPosition(QPointF(m_xAxis.persistentValue(), m_yAxis.persistentValue())); + target()->setRotation(m_rotationAxis.persistentValue()); + target()->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; + m_xAxis.updateValue(activeTranslation.x(), accumulated.x(), + activeTranslation.x() - m_xAxis.activeValue()); + m_yAxis.updateValue(activeTranslation.y(), accumulated.y(), + activeTranslation.y() - m_yAxis.activeValue()); } qCDebug(lcPinchHandler) << "centroid" << centroid().scenePressPosition() << "->" << centroid().scenePosition() << ", distance" << m_startDistance << "->" << dist - << ", scale" << m_startScale << "->" << m_accumulatedScale - << ", activeRotation" << m_activeRotation - << ", rotation" << rotation - << " from " << event->device()->type(); + << ", scale" << m_scaleAxis.m_startValue << "->" << m_scaleAxis.m_accumulatedValue + << ", rotation" << m_rotationAxis.m_startValue << "->" << m_rotationAxis.m_accumulatedValue + << ", translation" << translation() << " from " << event->device()->type(); emit updated(); } +QPointF QQuickPinchHandler::startPos() +{ + return {m_xAxis.m_startValue, m_yAxis.m_startValue}; +} + /*! \readonly \qmlproperty QtQuick::HandlerPoint QtQuick::PinchHandler::centroid diff --git a/src/quick/handlers/qquickpinchhandler_p.h b/src/quick/handlers/qquickpinchhandler_p.h index 6fff85639e..e788b490eb 100644 --- a/src/quick/handlers/qquickpinchhandler_p.h +++ b/src/quick/handlers/qquickpinchhandler_p.h @@ -23,6 +23,8 @@ QT_BEGIN_NAMESPACE +using namespace Qt::StringLiterals; + class Q_QUICK_PRIVATE_EXPORT QQuickPinchHandler : public QQuickMultiPointHandler { Q_OBJECT @@ -36,31 +38,35 @@ class Q_QUICK_PRIVATE_EXPORT QQuickPinchHandler : public QQuickMultiPointHandler Q_PROPERTY(QVector2D translation READ translation NOTIFY updated) Q_PROPERTY(QQuickDragAxis * xAxis READ xAxis CONSTANT) Q_PROPERTY(QQuickDragAxis * yAxis READ yAxis CONSTANT) + Q_PROPERTY(QQuickDragAxis * scaleAxis READ scaleAxis CONSTANT) + Q_PROPERTY(QQuickDragAxis * rotationAxis READ rotationAxis CONSTANT) QML_NAMED_ELEMENT(PinchHandler) QML_ADDED_IN_VERSION(2, 12) public: explicit QQuickPinchHandler(QQuickItem *parent = nullptr); - qreal minimumScale() const { return m_minimumScale; } + qreal minimumScale() const { return m_scaleAxis.minimum(); } void setMinimumScale(qreal minimumScale); - qreal maximumScale() const { return m_maximumScale; } + qreal maximumScale() const { return m_scaleAxis.maximum(); } void setMaximumScale(qreal maximumScale); - qreal minimumRotation() const { return m_minimumRotation; } + qreal minimumRotation() const { return m_rotationAxis.minimum(); } void setMinimumRotation(qreal minimumRotation); - qreal maximumRotation() const { return m_maximumRotation; } + qreal maximumRotation() const { return m_rotationAxis.maximum(); } void setMaximumRotation(qreal maximumRotation); - QVector2D translation() const { return m_activeTranslation; } - qreal scale() const { return m_accumulatedScale; } - qreal activeScale() const { return m_activeScale; } - qreal rotation() const { return m_activeRotation; } + QVector2D translation() const { return QVector2D(QPointF(m_xAxis.activeValue(), m_yAxis.activeValue())); } + qreal scale() const { return m_scaleAxis.m_accumulatedValue; } + qreal activeScale() const { return m_scaleAxis.m_activeValue; } + qreal rotation() const { return m_rotationAxis.m_accumulatedValue; } QQuickDragAxis *xAxis() { return &m_xAxis; } QQuickDragAxis *yAxis() { return &m_yAxis; } + QQuickDragAxis *scaleAxis() { return &m_scaleAxis; } + QQuickDragAxis *rotationAxis() { return &m_rotationAxis; } Q_SIGNALS: void minimumScaleChanged(); @@ -74,27 +80,16 @@ protected: void onActiveChanged() override; void handlePointerEventImpl(QPointerEvent *event) override; -private: - // properties - qreal m_activeScale = 1; - qreal m_accumulatedScale = 1; - qreal m_activeRotation = 0; - QVector2D m_activeTranslation = QVector2D(0, 0); - - qreal m_minimumScale = -qInf(); - qreal m_maximumScale = qInf(); + QPointF startPos(); - qreal m_minimumRotation = -qInf(); - qreal m_maximumRotation = qInf(); - - QQuickDragAxis m_xAxis; - QQuickDragAxis m_yAxis; +private: + QQuickDragAxis m_xAxis = {this, u"x"_s}; + QQuickDragAxis m_yAxis = {this, u"y"_s}; + QQuickDragAxis m_scaleAxis = {this, u"scale"_s, true, 1}; + QQuickDragAxis m_rotationAxis = {this, u"rotation"_s, true}; // internal - qreal m_startScale = 1; - qreal m_startRotation = 0; qreal m_startDistance = 0; - QPointF m_startPos; qreal m_accumulatedStartCentroidDistance = 0; QVector<PointData> m_startAngles; QQuickMatrix4x4 m_transform; |