1/****************************************************************************
2**
3** Copyright (C) 2020 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtQuick module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qquickevents_p_p.h"
41#include <QtCore/qmap.h>
42#include <QtGui/private/qguiapplication_p.h>
43#include <QtGui/private/qtouchdevice_p.h>
44#include <QtGui/private/qevent_p.h>
45#include <QtQuick/private/qquickitem_p.h>
46#include <QtQuick/private/qquickpointerhandler_p.h>
47#include <QtQuick/private/qquickwindow_p.h>
48#include <private/qdebug_p.h>
49
50QT_BEGIN_NAMESPACE
51
52Q_LOGGING_CATEGORY(lcPointerEvents, "qt.quick.pointer.events")
53Q_LOGGING_CATEGORY(lcPointerGrab, "qt.quick.pointer.grab")
54
55/*!
56 \qmltype KeyEvent
57 \instantiates QQuickKeyEvent
58 \inqmlmodule QtQuick
59 \ingroup qtquick-input-events
60
61 \brief Provides information about a key event.
62
63 For example, the following changes the Item's state property when the Enter
64 key is pressed:
65 \qml
66Item {
67 focus: true
68 Keys.onPressed: { if (event.key == Qt.Key_Enter) state = 'ShowDetails'; }
69}
70 \endqml
71*/
72
73/*!
74 \qmlproperty int QtQuick::KeyEvent::key
75
76 This property holds the code of the key that was pressed or released.
77
78 See \l {Qt::Key}{Qt.Key} for the list of keyboard codes. These codes are
79 independent of the underlying window system. Note that this
80 function does not distinguish between capital and non-capital
81 letters; use the \l {KeyEvent::}{text} property for this purpose.
82
83 A value of either 0 or \l {Qt::Key_unknown}{Qt.Key_Unknown} means that the event is not
84 the result of a known key; for example, it may be the result of
85 a compose sequence, a keyboard macro, or due to key event
86 compression.
87*/
88
89/*!
90 \qmlproperty string QtQuick::KeyEvent::text
91
92 This property holds the Unicode text that the key generated.
93 The text returned can be an empty string in cases where modifier keys,
94 such as Shift, Control, Alt, and Meta, are being pressed or released.
95 In such cases \c key will contain a valid value
96*/
97
98/*!
99 \qmlproperty bool QtQuick::KeyEvent::isAutoRepeat
100
101 This property holds whether this event comes from an auto-repeating key.
102*/
103
104/*!
105 \qmlproperty quint32 QtQuick::KeyEvent::nativeScanCode
106
107 This property contains the native scan code of the key that was pressed. It is
108 passed through from QKeyEvent unchanged.
109
110 \sa QKeyEvent::nativeScanCode()
111*/
112
113/*!
114 \qmlproperty int QtQuick::KeyEvent::count
115
116 This property holds the number of keys involved in this event. If \l KeyEvent::text
117 is not empty, this is simply the length of the string.
118*/
119
120/*!
121 \qmlproperty bool QtQuick::KeyEvent::accepted
122
123 Setting \a accepted to true prevents the key event from being
124 propagated to the item's parent.
125
126 Generally, if the item acts on the key event then it should be accepted
127 so that ancestor items do not also respond to the same event.
128*/
129
130/*!
131 \qmlproperty int QtQuick::KeyEvent::modifiers
132
133 This property holds the keyboard modifier flags that existed immediately
134 before the event occurred.
135
136 It contains a bitwise combination of:
137 \list
138 \li \l {Qt::NoModifier} {Qt.NoModifier} - No modifier key is pressed.
139 \li \l {Qt::ShiftModifier} {Qt.ShiftModifier} - A Shift key on the keyboard is pressed.
140 \li \l {Qt::ControlModifier} {Qt.ControlModifier} - A Ctrl key on the keyboard is pressed.
141 \li \l {Qt::AltModifier} {Qt.AltModifier} - An Alt key on the keyboard is pressed.
142 \li \l {Qt::MetaModifier} {Qt.MetaModifier} - A Meta key on the keyboard is pressed.
143 \li \l {Qt::KeypadModifier} {Qt.KeypadModifier} - A keypad button is pressed.
144 \li \l {Qt::GroupSwitchModifier} {Qt.GroupSwitchModifier} - X11 only. A Mode_switch key on the keyboard is pressed.
145 \endlist
146
147 For example, to react to a Shift key + Enter key combination:
148 \qml
149 Item {
150 focus: true
151 Keys.onPressed: {
152 if ((event.key == Qt.Key_Enter) && (event.modifiers & Qt.ShiftModifier))
153 doSomething();
154 }
155 }
156 \endqml
157*/
158
159/*!
160 \qmlmethod bool QtQuick::KeyEvent::matches(StandardKey key)
161 \since 5.2
162
163 Returns \c true if the key event matches the given standard \a key; otherwise returns \c false.
164
165 \qml
166 Item {
167 focus: true
168 Keys.onPressed: {
169 if (event.matches(StandardKey.Undo))
170 myModel.undo();
171 else if (event.matches(StandardKey.Redo))
172 myModel.redo();
173 }
174 }
175 \endqml
176
177 \sa QKeySequence::StandardKey
178*/
179
180/*!
181 \qmltype MouseEvent
182 \instantiates QQuickMouseEvent
183 \inqmlmodule QtQuick
184 \ingroup qtquick-input-events
185
186 \brief Provides information about a mouse event.
187
188 The position of the mouse can be found via the \l {Item::x} {x} and \l {Item::y} {y} properties.
189 The button that caused the event is available via the \l button property.
190
191 \sa MouseArea
192*/
193
194/*!
195 \internal
196 \class QQuickMouseEvent
197*/
198
199/*!
200 \qmlproperty real QtQuick::MouseEvent::x
201 \qmlproperty real QtQuick::MouseEvent::y
202
203 These properties hold the coordinates of the position supplied by the mouse event.
204*/
205
206
207/*!
208 \qmlproperty bool QtQuick::MouseEvent::accepted
209
210 Setting \a accepted to true prevents the mouse event from being
211 propagated to items below this item.
212
213 Generally, if the item acts on the mouse event then it should be accepted
214 so that items lower in the stacking order do not also respond to the same event.
215*/
216
217/*!
218 \qmlproperty enumeration QtQuick::MouseEvent::button
219
220 This property holds the button that caused the event. It can be one of:
221 \list
222 \li \l {Qt::LeftButton} {Qt.LeftButton}
223 \li \l {Qt::RightButton} {Qt.RightButton}
224 \li \l {Qt::MiddleButton} {Qt.MiddleButton}
225 \endlist
226*/
227
228/*!
229 \qmlproperty bool QtQuick::MouseEvent::wasHeld
230
231 This property is true if the mouse button has been held pressed longer
232 than the threshold (800ms).
233*/
234
235/*!
236 \qmlproperty int QtQuick::MouseEvent::buttons
237
238 This property holds the mouse buttons pressed when the event was generated.
239 For mouse move events, this is all buttons that are pressed down. For mouse
240 press and double click events this includes the button that caused the event.
241 For mouse release events this excludes the button that caused the event.
242
243 It contains a bitwise combination of:
244 \list
245 \li \l {Qt::LeftButton} {Qt.LeftButton}
246 \li \l {Qt::RightButton} {Qt.RightButton}
247 \li \l {Qt::MiddleButton} {Qt.MiddleButton}
248 \endlist
249*/
250
251/*!
252 \qmlproperty int QtQuick::MouseEvent::modifiers
253
254 This property holds the keyboard modifier flags that existed immediately
255 before the event occurred.
256
257 It contains a bitwise combination of:
258 \list
259 \li \l {Qt::NoModifier} {Qt.NoModifier} - No modifier key is pressed.
260 \li \l {Qt::ShiftModifier} {Qt.ShiftModifier} - A Shift key on the keyboard is pressed.
261 \li \l {Qt::ControlModifier} {Qt.ControlModifier} - A Ctrl key on the keyboard is pressed.
262 \li \l {Qt::AltModifier} {Qt.AltModifier} - An Alt key on the keyboard is pressed.
263 \li \l {Qt::MetaModifier} {Qt.MetaModifier} - A Meta key on the keyboard is pressed.
264 \li \l {Qt::KeypadModifier} {Qt.KeypadModifier} - A keypad button is pressed.
265 \endlist
266
267 For example, to react to a Shift key + Left mouse button click:
268 \qml
269 MouseArea {
270 onClicked: {
271 if ((mouse.button == Qt.LeftButton) && (mouse.modifiers & Qt.ShiftModifier))
272 doSomething();
273 }
274 }
275 \endqml
276*/
277
278/*!
279 \qmlproperty int QtQuick::MouseEvent::source
280 \since 5.7
281
282 This property holds the source of the mouse event.
283
284 The mouse event source can be used to distinguish between genuine and
285 artificial mouse events. When using other pointing devices such as
286 touchscreens and graphics tablets, if the application does not make use of
287 the actual touch or tablet events, mouse events may be synthesized by the
288 operating system or by Qt itself.
289
290 The value can be one of:
291
292 \list
293 \li \l{Qt::MouseEventNotSynthesized} {Qt.MouseEventNotSynthesized}
294 - The most common value. On platforms where such information is
295 available, this value indicates that the event represents a genuine
296 mouse event from the system.
297
298 \li \l{Qt::MouseEventSynthesizedBySystem} {Qt.MouseEventSynthesizedBySystem} - Indicates that the mouse event was
299 synthesized from a touch or tablet event by the platform.
300
301 \li \l{Qt::MouseEventSynthesizedByQt} {Qt.MouseEventSynthesizedByQt}
302 - Indicates that the mouse event was synthesized from an unhandled
303 touch or tablet event by Qt.
304
305 \li \l{Qt::MouseEventSynthesizedByApplication} {Qt.MouseEventSynthesizedByApplication}
306 - Indicates that the mouse event was synthesized by the application.
307 This allows distinguishing application-generated mouse events from
308 the ones that are coming from the system or are synthesized by Qt.
309 \endlist
310
311 For example, to react only to events which come from an actual mouse:
312 \qml
313 MouseArea {
314 onPressed: if (mouse.source !== Qt.MouseEventNotSynthesized) {
315 mouse.accepted = false
316 }
317
318 onClicked: doSomething()
319 }
320 \endqml
321
322 If the handler for the press event rejects the event, it will be propagated
323 further, and then another Item underneath can handle synthesized events
324 from touchscreens. For example, if a Flickable is used underneath (and the
325 MouseArea is not a child of the Flickable), it can be useful for the
326 MouseArea to handle genuine mouse events in one way, while allowing touch
327 events to fall through to the Flickable underneath, so that the ability to
328 flick on a touchscreen is retained. In that case the ability to drag the
329 Flickable via mouse would be lost, but it does not prevent Flickable from
330 receiving mouse wheel events.
331*/
332
333/*!
334 \qmlproperty int QtQuick::MouseEvent::flags
335 \since 5.11
336
337 This property holds the flags that provide additional information about the
338 mouse event.
339
340 \list
341 \li \l {Qt::MouseEventCreatedDoubleClick} {Qt.MouseEventCreatedDoubleClick}
342 - Indicates that Qt has created a double click event from this event.
343 This flag is set in the event originating from a button press, and not
344 in the resulting double click event.
345 \endlist
346*/
347
348/*!
349 \qmltype WheelEvent
350 \instantiates QQuickWheelEvent
351 \inqmlmodule QtQuick
352 \ingroup qtquick-input-events
353 \brief Provides information about a mouse wheel event.
354
355 The position of the mouse can be found via the
356 \l {Item::x} {x} and \l {Item::y} {y} properties.
357
358 \sa MouseArea
359*/
360
361/*!
362 \internal
363 \class QQuickWheelEvent
364*/
365
366/*!
367 \qmlproperty real QtQuick::WheelEvent::x
368 \qmlproperty real QtQuick::WheelEvent::y
369
370 These properties hold the coordinates of the position supplied by the wheel event.
371*/
372
373/*!
374 \qmlproperty bool QtQuick::WheelEvent::accepted
375
376 Setting \a accepted to true prevents the wheel event from being
377 propagated to items below this item.
378
379 Generally, if the item acts on the wheel event then it should be accepted
380 so that items lower in the stacking order do not also respond to the same event.
381*/
382
383/*!
384 \qmlproperty int QtQuick::WheelEvent::buttons
385
386 This property holds the mouse buttons pressed when the wheel event was generated.
387
388 It contains a bitwise combination of:
389 \list
390 \li \l {Qt::LeftButton} {Qt.LeftButton}
391 \li \l {Qt::RightButton} {Qt.RightButton}
392 \li \l {Qt::MiddleButton} {Qt.MiddleButton}
393 \endlist
394*/
395
396/*!
397 \qmlproperty point QtQuick::WheelEvent::angleDelta
398
399 This property holds the distance that the wheel is rotated in wheel degrees.
400 The x and y cordinate of this property holds the delta in horizontal and
401 vertical orientation.
402
403 A positive value indicates that the wheel was rotated up/right;
404 a negative value indicates that the wheel was rotated down/left.
405
406 Most mouse types work in steps of 15 degrees, in which case the delta value is a
407 multiple of 120; i.e., 120 units * 1/8 = 15 degrees.
408*/
409
410/*!
411 \qmlproperty point QtQuick::WheelEvent::pixelDelta
412
413 This property holds the delta in screen pixels and is available in platforms that
414 have high-resolution trackpads, such as \macos.
415 The x and y cordinate of this property holds the delta in horizontal and
416 vertical orientation. The value should be used directly to scroll content on screen.
417
418 For platforms without high-resolution trackpad support, pixelDelta will always be (0,0),
419 and angleDelta should be used instead.
420*/
421
422/*!
423 \qmlproperty int QtQuick::WheelEvent::modifiers
424
425 This property holds the keyboard modifier flags that existed immediately
426 before the event occurred.
427
428 It contains a bitwise combination of:
429 \list
430 \li \l {Qt::NoModifier} {Qt.NoModifier} - No modifier key is pressed.
431 \li \l {Qt::ShiftModifier} {Qt.ShiftModifier} - A Shift key on the keyboard is pressed.
432 \li \l {Qt::ControlModifier} {Qt.ControlModifier} - A Ctrl key on the keyboard is pressed.
433 \li \l {Qt::AltModifier} {Qt.AltModifier} - An Alt key on the keyboard is pressed.
434 \li \l {Qt::MetaModifier} {Qt.MetaModifier} - A Meta key on the keyboard is pressed.
435 \li \l {Qt::KeypadModifier} {Qt.KeypadModifier} - A keypad button is pressed.
436 \endlist
437
438 For example, to react to a Control key pressed during the wheel event:
439 \qml
440 MouseArea {
441 onWheel: {
442 if (wheel.modifiers & Qt.ControlModifier) {
443 adjustZoom(wheel.angleDelta.y / 120);
444 }
445 }
446 }
447 \endqml
448*/
449
450/*!
451 \qmlproperty bool QtQuick::WheelEvent::inverted
452
453 Returns whether the delta values delivered with the event are inverted.
454
455 Normally, a vertical wheel will produce a WheelEvent with positive delta
456 values if the top of the wheel is rotating away from the hand operating it.
457 Similarly, a horizontal wheel movement will produce a QWheelEvent with
458 positive delta values if the top of the wheel is moved to the left.
459
460 However, on some platforms this is configurable, so that the same
461 operations described above will produce negative delta values (but with the
462 same magnitude). For instance, in a QML component (such as a tumbler or a
463 slider) where it is appropriate to synchronize the movement or rotation of
464 an item with the direction of the wheel, regardless of the system settings,
465 the wheel event handler can use the inverted property to decide whether to
466 negate the angleDelta or pixelDelta values.
467
468 \note Many platforms provide no such information. On such platforms
469 \l inverted always returns false.
470*/
471
472/*!
473 \qmltype PointerDevice
474 \instantiates QQuickPointerDevice
475 \inqmlmodule QtQuick
476 \ingroup qtquick-input-events
477
478 \brief Provides information about a pointing device.
479
480 A pointing device can be a mouse, a touchscreen, or a stylus on a graphics
481 tablet.
482
483 \sa PointerEvent, PointerHandler
484*/
485
486/*!
487 \readonly
488 \qmlproperty enumeration QtQuick::PointerDevice::type
489
490 This property holds the type of the pointing device.
491
492 Valid values are:
493
494 \value DeviceType.UnknownDevice
495 the device cannot be identified
496 \value DeviceType.Mouse
497 a mouse
498 \value DeviceType.TouchScreen
499 a touchscreen providing absolute coordinates
500 \value DeviceType.TouchPad
501 a trackpad or touchpad providing relative coordinates
502 \value DeviceType.Stylus
503 a pen-like device
504 \value DeviceType.Airbrush
505 a stylus with a thumbwheel to adjust
506 \l {QTabletEvent::tangentialPressure}{tangentialPressure}
507 \value DeviceType.Puck
508 a device that is similar to a flat mouse with a
509 transparent circle with cross-hairs
510 (same as \l {QTabletEvent::Puck} {Puck})
511 \value DeviceType.AllDevices
512 any of the above; used as a default value for construction
513
514 \sa QTouchDevice::DeviceType
515*/
516
517/*!
518 \readonly
519 \qmlproperty enumeration QtQuick::PointerDevice::pointerType
520
521 This property holds a value indicating what is interacting with
522 the device. Think of the device as having a planar 2D surface, and
523 the value of this property as identifying what interacts with the
524 device.
525
526 There is some redundancy between this property and \l {PointerDevice::type}.
527 If a tocuchscreen is used, then the device is TouchScreen and
528 pointerType is Finger (always).
529
530 Valid values are:
531
532 \value PointerDevice.GenericPointer
533 a mouse or something acting like a mouse (the core pointer on X11)
534 \value PointerDevice.Finger
535 the user's finger
536 \value PointerDevice.Pen
537 the drawing end of a stylus
538 \value PointerDevice.Eraser
539 the other end of the stylus (if it has a virtual eraser on the other end)
540 \value PointerDevice.Cursor
541 a cursor in the pre-computer sense of the word
542 \value PointerDevice.AllPointerTypes
543 any of the above (used as a default value in constructors)
544*/
545
546
547/*!
548 \readonly
549 \qmlproperty enumeration QtQuick::PointerDevice::capabilities
550
551 This property holds a bitwise combination of the capabilities of the
552 pointing device. It tells you under which conditions events are sent,
553 and which properties of PointerEvent are expected to be valid.
554
555 Valid values are:
556
557 \value CapabilityFlag.Position
558 the \l {QtQuick::EventPoint::position}{position} and
559 \l {QtQuick::EventPoint::scenePosition}{scenePosition} properties
560 \value CapabilityFlag.Area
561 the \l {QtQuick::EventTouchPoint::ellipseDiameters}{ellipseDiameters} property
562 \value CapabilityFlag.Pressure
563 the \l {QtQuick::EventTouchPoint::pressure}{pressure} property
564 \value CapabilityFlag.Velocity
565 the \l {QtQuick::EventPoint::velocity}{velocity} property
566 \value CapabilityFlag.Scroll
567 a \l {QtQuick::PointerDevice::type}{Mouse} has a wheel, or the
568 operating system recognizes scroll gestures on a
569 \l {QtQuick::PointerDevice::type}{TouchPad}
570 \value CapabilityFlag.Hover
571 events are sent even when no button is pressed, or the finger or stylus
572 is not in contact with the surface
573 \value CapabilityFlag.Rotation
574 the \l {QtQuick::EventTouchPoint::rotation}{rotation} property
575 \value CapabilityFlag.XTilt
576 horizontal angle between a stylus and the axis perpendicular to the surface
577 \value CapabilityFlag.YTilt
578 vertical angle between a stylus and the axis perpendicular to the surface
579
580 \sa QTouchDevice::capabilities
581*/
582
583typedef QHash<const QTouchDevice *, QQuickPointerDevice *> PointerDeviceForTouchDeviceHash;
584Q_GLOBAL_STATIC(PointerDeviceForTouchDeviceHash, g_touchDevices)
585
586struct ConstructableQQuickPointerDevice : public QQuickPointerDevice
587{
588 ConstructableQQuickPointerDevice(DeviceType devType, PointerType pType, Capabilities caps,
589 int maxPoints, int buttonCount, const QString &name,
590 qint64 uniqueId = 0)
591 : QQuickPointerDevice(devType, pType, caps, maxPoints, buttonCount, name, uniqueId) {}
592
593};
594Q_GLOBAL_STATIC_WITH_ARGS(ConstructableQQuickPointerDevice, g_genericMouseDevice,
595 (QQuickPointerDevice::Mouse,
596 QQuickPointerDevice::GenericPointer,
597 QQuickPointerDevice::Position | QQuickPointerDevice::Scroll | QQuickPointerDevice::Hover,
598 1, 3, QLatin1String("core pointer"), 0))
599
600#if QT_CONFIG(tabletevent)
601typedef QHash<qint64, QQuickPointerDevice *> PointerDeviceForDeviceIdHash;
602Q_GLOBAL_STATIC(PointerDeviceForDeviceIdHash, g_tabletDevices)
603#endif
604
605// debugging helpers
606static const char *pointStateString(const QQuickEventPoint *point)
607{
608 static const QMetaEnum stateMetaEnum = point->metaObject()->enumerator(index: point->metaObject()->indexOfEnumerator(name: "State"));
609 return stateMetaEnum.valueToKey(value: point->state());
610}
611
612static const QString pointDeviceName(const QQuickEventPoint *point)
613{
614 auto device = static_cast<const QQuickPointerEvent *>(point->parent())->device();
615 QString deviceName = (device ? device->name() : QLatin1String("null device"));
616 deviceName.resize(size: 16, fillChar: ' '); // shorten, and align in case of sequential output
617 return deviceName;
618}
619
620
621QQuickPointerDevice *QQuickPointerDevice::touchDevice(const QTouchDevice *d)
622{
623 if (g_touchDevices->contains(akey: d))
624 return g_touchDevices->value(akey: d);
625
626 QQuickPointerDevice::DeviceType type = QQuickPointerDevice::TouchScreen;
627 QString name;
628 int maximumTouchPoints = 10;
629 QQuickPointerDevice::Capabilities caps = QQuickPointerDevice::Capabilities(QTouchDevice::Position);
630 if (d) {
631 caps = static_cast<QQuickPointerDevice::Capabilities>(static_cast<int>(d->capabilities()) & 0xFF);
632 if (d->type() == QTouchDevice::TouchPad) {
633 type = QQuickPointerDevice::TouchPad;
634 caps |= QQuickPointerDevice::Scroll;
635 }
636 name = d->name();
637 maximumTouchPoints = d->maximumTouchPoints();
638 } else {
639 qWarning() << "QQuickWindowPrivate::touchDevice: creating touch device from nullptr device in QTouchEvent";
640 }
641
642 QQuickPointerDevice *dev = new QQuickPointerDevice(type, QQuickPointerDevice::Finger,
643 caps, maximumTouchPoints, 0, name, 0);
644 g_touchDevices->insert(akey: d, avalue: dev);
645 return dev;
646}
647
648const QTouchDevice *QQuickPointerDevice::qTouchDevice() const
649{
650 return g_touchDevices->key(avalue: const_cast<QQuickPointerDevice *>(this));
651}
652
653QList<QQuickPointerDevice*> QQuickPointerDevice::touchDevices()
654{
655 return g_touchDevices->values();
656}
657
658QQuickPointerDevice *QQuickPointerDevice::genericMouseDevice()
659{
660 return g_genericMouseDevice;
661}
662
663#if QT_CONFIG(tabletevent)
664QQuickPointerDevice *QQuickPointerDevice::tabletDevice(const QTabletEvent *event)
665{
666 // QTabletEvent::uniqueId() is the same for the pointy end and the eraser end of the stylus.
667 // We need to make those unique. QTabletEvent::PointerType only needs 2 bits' worth of storage.
668 // The key into g_tabletDevices just needs to be unique; we don't need to extract uniqueId
669 // back out of it, because QQuickPointerDevice stores that separately anyway.
670 // So the shift-and-add can be thought of as a sort of hash function, even though
671 // most of the time the result will be recognizable because the uniqueId MSBs are often 0.
672 qint64 key = event->uniqueId() + (qint64(event->pointerType()) << 60);
673 auto it = g_tabletDevices->find(akey: key);
674 if (it != g_tabletDevices->end())
675 return it.value();
676
677 DeviceType type = UnknownDevice;
678 int buttonCount = 0;
679 Capabilities caps = Position | Pressure | Hover;
680 // TODO Qt 6: we can't know for sure about XTilt or YTilt until we have a
681 // QTabletDevice populated with capabilities provided by QPA plugins
682
683 switch (event->deviceType()) {
684 case QTabletEvent::Stylus:
685 type = QQuickPointerDevice::Stylus;
686 buttonCount = 3;
687 break;
688 case QTabletEvent::RotationStylus:
689 type = QQuickPointerDevice::Stylus;
690 caps |= QQuickPointerDevice::Rotation;
691 buttonCount = 1;
692 break;
693 case QTabletEvent::Airbrush:
694 type = QQuickPointerDevice::Airbrush;
695 buttonCount = 2;
696 break;
697 case QTabletEvent::Puck:
698 type = QQuickPointerDevice::Puck;
699 buttonCount = 3;
700 break;
701 case QTabletEvent::FourDMouse:
702 type = QQuickPointerDevice::Mouse;
703 caps |= QQuickPointerDevice::Rotation;
704 buttonCount = 3;
705 break;
706 default:
707 type = QQuickPointerDevice::UnknownDevice;
708 break;
709 }
710
711 PointerType ptype = GenericPointer;
712 switch (event->pointerType()) {
713 case QTabletEvent::Pen:
714 ptype = Pen;
715 if (type == QQuickPointerDevice::UnknownDevice)
716 type = QQuickPointerDevice::Stylus;
717 break;
718 case QTabletEvent::Eraser:
719 ptype = Eraser;
720 break;
721 case QTabletEvent::Cursor:
722 ptype = Cursor;
723 break;
724 case QTabletEvent::UnknownPointer:
725 break;
726 }
727
728 QQuickPointerDevice *device = new QQuickPointerDevice(type, ptype, caps, 1, buttonCount,
729 QLatin1String("tablet tool ") + QString::number(event->uniqueId()), event->uniqueId());
730
731 g_tabletDevices->insert(akey: key, avalue: device);
732 return device;
733}
734#endif
735
736/*!
737 \qmltype EventPoint
738 \qmlabstract
739 \instantiates QQuickEventPoint
740 \inqmlmodule QtQuick
741 \ingroup qtquick-input-events
742 \brief Provides information about an individual point within a PointerEvent.
743
744 A PointerEvent contains an EventPoint for each point of contact: one corresponding
745 to the mouse cursor, or one for each finger touching a touchscreen.
746
747 \sa PointerEvent, PointerHandler
748*/
749
750/*!
751 \readonly
752 \qmlproperty point QtQuick::EventPoint::position
753
754 This property holds the coordinates of the position supplied by the event,
755 relative to the upper-left corner of the Item which has the PointerHandler.
756 If a contact patch is available from the pointing device, this point
757 represents its centroid.
758*/
759
760/*!
761 \readonly
762 \qmlproperty point QtQuick::EventPoint::scenePosition
763
764 This property holds the coordinates of the position supplied by the event,
765 relative to the scene. If a contact patch is available from the
766 \l {QtQuick::PointerEvent::device} {device}, this point represents its centroid.
767*/
768
769/*!
770 \readonly
771 \qmlproperty point QtQuick::EventPoint::scenePressPosition
772
773 This property holds the scene-relative position at which the press event
774 (on a touch device) or most recent change in QQuickPointerEvent::buttons()
775 (on a mouse or tablet stylus) occurred.
776*/
777
778/*!
779 \readonly
780 \qmlproperty point QtQuick::EventPoint::sceneGrabPosition
781
782 This property holds the scene-relative position at which the EventPoint was
783 located when setGrabber() was called most recently.
784*/
785
786/*!
787 \readonly
788 \qmlproperty vector2d QtQuick::EventPoint::velocity
789
790 This property holds average recent velocity: how fast and in which
791 direction the event point has been moving recently.
792*/
793
794/*!
795 \readonly
796 \qmlproperty int QtQuick::EventPoint::state
797
798 This property tells what the user is currently doing at this point.
799
800 It can be one of:
801 \value Pressed
802 The user's finger is now pressing a touchscreen, button or stylus
803 which was not pressed already
804 \value Updated
805 The touchpoint or position is being moved, with no change in pressed state
806 \value Stationary
807 The touchpoint or position is not being moved, and there is also
808 no change in pressed state
809 \value Released
810 The user's finger has now released a touch point, button or stylus
811 which was pressed
812*/
813
814/*!
815 \readonly
816 \qmlproperty int QtQuick::EventPoint::pointId
817
818 This property holds the ID of the point, if any.
819
820 Touchpoints have automatically-incrementing IDs: each time the user
821 presses a finger against the touchscreen, it will be a larger number.
822 In other cases, it will be -1.
823
824 \sa {QtQuick::EventTouchPoint::uniqueId}{uniqueId}
825*/
826
827/*!
828 \readonly
829 \qmlproperty bool QtQuick::EventPoint::accepted
830
831 Indicates whether this point has been accepted during delivery thus far.
832 This flag cannot be usefully set from QML.
833*/
834
835/*!
836 \readonly
837 \qmlproperty real QtQuick::EventPoint::timeHeld
838
839 This property holds the amount of time in seconds that the button or touchpoint has
840 been held. It can be used to detect a "long press", and can drive an
841 animation to show progress toward activation of the "long press" action.
842*/
843
844void QQuickEventPoint::reset(Qt::TouchPointState state, const QPointF &scenePos, int pointId, ulong timestamp, const QVector2D &velocity)
845{
846 m_scenePos = scenePos;
847 m_pointId = pointId;
848 m_accept = false;
849 m_state = static_cast<QQuickEventPoint::State>(state);
850 m_timestamp = timestamp;
851 if (state == Qt::TouchPointPressed) {
852 m_pressTimestamp = timestamp;
853 m_scenePressPos = scenePos;
854 }
855 m_velocity = (Q_LIKELY(velocity.isNull()) ? estimatedVelocity() : velocity);
856}
857
858void QQuickEventPoint::localizePosition(QQuickItem *target)
859{
860 if (target)
861 m_pos = target->mapFromScene(point: scenePosition());
862 else
863 m_pos = QPointF();
864}
865
866/*!
867 If this point has an exclusive grabber, returns a pointer to it; else
868 returns null, if there is no grabber. The grabber could be either
869 an Item or a PointerHandler.
870*/
871QObject *QQuickEventPoint::exclusiveGrabber() const
872{
873 return m_exclusiveGrabber.data();
874}
875
876/*!
877 Set the given Item or PointerHandler as the exclusive grabber of this point.
878 If there was already an exclusive grab, it will be canceled. If there
879 were passive grabbers, they will continue to lurk, but the exclusive grab
880 is a behavioral override of the passive grab as long as it remains.
881 If you already know whether the grabber is to be an Item or a PointerHandler,
882 you should instead call setGrabberItem() or setGrabberPointerHandler(),
883 because it is slightly more efficient.
884*/
885void QQuickEventPoint::setExclusiveGrabber(QObject *grabber)
886{
887 if (QQuickPointerHandler *phGrabber = qmlobject_cast<QQuickPointerHandler *>(object: grabber))
888 setGrabberPointerHandler(exclusiveGrabber: phGrabber, exclusive: true);
889 else
890 setGrabberItem(static_cast<QQuickItem *>(grabber));
891}
892
893/*!
894 If the exclusive grabber of this point is an Item, returns a
895 pointer to that Item; else returns null, if there is no grabber or if
896 the grabber is a PointerHandler.
897*/
898QQuickItem *QQuickEventPoint::grabberItem() const
899{
900 return (m_grabberIsHandler ? nullptr : static_cast<QQuickItem *>(m_exclusiveGrabber.data()));
901}
902
903/*!
904 Set the given Item \a grabber as the exclusive grabber of this point.
905 If there was already an exclusive grab, it will be canceled. If there
906 were passive grabbers, they will continue to lurk, but the exclusive grab
907 is a behavioral override of the passive grab as long as it remains.
908*/
909void QQuickEventPoint::setGrabberItem(QQuickItem *grabber)
910{
911 if (grabber != m_exclusiveGrabber.data()) {
912 QQuickPointerHandler *oldGrabberHandler = grabberPointerHandler();
913 if (oldGrabberHandler && !oldGrabberHandler->approveGrabTransition(point: this, proposedGrabber: grabber))
914 return;
915 if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) {
916 qCDebug(lcPointerGrab) << pointDeviceName(point: this) << "point" << Qt::hex << m_pointId << pointStateString(point: this) << "@" << m_scenePos
917 << ": grab" << m_exclusiveGrabber << "->" << grabber;
918 }
919 QQuickItem *oldGrabberItem = grabberItem();
920 m_exclusiveGrabber = QPointer<QObject>(grabber);
921 m_grabberIsHandler = false;
922 m_sceneGrabPos = m_scenePos;
923 if (oldGrabberHandler) {
924 oldGrabberHandler->onGrabChanged(grabber: oldGrabberHandler, transition: (grabber ? CancelGrabExclusive : UngrabExclusive), point: this);
925 } else if (oldGrabberItem && oldGrabberItem != grabber && grabber && grabber->window()) {
926 QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(c: grabber->window());
927 windowPriv->sendUngrabEvent(grabber: oldGrabberItem, touch: windowPriv->isDeliveringTouchAsMouse());
928 }
929 if (grabber) {
930 for (QPointer<QQuickPointerHandler> passiveGrabber : m_passiveGrabbers)
931 if (passiveGrabber)
932 passiveGrabber->onGrabChanged(grabber: passiveGrabber, transition: OverrideGrabPassive, point: this);
933 }
934 }
935}
936
937/*!
938 If the exclusive grabber of this point is a PointerHandler, returns a
939 pointer to that handler; else returns null, if there is no grabber or if
940 the grabber is an Item.
941*/
942QQuickPointerHandler *QQuickEventPoint::grabberPointerHandler() const
943{
944 return (m_grabberIsHandler ? static_cast<QQuickPointerHandler *>(m_exclusiveGrabber.data()) : nullptr);
945}
946
947/*!
948 Set the given PointerHandler \a grabber as grabber of this point. If \a
949 exclusive is true, it will override any other grabs; if false, \a grabber
950 will be added to the list of passive grabbers of this point.
951*/
952void QQuickEventPoint::setGrabberPointerHandler(QQuickPointerHandler *grabber, bool exclusive)
953{
954 if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) {
955 if (exclusive) {
956 if (m_exclusiveGrabber != grabber)
957 qCDebug(lcPointerGrab) << pointDeviceName(point: this) << "point" << Qt::hex << m_pointId << pointStateString(point: this)
958 << ": grab (exclusive)" << m_exclusiveGrabber << "->" << grabber;
959 } else {
960 qCDebug(lcPointerGrab) << pointDeviceName(point: this) << "point" << Qt::hex << m_pointId << pointStateString(point: this)
961 << ": grab (passive)" << grabber;
962 }
963 }
964 if (exclusive) {
965 if (grabber != m_exclusiveGrabber.data()) {
966 QQuickPointerHandler *oldGrabberHandler = grabberPointerHandler();
967 QQuickItem *oldGrabberItem = grabberItem();
968 m_exclusiveGrabber = QPointer<QObject>(grabber);
969 m_grabberIsHandler = true;
970 m_sceneGrabPos = m_scenePos;
971 if (grabber) {
972 grabber->onGrabChanged(grabber, transition: GrabExclusive, point: this);
973 for (QPointer<QQuickPointerHandler> passiveGrabber : m_passiveGrabbers) {
974 if (!passiveGrabber.isNull() && passiveGrabber != grabber)
975 passiveGrabber->onGrabChanged(grabber, transition: OverrideGrabPassive, point: this);
976 }
977 }
978 if (oldGrabberHandler) {
979 oldGrabberHandler->onGrabChanged(grabber: oldGrabberHandler, transition: (grabber ? CancelGrabExclusive : UngrabExclusive), point: this);
980 } else if (oldGrabberItem) {
981 if (pointerEvent()->asPointerTouchEvent())
982 oldGrabberItem->touchUngrabEvent();
983 else if (pointerEvent()->asPointerMouseEvent())
984 oldGrabberItem->mouseUngrabEvent();
985 }
986 // touchUngrabEvent() can result in the grabber being set to null (MPTA does that, for example).
987 // So set it again to ensure that final state is what we want.
988 m_exclusiveGrabber = QPointer<QObject>(grabber);
989 m_grabberIsHandler = true;
990 m_sceneGrabPos = m_scenePos;
991 }
992 } else {
993 if (!grabber) {
994 qDebug() << "can't set passive grabber to null";
995 return;
996 }
997 auto ptr = QPointer<QQuickPointerHandler>(grabber);
998 if (!m_passiveGrabbers.contains(t: ptr)) {
999 m_passiveGrabbers.append(t: ptr);
1000 grabber->onGrabChanged(grabber, transition: GrabPassive, point: this);
1001 }
1002 }
1003}
1004
1005/*!
1006 If this point has an existing exclusive grabber (Item or PointerHandler),
1007 inform the grabber that its grab is canceled, and remove it as grabber.
1008 This normally happens when the grab is stolen by another Item.
1009*/
1010void QQuickEventPoint::cancelExclusiveGrab()
1011{
1012 if (m_exclusiveGrabber.isNull())
1013 qWarning(msg: "cancelGrab: no grabber");
1014 else
1015 cancelExclusiveGrabImpl();
1016}
1017
1018void QQuickEventPoint::cancelExclusiveGrabImpl(QTouchEvent *cancelEvent)
1019{
1020 if (m_exclusiveGrabber.isNull())
1021 return;
1022 if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) {
1023 qCDebug(lcPointerGrab) << pointDeviceName(point: this) << "point" << Qt::hex << m_pointId << pointStateString(point: this)
1024 << ": grab (exclusive)" << m_exclusiveGrabber << "-> nullptr";
1025 }
1026 if (auto handler = grabberPointerHandler()) {
1027 handler->onGrabChanged(grabber: handler, transition: CancelGrabExclusive, point: this);
1028 } else if (auto item = grabberItem()) {
1029 if (cancelEvent)
1030 QCoreApplication::sendEvent(receiver: item, event: cancelEvent);
1031 else
1032 item->touchUngrabEvent();
1033 }
1034 m_exclusiveGrabber.clear();
1035}
1036
1037/*!
1038 If this point has the given \a handler as a passive grabber,
1039 inform the grabber that its grab is canceled, and remove it as grabber.
1040 This normally happens when another Item or PointerHandler does an exclusive grab.
1041*/
1042void QQuickEventPoint::cancelPassiveGrab(QQuickPointerHandler *handler)
1043{
1044 if (removePassiveGrabber(handler)) {
1045 if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) {
1046 qCDebug(lcPointerGrab) << pointDeviceName(point: this) << "point" << Qt::hex << m_pointId << pointStateString(point: this)
1047 << ": grab (passive)" << handler << "removed";
1048 }
1049 handler->onGrabChanged(grabber: handler, transition: CancelGrabPassive, point: this);
1050 }
1051}
1052
1053/*!
1054 If this point has the given \a handler as a passive grabber, remove it as grabber.
1055 Returns true if it was removed, false if it wasn't a grabber.
1056*/
1057bool QQuickEventPoint::removePassiveGrabber(QQuickPointerHandler *handler)
1058{
1059 return m_passiveGrabbers.removeOne(t: handler);
1060}
1061
1062/*!
1063 If the given \a handler is grabbing this point passively, exclusively
1064 or both, cancel the grab and remove it as grabber.
1065 This normally happens when the handler decides that the behavior of this
1066 point can no longer satisfy the handler's behavioral constraints within
1067 the remainder of the gesture which the user is performing: for example
1068 the handler tries to detect a tap but a drag is occurring instead, or
1069 it tries to detect a drag in one direction but the drag is going in
1070 another direction. In such cases the handler no longer needs or wants
1071 to be informed of any further movements of this point.
1072*/
1073void QQuickEventPoint::cancelAllGrabs(QQuickPointerHandler *handler)
1074{
1075 if (m_exclusiveGrabber == handler) {
1076 handler->onGrabChanged(grabber: handler, transition: CancelGrabExclusive, point: this);
1077 m_exclusiveGrabber.clear();
1078 }
1079 cancelPassiveGrab(handler);
1080}
1081
1082/*!
1083 Sets this point as \a accepted (true) or rejected (false).
1084
1085 During delivery of the current event to the Items in the scene, each Item
1086 or Pointer Handler should accept the points for which it is taking
1087 responsibility. As soon as all points within the event are accepted, event
1088 propagation stops. However accepting the point does not imply any kind of
1089 grab, passive or exclusive.
1090
1091 \sa setExclusiveGrabber, QQuickPointerHandler::setPassiveGrab, QQuickPointerHandler::setExclusiveGrab
1092*/
1093void QQuickEventPoint::setAccepted(bool accepted)
1094{
1095 if (m_accept != accepted) {
1096 qCDebug(lcPointerEvents) << this << m_accept << "->" << accepted;
1097 m_accept = accepted;
1098 }
1099}
1100
1101
1102/*!
1103 \qmltype EventTouchPoint
1104 \qmlabstract
1105 \instantiates QQuickEventTouchPoint
1106 \inqmlmodule QtQuick
1107 \ingroup qtquick-input-events
1108 \brief Provides information about an individual touch point within a PointerEvent.
1109
1110 \sa PointerEvent, PointerHandler
1111*/
1112
1113/*!
1114 \readonly
1115 \qmlproperty QPointerUniqueId QtQuick::EventTouchPoint::uniqueId
1116
1117 This property holds the unique ID of the fiducial or stylus in use, if any.
1118
1119 On touchscreens that can track physical objects (such as knobs or game
1120 pieces) in addition to fingers, each object usually has a unique ID.
1121 Likewise, each stylus that can be used with a graphics tablet usually has a
1122 unique serial number. Qt so far only supports numeric IDs. You can get the
1123 actual number as uniqueId.numeric, but that is a device-specific detail.
1124 In the future, there may be support for non-numeric IDs, so you should
1125 not assume that the number is meaningful.
1126
1127 If you need to identify specific objects, your application should provide
1128 UI for registering objects and mapping them to functionality: allow the
1129 user to select a meaning, virtual tool, or action, prompt the user to bring
1130 the object into proximity, and store a mapping from uniqueId to its
1131 purpose, for example in \l Settings.
1132*/
1133
1134/*!
1135 \readonly
1136 \qmlproperty qreal QtQuick::EventTouchPoint::rotation
1137
1138 This property holds the rotation angle of the stylus on a graphics tablet
1139 or the contact patch of a touchpoint on a touchscreen.
1140
1141 It is valid only with certain tablet stylus devices and touchscreens that
1142 can measure the rotation angle. Otherwise, it will be zero.
1143*/
1144
1145/*!
1146 \readonly
1147 \qmlproperty qreal QtQuick::EventTouchPoint::pressure
1148
1149 This property tells how hard the user is pressing the stylus on a graphics
1150 tablet or the finger against a touchscreen, in the range from \c 0 (no
1151 measurable pressure) to \c 1.0 (maximum pressure which the device can
1152 measure).
1153
1154 It is valid only with certain tablets and touchscreens that can measure
1155 pressure. Otherwise, it will be \c 1.0 when pressed.
1156*/
1157
1158/*!
1159 \readonly
1160 \qmlproperty size QtQuick::EventTouchPoint::ellipseDiameters
1161
1162 This property holds the diameters of the contact patch, if the event
1163 comes from a touchpoint and the \l {QtQuick::PointerEvent::device} {device}
1164 provides this information.
1165
1166 A touchpoint is modeled as an elliptical area where the finger is
1167 pressed against the touchscreen. (In fact, it could also be
1168 modeled as a bitmap; but in that case we expect an elliptical
1169 bounding estimate to be fitted to the contact patch before the
1170 event is sent.) The harder the user presses, the larger the
1171 contact patch; so, these diameters provide an alternate way of
1172 detecting pressure, in case the device does not include a separate
1173 pressure sensor. The ellipse is centered on
1174 \l {QtQuick::EventPoint::scenePosition} {scenePosition}
1175 (\l {QtQuick::EventPoint::position} {position} in the PointerHandler's
1176 Item's local coordinates). The \l rotation property provides the
1177 rotation of the ellipse, if known. It is expected that if the
1178 \l rotation is zero, the verticalDiameter of the ellipse is the
1179 larger one (the major axis), because of the usual hand position,
1180 reaching upward or outward across the surface.
1181
1182 If the contact patch is unknown, or the \l {QtQuick::PointerEvent::device} {device}
1183 is not a touchscreen, these values will be zero.
1184*/
1185
1186QQuickEventTouchPoint::QQuickEventTouchPoint(QQuickPointerTouchEvent *parent)
1187 : QQuickEventPoint(parent), m_rotation(0), m_pressure(0)
1188{}
1189
1190void QQuickEventTouchPoint::reset(const QTouchEvent::TouchPoint &tp, ulong timestamp)
1191{
1192 QQuickEventPoint::reset(state: tp.state(), scenePos: tp.scenePos(), pointId: tp.id(), timestamp, velocity: tp.velocity());
1193 m_exclusiveGrabber.clear();
1194 m_passiveGrabbers.clear();
1195 m_rotation = tp.rotation();
1196 m_pressure = tp.pressure();
1197 m_ellipseDiameters = tp.ellipseDiameters();
1198 m_uniqueId = tp.uniqueId();
1199}
1200
1201struct PointVelocityData {
1202 QVector2D velocity;
1203 QPointF pos;
1204 ulong timestamp = 0;
1205};
1206
1207typedef QMap<quint64, PointVelocityData> PointDataForPointIdMap;
1208Q_GLOBAL_STATIC(PointDataForPointIdMap, g_previousPointData)
1209static const int PointVelocityAgeLimit = 500; // milliseconds
1210
1211/*!
1212 \internal
1213 Estimates the velocity based on a weighted average of all previous velocities.
1214 The older the velocity is, the less significant it becomes for the estimate.
1215*/
1216QVector2D QQuickEventPoint::estimatedVelocity() const
1217{
1218 auto prevPointIt = g_previousPointData->find(akey: m_pointId);
1219 auto end = g_previousPointData->end();
1220 if (prevPointIt == end) {
1221 // cleanup events older than PointVelocityAgeLimit
1222 for (auto it = g_previousPointData->begin(); it != end; ) {
1223 if (m_timestamp - it->timestamp > PointVelocityAgeLimit)
1224 it = g_previousPointData->erase(it);
1225 else
1226 ++it;
1227 }
1228 prevPointIt = g_previousPointData->insert(akey: m_pointId, avalue: PointVelocityData());
1229 }
1230
1231 auto &prevPoint = prevPointIt.value();
1232 const ulong timeElapsed = m_timestamp - prevPoint.timestamp;
1233 if (timeElapsed == 0) // in case we call estimatedVelocity() twice on the same QQuickEventPoint
1234 return m_velocity;
1235
1236 QVector2D newVelocity;
1237 if (prevPoint.timestamp != 0)
1238 newVelocity = QVector2D(m_scenePos - prevPoint.pos) / timeElapsed;
1239
1240 // VERY simple kalman filter: does a weighted average
1241 // where the older velocities get less and less significant
1242 static const float KalmanGain = 0.7f;
1243 QVector2D filteredVelocity = newVelocity * KalmanGain + m_velocity * (1.0f - KalmanGain);
1244
1245 prevPoint.velocity = filteredVelocity;
1246 prevPoint.pos = m_scenePos;
1247 prevPoint.timestamp = m_timestamp;
1248 return filteredVelocity;
1249}
1250
1251/*!
1252 \qmltype PointerEvent
1253 \instantiates QQuickPointerEvent
1254 \inqmlmodule QtQuick
1255 \ingroup qtquick-input-events
1256
1257 \brief Provides information about an event from a pointing device.
1258
1259 A PointerEvent is an event describing contact or movement across a surface,
1260 provided by a mouse, a touchpoint (single finger on a touchscreen), or a
1261 stylus on a graphics tablet. The \l {QtQuick::PointerEvent::device} {device}
1262 property provides more information about where the event came from.
1263
1264 \sa PointerHandler
1265
1266 \image touchpoint-metrics.png
1267*/
1268
1269/*!
1270 \internal
1271 \class QQuickPointerEvent
1272
1273 QQuickPointerEvent is used as a long-lived object to store data related to
1274 an event from a pointing device, such as a mouse, touch or tablet event,
1275 during event delivery. It also provides properties which may be used later
1276 to expose the event to QML, the same as is done with QQuickMouseEvent,
1277 QQuickTouchPoint, QQuickKeyEvent, etc. Since only one event can be
1278 delivered at a time, this class is effectively a singleton. We don't worry
1279 about the QObject overhead because the instances are long-lived: we don't
1280 dynamically create and destroy objects of this type for each event.
1281*/
1282
1283/*!
1284 \readonly
1285 \qmlproperty enumeration QtQuick::PointerEvent::button
1286
1287 This property holds the \l {Qt::MouseButton}{button} that caused the event,
1288 if any. If the \l {QtQuick::PointerEvent::device} {device} does not have
1289 buttons, or the event is a hover event, it will be \c Qt.NoButton.
1290*/
1291
1292/*!
1293 \readonly
1294 \qmlproperty int QtQuick::PointerEvent::buttons
1295
1296 This property holds the combination of mouse or stylus
1297 \l {Qt::MouseButton}{buttons} pressed when the event was generated. For move
1298 events, this is all buttons that are pressed down. For press events, this
1299 includes the button that caused the event, as well as any others that were
1300 already held. For release events, this excludes the button that caused the
1301 event.
1302*/
1303
1304/*!
1305 \readonly
1306 \qmlproperty int QtQuick::PointerEvent::modifiers
1307
1308 This property holds the \l {Qt::KeyboardModifier}{keyboard modifier} flags
1309 that existed immediately before the event occurred.
1310
1311 It contains a bitwise combination of the following flags:
1312 \value Qt.NoModifier
1313 No modifier key is pressed.
1314 \value Qt.ShiftModifier
1315 A Shift key on the keyboard is pressed.
1316 \value Qt.ControlModifier
1317 A Ctrl key on the keyboard is pressed.
1318 \value Qt.AltModifier
1319 An Alt key on the keyboard is pressed.
1320 \value Qt.MetaModifier
1321 A Meta key on the keyboard is pressed.
1322 \value Qt.KeypadModifier
1323 A keypad button is pressed.
1324
1325 For example, to react to a Shift key + Left mouse button click:
1326 \qml
1327 Item {
1328 TapHandler {
1329 onTapped: {
1330 if ((event.button == Qt.LeftButton) && (event.modifiers & Qt.ShiftModifier))
1331 doSomething();
1332 }
1333 }
1334 }
1335 \endqml
1336*/
1337
1338/*!
1339 \readonly
1340 \qmlproperty PointerDevice QtQuick::PointerEvent::device
1341
1342 This property holds the device that generated the event.
1343*/
1344
1345QQuickPointerEvent::~QQuickPointerEvent()
1346{}
1347
1348QQuickPointerMouseEvent::QQuickPointerMouseEvent(QObject *parent, QQuickPointerDevice *device)
1349 : QQuickSinglePointEvent(parent, device)
1350{
1351 m_point = new QQuickEventPoint(this);
1352}
1353
1354QQuickPointerEvent *QQuickPointerMouseEvent::reset(QEvent *event)
1355{
1356 auto ev = static_cast<QMouseEvent*>(event);
1357 m_event = ev;
1358 if (!event)
1359 return this;
1360
1361 m_device = QQuickPointerDevice::genericMouseDevice();
1362 m_device->eventDeliveryTargets().clear();
1363 m_button = ev->button();
1364 m_pressedButtons = ev->buttons();
1365 Qt::TouchPointState state = Qt::TouchPointStationary;
1366 switch (ev->type()) {
1367 case QEvent::MouseButtonPress:
1368 m_point->clearPassiveGrabbers();
1369 Q_FALLTHROUGH();
1370 case QEvent::MouseButtonDblClick:
1371 state = Qt::TouchPointPressed;
1372 break;
1373 case QEvent::MouseButtonRelease:
1374 state = Qt::TouchPointReleased;
1375 break;
1376 case QEvent::MouseMove:
1377 state = Qt::TouchPointMoved;
1378 break;
1379 default:
1380 break;
1381 }
1382 m_point->reset(state, scenePos: ev->windowPos(), pointId: quint64(1) << 24, timestamp: ev->timestamp()); // mouse has device ID 1
1383 return this;
1384}
1385
1386void QQuickSinglePointEvent::localize(QQuickItem *target)
1387{
1388 m_point->localizePosition(target);
1389}
1390
1391QQuickPointerEvent *QQuickPointerTouchEvent::reset(QEvent *event)
1392{
1393 auto ev = static_cast<QTouchEvent*>(event);
1394 m_event = ev;
1395 if (!event)
1396 return this;
1397
1398 m_device = QQuickPointerDevice::touchDevice(d: ev->device());
1399 m_device->eventDeliveryTargets().clear();
1400 m_button = Qt::NoButton;
1401 m_pressedButtons = Qt::NoButton;
1402
1403 const QList<QTouchEvent::TouchPoint> &tps = ev->touchPoints();
1404 int newPointCount = tps.count();
1405 m_touchPoints.reserve(asize: newPointCount);
1406
1407 for (int i = m_touchPoints.size(); i < newPointCount; ++i)
1408 m_touchPoints.insert(i, t: new QQuickEventTouchPoint(this));
1409
1410 // Make sure the grabbers and on-pressed values are right from one event to the next
1411 struct ToPreserve {
1412 int pointId; // just for double-checking
1413 ulong pressTimestamp;
1414 QPointF scenePressPos;
1415 QPointF sceneGrabPos;
1416 QObject * grabber;
1417 QVector <QPointer <QQuickPointerHandler> > passiveGrabbers;
1418
1419 ToPreserve() : pointId(0), pressTimestamp(0), grabber(nullptr) {}
1420 };
1421 QVector<ToPreserve> preserves(newPointCount); // jar of pickled touchpoints, in order of points in the _new_ event
1422
1423 // Copy stuff we need to preserve, because the order of points might have changed in the event.
1424 // The ID is all that we can rely on (release might remove the first point etc).
1425 for (int i = 0; i < newPointCount; ++i) {
1426 int pid = tps.at(i).id();
1427 if (auto point = pointById(pointId: pid)) {
1428 preserves[i].pointId = pid;
1429 preserves[i].pressTimestamp = point->m_pressTimestamp;
1430 preserves[i].scenePressPos = point->scenePressPosition();
1431 preserves[i].sceneGrabPos = point->sceneGrabPosition();
1432 preserves[i].grabber = point->exclusiveGrabber();
1433 preserves[i].passiveGrabbers = point->passiveGrabbers();
1434 }
1435 }
1436
1437 for (int i = 0; i < newPointCount; ++i) {
1438 auto point = m_touchPoints.at(i);
1439 point->reset(tp: tps.at(i), timestamp: ev->timestamp());
1440 const auto &preserved = preserves.at(i);
1441 if (point->state() == QQuickEventPoint::Pressed) {
1442 if (preserved.grabber)
1443 qWarning() << "TouchPointPressed without previous release event" << point;
1444 point->setGrabberItem(nullptr);
1445 point->clearPassiveGrabbers();
1446 } else {
1447 // Restore the grabbers without notifying (don't call onGrabChanged)
1448 Q_ASSERT(preserved.pointId == 0 || preserved.pointId == point->pointId());
1449 point->m_pressTimestamp = preserved.pressTimestamp;
1450 point->m_scenePressPos = preserved.scenePressPos;
1451 point->m_sceneGrabPos = preserved.sceneGrabPos;
1452 point->m_exclusiveGrabber = preserved.grabber;
1453 point->m_grabberIsHandler = (qmlobject_cast<QQuickPointerHandler *>(object: point->m_exclusiveGrabber) != nullptr);
1454 point->m_passiveGrabbers = preserved.passiveGrabbers;
1455 }
1456 }
1457 m_pointCount = newPointCount;
1458 return this;
1459}
1460
1461void QQuickPointerTouchEvent::localize(QQuickItem *target)
1462{
1463 for (auto point : qAsConst(t&: m_touchPoints))
1464 point->localizePosition(target);
1465}
1466
1467#if QT_CONFIG(gestures)
1468QQuickPointerNativeGestureEvent::QQuickPointerNativeGestureEvent(QObject *parent, QQuickPointerDevice *device)
1469 : QQuickSinglePointEvent(parent, device)
1470{
1471 m_point = new QQuickEventPoint(this);
1472}
1473
1474QQuickPointerEvent *QQuickPointerNativeGestureEvent::reset(QEvent *event)
1475{
1476 auto ev = static_cast<QNativeGestureEvent*>(event);
1477 m_event = ev;
1478 if (!event)
1479 return this;
1480
1481 m_device = QQuickPointerDevice::touchDevice(d: ev->device());
1482 m_device->eventDeliveryTargets().clear();
1483 Qt::TouchPointState state = Qt::TouchPointMoved;
1484 switch (type()) {
1485 case Qt::BeginNativeGesture:
1486 state = Qt::TouchPointPressed;
1487 break;
1488 case Qt::EndNativeGesture:
1489 state = Qt::TouchPointReleased;
1490 break;
1491 default:
1492 break;
1493 }
1494 quint64 deviceId = QTouchDevicePrivate::get(q: const_cast<QTouchDevice *>(ev->device()))->id; // a bit roundabout since QTouchDevice::mTouchDeviceId is protected
1495 m_point->reset(state, scenePos: ev->windowPos(), pointId: deviceId << 24, timestamp: ev->timestamp());
1496 return this;
1497}
1498#endif // QT_CONFIG(gestures)
1499
1500QQuickEventPoint *QQuickSinglePointEvent::point(int i) const
1501{
1502 if (i == 0)
1503 return m_point;
1504 return nullptr;
1505}
1506
1507
1508/*!
1509 \qmltype PointerScrollEvent
1510 \instantiates QQuickPointerScrollEvent
1511 \inqmlmodule QtQuick
1512 \ingroup qtquick-input-events
1513 \brief Provides information about a scrolling event, such as from a mouse wheel.
1514
1515 \sa WheelHandler
1516*/
1517
1518/*!
1519 \internal
1520 \class QQuickPointerScrollEvent
1521*/
1522
1523/*!
1524 \readonly
1525 \qmlproperty PointerDevice QtQuick::PointerScrollEvent::device
1526
1527 This property holds the device that generated the event.
1528*/
1529
1530/*!
1531 \qmlproperty int QtQuick::PointerScrollEvent::buttons
1532
1533 This property holds the mouse buttons pressed when the wheel event was generated.
1534
1535 It contains a bitwise combination of:
1536 \list
1537 \li \l {Qt::LeftButton} {Qt.LeftButton}
1538 \li \l {Qt::RightButton} {Qt.RightButton}
1539 \li \l {Qt::MiddleButton} {Qt.MiddleButton}
1540 \endlist
1541*/
1542
1543/*!
1544 \readonly
1545 \qmlproperty int QtQuick::PointerScrollEvent::modifiers
1546
1547 This property holds the \l {Qt::KeyboardModifier}{keyboard modifier} keys
1548 that were pressed immediately before the event occurred.
1549
1550 It contains a bitwise combination of the following flags:
1551 \value Qt.NoModifier
1552 No modifier key is pressed.
1553 \value Qt.ShiftModifier
1554 A Shift key on the keyboard is pressed.
1555 \value Qt.ControlModifier
1556 A Ctrl key on the keyboard is pressed.
1557 \value Qt.AltModifier
1558 An Alt key on the keyboard is pressed.
1559 \value Qt.MetaModifier
1560 A Meta key on the keyboard is pressed.
1561 \value Qt.KeypadModifier
1562 A keypad button is pressed.
1563
1564 For example, to react to a Shift key + Left mouse button click:
1565 \qml
1566 Item {
1567 TapHandler {
1568 onTapped: {
1569 if ((event.button == Qt.LeftButton) && (event.modifiers & Qt.ShiftModifier))
1570 doSomething();
1571 }
1572 }
1573 }
1574 \endqml
1575*/
1576
1577/*!
1578 \qmlproperty point QtQuick::PointerScrollEvent::angleDelta
1579
1580 This property holds the distance that the wheel is rotated in wheel degrees.
1581 The x and y cordinate of this property holds the delta in horizontal and
1582 vertical orientation.
1583
1584 A positive value indicates that the wheel was rotated up/right;
1585 a negative value indicates that the wheel was rotated down/left.
1586
1587 Most mouse types work in steps of 15 degrees, in which case the delta value is a
1588 multiple of 120; i.e., 120 units * 1/8 = 15 degrees.
1589*/
1590
1591/*!
1592 \qmlproperty point QtQuick::PointerScrollEvent::pixelDelta
1593
1594 This property holds the delta in screen pixels and is available in platforms that
1595 have high-resolution trackpads, such as \macos.
1596 The x and y coordinates of this property hold the delta in horizontal and
1597 vertical orientation. The value should be used directly to scroll content on screen.
1598
1599 For platforms without high-resolution touchpad support, pixelDelta will
1600 always be (0,0), and angleDelta should be used instead.
1601*/
1602
1603/*!
1604 \qmlproperty bool QtQuick::PointerScrollEvent::hasAngleDelta
1605
1606 Returns whether the \l angleDelta property has a non-null value.
1607*/
1608
1609/*!
1610 \qmlproperty bool QtQuick::PointerScrollEvent::hasPixelDelta
1611
1612 Returns whether the \l pixelDelta property has a non-null value.
1613*/
1614
1615/*!
1616 \qmlproperty bool QtQuick::PointerScrollEvent::inverted
1617
1618 Returns whether the delta values delivered with the event are inverted.
1619
1620 Normally, a vertical wheel will produce a PointerScrollEvent with positive delta
1621 values if the top of the wheel is rotating away from the hand operating it.
1622 Similarly, a horizontal wheel movement will produce a PointerScrollEvent with
1623 positive delta values if the top of the wheel is moved to the left.
1624
1625 However, on some platforms this is configurable, so that the same
1626 operations described above will produce negative delta values (but with the
1627 same magnitude). In a QML component (such as a tumbler or a slider) where
1628 it is appropriate to synchronize the movement or rotation of an item with
1629 the direction of the wheel, regardless of the system settings, the wheel
1630 event handler can use the inverted property to decide whether to negate the
1631 \l angleDelta or \l pixelDelta values.
1632
1633 \note Many platforms provide no such information. On such platforms,
1634 \c inverted always returns false.
1635*/
1636QQuickPointerScrollEvent::QQuickPointerScrollEvent(QObject *parent, QQuickPointerDevice *device)
1637 : QQuickSinglePointEvent(parent, device)
1638{
1639 m_point = new QQuickEventPoint(this);
1640}
1641
1642QQuickPointerEvent *QQuickPointerScrollEvent::reset(QEvent *event)
1643{
1644 m_event = static_cast<QInputEvent*>(event);
1645 if (!event)
1646 return this;
1647#if QT_CONFIG(wheelevent)
1648 if (event->type() == QEvent::Wheel) {
1649 auto ev = static_cast<QWheelEvent*>(event);
1650 m_device = QQuickPointerDevice::genericMouseDevice();
1651 m_device->eventDeliveryTargets().clear();
1652 // m_button = Qt::NoButton;
1653 m_pressedButtons = ev->buttons();
1654 m_angleDelta = QVector2D(ev->angleDelta());
1655 m_pixelDelta = QVector2D(ev->pixelDelta());
1656 m_phase = ev->phase();
1657 m_synthSource = ev->source();
1658 m_inverted = ev->inverted();
1659
1660 m_point->reset(state: Qt::TouchPointMoved, scenePos: ev->position(), pointId: quint64(1) << 24, timestamp: ev->timestamp()); // mouse has device ID 1
1661 }
1662#endif
1663 // TODO else if (event->type() == QEvent::Scroll) ...
1664 return this;
1665}
1666
1667void QQuickPointerScrollEvent::localize(QQuickItem *target)
1668{
1669 m_point->localizePosition(target);
1670}
1671
1672QQuickEventPoint *QQuickPointerTouchEvent::point(int i) const
1673{
1674 if (i >= 0 && i < m_pointCount)
1675 return m_touchPoints.at(i);
1676 return nullptr;
1677}
1678
1679QQuickEventPoint::QQuickEventPoint(QQuickPointerEvent *parent)
1680 : QObject(parent), m_pointId(0), m_exclusiveGrabber(nullptr), m_timestamp(0), m_pressTimestamp(0),
1681 m_state(QQuickEventPoint::Released), m_accept(false), m_grabberIsHandler(false)
1682{
1683 Q_UNUSED(m_reserved);
1684}
1685
1686QQuickPointerEvent *QQuickEventPoint::pointerEvent() const
1687{
1688 return static_cast<QQuickPointerEvent *>(parent());
1689}
1690
1691bool QQuickSinglePointEvent::allPointsAccepted() const
1692{
1693 return m_point->isAccepted();
1694}
1695
1696bool QQuickSinglePointEvent::allUpdatedPointsAccepted() const
1697{
1698 return m_point->state() == QQuickEventPoint::Pressed || m_point->isAccepted();
1699}
1700
1701bool QQuickSinglePointEvent::allPointsGrabbed() const
1702{
1703 return m_point->exclusiveGrabber() != nullptr;
1704}
1705
1706QMouseEvent *QQuickPointerMouseEvent::asMouseEvent(const QPointF &localPos) const
1707{
1708 if (!m_event)
1709 return nullptr;
1710 auto event = static_cast<QMouseEvent *>(m_event);
1711 event->setLocalPos(localPos);
1712 return event;
1713}
1714
1715/*!
1716 Returns the exclusive grabber of this event, if any, in a vector.
1717*/
1718QVector<QObject *> QQuickSinglePointEvent::exclusiveGrabbers() const
1719{
1720 QVector<QObject *> result;
1721 if (QObject *grabber = m_point->exclusiveGrabber())
1722 result << grabber;
1723 return result;
1724}
1725
1726/*!
1727 Remove all passive and exclusive grabbers of this event, without notifying.
1728*/
1729void QQuickSinglePointEvent::clearGrabbers() const
1730{
1731 m_point->setGrabberItem(nullptr);
1732 m_point->clearPassiveGrabbers();
1733}
1734
1735/*!
1736 Returns whether the given \a handler is the exclusive grabber of this event.
1737*/
1738bool QQuickSinglePointEvent::hasExclusiveGrabber(const QQuickPointerHandler *handler) const
1739{
1740 return handler && (m_point->exclusiveGrabber() == handler);
1741}
1742
1743bool QQuickPointerMouseEvent::isPressEvent() const
1744{
1745 if (!m_event)
1746 return false;
1747 auto me = static_cast<QMouseEvent*>(m_event);
1748 return ((me->type() == QEvent::MouseButtonPress || me->type() == QEvent::MouseButtonDblClick) &&
1749 (me->buttons() & me->button()) == me->buttons());
1750}
1751
1752bool QQuickPointerMouseEvent::isDoubleClickEvent() const
1753{
1754 if (!m_event)
1755 return false;
1756 auto me = static_cast<QMouseEvent*>(m_event);
1757 return (me->type() == QEvent::MouseButtonDblClick);
1758}
1759
1760bool QQuickPointerMouseEvent::isUpdateEvent() const
1761{
1762 if (!m_event)
1763 return false;
1764 auto me = static_cast<QMouseEvent*>(m_event);
1765 return me->type() == QEvent::MouseMove;
1766}
1767
1768bool QQuickPointerMouseEvent::isReleaseEvent() const
1769{
1770 if (!m_event)
1771 return false;
1772 auto me = static_cast<QMouseEvent*>(m_event);
1773 return me && me->type() == QEvent::MouseButtonRelease;
1774}
1775
1776bool QQuickPointerTouchEvent::allPointsAccepted() const
1777{
1778 for (int i = 0; i < m_pointCount; ++i) {
1779 if (!m_touchPoints.at(i)->isAccepted())
1780 return false;
1781 }
1782 return true;
1783}
1784
1785bool QQuickPointerTouchEvent::allUpdatedPointsAccepted() const
1786{
1787 for (int i = 0; i < m_pointCount; ++i) {
1788 auto point = m_touchPoints.at(i);
1789 if (point->state() != QQuickEventPoint::Pressed && !point->isAccepted())
1790 return false;
1791 }
1792 return true;
1793}
1794
1795bool QQuickPointerTouchEvent::allPointsGrabbed() const
1796{
1797 for (int i = 0; i < m_pointCount; ++i) {
1798 if (!m_touchPoints.at(i)->exclusiveGrabber())
1799 return false;
1800 }
1801 return true;
1802}
1803
1804/*!
1805 Returns the exclusive grabbers of all points in this event, if any, in a vector.
1806*/
1807QVector<QObject *> QQuickPointerTouchEvent::exclusiveGrabbers() const
1808{
1809 QVector<QObject *> result;
1810 for (int i = 0; i < m_pointCount; ++i) {
1811 if (QObject *grabber = m_touchPoints.at(i)->exclusiveGrabber()) {
1812 if (!result.contains(t: grabber))
1813 result << grabber;
1814 }
1815 }
1816 return result;
1817}
1818
1819/*!
1820 Remove all passive and exclusive grabbers of all touchpoints in this event,
1821 without notifying.
1822*/
1823void QQuickPointerTouchEvent::clearGrabbers() const
1824{
1825 for (auto point: m_touchPoints) {
1826 point->setGrabberItem(nullptr);
1827 point->clearPassiveGrabbers();
1828 }
1829}
1830
1831Qt::TouchPointStates QQuickPointerTouchEvent::touchPointStates() const
1832{
1833 return m_event
1834 ? static_cast<QTouchEvent*>(m_event)->touchPointStates()
1835 : Qt::TouchPointStates();
1836}
1837
1838/*!
1839 Returns whether the given \a handler is the exclusive grabber of any
1840 touchpoint within this event.
1841*/
1842bool QQuickPointerTouchEvent::hasExclusiveGrabber(const QQuickPointerHandler *handler) const
1843{
1844 for (auto point: m_touchPoints)
1845 if (point->exclusiveGrabber() == handler)
1846 return true;
1847 return false;
1848}
1849
1850bool QQuickPointerTouchEvent::isPressEvent() const
1851{
1852 return touchPointStates() & Qt::TouchPointPressed;
1853}
1854
1855bool QQuickPointerTouchEvent::isUpdateEvent() const
1856{
1857 return touchPointStates() & (Qt::TouchPointMoved | Qt::TouchPointStationary);
1858}
1859
1860bool QQuickPointerTouchEvent::isReleaseEvent() const
1861{
1862 return touchPointStates() & Qt::TouchPointReleased;
1863}
1864
1865QVector<QPointF> QQuickPointerEvent::unacceptedPressedPointScenePositions() const
1866{
1867 QVector<QPointF> points;
1868 for (int i = 0; i < pointCount(); ++i) {
1869 if (!point(i)->isAccepted() && point(i)->state() == QQuickEventPoint::Pressed)
1870 points << point(i)->scenePosition();
1871 }
1872 return points;
1873}
1874
1875/*!
1876 \internal
1877 Populate the reusable synth-mouse event from one touchpoint.
1878 It's required that isTouchEvent() be true when this is called.
1879 If the touchpoint cannot be found, this returns nullptr.
1880 Ownership of the event is NOT transferred to the caller.
1881*/
1882QMouseEvent *QQuickPointerTouchEvent::syntheticMouseEvent(int pointID, QQuickItem *relativeTo) const
1883{
1884 const QTouchEvent::TouchPoint *p = touchPointById(pointId: pointID);
1885 if (!p)
1886 return nullptr;
1887 QEvent::Type type;
1888 Qt::MouseButton buttons = Qt::LeftButton;
1889 switch (p->state()) {
1890 case Qt::TouchPointPressed:
1891 type = QEvent::MouseButtonPress;
1892 break;
1893 case Qt::TouchPointMoved:
1894 case Qt::TouchPointStationary:
1895 type = QEvent::MouseMove;
1896 break;
1897 case Qt::TouchPointReleased:
1898 type = QEvent::MouseButtonRelease;
1899 buttons = Qt::NoButton;
1900 break;
1901 default:
1902 Q_ASSERT(false);
1903 return nullptr;
1904 }
1905 m_synthMouseEvent = QMouseEvent(type, relativeTo->mapFromScene(point: p->scenePos()),
1906 p->scenePos(), p->screenPos(), Qt::LeftButton, buttons, m_event->modifiers());
1907 m_synthMouseEvent.setAccepted(true);
1908 m_synthMouseEvent.setTimestamp(m_event->timestamp());
1909 // In the future we will try to always have valid velocity in every QQuickEventPoint.
1910 // QQuickFlickablePrivate::handleMouseMoveEvent() checks for QTouchDevice::Velocity
1911 // and if it is set, then it does not need to do its own velocity calculations.
1912 // That's probably the only usecase for this, so far. Some day Flickable should handle
1913 // pointer events, and then passing touchpoint velocity via QMouseEvent will be obsolete.
1914 // Conveniently (by design), QTouchDevice::Velocity == QQuickPointerDevice.Velocity
1915 // so that we don't need to convert m_device->capabilities().
1916 if (m_device)
1917 QGuiApplicationPrivate::setMouseEventCapsAndVelocity(event: &m_synthMouseEvent, caps: m_device->capabilities(), velocity: p->velocity());
1918 QGuiApplicationPrivate::setMouseEventSource(event: &m_synthMouseEvent, source: Qt::MouseEventSynthesizedByQt);
1919 return &m_synthMouseEvent;
1920}
1921
1922#if QT_CONFIG(tabletevent)
1923QQuickPointerTabletEvent::QQuickPointerTabletEvent(QObject *parent, QQuickPointerDevice *device)
1924 : QQuickSinglePointEvent(parent, device)
1925{
1926 m_point = new QQuickEventTabletPoint(this);
1927}
1928
1929QQuickPointerEvent *QQuickPointerTabletEvent::reset(QEvent *event)
1930{
1931 auto ev = static_cast<QTabletEvent*>(event);
1932 m_event = ev;
1933 if (!event)
1934 return this;
1935
1936 Q_ASSERT(m_device == QQuickPointerDevice::tabletDevice(ev));
1937 m_device->eventDeliveryTargets().clear();
1938 m_button = ev->button();
1939 m_pressedButtons = ev->buttons();
1940 static_cast<QQuickEventTabletPoint *>(m_point)->reset(e: ev);
1941 return this;
1942}
1943
1944QQuickEventTabletPoint::QQuickEventTabletPoint(QQuickPointerTabletEvent *parent)
1945 : QQuickEventPoint(parent)
1946{
1947}
1948
1949void QQuickEventTabletPoint::reset(const QTabletEvent *ev)
1950{
1951 Qt::TouchPointState state = Qt::TouchPointStationary;
1952 switch (ev->type()) {
1953 case QEvent::TabletPress:
1954 state = Qt::TouchPointPressed;
1955 clearPassiveGrabbers();
1956 break;
1957 case QEvent::TabletRelease:
1958 state = Qt::TouchPointReleased;
1959 break;
1960 case QEvent::TabletMove:
1961 state = Qt::TouchPointMoved;
1962 break;
1963 default:
1964 break;
1965 }
1966 QQuickEventPoint::reset(state, scenePos: ev->posF(), pointId: 1, timestamp: ev->timestamp());
1967 m_rotation = ev->rotation();
1968 m_pressure = ev->pressure();
1969 m_tangentialPressure = ev->tangentialPressure();
1970 m_tilt = QVector2D(ev->xTilt(), ev->yTilt());
1971}
1972
1973bool QQuickPointerTabletEvent::isPressEvent() const
1974{
1975 auto me = static_cast<QTabletEvent *>(m_event);
1976 return me->type() == QEvent::TabletPress;
1977}
1978
1979bool QQuickPointerTabletEvent::isUpdateEvent() const
1980{
1981 auto me = static_cast<QTabletEvent *>(m_event);
1982 return me->type() == QEvent::TabletMove;
1983}
1984
1985bool QQuickPointerTabletEvent::isReleaseEvent() const
1986{
1987 auto me = static_cast<QTabletEvent *>(m_event);
1988 return me->type() == QEvent::TabletRelease;
1989}
1990
1991QTabletEvent *QQuickPointerTabletEvent::asTabletEvent() const
1992{
1993 return static_cast<QTabletEvent *>(m_event);
1994}
1995#endif // QT_CONFIG(tabletevent)
1996
1997#if QT_CONFIG(gestures)
1998bool QQuickPointerNativeGestureEvent::isPressEvent() const
1999{
2000 return type() == Qt::BeginNativeGesture;
2001}
2002
2003bool QQuickPointerNativeGestureEvent::isUpdateEvent() const
2004{
2005 switch (type()) {
2006 case Qt::BeginNativeGesture:
2007 case Qt::EndNativeGesture:
2008 return false;
2009 default:
2010 return true;
2011 }
2012}
2013
2014bool QQuickPointerNativeGestureEvent::isReleaseEvent() const
2015{
2016 return type() == Qt::EndNativeGesture;
2017}
2018
2019Qt::NativeGestureType QQuickPointerNativeGestureEvent::type() const
2020{
2021 return static_cast<QNativeGestureEvent *>(m_event)->gestureType();
2022}
2023
2024qreal QQuickPointerNativeGestureEvent::value() const
2025{
2026 return static_cast<QNativeGestureEvent *>(m_event)->value();
2027}
2028#endif // QT_CONFIG(gestures)
2029
2030/*!
2031 Returns whether the scroll event has Qt::ScrollBegin phase. On touchpads
2032 which provide phase information, this is true when the fingers are placed
2033 on the touchpad and scrolling begins. On other devices where this
2034 information is not available, it remains false.
2035*/
2036bool QQuickPointerScrollEvent::isPressEvent() const
2037{
2038 return phase() == Qt::ScrollBegin;
2039}
2040
2041/*!
2042 Returns true when the scroll event has Qt::ScrollUpdate phase, or when the
2043 phase is unknown. Some multi-touch-capable touchpads and trackpads provide
2044 phase information; whereas ordinary mouse wheels and other types of
2045 trackpads do not, and in such cases this is always true.
2046*/
2047bool QQuickPointerScrollEvent::isUpdateEvent() const
2048{
2049 return phase() == Qt::ScrollUpdate || phase() == Qt::NoScrollPhase;
2050}
2051
2052/*!
2053 Returns whether the scroll event has Qt::ScrollBegin phase. On touchpads
2054 which provide phase information, this is true when the fingers are lifted
2055 from the touchpad. On other devices where this information is not
2056 available, it remains false.
2057*/
2058bool QQuickPointerScrollEvent::isReleaseEvent() const
2059{
2060 return phase() == Qt::ScrollEnd;
2061}
2062
2063/*!
2064 \internal
2065 Returns a pointer to the QQuickEventPoint which has the \a pointId as
2066 \l {QQuickEventPoint::pointId}{pointId}.
2067 Returns nullptr if there is no point with that ID.
2068
2069 \fn QQuickPointerEvent::pointById(int pointId) const
2070*/
2071QQuickEventPoint *QQuickSinglePointEvent::pointById(int pointId) const
2072{
2073 if (m_point && pointId == m_point->pointId())
2074 return m_point;
2075 return nullptr;
2076}
2077
2078QQuickEventPoint *QQuickPointerTouchEvent::pointById(int pointId) const
2079{
2080 auto it = std::find_if(first: m_touchPoints.constBegin(), last: m_touchPoints.constEnd(),
2081 pred: [pointId](const QQuickEventTouchPoint *tp) { return tp->pointId() == pointId; } );
2082 if (it != m_touchPoints.constEnd())
2083 return *it;
2084 return nullptr;
2085}
2086
2087/*!
2088 \internal
2089 Returns a pointer to the original TouchPoint which has the same
2090 \l {QTouchEvent::TouchPoint::id}{id} as \a pointId, if the original event is a
2091 QTouchEvent, and if that point is found. Otherwise, returns nullptr.
2092*/
2093const QTouchEvent::TouchPoint *QQuickPointerTouchEvent::touchPointById(int pointId) const
2094{
2095 const QTouchEvent *ev = asTouchEvent();
2096 if (!ev)
2097 return nullptr;
2098 const QList<QTouchEvent::TouchPoint> &tps = ev->touchPoints();
2099 auto it = std::find_if(first: tps.constBegin(), last: tps.constEnd(),
2100 pred: [pointId](QTouchEvent::TouchPoint const& tp) { return tp.id() == pointId; } );
2101 // return the pointer to the actual TP in QTouchEvent::_touchPoints
2102 return (it == tps.constEnd() ? nullptr : it.operator->());
2103}
2104
2105/*!
2106 \internal
2107 Make a new QTouchEvent, giving it a subset of the original touch points.
2108
2109 Returns a nullptr if all points are stationary, or there are no points inside the item,
2110 or none of the points were pressed inside and the item was not grabbing any of them
2111 and isFiltering is false. When isFiltering is true, it is assumed that the item
2112 cares about all points which are inside its bounds, because most filtering items
2113 need to monitor eventpoint movements until a drag threshold is exceeded or the
2114 requirements for a gesture to be recognized are met in some other way.
2115*/
2116QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(QQuickItem *item, bool isFiltering) const
2117{
2118 QList<QTouchEvent::TouchPoint> touchPoints;
2119 Qt::TouchPointStates eventStates;
2120 // TODO maybe add QQuickItem::mapVector2DFromScene(QVector2D) to avoid needing QQuickItemPrivate here
2121 // Or else just document that velocity is always scene-relative and is not scaled and rotated with the item
2122 // but that would require changing tst_qquickwindow::touchEvent_velocity(): it expects transformed velocity
2123
2124 bool anyPressOrReleaseInside = false;
2125 bool anyStationaryWithModifiedPropertyInside = false;
2126 bool anyGrabber = false;
2127 QMatrix4x4 transformMatrix(QQuickItemPrivate::get(item)->windowToItemTransform());
2128 for (int i = 0; i < m_pointCount; ++i) {
2129 auto p = m_touchPoints.at(i);
2130 if (p->isAccepted())
2131 continue;
2132 // include points where item is the grabber
2133 bool isGrabber = p->exclusiveGrabber() == item;
2134 if (isGrabber)
2135 anyGrabber = true;
2136 // include points inside the bounds if no other item is the grabber or if the item is filtering
2137 bool isInside = item->contains(point: item->mapFromScene(point: p->scenePosition()));
2138 bool hasAnotherGrabber = p->exclusiveGrabber() && p->exclusiveGrabber() != item;
2139
2140 // filtering: (childMouseEventFilter) include points that are grabbed by children of the target item
2141 bool grabberIsChild = false;
2142 auto parent = p->grabberItem();
2143 while (isFiltering && parent) {
2144 if (parent == item) {
2145 grabberIsChild = true;
2146 break;
2147 }
2148 parent = parent->parentItem();
2149 }
2150
2151 bool filterRelevant = isFiltering && grabberIsChild;
2152 if (!(isGrabber || (isInside && (!hasAnotherGrabber || isFiltering)) || filterRelevant))
2153 continue;
2154 if ((p->state() == QQuickEventPoint::Pressed || p->state() == QQuickEventPoint::Released) && isInside)
2155 anyPressOrReleaseInside = true;
2156 const QTouchEvent::TouchPoint *tp = touchPointById(pointId: p->pointId());
2157 if (tp) {
2158 if (isInside && tp->d->stationaryWithModifiedProperty)
2159 anyStationaryWithModifiedPropertyInside = true;
2160 eventStates |= tp->state();
2161 QTouchEvent::TouchPoint tpCopy = *tp;
2162 tpCopy.setPos(item->mapFromScene(point: tpCopy.scenePos()));
2163 tpCopy.setLastPos(item->mapFromScene(point: tpCopy.lastScenePos()));
2164 tpCopy.setStartPos(item->mapFromScene(point: tpCopy.startScenePos()));
2165 tpCopy.setEllipseDiameters(tpCopy.ellipseDiameters());
2166 tpCopy.setVelocity(transformMatrix.mapVector(vector: tpCopy.velocity()).toVector2D());
2167 touchPoints << tpCopy;
2168 }
2169 }
2170
2171 // Now touchPoints will have only points which are inside the item.
2172 // But if none of them were just pressed inside, and the item has no other reason to care, ignore them anyway.
2173 if ((eventStates == Qt::TouchPointStationary && !anyStationaryWithModifiedPropertyInside) ||
2174 touchPoints.isEmpty() || (!anyPressOrReleaseInside && !anyGrabber && !isFiltering))
2175 return nullptr;
2176
2177 // if all points have the same state, set the event type accordingly
2178 const QTouchEvent &event = *asTouchEvent();
2179 QEvent::Type eventType = event.type();
2180 switch (eventStates) {
2181 case Qt::TouchPointPressed:
2182 eventType = QEvent::TouchBegin;
2183 break;
2184 case Qt::TouchPointReleased:
2185 eventType = QEvent::TouchEnd;
2186 break;
2187 default:
2188 eventType = QEvent::TouchUpdate;
2189 break;
2190 }
2191
2192 QTouchEvent *touchEvent = new QTouchEvent(eventType);
2193 touchEvent->setWindow(event.window());
2194 touchEvent->setTarget(item);
2195 touchEvent->setDevice(event.device());
2196 touchEvent->setModifiers(event.modifiers());
2197 touchEvent->setTouchPoints(touchPoints);
2198 touchEvent->setTouchPointStates(eventStates);
2199 touchEvent->setTimestamp(event.timestamp());
2200 touchEvent->accept();
2201 return touchEvent;
2202}
2203
2204QTouchEvent *QQuickPointerTouchEvent::asTouchEvent() const
2205{
2206 return static_cast<QTouchEvent *>(m_event);
2207}
2208
2209#ifndef QT_NO_DEBUG_STREAM
2210
2211Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickPointerDevice *dev)
2212{
2213 QDebugStateSaver saver(dbg);
2214 dbg.nospace();
2215 if (!dev) {
2216 dbg << "QQuickPointerDevice(0)";
2217 return dbg;
2218 }
2219 dbg << "QQuickPointerDevice("<< dev->name() << ' ';
2220 QtDebugUtils::formatQEnum(debug&: dbg, value: dev->type());
2221 dbg << ' ';
2222 QtDebugUtils::formatQEnum(debug&: dbg, value: dev->pointerType());
2223 dbg << " caps:";
2224 QtDebugUtils::formatQFlags(debug&: dbg, value: dev->capabilities());
2225 if (dev->type() == QQuickPointerDevice::TouchScreen ||
2226 dev->type() == QQuickPointerDevice::TouchPad)
2227 dbg << " maxTouchPoints:" << dev->maximumTouchPoints();
2228 else
2229 dbg << " buttonCount:" << dev->buttonCount();
2230 dbg << ')';
2231 return dbg;
2232}
2233
2234Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickPointerEvent *event)
2235{
2236 QDebugStateSaver saver(dbg);
2237 dbg.nospace();
2238 if (!event) {
2239 dbg << "QQuickPointerEvent(0)";
2240 return dbg;
2241 }
2242 dbg << "QQuickPointerEvent(";
2243 dbg << event->timestamp();
2244 dbg << " dev:";
2245 QtDebugUtils::formatQEnum(debug&: dbg, value: event->device()->type());
2246 if (event->buttons() != Qt::NoButton) {
2247 dbg << " buttons:";
2248 QtDebugUtils::formatQEnum(debug&: dbg, value: event->buttons());
2249 }
2250 dbg << " [";
2251 int c = event->pointCount();
2252 for (int i = 0; i < c; ++i)
2253 dbg << event->point(i) << ' ';
2254 dbg << "])";
2255 return dbg;
2256}
2257
2258Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickEventPoint *event)
2259{
2260 QDebugStateSaver saver(dbg);
2261 dbg.nospace();
2262 if (!event) {
2263 dbg << "QQuickEventPoint(0)";
2264 return dbg;
2265 }
2266 dbg << "QQuickEventPoint(accepted:" << event->isAccepted()
2267 << " state:";
2268 QtDebugUtils::formatQEnum(debug&: dbg, value: event->state());
2269 dbg << " scenePos:" << event->scenePosition() << " id:" << Qt::hex << event->pointId() << Qt::dec
2270 << " timeHeld:" << event->timeHeld() << ')';
2271 return dbg;
2272}
2273
2274#endif
2275
2276QT_END_NAMESPACE
2277

source code of qtdeclarative/src/quick/items/qquickevents.cpp