aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/items/qquickevents.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/items/qquickevents.cpp')
-rw-r--r--src/quick/items/qquickevents.cpp1020
1 files changed, 961 insertions, 59 deletions
diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp
index 448b63c347..8653d758de 100644
--- a/src/quick/items/qquickevents.cpp
+++ b/src/quick/items/qquickevents.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQuick module of the Qt Toolkit.
@@ -38,8 +38,11 @@
****************************************************************************/
#include "qquickevents_p_p.h"
+#include <QtCore/qmap.h>
#include <QtGui/private/qguiapplication_p.h>
+#include <QtGui/private/qtouchdevice_p.h>
#include <QtQuick/private/qquickitem_p.h>
+#include <QtQuick/private/qquickpointerhandler_p.h>
#include <QtQuick/private/qquickwindow_p.h>
#include <private/qdebug_p.h>
@@ -445,10 +448,96 @@ Item {
\l inverted always returns false.
*/
-typedef QHash<QTouchDevice *, QQuickPointerDevice *> PointerDeviceForTouchDeviceHash;
+/*!
+ \qmltype PointerDevice
+ \instantiates QQuickPointerDevice
+ \inqmlmodule QtQuick
+ \ingroup qtquick-input-events
+
+ \brief Provides information about a pointing device
+
+ A pointing device can be a mouse, a touchscreen, or a stylus on a graphics
+ tablet.
+
+ \sa PointerEvent, PointerHandler
+*/
+
+/*!
+ \readonly
+ \qmlproperty enumeration QtQuick::PointerDevice::type
+
+ This property holds the type of the pointing device.
+
+ Valid values are:
+
+ \value DeviceType.UnknownDevice
+ the device cannot be identified
+ \value DeviceType.Mouse
+ a mouse
+ \value DeviceType.TouchScreen
+ a touchscreen providing absolute coordinates
+ \value DeviceType.TouchPad
+ a trackpad or touchpad providing relative coordinates
+ \value DeviceType.Stylus
+ a pen-like device
+ \value DeviceType.Airbrush
+ a stylus with a thumbwheel to adjust
+ \l {QTabletEvent::tangentialPressure}{tangentialPressure}
+ \value DeviceType.Puck
+ a device that is similar to a flat mouse with a
+ transparent circle with cross-hairs (same as \l QTabletEvent::Puck)
+
+ \sa QTouchDevice::DeviceType
+*/
+
+/*!
+ \readonly
+ \qmlproperty enumeration QtQuick::PointerDevice::capabilities
+
+ This property holds a bitwise combination of the capabilities of the
+ pointing device. It tells you under which conditions events are sent,
+ and which properties of PointerEvent are expected to be valid.
+
+ Valid values are:
+
+ \value CapabilityFlag.Position
+ the \l {QtQuick::EventPoint::position}{position} and
+ \l {QtQuick::EventPoint::scenePosition}{scenePosition} properties
+ \value CapabilityFlag.Area
+ the \l {QtQuick::EventTouchPoint::ellipseDiameters}{ellipseDiameters} property
+ \value CapabilityFlag.Pressure
+ the \l {QtQuick::EventTouchPoint::pressure}{pressure} property
+ \value CapabilityFlag.Velocity
+ the \l {QtQuick::PointerEvent::velocity}{velocity} property
+ \value CapabilityFlag.Scroll
+ a \l {QtQuick::PointerDevice::DeviceType::Mouse}{Mouse} has a wheel, or the
+ operating system recognizes scroll gestures on a
+ \l {QtQuick::PointerDevice::DeviceType::TouchPad}{TouchPad}
+ \value CapabilityFlag.Hover
+ events are sent even when no button is pressed, or the finger or stylus
+ is not in contact with the surface
+ \value CapabilityFlag.Rotation
+ the \l {QtQuick::EventTouchPoint::rotation}{rotation} property
+ \value CapabilityFlag.XTilt
+ horizontal angle between a stylus and the axis perpendicular to the surface
+ \value CapabilityFlag.YTilt
+ vertical angle between a stylus and the axis perpendicular to the surface
+
+ \sa QTouchDevice::capabilities
+*/
+
+typedef QHash<const QTouchDevice *, QQuickPointerDevice *> PointerDeviceForTouchDeviceHash;
Q_GLOBAL_STATIC(PointerDeviceForTouchDeviceHash, g_touchDevices)
-Q_GLOBAL_STATIC_WITH_ARGS(QQuickPointerDevice, g_genericMouseDevice,
+struct ConstructableQQuickPointerDevice : public QQuickPointerDevice
+{
+ ConstructableQQuickPointerDevice(DeviceType devType, PointerType pType, Capabilities caps,
+ int maxPoints, int buttonCount, const QString &name,
+ qint64 uniqueId = 0)
+ : QQuickPointerDevice(devType, pType, caps, maxPoints, buttonCount, name, uniqueId) {}
+
+};
+Q_GLOBAL_STATIC_WITH_ARGS(ConstructableQQuickPointerDevice, g_genericMouseDevice,
(QQuickPointerDevice::Mouse,
QQuickPointerDevice::GenericPointer,
QQuickPointerDevice::Position | QQuickPointerDevice::Scroll | QQuickPointerDevice::Hover,
@@ -457,7 +546,23 @@ Q_GLOBAL_STATIC_WITH_ARGS(QQuickPointerDevice, g_genericMouseDevice,
typedef QHash<qint64, QQuickPointerDevice *> PointerDeviceForDeviceIdHash;
Q_GLOBAL_STATIC(PointerDeviceForDeviceIdHash, g_tabletDevices)
-QQuickPointerDevice *QQuickPointerDevice::touchDevice(QTouchDevice *d)
+// debugging helpers
+static const char *pointStateString(const QQuickEventPoint *point)
+{
+ static const QMetaEnum stateMetaEnum = point->metaObject()->enumerator(point->metaObject()->indexOfEnumerator("State"));
+ return stateMetaEnum.valueToKey(point->state());
+}
+
+static const QString pointDeviceName(const QQuickEventPoint *point)
+{
+ auto device = static_cast<const QQuickPointerEvent *>(point->parent())->device();
+ QString deviceName = (device ? device->name() : QLatin1String("null device"));
+ deviceName.resize(16, ' '); // shorten, and align in case of sequential output
+ return deviceName;
+}
+
+
+QQuickPointerDevice *QQuickPointerDevice::touchDevice(const QTouchDevice *d)
{
if (g_touchDevices->contains(d))
return g_touchDevices->value(d);
@@ -505,37 +610,358 @@ QQuickPointerDevice *QQuickPointerDevice::tabletDevice(qint64 id)
return nullptr;
}
-void QQuickEventPoint::reset(Qt::TouchPointState state, QPointF scenePos, quint64 pointId, ulong timestamp)
+/*!
+ \qmltype EventPoint
+ \qmlabstract
+ \instantiates QQuickEventPoint
+ \inqmlmodule QtQuick
+ \ingroup qtquick-input-events
+ \brief Provides information about an individual point within a PointerEvent
+
+ A PointerEvent contains an EventPoint for each point of contact: one corresponding
+ to the mouse cursor, or one for each finger touching a touchscreen.
+
+ \sa PointerEvent, PointerHandler
+*/
+
+/*!
+ \readonly
+ \qmlproperty point QtQuick::EventPoint::position
+
+ This property holds the coordinates of the position supplied by the event,
+ relative to the upper-left corner of the Item which has the PointerHandler.
+ If a contact patch is available from the pointing device, this point
+ represents its centroid.
+*/
+
+/*!
+ \readonly
+ \qmlproperty point QtQuick::EventPoint::scenePosition
+
+ This property holds the coordinates of the position supplied by the event,
+ relative to the scene. If a contact patch is available from the \l device,
+ this point represents its centroid.
+*/
+
+/*!
+ \readonly
+ \qmlproperty point QtQuick::EventPoint::scenePressPosition
+
+ This property holds the scene-relative position at which the press event
+ (on a touch device) or most recent change in QQuickPointerEvent::buttons()
+ (on a mouse or tablet stylus) occurred.
+*/
+
+/*!
+ \readonly
+ \qmlproperty point QtQuick::EventPoint::sceneGrabPosition
+
+ This property holds the scene-relative position at which the EventPoint was
+ located when setGrabber() was called most recently.
+*/
+
+/*!
+ \readonly
+ \qmlproperty vector2d QtQuick::EventPoint::velocity
+
+ This property holds average recent velocity: how fast and in which
+ direction the event point has been moving recently.
+*/
+
+/*!
+ \readonly
+ \qmlproperty int QtQuick::EventPoint::state
+
+ This property tells what the user is currently doing at this point.
+
+ It can be one of:
+ \value Pressed
+ The user's finger is now pressing a touchscreen, button or stylus
+ which was not pressed already
+ \value Updated
+ The touchpoint or position is being moved, with no change in pressed state
+ \value Stationary
+ The touchpoint or position is not being moved, and there is also
+ no change in pressed state
+ \value Released
+ The user's finger has now released a touch point, button or stylus
+ which was pressed
+*/
+
+/*!
+ \readonly
+ \qmlproperty int QtQuick::EventPoint::pointId
+
+ This property holds the ID of the event, if any.
+
+ Touchpoints have automatically-incrementing IDs: each time the user
+ presses a finger against the touchscreen, it will be a larger number.
+ In other cases, it will be -1.
+
+ \sa PointerDevice.uniqueId
+*/
+
+/*!
+ \readonly
+ \qmlproperty bool QtQuick::EventPoint::accepted
+
+ Setting \a accepted to true prevents the event from being propagated to
+ Items below the PointerHandler's Item.
+
+ Generally, if the handler acts on the mouse event, then it should be
+ accepted so that items lower in the stacking order do not also respond to
+ the same event.
+*/
+
+/*!
+ \readonly
+ \qmlproperty real QtQuick::EventPoint::timeHeld
+
+ This property holds the amount of time in seconds that the button or touchpoint has
+ been held. It can be used to detect a "long press", and can drive an
+ animation to show progress toward activation of the "long press" action.
+*/
+
+void QQuickEventPoint::reset(Qt::TouchPointState state, const QPointF &scenePos, int pointId, ulong timestamp, const QVector2D &velocity)
{
m_scenePos = scenePos;
m_pointId = pointId;
- m_valid = true;
m_accept = false;
m_state = static_cast<QQuickEventPoint::State>(state);
m_timestamp = timestamp;
- if (state == Qt::TouchPointPressed)
+ if (state == Qt::TouchPointPressed) {
m_pressTimestamp = timestamp;
- // TODO calculate velocity
+ m_scenePressPos = scenePos;
+ }
+ m_velocity = (Q_LIKELY(velocity.isNull()) ? estimatedVelocity() : velocity);
+}
+
+void QQuickEventPoint::localizePosition(QQuickItem *target)
+{
+ if (target)
+ m_pos = target->mapFromScene(scenePosition());
+ else
+ m_pos = QPointF();
+}
+
+/*!
+ If this point has an exclusive grabber, returns a pointer to it; else
+ returns null, if there is no grabber. The grabber could be either
+ an Item or a PointerHandler.
+*/
+QObject *QQuickEventPoint::exclusiveGrabber() const
+{
+ return m_exclusiveGrabber.data();
+}
+
+/*!
+ Set the given Item or PointerHandler as the exclusive grabber of this point.
+ If there was already an exclusive grab, it will be canceled. If there
+ were passive grabbers, they will continue to lurk, but the exclusive grab
+ is a behavioral override of the passive grab as long as it remains.
+ If you already know whether the grabber is to be an Item or a PointerHandler,
+ you should instead call setGrabberItem() or setGrabberPointerHandler(),
+ because it is slightly more efficient.
+*/
+void QQuickEventPoint::setExclusiveGrabber(QObject *grabber)
+{
+ if (QQuickPointerHandler *phGrabber = qmlobject_cast<QQuickPointerHandler *>(grabber))
+ setGrabberPointerHandler(phGrabber, true);
+ else
+ setGrabberItem(static_cast<QQuickItem *>(grabber));
+}
+
+/*!
+ If the exclusive grabber of this point is an Item, returns a
+ pointer to that Item; else returns null, if there is no grabber or if
+ the grabber is a PointerHandler.
+*/
+QQuickItem *QQuickEventPoint::grabberItem() const
+{
+ return (m_grabberIsHandler ? nullptr : static_cast<QQuickItem *>(m_exclusiveGrabber.data()));
+}
+
+/*!
+ Set the given Item \a grabber as the exclusive grabber of this point.
+ If there was already an exclusive grab, it will be canceled. If there
+ were passive grabbers, they will continue to lurk, but the exclusive grab
+ is a behavioral override of the passive grab as long as it remains.
+*/
+void QQuickEventPoint::setGrabberItem(QQuickItem *grabber)
+{
+ if (grabber != m_exclusiveGrabber.data()) {
+ if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) {
+ qCDebug(lcPointerGrab) << pointDeviceName(this) << "point" << hex << m_pointId << pointStateString(this)
+ << ": grab" << m_exclusiveGrabber << "->" << grabber;
+ }
+ QQuickPointerHandler *oldGrabberHandler = grabberPointerHandler();
+ QQuickItem *oldGrabberItem = grabberItem();
+ m_exclusiveGrabber = QPointer<QObject>(grabber);
+ m_grabberIsHandler = false;
+ m_sceneGrabPos = m_scenePos;
+ if (oldGrabberHandler)
+ oldGrabberHandler->onGrabChanged(oldGrabberHandler, CancelGrabExclusive, this);
+ else if (oldGrabberItem && oldGrabberItem != grabber && grabber && pointerEvent()->asPointerTouchEvent())
+ oldGrabberItem->touchUngrabEvent();
+ for (QPointer<QQuickPointerHandler> passiveGrabber : m_passiveGrabbers)
+ passiveGrabber->onGrabChanged(passiveGrabber, OverrideGrabPassive, this);
+ }
}
-QQuickItem *QQuickEventPoint::grabber() const
+/*!
+ If the exclusive grabber of this point is a PointerHandler, returns a
+ pointer to that handler; else returns null, if there is no grabber or if
+ the grabber is an Item.
+*/
+QQuickPointerHandler *QQuickEventPoint::grabberPointerHandler() const
{
- return m_grabber.data();
+ return (m_grabberIsHandler ? static_cast<QQuickPointerHandler *>(m_exclusiveGrabber.data()) : nullptr);
}
-void QQuickEventPoint::setGrabber(QQuickItem *grabber)
+/*!
+ Set the given PointerHandler \a grabber as grabber of this point. If \a
+ exclusive is true, it will override any other grabs; if false, \a grabber
+ will be added to the list of passive grabbers of this point.
+*/
+void QQuickEventPoint::setGrabberPointerHandler(QQuickPointerHandler *grabber, bool exclusive)
{
- if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled()) && m_grabber.data() != grabber) {
- auto device = static_cast<const QQuickPointerEvent *>(parent())->device();
- static const QMetaEnum stateMetaEnum = metaObject()->enumerator(metaObject()->indexOfEnumerator("State"));
- QString deviceName = (device ? device->name() : QLatin1String("null device"));
- deviceName.resize(16, ' '); // shorten, and align in case of sequential output
- qCDebug(lcPointerGrab) << deviceName << "point" << hex << m_pointId << stateMetaEnum.valueToKey(state())
- << ": grab" << m_grabber << "->" << grabber;
+ if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) {
+ if (exclusive) {
+ if (m_exclusiveGrabber != grabber)
+ qCDebug(lcPointerGrab) << pointDeviceName(this) << "point" << hex << m_pointId << pointStateString(this)
+ << ": grab (exclusive)" << m_exclusiveGrabber << "->" << grabber;
+ } else {
+ qCDebug(lcPointerGrab) << pointDeviceName(this) << "point" << hex << m_pointId << pointStateString(this)
+ << ": grab (passive)" << grabber;
+ }
+ }
+ if (exclusive) {
+ if (grabber != m_exclusiveGrabber.data()) {
+ if (grabber) {
+ // set variables before notifying the new grabber
+ m_exclusiveGrabber = QPointer<QObject>(grabber);
+ m_grabberIsHandler = true;
+ m_sceneGrabPos = m_scenePos;
+ grabber->onGrabChanged(grabber, GrabExclusive, this);
+ for (QPointer<QQuickPointerHandler> passiveGrabber : m_passiveGrabbers) {
+ if (passiveGrabber != grabber)
+ passiveGrabber->onGrabChanged(grabber, OverrideGrabPassive, this);
+ }
+ } else if (QQuickPointerHandler *oldGrabberPointerHandler = qmlobject_cast<QQuickPointerHandler *>(m_exclusiveGrabber.data())) {
+ oldGrabberPointerHandler->onGrabChanged(oldGrabberPointerHandler, UngrabExclusive, this);
+ } else if (!m_exclusiveGrabber.isNull()) {
+ // If there is a previous grabber and it's not a PointerHandler, it must be an Item.
+ QQuickItem *oldGrabberItem = static_cast<QQuickItem *>(m_exclusiveGrabber.data());
+ // If this point came from a touchscreen, notify that previous grabber Item that it's losing its touch grab.
+ if (pointerEvent()->asPointerTouchEvent())
+ oldGrabberItem->touchUngrabEvent();
+ }
+ // set variables after notifying the old grabber
+ m_exclusiveGrabber = QPointer<QObject>(grabber);
+ m_grabberIsHandler = true;
+ m_sceneGrabPos = m_scenePos;
+ }
+ } else {
+ if (!grabber) {
+ qDebug() << "can't set passive grabber to null";
+ return;
+ }
+ auto ptr = QPointer<QQuickPointerHandler>(grabber);
+ if (!m_passiveGrabbers.contains(ptr)) {
+ m_passiveGrabbers.append(ptr);
+ grabber->onGrabChanged(grabber, GrabPassive, this);
+ }
}
- m_grabber = QPointer<QQuickItem>(grabber);
}
+/*!
+ If this point has an existing exclusive grabber (Item or PointerHandler),
+ inform the grabber that its grab is canceled, and remove it as grabber.
+ This normally happens when the grab is stolen by another Item.
+*/
+void QQuickEventPoint::cancelExclusiveGrab()
+{
+ if (m_exclusiveGrabber.isNull())
+ qWarning("cancelGrab: no grabber");
+ else
+ cancelExclusiveGrabImpl();
+}
+
+void QQuickEventPoint::cancelExclusiveGrabImpl(QTouchEvent *cancelEvent)
+{
+ if (m_exclusiveGrabber.isNull())
+ return;
+ if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) {
+ qCDebug(lcPointerGrab) << pointDeviceName(this) << "point" << hex << m_pointId << pointStateString(this)
+ << ": grab (exclusive)" << m_exclusiveGrabber << "-> nullptr";
+ }
+ if (auto handler = grabberPointerHandler()) {
+ handler->onGrabChanged(handler, CancelGrabExclusive, this);
+ } else if (auto item = grabberItem()) {
+ if (cancelEvent)
+ QCoreApplication::sendEvent(item, cancelEvent);
+ else
+ item->touchUngrabEvent();
+ }
+ m_exclusiveGrabber.clear();
+}
+
+/*!
+ If this point has the given \a handler as a passive grabber,
+ inform the grabber that its grab is canceled, and remove it as grabber.
+ This normally happens when another Item or PointerHandler does an exclusive grab.
+*/
+void QQuickEventPoint::cancelPassiveGrab(QQuickPointerHandler *handler)
+{
+ if (removePassiveGrabber(handler)) {
+ if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) {
+ qCDebug(lcPointerGrab) << pointDeviceName(this) << "point" << hex << m_pointId << pointStateString(this)
+ << ": grab (passive)" << handler << "removed";
+ }
+ handler->onGrabChanged(handler, CancelGrabPassive, this);
+ }
+}
+
+/*!
+ If this point has the given \a handler as a passive grabber, remove it as grabber.
+ Returns true if it was removed, false if it wasn't a grabber.
+*/
+bool QQuickEventPoint::removePassiveGrabber(QQuickPointerHandler *handler)
+{
+ return m_passiveGrabbers.removeOne(handler);
+}
+
+/*!
+ If the given \a handler is grabbing this point passively, exclusively
+ or both, cancel the grab and remove it as grabber.
+ This normally happens when the handler decides that the behavior of this
+ point can no longer satisfy the handler's behavioral constraints within
+ the remainder of the gesture which the user is performing: for example
+ the handler tries to detect a tap but a drag is occurring instead, or
+ it tries to detect a drag in one direction but the drag is going in
+ another direction. In such cases the handler no longer needs or wants
+ to be informed of any further movements of this point.
+*/
+void QQuickEventPoint::cancelAllGrabs(QQuickPointerHandler *handler)
+{
+ if (m_exclusiveGrabber == handler) {
+ handler->onGrabChanged(handler, CancelGrabExclusive, this);
+ m_exclusiveGrabber.clear();
+ }
+ cancelPassiveGrab(handler);
+}
+
+/*!
+ Sets this point as \a accepted (true) or rejected (false).
+
+ During delivery of the current event to the Items in the scene, each Item
+ or Pointer Handler should accept the points for which it is taking
+ responsibility. As soon as all points within the event are accepted, event
+ propagation stops. However accepting the point does not imply any kind of
+ grab, passive or exclusive.
+
+ \sa setExclusiveGrabber, QQuickPointerHandler::setPassiveGrab, QQuickPointerHandler::setExclusiveGrab
+*/
void QQuickEventPoint::setAccepted(bool accepted)
{
if (m_accept != accepted) {
@@ -544,18 +970,175 @@ void QQuickEventPoint::setAccepted(bool accepted)
}
}
+
+/*!
+ \qmltype EventTouchPoint
+ \qmlabstract
+ \instantiates QQuickEventTouchPoint
+ \inqmlmodule QtQuick
+ \ingroup qtquick-input-events
+ \brief Provides information about an individual touch point within a PointerEvent
+
+ \sa PointerEvent, PointerHandler
+*/
+
+/*!
+ \readonly
+ \qmlproperty QPointerUniqueId QtQuick::EventTouchPoint::uniqueId
+
+ This property holds the unique ID of the fiducial or stylus in use, if any.
+
+ On touchscreens that can track physical objects (such as knobs or game
+ pieces) in addition to fingers, each object usually has a unique ID.
+ Likewise, each stylus that can be used with a graphics tablet usually has a
+ unique serial number. Qt so far only supports numeric IDs. You can get the
+ actual number as uniqueId.numeric, but that is a device-specific detail.
+ In the future, there may be support for non-numeric IDs, so you should
+ not assume that the number is meaningful.
+
+ If you need to identify specific objects, your application should provide
+ UI for registering objects and mapping them to functionality: allow the
+ user to select a meaning, virtual tool, or action, prompt the user to bring
+ the object into proximity, and store a mapping from uniqueId to its
+ purpose, for example in \l Settings.
+*/
+
+/*!
+ \readonly
+ \qmlproperty qreal QtQuick::EventTouchPoint::rotation
+
+ This property holds the rotation angle of the stylus on a graphics tablet
+ or the contact patch of a touchpoint on a touchscreen.
+
+ It is valid only with certain tablet stylus devices and touchscreens that
+ can measure the rotation angle. Otherwise, it will be zero.
+*/
+
+/*!
+ \readonly
+ \qmlproperty qreal QtQuick::EventTouchPoint::pressure
+
+ This property tells how hard the user is pressing the stylus on a graphics
+ tablet or the finger against a touchscreen, in the range from \c 0 (no
+ measurable pressure) to \c 1.0 (maximum pressure which the device can
+ measure).
+
+ It is valid only with certain tablets and touchscreens that can measure
+ pressure. Otherwise, it will be \c 1.0 when pressed.
+*/
+
+/*!
+ \readonly
+ \qmlproperty size QtQuick::EventTouchPoint::ellipseDiameters
+
+ This property holds the diameters of the contact patch, if the event
+ comes from a touchpoint and the \l device provides this information.
+
+ A touchpoint is modeled as an elliptical area where the finger is pressed
+ against the touchscreen. (In fact, it could also be modeled as a bitmap; but
+ in that case we expect an elliptical bounding estimate to be fitted to the
+ contact patch before the event is sent.) The harder the user presses, the
+ larger the contact patch; so, these diameters provide an alternate way of
+ detecting pressure, in case the device does not include a separate pressure
+ sensor. The ellipse is centered on \l scenePos (\l pos in the PointerHandler's
+ Item's local coordinates). The \l rotation property provides the
+ rotation of the ellipse, if known. It is expected that if the \l rotation
+ is zero, the verticalDiameter of the ellipse is the larger one (the major axis),
+ because of the usual hand position, reaching upward or outward across the surface.
+
+ If the contact patch is unknown, or the \l device is not a touchscreen,
+ these values will be zero.
+*/
+
QQuickEventTouchPoint::QQuickEventTouchPoint(QQuickPointerTouchEvent *parent)
: QQuickEventPoint(parent), m_rotation(0), m_pressure(0)
{}
void QQuickEventTouchPoint::reset(const QTouchEvent::TouchPoint &tp, ulong timestamp)
{
- QQuickEventPoint::reset(tp.state(), tp.scenePos(), tp.id(), timestamp);
+ QQuickEventPoint::reset(tp.state(), tp.scenePos(), tp.id(), timestamp, tp.velocity());
+ m_exclusiveGrabber.clear();
+ m_passiveGrabbers.clear();
m_rotation = tp.rotation();
m_pressure = tp.pressure();
+ m_ellipseDiameters = tp.ellipseDiameters();
m_uniqueId = tp.uniqueId();
}
+struct PointVelocityData {
+ QVector2D velocity;
+ QPointF pos;
+ ulong timestamp;
+};
+
+typedef QMap<quint64, PointVelocityData*> PointDataForPointIdMap;
+Q_GLOBAL_STATIC(PointDataForPointIdMap, g_previousPointData)
+static const int PointVelocityAgeLimit = 500; // milliseconds
+
+/*!
+ \internal
+ Estimates the velocity based on a weighted average of all previous velocities.
+ The older the velocity is, the less significant it becomes for the estimate.
+*/
+QVector2D QQuickEventPoint::estimatedVelocity() const
+{
+ PointVelocityData *prevPoint = g_previousPointData->value(m_pointId);
+ if (!prevPoint) {
+ // cleanup events older than PointVelocityAgeLimit
+ auto end = g_previousPointData->end();
+ for (auto it = g_previousPointData->begin(); it != end; ) {
+ PointVelocityData *data = it.value();
+ if (m_timestamp - data->timestamp > PointVelocityAgeLimit) {
+ it = g_previousPointData->erase(it);
+ delete data;
+ } else {
+ ++it;
+ }
+ }
+ // TODO optimize: stop this dynamic memory thrashing
+ prevPoint = new PointVelocityData;
+ prevPoint->velocity = QVector2D();
+ prevPoint->timestamp = 0;
+ prevPoint->pos = QPointF();
+ g_previousPointData->insert(m_pointId, prevPoint);
+ }
+ const ulong timeElapsed = m_timestamp - prevPoint->timestamp;
+ if (timeElapsed == 0) // in case we call estimatedVelocity() twice on the same QQuickEventPoint
+ return m_velocity;
+
+ QVector2D newVelocity;
+ if (prevPoint->timestamp != 0)
+ newVelocity = QVector2D(m_scenePos - prevPoint->pos)/timeElapsed;
+
+ // VERY simple kalman filter: does a weighted average
+ // where the older velocities get less and less significant
+ static const float KalmanGain = 0.7f;
+ QVector2D filteredVelocity = newVelocity * KalmanGain + m_velocity * (1.0f - KalmanGain);
+
+ prevPoint->velocity = filteredVelocity;
+ prevPoint->pos = m_scenePos;
+ prevPoint->timestamp = m_timestamp;
+ return filteredVelocity;
+}
+
+/*!
+ \qmltype PointerEvent
+ \instantiates QQuickPointerEvent
+ \inqmlmodule QtQuick
+ \ingroup qtquick-input-events
+
+ \brief Provides information about an event from a pointing device
+
+ A PointerEvent is an event describing contact or movement across a surface,
+ provided by a mouse, a touchpoint (single finger on a touchscreen), or a
+ stylus on a graphics tablet. The \l device property provides more
+ information about where the event came from.
+
+ \sa PointerHandler
+
+ \image touchpoint-metrics.png
+*/
+
/*!
\internal
\class QQuickPointerEvent
@@ -570,6 +1153,68 @@ void QQuickEventTouchPoint::reset(const QTouchEvent::TouchPoint &tp, ulong times
dynamically create and destroy objects of this type for each event.
*/
+/*!
+ \readonly
+ \qmlproperty enumeration QtQuick::PointerEvent::button
+
+ This property holds the \l {Qt::MouseButton}{button} that caused the event,
+ if any. If the \l device does not have buttons, or the event is a hover
+ event, it will be \c Qt.NoButton.
+*/
+
+/*!
+ \readonly
+ \qmlproperty int QtQuick::PointerEvent::buttons
+
+ This property holds the combination of mouse or stylus
+ \l {Qt::MouseButton}{buttons} pressed when the event was generated. For move
+ events, this is all buttons that are pressed down. For press events, this
+ includes the button that caused the event, as well as any others that were
+ already held. For release events, this excludes the button that caused the
+ event.
+*/
+
+/*!
+ \readonly
+ \qmlproperty int QtQuick::PointerEvent::modifiers
+
+ This property holds the \l {Qt::KeyboardModifier}{keyboard modifier} flags
+ that existed immediately before the event occurred.
+
+ It contains a bitwise combination of the following flags:
+ \value Qt.NoModifier
+ No modifier key is pressed.
+ \value Qt.ShiftModifier
+ A Shift key on the keyboard is pressed.
+ \value Qt.ControlModifier
+ A Ctrl key on the keyboard is pressed.
+ \value Qt.AltModifier
+ An Alt key on the keyboard is pressed.
+ \value Qt.MetaModifier
+ A Meta key on the keyboard is pressed.
+ \value Qt.KeypadModifier
+ A keypad button is pressed.
+
+ For example, to react to a Shift key + Left mouse button click:
+ \qml
+ Item {
+ TapHandler {
+ onTapped: {
+ if ((event.button == Qt.LeftButton) && (event.modifiers & Qt.ShiftModifier))
+ doSomething();
+ }
+ }
+ }
+ \endqml
+*/
+
+/*!
+ \readonly
+ \qmlproperty PointerDevice QtQuick::PointerEvent::device
+
+ This property holds the device that generated the event.
+*/
+
QQuickPointerEvent::~QQuickPointerEvent()
{}
@@ -581,11 +1226,14 @@ QQuickPointerEvent *QQuickPointerMouseEvent::reset(QEvent *event)
return this;
m_device = QQuickPointerDevice::genericMouseDevice();
+ m_device->eventDeliveryTargets().clear();
m_button = ev->button();
m_pressedButtons = ev->buttons();
Qt::TouchPointState state = Qt::TouchPointStationary;
switch (ev->type()) {
case QEvent::MouseButtonPress:
+ m_mousePoint->clearPassiveGrabbers();
+ Q_FALLTHROUGH();
case QEvent::MouseButtonDblClick:
state = Qt::TouchPointPressed;
break;
@@ -598,10 +1246,15 @@ QQuickPointerEvent *QQuickPointerMouseEvent::reset(QEvent *event)
default:
break;
}
- m_mousePoint->reset(state, ev->windowPos(), 0, ev->timestamp()); // mouse is 0
+ m_mousePoint->reset(state, ev->windowPos(), quint64(1) << 24, ev->timestamp()); // mouse has device ID 1
return this;
}
+void QQuickPointerMouseEvent::localize(QQuickItem *target)
+{
+ m_mousePoint->localizePosition(target);
+}
+
QQuickPointerEvent *QQuickPointerTouchEvent::reset(QEvent *event)
{
auto ev = static_cast<QTouchEvent*>(event);
@@ -610,6 +1263,7 @@ QQuickPointerEvent *QQuickPointerTouchEvent::reset(QEvent *event)
return this;
m_device = QQuickPointerDevice::touchDevice(ev->device());
+ m_device->eventDeliveryTargets().clear();
m_button = Qt::NoButton;
m_pressedButtons = Qt::NoButton;
@@ -620,32 +1274,93 @@ QQuickPointerEvent *QQuickPointerTouchEvent::reset(QEvent *event)
for (int i = m_touchPoints.size(); i < newPointCount; ++i)
m_touchPoints.insert(i, new QQuickEventTouchPoint(this));
- // Make sure the grabbers are right from one event to the next
- QVector<QQuickItem*> grabbers;
- // Copy all grabbers, because the order of points might have changed in the event.
+ // Make sure the grabbers and on-pressed values are right from one event to the next
+ struct ToPreserve {
+ int pointId; // just for double-checking
+ ulong pressTimestamp;
+ QPointF scenePressPos;
+ QPointF sceneGrabPos;
+ QObject * grabber;
+ QVector <QPointer <QQuickPointerHandler> > passiveGrabbers;
+
+ ToPreserve() : pointId(0), pressTimestamp(0), grabber(nullptr) {}
+ };
+ QVector<ToPreserve> preserves(newPointCount); // jar of pickled touchpoints, in order of points in the _new_ event
+
+ // Copy stuff we need to preserve, because the order of points might have changed in the event.
// The ID is all that we can rely on (release might remove the first point etc).
for (int i = 0; i < newPointCount; ++i) {
- QQuickItem *grabber = nullptr;
- if (auto point = pointById(tps.at(i).id()))
- grabber = point->grabber();
- grabbers.append(grabber);
+ int pid = tps.at(i).id();
+ if (auto point = pointById(pid)) {
+ preserves[i].pointId = pid;
+ preserves[i].pressTimestamp = point->m_pressTimestamp;
+ preserves[i].scenePressPos = point->scenePressPosition();
+ preserves[i].sceneGrabPos = point->sceneGrabPosition();
+ preserves[i].grabber = point->exclusiveGrabber();
+ preserves[i].passiveGrabbers = point->passiveGrabbers();
+ }
}
for (int i = 0; i < newPointCount; ++i) {
auto point = m_touchPoints.at(i);
point->reset(tps.at(i), ev->timestamp());
+ const auto &preserved = preserves.at(i);
if (point->state() == QQuickEventPoint::Pressed) {
- if (grabbers.at(i))
+ if (preserved.grabber)
qWarning() << "TouchPointPressed without previous release event" << point;
- point->setGrabber(nullptr);
+ point->setGrabberItem(nullptr);
+ point->clearPassiveGrabbers();
} else {
- point->setGrabber(grabbers.at(i));
+ // Restore the grabbers without notifying (don't call onGrabChanged)
+ Q_ASSERT(preserved.pointId == 0 || preserved.pointId == point->pointId());
+ point->m_pressTimestamp = preserved.pressTimestamp;
+ point->m_scenePressPos = preserved.scenePressPos;
+ point->m_sceneGrabPos = preserved.sceneGrabPos;
+ point->m_exclusiveGrabber = preserved.grabber;
+ point->m_grabberIsHandler = (qmlobject_cast<QQuickPointerHandler *>(point->m_exclusiveGrabber) != nullptr);
+ point->m_passiveGrabbers = preserved.passiveGrabbers;
}
}
m_pointCount = newPointCount;
return this;
}
+void QQuickPointerTouchEvent::localize(QQuickItem *target)
+{
+ for (auto point : qAsConst(m_touchPoints))
+ point->localizePosition(target);
+}
+
+QQuickPointerEvent *QQuickPointerNativeGestureEvent::reset(QEvent *event)
+{
+ auto ev = static_cast<QNativeGestureEvent*>(event);
+ m_event = ev;
+ if (!event)
+ return this;
+
+ m_device = QQuickPointerDevice::touchDevice(ev->device());
+ m_device->eventDeliveryTargets().clear();
+ Qt::TouchPointState state = Qt::TouchPointMoved;
+ switch (type()) {
+ case Qt::BeginNativeGesture:
+ state = Qt::TouchPointPressed;
+ break;
+ case Qt::EndNativeGesture:
+ state = Qt::TouchPointReleased;
+ break;
+ default:
+ break;
+ }
+ quint64 deviceId = QTouchDevicePrivate::get(const_cast<QTouchDevice *>(ev->device()))->id; // a bit roundabout since QTouchDevice::mTouchDeviceId is protected
+ m_gesturePoint->reset(state, ev->windowPos(), deviceId << 24, ev->timestamp());
+ return this;
+}
+
+void QQuickPointerNativeGestureEvent::localize(QQuickItem *target)
+{
+ m_gesturePoint->localizePosition(target);
+}
+
QQuickEventPoint *QQuickPointerMouseEvent::point(int i) const {
if (i == 0)
return m_mousePoint;
@@ -658,9 +1373,15 @@ QQuickEventPoint *QQuickPointerTouchEvent::point(int i) const {
return nullptr;
}
+QQuickEventPoint *QQuickPointerNativeGestureEvent::point(int i) const {
+ if (i == 0)
+ return m_gesturePoint;
+ return nullptr;
+}
+
QQuickEventPoint::QQuickEventPoint(QQuickPointerEvent *parent)
- : QObject(parent), m_pointId(0), m_grabber(nullptr), m_timestamp(0), m_pressTimestamp(0),
- m_state(QQuickEventPoint::Released), m_valid(false), m_accept(false)
+ : QObject(parent), m_pointId(0), m_exclusiveGrabber(nullptr), m_timestamp(0), m_pressTimestamp(0),
+ m_state(QQuickEventPoint::Released), m_accept(false), m_grabberIsHandler(false)
{
Q_UNUSED(m_reserved);
}
@@ -674,6 +1395,15 @@ bool QQuickPointerMouseEvent::allPointsAccepted() const {
return m_mousePoint->isAccepted();
}
+bool QQuickPointerMouseEvent::allUpdatedPointsAccepted() const {
+ return m_mousePoint->state() == QQuickEventPoint::Pressed || m_mousePoint->isAccepted();
+}
+
+bool QQuickPointerMouseEvent::allPointsGrabbed() const
+{
+ return m_mousePoint->exclusiveGrabber() != nullptr;
+}
+
QMouseEvent *QQuickPointerMouseEvent::asMouseEvent(const QPointF &localPos) const
{
auto event = static_cast<QMouseEvent *>(m_event);
@@ -681,16 +1411,31 @@ QMouseEvent *QQuickPointerMouseEvent::asMouseEvent(const QPointF &localPos) cons
return event;
}
-QVector<QQuickItem *> QQuickPointerMouseEvent::grabbers() const
+/*!
+ Returns the exclusive grabber of this event, if any, in a vector.
+*/
+QVector<QObject *> QQuickPointerMouseEvent::exclusiveGrabbers() const
{
- QVector<QQuickItem *> result;
- if (QQuickItem *grabber = m_mousePoint->grabber())
+ QVector<QObject *> result;
+ if (QObject *grabber = m_mousePoint->exclusiveGrabber())
result << grabber;
return result;
}
+/*!
+ Remove all passive and exclusive grabbers of this event, without notifying.
+*/
void QQuickPointerMouseEvent::clearGrabbers() const {
- m_mousePoint->setGrabber(nullptr);
+ m_mousePoint->setGrabberItem(nullptr);
+ m_mousePoint->clearPassiveGrabbers();
+}
+
+/*!
+ Returns whether the given \a handler is the exclusive grabber of this event.
+*/
+bool QQuickPointerMouseEvent::hasExclusiveGrabber(const QQuickPointerHandler *handler) const
+{
+ return m_mousePoint->exclusiveGrabber() == handler;
}
bool QQuickPointerMouseEvent::isPressEvent() const
@@ -700,6 +1445,24 @@ bool QQuickPointerMouseEvent::isPressEvent() const
(me->buttons() & me->button()) == me->buttons());
}
+bool QQuickPointerMouseEvent::isDoubleClickEvent() const
+{
+ auto me = static_cast<QMouseEvent*>(m_event);
+ return (me->type() == QEvent::MouseButtonDblClick);
+}
+
+bool QQuickPointerMouseEvent::isUpdateEvent() const
+{
+ auto me = static_cast<QMouseEvent*>(m_event);
+ return me->type() == QEvent::MouseMove;
+}
+
+bool QQuickPointerMouseEvent::isReleaseEvent() const
+{
+ auto me = static_cast<QMouseEvent*>(m_event);
+ return me && me->type() == QEvent::MouseButtonRelease;
+}
+
bool QQuickPointerTouchEvent::allPointsAccepted() const {
for (int i = 0; i < m_pointCount; ++i) {
if (!m_touchPoints.at(i)->isAccepted())
@@ -708,12 +1471,32 @@ bool QQuickPointerTouchEvent::allPointsAccepted() const {
return true;
}
-QVector<QQuickItem *> QQuickPointerTouchEvent::grabbers() const
-{
- QVector<QQuickItem *> result;
+bool QQuickPointerTouchEvent::allUpdatedPointsAccepted() const {
for (int i = 0; i < m_pointCount; ++i) {
auto point = m_touchPoints.at(i);
- if (QQuickItem *grabber = point->grabber()) {
+ if (point->state() != QQuickEventPoint::Pressed && !point->isAccepted())
+ return false;
+ }
+ return true;
+}
+
+bool QQuickPointerTouchEvent::allPointsGrabbed() const
+{
+ for (int i = 0; i < m_pointCount; ++i) {
+ if (!m_touchPoints.at(i)->exclusiveGrabber())
+ return false;
+ }
+ return true;
+}
+
+/*!
+ Returns the exclusive grabbers of all points in this event, if any, in a vector.
+*/
+QVector<QObject *> QQuickPointerTouchEvent::exclusiveGrabbers() const
+{
+ QVector<QObject *> result;
+ for (int i = 0; i < m_pointCount; ++i) {
+ if (QObject *grabber = m_touchPoints.at(i)->exclusiveGrabber()) {
if (!result.contains(grabber))
result << grabber;
}
@@ -721,9 +1504,27 @@ QVector<QQuickItem *> QQuickPointerTouchEvent::grabbers() const
return result;
}
+/*!
+ Remove all passive and exclusive grabbers of all touchpoints in this event,
+ without notifying.
+*/
void QQuickPointerTouchEvent::clearGrabbers() const {
+ for (auto point: m_touchPoints) {
+ point->setGrabberItem(nullptr);
+ point->clearPassiveGrabbers();
+ }
+}
+
+/*!
+ Returns whether the given \a handler is the exclusive grabber of any
+ touchpoint within this event.
+*/
+bool QQuickPointerTouchEvent::hasExclusiveGrabber(const QQuickPointerHandler *handler) const
+{
for (auto point: m_touchPoints)
- point->setGrabber(nullptr);
+ if (point->exclusiveGrabber() == handler)
+ return true;
+ return false;
}
bool QQuickPointerTouchEvent::isPressEvent() const
@@ -731,12 +1532,22 @@ bool QQuickPointerTouchEvent::isPressEvent() const
return static_cast<QTouchEvent*>(m_event)->touchPointStates() & Qt::TouchPointPressed;
}
+bool QQuickPointerTouchEvent::isUpdateEvent() const
+{
+ return static_cast<QTouchEvent*>(m_event)->touchPointStates() & (Qt::TouchPointMoved | Qt::TouchPointStationary);
+}
+
+bool QQuickPointerTouchEvent::isReleaseEvent() const
+{
+ return static_cast<QTouchEvent*>(m_event)->touchPointStates() & Qt::TouchPointReleased;
+}
+
QVector<QPointF> QQuickPointerEvent::unacceptedPressedPointScenePositions() const
{
QVector<QPointF> points;
for (int i = 0; i < pointCount(); ++i) {
if (!point(i)->isAccepted() && point(i)->state() == QQuickEventPoint::Pressed)
- points << point(i)->scenePos();
+ points << point(i)->scenePosition();
}
return points;
}
@@ -788,20 +1599,78 @@ QMouseEvent *QQuickPointerTouchEvent::syntheticMouseEvent(int pointID, QQuickIte
}
/*!
+ Returns the exclusive grabber of this event, if any, in a vector.
+*/
+QVector<QObject *> QQuickPointerNativeGestureEvent::exclusiveGrabbers() const
+{
+ QVector<QObject *> result;
+ if (QObject *grabber = m_gesturePoint->exclusiveGrabber())
+ result << grabber;
+ return result;
+}
+
+/*!
+ Remove all passive and exclusive grabbers of this event, without notifying.
+*/
+void QQuickPointerNativeGestureEvent::clearGrabbers() const {
+ m_gesturePoint->setGrabberItem(nullptr);
+ m_gesturePoint->clearPassiveGrabbers();
+}
+
+/*!
+ Returns whether the given \a handler is the exclusive grabber of this event.
+*/
+bool QQuickPointerNativeGestureEvent::hasExclusiveGrabber(const QQuickPointerHandler *handler) const
+{
+ return m_gesturePoint->exclusiveGrabber() == handler;
+}
+
+bool QQuickPointerNativeGestureEvent::isPressEvent() const
+{
+ return type() == Qt::BeginNativeGesture;
+}
+
+bool QQuickPointerNativeGestureEvent::isUpdateEvent() const
+{
+ switch (type()) {
+ case Qt::BeginNativeGesture:
+ case Qt::EndNativeGesture:
+ return false;
+ default:
+ return true;
+ }
+}
+
+bool QQuickPointerNativeGestureEvent::isReleaseEvent() const
+{
+ return type() == Qt::EndNativeGesture;
+}
+
+Qt::NativeGestureType QQuickPointerNativeGestureEvent::type() const
+{
+ return static_cast<QNativeGestureEvent *>(m_event)->gestureType();
+}
+
+qreal QQuickPointerNativeGestureEvent::value() const
+{
+ return static_cast<QNativeGestureEvent *>(m_event)->value();
+}
+
+/*!
\internal
Returns a pointer to the QQuickEventPoint which has the \a pointId as
\l {QQuickEventPoint::pointId}{pointId}.
Returns nullptr if there is no point with that ID.
- \fn QQuickPointerEvent::pointById(quint64 pointId) const
+ \fn QQuickPointerEvent::pointById(int pointId) const
*/
-QQuickEventPoint *QQuickPointerMouseEvent::pointById(quint64 pointId) const {
+QQuickEventPoint *QQuickPointerMouseEvent::pointById(int pointId) const {
if (m_mousePoint && pointId == m_mousePoint->pointId())
return m_mousePoint;
return nullptr;
}
-QQuickEventPoint *QQuickPointerTouchEvent::pointById(quint64 pointId) const {
+QQuickEventPoint *QQuickPointerTouchEvent::pointById(int pointId) const {
auto it = std::find_if(m_touchPoints.constBegin(), m_touchPoints.constEnd(),
[pointId](const QQuickEventTouchPoint *tp) { return tp->pointId() == pointId; } );
if (it != m_touchPoints.constEnd())
@@ -809,6 +1678,12 @@ QQuickEventPoint *QQuickPointerTouchEvent::pointById(quint64 pointId) const {
return nullptr;
}
+QQuickEventPoint *QQuickPointerNativeGestureEvent::pointById(int pointId) const {
+ if (m_gesturePoint && pointId == m_gesturePoint->pointId())
+ return m_gesturePoint;
+ return nullptr;
+}
+
/*!
\internal
@@ -831,7 +1706,12 @@ const QTouchEvent::TouchPoint *QQuickPointerTouchEvent::touchPointById(int point
\internal
Make a new QTouchEvent, giving it a subset of the original touch points.
- Returns a nullptr if all points are stationary or there are no points inside the item.
+ Returns a nullptr if all points are stationary, or there are no points inside the item,
+ or none of the points were pressed inside and the item was not grabbing any of them
+ and isFiltering is false. When isFiltering is true, it is assumed that the item
+ cares about all points which are inside its bounds, because most filtering items
+ need to monitor eventpoint movements until a drag threshold is exceeded or the
+ requirements for a gesture to be recognized are met in some other way.
*/
QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(QQuickItem *item, bool isFiltering) const
{
@@ -841,19 +1721,24 @@ QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(QQuickItem *item, bool i
// Or else just document that velocity is always scene-relative and is not scaled and rotated with the item
// but that would require changing tst_qquickwindow::touchEvent_velocity(): it expects transformed velocity
+ bool anyPressOrReleaseInside = false;
+ bool anyGrabber = false;
QMatrix4x4 transformMatrix(QQuickItemPrivate::get(item)->windowToItemTransform());
for (int i = 0; i < m_pointCount; ++i) {
auto p = m_touchPoints.at(i);
if (p->isAccepted())
continue;
// include points where item is the grabber
- bool isGrabber = p->grabber() == item;
- // include newly pressed points inside the bounds
- bool isPressInside = p->state() == QQuickEventPoint::Pressed && item->contains(item->mapFromScene(p->scenePos()));
+ bool isGrabber = p->exclusiveGrabber() == item;
+ if (isGrabber)
+ anyGrabber = true;
+ // include points inside the bounds if no other item is the grabber or if the item is filtering
+ bool isInside = item->contains(item->mapFromScene(p->scenePosition()));
+ bool hasAnotherGrabber = p->exclusiveGrabber() && p->exclusiveGrabber() != item;
// filtering: (childMouseEventFilter) include points that are grabbed by children of the target item
bool grabberIsChild = false;
- auto parent = p->grabber();
+ auto parent = p->grabberItem();
while (isFiltering && parent) {
if (parent == item) {
grabberIsChild = true;
@@ -862,11 +1747,11 @@ QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(QQuickItem *item, bool i
parent = parent->parentItem();
}
- // when filtering, send points that are grabbed by a child and points that are not grabbed but inside
- bool filterRelevant = isFiltering && (grabberIsChild || (!p->grabber() && item->contains(item->mapFromScene(p->scenePos()))));
- if (!(isGrabber || isPressInside || filterRelevant))
+ bool filterRelevant = isFiltering && grabberIsChild;
+ if (!(isGrabber || (isInside && (!hasAnotherGrabber || isFiltering)) || filterRelevant))
continue;
-
+ if ((p->state() == QQuickEventPoint::Pressed || p->state() == QQuickEventPoint::Released) && isInside)
+ anyPressOrReleaseInside = true;
const QTouchEvent::TouchPoint *tp = touchPointById(p->pointId());
if (tp) {
eventStates |= tp->state();
@@ -880,7 +1765,9 @@ QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(QQuickItem *item, bool i
}
}
- if (eventStates == Qt::TouchPointStationary || touchPoints.isEmpty())
+ // Now touchPoints will have only points which are inside the item.
+ // But if none of them were just pressed inside, and the item has no other reason to care, ignore them anyway.
+ if (eventStates == Qt::TouchPointStationary || touchPoints.isEmpty() || (!anyPressOrReleaseInside && !anyGrabber && !isFiltering))
return nullptr;
// if all points have the same state, set the event type accordingly
@@ -915,6 +1802,19 @@ QTouchEvent *QQuickPointerTouchEvent::asTouchEvent() const
return static_cast<QTouchEvent *>(m_event);
}
+bool QQuickPointerNativeGestureEvent::allPointsAccepted() const {
+ return m_gesturePoint->isAccepted();
+}
+
+bool QQuickPointerNativeGestureEvent::allUpdatedPointsAccepted() const {
+ return m_gesturePoint->state() == QQuickEventPoint::Pressed || m_gesturePoint->isAccepted();
+}
+
+bool QQuickPointerNativeGestureEvent::allPointsGrabbed() const
+{
+ return m_gesturePoint->exclusiveGrabber() != nullptr;
+}
+
#ifndef QT_NO_DEBUG_STREAM
Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickPointerDevice *dev) {
@@ -942,7 +1842,9 @@ Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickPointerDevice *
Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickPointerEvent *event) {
QDebugStateSaver saver(dbg);
dbg.nospace();
- dbg << "QQuickPointerEvent(dev:";
+ dbg << "QQuickPointerEvent(";
+ dbg << event->timestamp();
+ dbg << " dev:";
QtDebugUtils::formatQEnum(dbg, event->device()->type());
if (event->buttons() != Qt::NoButton) {
dbg << " buttons:";
@@ -959,10 +1861,10 @@ Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickPointerEvent *e
Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickEventPoint *event) {
QDebugStateSaver saver(dbg);
dbg.nospace();
- dbg << "QQuickEventPoint(valid:" << event->isValid() << " accepted:" << event->isAccepted()
+ dbg << "QQuickEventPoint(accepted:" << event->isAccepted()
<< " state:";
QtDebugUtils::formatQEnum(dbg, event->state());
- dbg << " scenePos:" << event->scenePos() << " id:" << hex << event->pointId() << dec
+ dbg << " scenePos:" << event->scenePosition() << " id:" << hex << event->pointId() << dec
<< " timeHeld:" << event->timeHeld() << ')';
return dbg;
}