Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qquickmultipointhandler.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
6#include <private/qquickitem_p.h>
7#include <QLineF>
8#include <QMouseEvent>
9#include <QDebug>
10
12
26QQuickMultiPointHandler::QQuickMultiPointHandler(QQuickItem *parent, int minimumPointCount, int maximumPointCount)
27 : QQuickPointerDeviceHandler(*(new QQuickMultiPointHandlerPrivate(minimumPointCount, maximumPointCount)), parent)
28{
29}
30
32{
35 return false;
36
37 if (event->type() == QEvent::Wheel)
38 return false;
39
40 bool ret = false;
41#if QT_CONFIG(gestures)
42 if (event->type() == QEvent::NativeGesture && event->point(0).state() != QEventPoint::Released)
43 ret = true;
44#endif
45
46 // If points were pressed or released within parentItem, reset stored state
47 // and check eligible points again. This class of handlers is intended to
48 // handle a specific number of points, so a differing number of points will
49 // usually result in different behavior. But otherwise if the currentPoints
50 // are all still there in the event, we're good to go (do not reset
51 // currentPoints, because we don't want to lose the pressPosition, and do
52 // not want to reshuffle the order either).
53 const auto candidatePoints = eligiblePoints(event);
54 if (candidatePoints.size() != d->currentPoints.size()) {
55 d->currentPoints.clear();
56 if (active()) {
57 setActive(false);
58 d->centroid.reset();
60 }
61 } else if (hasCurrentPoints(event)) {
62 return true;
63 }
64
65 ret = ret || (candidatePoints.size() >= minimumPointCount() && candidatePoints.size() <= maximumPointCount());
66 if (ret) {
67 const int c = candidatePoints.size();
68 d->currentPoints.resize(c);
69 for (int i = 0; i < c; ++i) {
70 d->currentPoints[i].reset(event, candidatePoints[i]);
71 if (auto par = parentItem())
72 d->currentPoints[i].localize(par);
73 }
74 } else {
75 d->currentPoints.clear();
76 }
77 return ret;
78}
79
81{
84 // event's points can be reordered since the previous event, which is why currentPoints
85 // is _not_ a shallow copy of the QQuickPointerTouchEvent::m_touchPoints vector.
86 // So we have to update our currentPoints instances based on the given event.
87 for (QQuickHandlerPoint &p : d->currentPoints) {
88 if (const QEventPoint *ep = event->pointById(p.id()))
89 p.reset(event, *ep);
90 }
91 QPointF sceneGrabPos = d->centroid.sceneGrabPosition();
92 d->centroid.reset(d->currentPoints);
93 d->centroid.m_sceneGrabPosition = sceneGrabPos; // preserve as it was
95}
96
98{
100 if (active()) {
101 d->centroid.m_sceneGrabPosition = d->centroid.m_scenePosition;
102 } else {
103 // Don't call centroid.reset() here, because in a QML onActiveChanged
104 // callback, we'd like to see what the position _was_, what the velocity _was_, etc.
105 // (having them undefined is not useful)
106 // But pressedButtons and pressedModifiers are meant to be more real-time than those
107 // (which seems a bit inconsistent, from one side).
108 d->centroid.m_pressedButtons = Qt::NoButton;
109 d->centroid.m_pressedModifiers = Qt::NoModifier;
110 }
111}
112
114{
116 // If another handler or item takes over this set of points, assume it has
117 // decided that it's the better fit for them. Don't immediately re-grab
118 // at the next opportunity. This should help to avoid grab cycles
119 // (e.g. between DragHandler and PinchHandler).
121 d->currentPoints.clear();
122 if (grabber != this)
123 return;
124 switch (transition) {
126 for (auto &pt : d->currentPoints)
127 if (pt.id() == point.id()) {
128 pt.m_sceneGrabPosition = point.scenePosition();
129 break;
130 }
131 QQuickPointerHandler::onGrabChanged(grabber, transition, event, point);
132 break;
138 QQuickPointerHandler::onGrabChanged(grabber, transition, event, point);
139 break;
141 return; // don't emit
142 }
143}
144
146{
147 QVector<QEventPoint> ret;
148 // If one or more points are newly pressed or released, all non-released points are candidates for this handler.
149 // In other cases however, check whether it would be OK to steal the grab if the handler chooses to do that.
150 bool stealingAllowed = event->isBeginEvent() || event->isEndEvent();
151 for (int i = 0; i < event->pointCount(); ++i) {
152 auto &p = event->point(i);
154 if (static_cast<QMouseEvent *>(event)->buttons() == Qt::NoButton)
155 continue;
156 }
157 if (!stealingAllowed) {
158 QObject *exclusiveGrabber = event->exclusiveGrabber(p);
159 if (exclusiveGrabber && exclusiveGrabber != this && !canGrab(event, p))
160 continue;
161 }
162 if (p.state() != QEventPoint::Released && wantsEventPoint(event, p))
163 ret << p;
164 }
165 return ret;
166}
167
182{
183 Q_D(const QQuickMultiPointHandler);
184 return d->minimumPointCount;
185}
186
188{
190 if (d->minimumPointCount == c)
191 return;
192
193 d->minimumPointCount = c;
195 if (d->maximumPointCount < 0)
197}
198
215{
216 Q_D(const QQuickMultiPointHandler);
217 return d->maximumPointCount >= 0 ? d->maximumPointCount : d->minimumPointCount;
218}
219
221{
223 if (d->maximumPointCount == maximumPointCount)
224 return;
225
226 d->maximumPointCount = maximumPointCount;
228}
229
239{
240 Q_D(const QQuickMultiPointHandler);
241 return d->centroid;
242}
243
254
255QVector<QQuickHandlerPoint> &QQuickMultiPointHandler::currentPoints()
256{
258 return d->currentPoints;
259}
260
262{
263 Q_D(const QQuickMultiPointHandler);
264 if (event->pointCount() < d->currentPoints.size() || d->currentPoints.size() == 0)
265 return false;
266 // TODO optimize: either ensure the points are sorted,
267 // or use std::equal with a predicate
268 for (const QQuickHandlerPoint &p : std::as_const(d->currentPoints)) {
269 const QEventPoint *ep = event->pointById(p.id());
270 if (!ep)
271 return false;
272 if (ep->state() == QEventPoint::Released)
273 return false;
274 }
275 return true;
276}
277
279{
280 Q_D(const QQuickMultiPointHandler);
281 qreal ret = 0;
282 if (Q_UNLIKELY(d->currentPoints.size() == 0))
283 return ret;
284 for (const QQuickHandlerPoint &p : d->currentPoints)
285 ret += QVector2D(p.scenePosition() - ref).length();
286 return ret / d->currentPoints.size();
287}
288
290{
291 Q_D(const QQuickMultiPointHandler);
292 // TODO cache it in setActive()?
293 qreal ret = 0;
294 if (Q_UNLIKELY(d->currentPoints.size() == 0))
295 return ret;
296 for (const QQuickHandlerPoint &p : d->currentPoints)
297 ret += QVector2D(p.sceneGrabPosition() - ref).length();
298 return ret / d->currentPoints.size();
299}
300
301QVector<QQuickMultiPointHandler::PointData> QQuickMultiPointHandler::angles(const QPointF &ref) const
302{
303 Q_D(const QQuickMultiPointHandler);
304 QVector<PointData> angles;
305 angles.reserve(d->currentPoints.size());
306 for (const QQuickHandlerPoint &p : d->currentPoints) {
307 qreal angle = QLineF(ref, p.scenePosition()).angle();
308 angles.append(PointData(p.id(), -angle)); // convert to clockwise, to be consistent with QQuickItem::rotation
309 }
310 return angles;
311}
312
313qreal QQuickMultiPointHandler::averageAngleDelta(const QVector<PointData> &old, const QVector<PointData> &newAngles)
314{
315 qreal avgAngleDelta = 0;
316 int numSamples = 0;
317
318 auto oldBegin = old.constBegin();
319
320 for (PointData newData : newAngles) {
321 quint64 id = newData.id;
322 auto it = std::find_if(oldBegin, old.constEnd(), [id] (PointData pd) { return pd.id == id; });
323 qreal angleD = 0;
324 if (it != old.constEnd()) {
325 PointData oldData = *it;
326 // We might rotate from 359 degrees to 1 degree. However, this
327 // should be interpreted as a rotation of +2 degrees instead of
328 // -358 degrees. Therefore, we call remainder() to translate the angle
329 // to be in the range [-180, 180] (-350 to +10 etc)
330 angleD = remainder(newData.angle - oldData.angle, qreal(360));
331 // optimization: narrow down the O(n^2) search to optimally O(n)
332 // if both vectors have the same points and they are in the same order
333 if (it == oldBegin)
334 ++oldBegin;
335 numSamples++;
336 }
337 avgAngleDelta += angleD;
338 }
339 if (numSamples > 1)
340 avgAngleDelta /= numSamples;
341
342 return avgAngleDelta;
343}
344
345void QQuickMultiPointHandler::acceptPoints(const QVector<QEventPoint> &points)
346{
347 // "auto point" is a copy, but it's OK because
348 // setAccepted() changes QEventPointPrivate::accept via the shared d-pointer
349 for (auto point : points)
350 point.setAccepted();
351}
352
354{
355 if (points.isEmpty())
356 return false;
357 bool allowed = true;
358 for (auto &point : points) {
359 if (event->exclusiveGrabber(point) != this && !canGrab(event, point)) {
360 allowed = false;
361 break;
362 }
363 }
364 if (allowed) {
365 for (const auto &point : std::as_const(points))
366 setExclusiveGrab(event, point);
367 }
368 return allowed;
369}
370
372{
374 if (QQuickItem *t = target()) {
375 d->xMetaProperty().write(t, pos.x());
376 d->yMetaProperty().write(t, pos.y());
377 d->centroid.m_position = t->mapFromScene(d->centroid.m_scenePosition);
378 } else {
379 qWarning() << "moveTarget: target is null";
380 }
381}
382
385 , minimumPointCount(minPointCount)
386 , maximumPointCount(maxPointCount)
387{
388}
389
391{
392 Q_Q(const QQuickMultiPointHandler);
393 if (!xProperty.isValid() && q->target()) {
394 const QMetaObject *targetMeta = q->target()->metaObject();
395 xProperty = targetMeta->property(targetMeta->indexOfProperty("x"));
396 }
397 return xProperty;
398}
399
401{
402 Q_Q(const QQuickMultiPointHandler);
403 if (!yProperty.isValid() && q->target()) {
404 const QMetaObject *targetMeta = q->target()->metaObject();
405 yProperty = targetMeta->property(targetMeta->indexOfProperty("y"));
406 }
407 return yProperty;
408}
409
411
412#include "moc_qquickmultipointhandler_p.cpp"
The QEventPoint class provides information about a point in a QPointerEvent.
Definition qeventpoint.h:20
int id
the ID number of this event point.
Definition qeventpoint.h:24
QPointF scenePosition
the scene position of this point.
Definition qeventpoint.h:39
State state
the current state of the event point.
Definition qeventpoint.h:26
@ NativeGesture
Definition qcoreevent.h:246
\inmodule QtCore\compares equality \compareswith equality QLine \endcompareswith
Definition qline.h:192
qreal angle() const
Definition qline.cpp:564
\inmodule QtCore
bool isValid() const
Returns true if this property is valid (readable); otherwise returns false.
\inmodule QtGui
Definition qevent.h:196
\inmodule QtCore
Definition qobject.h:103
\inmodule QtCore\reentrant
Definition qpoint.h:217
A base class for pointer events.
Definition qevent.h:73
GrabTransition
This enum represents a transition of exclusive or passive grab from one object (possibly nullptr) to ...
static bool isMouseEvent(const QPointerEvent *ev)
The QQuickItem class provides the most basic of all visual items in \l {Qt Quick}.
Definition qquickitem.h:63
QQuickMultiPointHandlerPrivate(int minPointCount, int maxPointCount)
bool hasCurrentPoints(QPointerEvent *event)
void onGrabChanged(QQuickPointerHandler *grabber, QPointingDevice::GrabTransition transition, QPointerEvent *event, QEventPoint &point) override
Notification that the grab has changed in some way which is relevant to this handler.
void handlePointerEventImpl(QPointerEvent *event) override
This function can be overridden to implement whatever behavior a specific subclass is intended to hav...
QQuickHandlerPoint & mutableCentroid()
Returns a modifiable reference to the point that will be returned by the \l centroid property.
QList< QQuickHandlerPoint > & currentPoints()
bool wantsPointerEvent(QPointerEvent *event) override
It is the responsibility of this function to decide whether the event could be relevant at all to thi...
qreal averageTouchPointDistance(const QPointF &ref)
qreal averageStartingDistance(const QPointF &ref)
void setMaximumPointCount(int maximumPointCount)
void acceptPoints(const QVector< QEventPoint > &points)
QVector< PointData > angles(const QPointF &ref) const
QQuickMultiPointHandler(QQuickItem *parent=nullptr, int minimumPointCount=2, int maximumPointCount=-1)
\qmltype MultiPointHandler
QVector< QEventPoint > eligiblePoints(QPointerEvent *event)
bool grabPoints(QPointerEvent *event, const QVector< QEventPoint > &points)
static qreal averageAngleDelta(const QVector< PointData > &old, const QVector< PointData > &newAngles)
bool wantsPointerEvent(QPointerEvent *event) override
It is the responsibility of this function to decide whether the event could be relevant at all to thi...
virtual bool canGrab(QPointerEvent *event, const QEventPoint &point)
Check whether it's OK to take an exclusive grab of the point.
QQuickItem * parentItem() const
\qmlproperty Item QtQuick::PointerHandler::parent
virtual bool wantsEventPoint(const QPointerEvent *event, const QEventPoint &point)
Returns true if the given point (as part of event) could be relevant at all to this handler,...
virtual void onGrabChanged(QQuickPointerHandler *grabber, QPointingDevice::GrabTransition transition, QPointerEvent *event, QEventPoint &point)
Notification that the grab has changed in some way which is relevant to this handler.
virtual void handlePointerEventImpl(QPointerEvent *event)
This function can be overridden to implement whatever behavior a specific subclass is intended to hav...
bool setExclusiveGrab(QPointerEvent *ev, const QEventPoint &point, bool grab=true)
Acquire or give up the exclusive grab of the given point, according to the grab state,...
The QVector2D class represents a vector or vertex in 2D space.
Definition qvectornd.h:31
QSet< QString >::iterator it
Combined button and popup list for selecting options.
@ NoButton
Definition qnamespace.h:57
@ NoModifier
#define Q_UNLIKELY(x)
#define qWarning
Definition qlogging.h:166
return ret
GLenum GLuint GLenum GLsizei length
GLfloat angle
GLint ref
struct _cl_event * event
GLfixed GLfixed GLint GLint GLfixed points
const GLubyte * c
GLdouble GLdouble t
Definition qopenglext.h:243
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLfloat GLfloat p
[1]
#define emit
unsigned long long quint64
Definition qtypes.h:61
double qreal
Definition qtypes.h:187
\inmodule QtCore