Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Toggle main menu visibility
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
// Qt-Security score:significant reason:default
4
5
#
include
"qquickmultipointhandler_p.h"
6
#
include
"qquickmultipointhandler_p_p.h"
7
#
include
<
private
/
qquickitem_p
.
h
>
8
#
include
<
QLineF
>
9
#
include
<
QMouseEvent
>
10
#
include
<
QDebug
>
11
12
QT_BEGIN_NAMESPACE
13
14
/*!
15
\qmltype MultiPointHandler
16
\since 5.10
17
\preliminary
18
\nativetype QQuickMultiPointHandler
19
\inherits PointerDeviceHandler
20
\inqmlmodule QtQuick
21
\brief Abstract handler for multi-point Pointer Events.
22
23
An intermediate class (not registered as a QML type)
24
for any type of handler which requires and acts upon a specific number
25
of multiple touchpoints.
26
*/
27
28
/*!
29
\class QQuickMultiPointHandler
30
\inmodule QtQuick
31
\internal
32
*/
33
QQuickMultiPointHandler::QQuickMultiPointHandler(QQuickItem *parent,
int
minimumPointCount,
int
maximumPointCount)
34
: QQuickPointerDeviceHandler(*(
new
QQuickMultiPointHandlerPrivate(minimumPointCount, maximumPointCount)), parent)
35
{
36
}
37
38
bool
QQuickMultiPointHandler::wantsPointerEvent(QPointerEvent *event)
39
{
40
Q_D(QQuickMultiPointHandler);
41
if
(!QQuickPointerDeviceHandler::wantsPointerEvent(event))
42
return
false
;
43
44
if
(event->type() == QEvent::Wheel)
45
return
false
;
46
47
bool
ret =
false
;
48
#
if
QT_CONFIG
(
gestures
)
49
if
(event->type() == QEvent::NativeGesture && event->point(0).state() != QEventPoint::Released)
50
ret =
true
;
51
#
endif
52
53
// If points were pressed or released within parentItem, reset stored state
54
// and check eligible points again. This class of handlers is intended to
55
// handle a specific number of points, so a differing number of points will
56
// usually result in different behavior. But otherwise if the currentPoints
57
// are all still there in the event, we're good to go (do not reset
58
// currentPoints, because we don't want to lose the pressPosition, and do
59
// not want to reshuffle the order either).
60
const
auto
candidatePoints = eligiblePoints(event);
61
if
(candidatePoints.size() != d->currentPoints.size()) {
62
d->currentPoints.clear();
63
if
(active()) {
64
setActive(
false
);
65
d->centroid.reset();
66
emit centroidChanged();
67
}
68
}
else
if
(hasCurrentPoints(event)) {
69
return
true
;
70
}
71
72
ret = ret || (candidatePoints.size() >= minimumPointCount() && candidatePoints.size() <= maximumPointCount());
73
if
(ret) {
74
const
int
c = candidatePoints.size();
75
d->currentPoints.resize(c);
76
for
(
int
i = 0; i < c; ++i) {
77
d->currentPoints[i].reset(event, candidatePoints[i]);
78
if
(
auto
par = parentItem())
79
d->currentPoints[i].localize(par);
80
}
81
}
else
{
82
d->currentPoints.clear();
83
}
84
return
ret;
85
}
86
87
void
QQuickMultiPointHandler::handlePointerEventImpl(QPointerEvent *event)
88
{
89
Q_D(QQuickMultiPointHandler);
90
QQuickPointerHandler::handlePointerEventImpl(event);
91
// event's points can be reordered since the previous event, which is why currentPoints
92
// is _not_ a shallow copy of the QQuickPointerTouchEvent::m_touchPoints vector.
93
// So we have to update our currentPoints instances based on the given event.
94
for
(QQuickHandlerPoint &p : d->currentPoints) {
95
if
(
const
QEventPoint *ep = event->pointById(p.id()))
96
p.reset(event, *ep);
97
}
98
QPointF sceneGrabPos = d->centroid.sceneGrabPosition();
99
d->centroid.reset(d->currentPoints);
100
d->centroid.m_sceneGrabPosition = sceneGrabPos;
// preserve as it was
101
emit centroidChanged();
102
}
103
104
void
QQuickMultiPointHandler::onActiveChanged()
105
{
106
Q_D(QQuickMultiPointHandler);
107
if
(active()) {
108
d->centroid.m_sceneGrabPosition = d->centroid.m_scenePosition;
109
}
else
{
110
// Don't call centroid.reset() here, because in a QML onActiveChanged
111
// callback, we'd like to see what the position _was_, what the velocity _was_, etc.
112
// (having them undefined is not useful)
113
// But pressedButtons and pressedModifiers are meant to be more real-time than those
114
// (which seems a bit inconsistent, from one side).
115
d->centroid.m_pressedButtons = Qt::NoButton;
116
d->centroid.m_pressedModifiers = Qt::NoModifier;
117
}
118
}
119
120
void
QQuickMultiPointHandler::onGrabChanged(QQuickPointerHandler *grabber, QPointingDevice::GrabTransition transition, QPointerEvent *event, QEventPoint &point)
121
{
122
Q_D(QQuickMultiPointHandler);
123
// If another handler or item takes over this set of points, assume it has
124
// decided that it's the better fit for them. Don't immediately re-grab
125
// at the next opportunity. This should help to avoid grab cycles
126
// (e.g. between DragHandler and PinchHandler).
127
if
(transition == QPointingDevice::UngrabExclusive || transition == QPointingDevice::CancelGrabExclusive)
128
d->currentPoints.clear();
129
if
(grabber !=
this
)
130
return
;
131
switch
(transition) {
132
case
QPointingDevice::GrabExclusive:
133
for
(
auto
&pt : d->currentPoints)
134
if
(pt.id() == point.id()) {
135
pt.m_sceneGrabPosition = point.scenePosition();
136
break
;
137
}
138
QQuickPointerHandler::onGrabChanged(grabber, transition, event, point);
139
break
;
140
case
QPointingDevice::GrabPassive:
141
case
QPointingDevice::UngrabPassive:
142
case
QPointingDevice::UngrabExclusive:
143
case
QPointingDevice::CancelGrabPassive:
144
case
QPointingDevice::CancelGrabExclusive:
145
QQuickPointerHandler::onGrabChanged(grabber, transition, event, point);
146
break
;
147
case
QPointingDevice::OverrideGrabPassive:
148
return
;
// don't emit
149
}
150
}
151
152
QList<QEventPoint> QQuickMultiPointHandler::eligiblePoints(QPointerEvent *event)
153
{
154
QList<QEventPoint> ret;
155
// If one or more points are newly pressed or released, all non-released points are candidates for this handler.
156
// In other cases however, check whether it would be OK to steal the grab if the handler chooses to do that.
157
bool
stealingAllowed = event->isBeginEvent() || event->isEndEvent();
158
for
(
int
i = 0; i < event->pointCount(); ++i) {
159
auto
&p = event->point(i);
160
if
(QQuickDeliveryAgentPrivate::isMouseEvent(event)) {
161
if
(
static_cast
<QMouseEvent *>(event)->buttons() == Qt::NoButton)
162
continue
;
163
}
164
if
(!stealingAllowed) {
165
QObject *exclusiveGrabber = event->exclusiveGrabber(p);
166
if
(exclusiveGrabber && exclusiveGrabber !=
this
&& !canGrab(event, p))
167
continue
;
168
}
169
if
(p.state() != QEventPoint::Released && wantsEventPoint(event, p))
170
ret << p;
171
}
172
return
ret;
173
}
174
175
/*!
176
\qmlproperty int MultiPointHandler::minimumPointCount
177
178
The minimum number of touchpoints required to activate this handler.
179
180
If a smaller number of touchpoints are in contact with the
181
\l {PointerHandler::parent}{parent}, they will be ignored.
182
183
Any ignored points are eligible to activate other Input Handlers that
184
have different constraints, on the same Item or on other Items.
185
186
The default value is 2.
187
*/
188
int
QQuickMultiPointHandler::minimumPointCount()
const
189
{
190
Q_D(
const
QQuickMultiPointHandler);
191
return
d->minimumPointCount;
192
}
193
194
void
QQuickMultiPointHandler::setMinimumPointCount(
int
c)
195
{
196
Q_D(QQuickMultiPointHandler);
197
if
(d->minimumPointCount == c)
198
return
;
199
200
d->minimumPointCount = c;
201
emit minimumPointCountChanged();
202
if
(d->maximumPointCount < 0)
203
emit maximumPointCountChanged();
204
}
205
206
/*!
207
\qmlproperty int MultiPointHandler::maximumPointCount
208
209
The maximum number of touchpoints this handler can utilize.
210
211
If a larger number of touchpoints are in contact with the
212
\l {PointerHandler::parent}{parent}, the required number of points will be
213
chosen in the order that they are pressed, and the remaining points will
214
be ignored.
215
216
Any ignored points are eligible to activate other Input Handlers that
217
have different constraints, on the same Item or on other Items.
218
219
The default value is the same as \l minimumPointCount.
220
*/
221
int
QQuickMultiPointHandler::maximumPointCount()
const
222
{
223
Q_D(
const
QQuickMultiPointHandler);
224
return
d->maximumPointCount >= 0 ? d->maximumPointCount : d->minimumPointCount;
225
}
226
227
void
QQuickMultiPointHandler::setMaximumPointCount(
int
maximumPointCount)
228
{
229
Q_D(QQuickMultiPointHandler);
230
if
(d->maximumPointCount == maximumPointCount)
231
return
;
232
233
d->maximumPointCount = maximumPointCount;
234
emit maximumPointCountChanged();
235
}
236
237
/*!
238
\readonly
239
\qmlproperty QtQuick::handlerPoint QtQuick::MultiPointHandler::centroid
240
241
A point exactly in the middle of the currently-pressed touch points.
242
If only one point is pressed, it's the same as that point.
243
A handler that has a \l target will normally transform it relative to this point.
244
*/
245
const
QQuickHandlerPoint &QQuickMultiPointHandler::centroid()
const
246
{
247
Q_D(
const
QQuickMultiPointHandler);
248
return
d->centroid;
249
}
250
251
/*!
252
Returns a modifiable reference to the point that will be returned by the
253
\l centroid property. If you modify it, you are responsible to emit
254
\l centroidChanged.
255
*/
256
QQuickHandlerPoint &QQuickMultiPointHandler::mutableCentroid()
257
{
258
Q_D(QQuickMultiPointHandler);
259
return
d->centroid;
260
}
261
262
QList<QQuickHandlerPoint> &QQuickMultiPointHandler::currentPoints()
263
{
264
Q_D(QQuickMultiPointHandler);
265
return
d->currentPoints;
266
}
267
268
bool
QQuickMultiPointHandler::hasCurrentPoints(QPointerEvent *event)
269
{
270
Q_D(
const
QQuickMultiPointHandler);
271
if
(event->pointCount() < d->currentPoints.size() || d->currentPoints.size() == 0)
272
return
false
;
273
// TODO optimize: either ensure the points are sorted,
274
// or use std::equal with a predicate
275
for
(
const
QQuickHandlerPoint &p : std::as_const(d->currentPoints)) {
276
const
QEventPoint *ep = event->pointById(p.id());
277
if
(!ep)
278
return
false
;
279
if
(ep->state() == QEventPoint::Released)
280
return
false
;
281
}
282
return
true
;
283
}
284
285
qreal QQuickMultiPointHandler::averageTouchPointDistance(
const
QPointF &ref)
286
{
287
Q_D(
const
QQuickMultiPointHandler);
288
qreal ret = 0;
289
if
(Q_UNLIKELY(d->currentPoints.size() == 0))
290
return
ret;
291
for
(
const
QQuickHandlerPoint &p : d->currentPoints)
292
ret += QVector2D(p.scenePosition() - ref).length();
293
return
ret / d->currentPoints.size();
294
}
295
296
qreal QQuickMultiPointHandler::averageStartingDistance(
const
QPointF &ref)
297
{
298
Q_D(
const
QQuickMultiPointHandler);
299
// TODO cache it in setActive()?
300
qreal ret = 0;
301
if
(Q_UNLIKELY(d->currentPoints.size() == 0))
302
return
ret;
303
for
(
const
QQuickHandlerPoint &p : d->currentPoints)
304
ret += QVector2D(p.sceneGrabPosition() - ref).length();
305
return
ret / d->currentPoints.size();
306
}
307
308
QList<QQuickMultiPointHandler::PointData> QQuickMultiPointHandler::angles(
const
QPointF &ref)
const
309
{
310
Q_D(
const
QQuickMultiPointHandler);
311
QList<PointData> angles;
312
angles.reserve(d->currentPoints.size());
313
for
(
const
QQuickHandlerPoint &p : d->currentPoints) {
314
qreal angle = QLineF(ref, p.scenePosition()).angle();
315
angles.append(PointData(p.id(), -angle));
// convert to clockwise, to be consistent with QQuickItem::rotation
316
}
317
return
angles;
318
}
319
320
qreal QQuickMultiPointHandler::averageAngleDelta(
const
QList<PointData> &old,
const
QList<PointData> &newAngles)
321
{
322
qreal avgAngleDelta = 0;
323
int
numSamples = 0;
324
325
auto
oldBegin = old.constBegin();
326
327
for
(PointData newData : newAngles) {
328
quint64 id = newData.id;
329
auto
it = std::find_if(oldBegin, old.constEnd(), [id] (PointData pd) {
return
pd.id == id; });
330
qreal angleD = 0;
331
if
(it != old.constEnd()) {
332
PointData oldData = *it;
333
// We might rotate from 359 degrees to 1 degree. However, this
334
// should be interpreted as a rotation of +2 degrees instead of
335
// -358 degrees. Therefore, we call remainder() to translate the angle
336
// to be in the range [-180, 180] (-350 to +10 etc)
337
angleD = remainder(newData.angle - oldData.angle, qreal(360));
338
// optimization: narrow down the O(n^2) search to optimally O(n)
339
// if both vectors have the same points and they are in the same order
340
if
(it == oldBegin)
341
++oldBegin;
342
numSamples++;
343
}
344
avgAngleDelta += angleD;
345
}
346
if
(numSamples > 1)
347
avgAngleDelta /= numSamples;
348
349
return
avgAngleDelta;
350
}
351
352
void
QQuickMultiPointHandler::acceptPoints(
const
QList<QEventPoint> &points)
353
{
354
// "auto point" is a copy, but it's OK because
355
// setAccepted() changes QEventPointPrivate::accept via the shared d-pointer
356
for
(
auto
point : points)
357
point.setAccepted();
358
}
359
360
bool
QQuickMultiPointHandler::grabPoints(QPointerEvent *event,
const
QList<QEventPoint> &points)
361
{
362
if
(points.isEmpty())
363
return
false
;
364
bool
allowed =
true
;
365
for
(
auto
&point : points) {
366
if
(event->exclusiveGrabber(point) !=
this
&& !canGrab(event, point)) {
367
allowed =
false
;
368
break
;
369
}
370
}
371
if
(allowed) {
372
for
(
const
auto
&point : std::as_const(points))
373
setExclusiveGrab(event, point);
374
}
375
return
allowed;
376
}
377
378
void
QQuickMultiPointHandler::moveTarget(QPointF pos)
379
{
380
Q_D(QQuickMultiPointHandler);
381
if
(QQuickItem *t = target()) {
382
d->xMetaProperty().write(t, pos.x());
383
d->yMetaProperty().write(t, pos.y());
384
d->centroid.m_position = t->mapFromScene(d->centroid.m_scenePosition);
385
}
else
{
386
qWarning() <<
"moveTarget: target is null"
;
387
}
388
}
389
390
QQuickMultiPointHandlerPrivate::QQuickMultiPointHandlerPrivate(
int
minPointCount,
int
maxPointCount)
391
: QQuickPointerDeviceHandlerPrivate()
392
, minimumPointCount(minPointCount)
393
, maximumPointCount(maxPointCount)
394
{
395
}
396
397
QMetaProperty &QQuickMultiPointHandlerPrivate::xMetaProperty()
const
398
{
399
Q_Q(
const
QQuickMultiPointHandler);
400
if
(!xProperty.isValid() && q->target()) {
401
const
QMetaObject *targetMeta = q->target()->metaObject();
402
xProperty = targetMeta->property(targetMeta->indexOfProperty(
"x"
));
403
}
404
return
xProperty;
405
}
406
407
QMetaProperty &QQuickMultiPointHandlerPrivate::yMetaProperty()
const
408
{
409
Q_Q(
const
QQuickMultiPointHandler);
410
if
(!yProperty.isValid() && q->target()) {
411
const
QMetaObject *targetMeta = q->target()->metaObject();
412
yProperty = targetMeta->property(targetMeta->indexOfProperty(
"y"
));
413
}
414
return
yProperty;
415
}
416
417
QT_END_NAMESPACE
418
419
#
include
"moc_qquickmultipointhandler_p.cpp"
QT_BEGIN_NAMESPACE
Combined button and popup list for selecting options.
Definition
qsequentialanimationgroup.cpp:47
qtdeclarative
src
quick
handlers
qquickmultipointhandler.cpp
Generated on
for Qt by
1.18.0