diff options
author | Shawn Rutledge <shawn.rutledge@qt.io> | 2018-06-29 15:16:52 +0200 |
---|---|---|
committer | Shawn Rutledge <shawn.rutledge@qt.io> | 2018-07-19 10:55:57 +0000 |
commit | d310ca768bb5f45bae4fcec9a5d8151b6a366b8d (patch) | |
tree | 479a66e595ea6eca8bb82978dd8eb1d2076caef0 /src/quick/handlers/qquickmultipointhandler.cpp | |
parent | b5d7be57ccb8ca60590ed30d04271e76acc84029 (diff) |
QQuickMultiPointHandler: store QQuickHandlerPoints, not QQEventPoint*s
QQuickPointerTouchEvent::reset(QEvent *event) reuses instances of
QQuickEventPoint from one touch event to the next, but it makes no
attempt to match up each instance with the same pointId from the event.
So from the perspective of Handlers, each event can have its
touchpoints in a different order, and therefore it's always wrong to
hold onto any QQuickEventPoint pointer between events. Instead we
use QQuickHandlerPoint for storage: both for exposing to QML, and for
internal storage in handlers that need to remember touchpoint state.
Without this change, any MultiPointHandler could "forget" which
point IDs it had chosen to react to, in any case where the event
contains additional points. It was using a QVector<QQuickEventPoint *>
to remember the chosen points, but each of those instances might be
assigned a different touchpoint during the handling of the next
touch event (and thus its ID would change "underneath").
Perhaps this went unnoticed until now because 1) the only subclass
of MultiPointHandler was PinchHandler, and we didn't often test
use cases with extra touchpoints beyond the ones involved in the pinch
and 2) on Linux/X11 they stayed in the same order in each event.
But as soon as we try to make DragHandler inherit MultiPointHandler,
it becomes clear that it does not succeed in holding on to a particular
touchpoint while dragging multiple DragHandlers with multiple fingers,
without this patch.
Change-Id: If7e0daa9ed77b263efc09f5ea73dfba6a3c8205c
Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
Diffstat (limited to 'src/quick/handlers/qquickmultipointhandler.cpp')
-rw-r--r-- | src/quick/handlers/qquickmultipointhandler.cpp | 54 |
1 files changed, 32 insertions, 22 deletions
diff --git a/src/quick/handlers/qquickmultipointhandler.cpp b/src/quick/handlers/qquickmultipointhandler.cpp index 9a3732ff84..b08b00e5a7 100644 --- a/src/quick/handlers/qquickmultipointhandler.cpp +++ b/src/quick/handlers/qquickmultipointhandler.cpp @@ -85,15 +85,31 @@ bool QQuickMultiPointHandler::wantsPointerEvent(QQuickPointerEvent *event) const QVector<QQuickEventPoint *> candidatePoints = eligiblePoints(event); const bool ret = (candidatePoints.size() >= minimumPointCount() && candidatePoints.size() <= maximumPointCount()); - if (ret) - m_currentPoints = candidatePoints; + if (ret) { + const int c = candidatePoints.count(); + m_currentPoints.resize(c); + for (int i = 0; i < c; ++i) + m_currentPoints[i].reset(candidatePoints[i]); + } else { + m_currentPoints.clear(); + } return ret; } void QQuickMultiPointHandler::handlePointerEventImpl(QQuickPointerEvent *event) { QQuickPointerHandler::handlePointerEventImpl(event); + // event's points can be reordered since the previous event, which is why m_currentPoints + // is _not_ a shallow copy of the QQuickPointerTouchEvent::m_touchPoints vector. + // So we have to update our m_currentPoints instances based on the given event. + for (QQuickHandlerPoint &p : m_currentPoints) { + const QQuickEventPoint *ep = event->pointById(p.id()); + if (ep) + p.reset(ep); + } + QPointF sceneGrabPos = m_centroid.sceneGrabPosition(); m_centroid.reset(m_currentPoints); + m_centroid.m_sceneGrabPosition = sceneGrabPos; // preserve as it was emit centroidChanged(); } @@ -175,24 +191,18 @@ void QQuickMultiPointHandler::setMaximumPointCount(int maximumPointCount) bool QQuickMultiPointHandler::hasCurrentPoints(QQuickPointerEvent *event) { - bool ret = true; - int c = event->pointCount(); - if (c < m_currentPoints.size()) + if (event->pointCount() < m_currentPoints.size() || m_currentPoints.size() == 0) return false; // TODO optimize: either ensure the points are sorted, // or use std::equal with a predicate - for (int i = 0; ret && i < c; ++i) { - if (event->point(i)->state() == QQuickEventPoint::Released) + for (const QQuickHandlerPoint &p : qAsConst(m_currentPoints)) { + const QQuickEventPoint *ep = event->pointById(p.id()); + if (!ep) + return false; + if (ep->state() == QQuickEventPoint::Released) return false; - bool found = false; - int pointId = event->point(i)->pointId(); - for (QQuickEventPoint *o : qAsConst(m_currentPoints)) - if (o && pointId == o->pointId()) - found = true; - if (!found) - ret = false; } - return ret; + return true; } qreal QQuickMultiPointHandler::averageTouchPointDistance(const QPointF &ref) @@ -200,8 +210,8 @@ qreal QQuickMultiPointHandler::averageTouchPointDistance(const QPointF &ref) qreal ret = 0; if (Q_UNLIKELY(m_currentPoints.size() == 0)) return ret; - for (QQuickEventPoint *point : qAsConst(m_currentPoints)) - ret += QVector2D(point->scenePosition() - ref).length(); + for (const QQuickHandlerPoint &p : m_currentPoints) + ret += QVector2D(p.scenePosition() - ref).length(); return ret / m_currentPoints.size(); } @@ -211,8 +221,8 @@ qreal QQuickMultiPointHandler::averageStartingDistance(const QPointF &ref) qreal ret = 0; if (Q_UNLIKELY(m_currentPoints.size() == 0)) return ret; - for (QQuickEventPoint *point : qAsConst(m_currentPoints)) - ret += QVector2D(point->sceneGrabPosition() - ref).length(); + for (const QQuickHandlerPoint &p : m_currentPoints) + ret += QVector2D(p.sceneGrabPosition() - ref).length(); return ret / m_currentPoints.size(); } @@ -220,9 +230,9 @@ QVector<QQuickMultiPointHandler::PointData> QQuickMultiPointHandler::angles(cons { QVector<PointData> angles; angles.reserve(m_currentPoints.count()); - for (QQuickEventPoint *point : qAsConst(m_currentPoints)) { - qreal angle = QLineF(ref, point->scenePosition()).angle(); - angles.append(PointData(point->pointId(), -angle)); // convert to clockwise, to be consistent with QQuickItem::rotation + for (const QQuickHandlerPoint &p : m_currentPoints) { + qreal angle = QLineF(ref, p.scenePosition()).angle(); + angles.append(PointData(p.id(), -angle)); // convert to clockwise, to be consistent with QQuickItem::rotation } return angles; } |