diff options
Diffstat (limited to 'src/quick/items')
-rw-r--r-- | src/quick/items/qquickevents.cpp | 549 | ||||
-rw-r--r-- | src/quick/items/qquickevents_p_p.h | 137 | ||||
-rw-r--r-- | src/quick/items/qquickflickable.cpp | 5 | ||||
-rw-r--r-- | src/quick/items/qquickitem.cpp | 63 | ||||
-rw-r--r-- | src/quick/items/qquickitem.h | 3 | ||||
-rw-r--r-- | src/quick/items/qquickitem_p.h | 5 | ||||
-rw-r--r-- | src/quick/items/qquickmousearea.cpp | 1 | ||||
-rw-r--r-- | src/quick/items/qquickmultipointtoucharea.cpp | 1 | ||||
-rw-r--r-- | src/quick/items/qquickpincharea.cpp | 1 | ||||
-rw-r--r-- | src/quick/items/qquickrectangle.cpp | 3 | ||||
-rw-r--r-- | src/quick/items/qquickwindow.cpp | 532 | ||||
-rw-r--r-- | src/quick/items/qquickwindow_p.h | 25 |
12 files changed, 1059 insertions, 266 deletions
diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 448b63c347..1e0d268f93 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,10 @@ ****************************************************************************/ #include "qquickevents_p_p.h" +#include <QtCore/qmap.h> #include <QtGui/private/qguiapplication_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> @@ -448,7 +450,15 @@ Item { typedef QHash<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,6 +467,22 @@ Q_GLOBAL_STATIC_WITH_ARGS(QQuickPointerDevice, g_genericMouseDevice, typedef QHash<qint64, QQuickPointerDevice *> PointerDeviceForDeviceIdHash; Q_GLOBAL_STATIC(PointerDeviceForDeviceIdHash, g_tabletDevices) +// 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(QTouchDevice *d) { if (g_touchDevices->contains(d)) @@ -505,37 +531,241 @@ QQuickPointerDevice *QQuickPointerDevice::tabletDevice(qint64 id) return nullptr; } -void QQuickEventPoint::reset(Qt::TouchPointState state, QPointF scenePos, quint64 pointId, ulong timestamp) +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(scenePos()); + 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); + } +} + +/*! + 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_grabberIsHandler ? static_cast<QQuickPointerHandler *>(m_exclusiveGrabber.data()) : nullptr); +} + +/*! + 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())) { + 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); + } + } +} + +/*! + 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); + } } -QQuickItem *QQuickEventPoint::grabber() const +/*! + 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_grabber.data(); + return m_passiveGrabbers.removeOne(handler); } -void QQuickEventPoint::setGrabber(QQuickItem *grabber) +/*! + 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 (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 (m_exclusiveGrabber == handler) { + handler->onGrabChanged(handler, CancelGrabExclusive, this); + m_exclusiveGrabber.clear(); } - m_grabber = QPointer<QQuickItem>(grabber); + cancelPassiveGrab(handler); } +/*! + Set this point as \a accepted (true) or rejected (false). + Accepting a point is intended to stop event propagation. + It does not imply any kind of grab, passive or exclusive. + TODO explain further under what conditions propagation really does stop... +*/ void QQuickEventPoint::setAccepted(bool accepted) { if (m_accept != accepted) { @@ -550,12 +780,74 @@ QQuickEventTouchPoint::QQuickEventTouchPoint(QQuickPointerTouchEvent *parent) 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); + } + if (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; + } + return QVector2D(); +} + /*! \internal \class QQuickPointerEvent @@ -581,11 +873,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 +893,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 +910,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 +921,63 @@ 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->scenePressPos(); + preserves[i].sceneGrabPos = point->sceneGrabPos(); + 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); +} + QQuickEventPoint *QQuickPointerMouseEvent::point(int i) const { if (i == 0) return m_mousePoint; @@ -659,8 +991,8 @@ QQuickEventPoint *QQuickPointerTouchEvent::point(int i) const { } 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 +1006,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 +1022,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 +1056,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->type() == QEvent::MouseButtonRelease; +} + bool QQuickPointerTouchEvent::allPointsAccepted() const { for (int i = 0; i < m_pointCount; ++i) { if (!m_touchPoints.at(i)->isAccepted()) @@ -708,12 +1082,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 +1115,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,6 +1143,16 @@ 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; @@ -793,15 +1215,15 @@ QMouseEvent *QQuickPointerTouchEvent::syntheticMouseEvent(int pointID, QQuickIte \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()) @@ -831,7 +1253,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 +1268,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->scenePos())); + 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 +1294,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 +1312,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 @@ -942,7 +1376,12 @@ 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("; + if (event->isValid()) + dbg << event->timestamp(); + else + dbg << "invalid"; + dbg << " dev:"; QtDebugUtils::formatQEnum(dbg, event->device()->type()); if (event->buttons() != Qt::NoButton) { dbg << " buttons:"; @@ -959,7 +1398,7 @@ 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 diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 3735d68a85..0d6e06ac41 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -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. @@ -68,6 +68,7 @@ class QQuickPointerEvent; class QQuickPointerMouseEvent; class QQuickPointerTabletEvent; class QQuickPointerTouchEvent; +class QQuickPointerHandler; class QQuickKeyEvent : public QObject { @@ -251,12 +252,17 @@ private: class Q_QUICK_PRIVATE_EXPORT QQuickEventPoint : public QObject { Q_OBJECT + Q_PROPERTY(QQuickPointerEvent *event READ pointerEvent) + Q_PROPERTY(QPointF pos READ pos) Q_PROPERTY(QPointF scenePos READ scenePos) + Q_PROPERTY(QPointF scenePressPos READ scenePressPos) + Q_PROPERTY(QPointF sceneGrabPos READ sceneGrabPos) Q_PROPERTY(State state READ state) - Q_PROPERTY(quint64 pointId READ pointId) + Q_PROPERTY(int pointId READ pointId) Q_PROPERTY(qreal timeHeld READ timeHeld) + Q_PROPERTY(QVector2D velocity READ velocity) Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted) - Q_PROPERTY(QQuickItem *grabber READ grabber WRITE setGrabber) + Q_PROPERTY(QObject *exclusiveGrabber READ exclusiveGrabber WRITE setExclusiveGrabber) public: enum State { @@ -266,35 +272,79 @@ public: Released = Qt::TouchPointReleased // Canceled = Qt::TouchPointReleased << 1 // 0x10 // TODO maybe }; - Q_ENUM(State) + Q_DECLARE_FLAGS(States, State) + Q_FLAG(States) + + enum GrabState { + GrabPassive = 0x01, + UngrabPassive = 0x02, + CancelGrabPassive = 0x03, + OverrideGrabPassive = 0x04, + GrabExclusive = 0x10, + UngrabExclusive = 0x20, + CancelGrabExclusive = 0x30, + }; + Q_ENUM(GrabState) QQuickEventPoint(QQuickPointerEvent *parent); - void reset(Qt::TouchPointState state, QPointF scenePos, quint64 pointId, ulong timestamp); - - void invalidate() { m_valid = false; } + void reset(Qt::TouchPointState state, const QPointF &scenePos, int pointId, ulong timestamp, const QVector2D &velocity = QVector2D()); + void localizePosition(QQuickItem *target); QQuickPointerEvent *pointerEvent() const; + QPointF pos() const { return m_pos; } QPointF scenePos() const { return m_scenePos; } + QPointF scenePressPos() const { return m_scenePressPos; } + QPointF sceneGrabPos() const { return m_sceneGrabPos; } + QVector2D velocity() const { return m_velocity; } State state() const { return m_state; } - quint64 pointId() const { return m_pointId; } - bool isValid() const { return m_valid; } + int pointId() const { return m_pointId; } qreal timeHeld() const { return (m_timestamp - m_pressTimestamp) / 1000.0; } bool isAccepted() const { return m_accept; } void setAccepted(bool accepted = true); - QQuickItem *grabber() const; - void setGrabber(QQuickItem *grabber); + QObject *exclusiveGrabber() const; + void setExclusiveGrabber(QObject *exclusiveGrabber); + + QQuickItem *grabberItem() const; + Q_DECL_DEPRECATED QQuickItem *grabber() const { return grabberItem(); } + void setGrabberItem(QQuickItem *exclusiveGrabber); + + QQuickPointerHandler *grabberPointerHandler() const; + void setGrabberPointerHandler(QQuickPointerHandler *exclusiveGrabber, bool exclusive = false); + + void cancelExclusiveGrab(); + void cancelPassiveGrab(QQuickPointerHandler *handler); + bool removePassiveGrabber(QQuickPointerHandler *handler); + void cancelAllGrabs(QQuickPointerHandler *handler); + + QVector<QPointer <QQuickPointerHandler> > passiveGrabbers() const { return m_passiveGrabbers; } + void setPassiveGrabbers(const QVector<QPointer <QQuickPointerHandler> > &grabbers) { m_passiveGrabbers = grabbers; } + void clearPassiveGrabbers() { m_passiveGrabbers.clear(); } + +protected: + void cancelExclusiveGrabImpl(QTouchEvent *cancelEvent = nullptr); private: + QVector2D estimatedVelocity() const; + +protected: + QPointF m_pos; QPointF m_scenePos; - quint64 m_pointId; - QPointer<QQuickItem> m_grabber; + QPointF m_scenePressPos; + QPointF m_sceneGrabPos; + QVector2D m_velocity; + int m_pointId; + QPointer<QObject> m_exclusiveGrabber; + QVector<QPointer <QQuickPointerHandler> > m_passiveGrabbers; ulong m_timestamp; ulong m_pressTimestamp; State m_state; - bool m_valid : 1; bool m_accept : 1; - int m_reserved : 30; + bool m_grabberIsHandler : 1; + int m_reserved : 29; + + friend class QQuickPointerTouchEvent; + friend class QQuickWindowPrivate; Q_DISABLE_COPY(QQuickEventPoint) }; @@ -304,6 +354,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickEventTouchPoint : public QQuickEventPoint Q_OBJECT Q_PROPERTY(qreal rotation READ rotation) Q_PROPERTY(qreal pressure READ pressure) + Q_PROPERTY(QSizeF ellipseDiameters READ ellipseDiameters) Q_PROPERTY(QPointingDeviceUniqueId uniqueId READ uniqueId) public: @@ -313,13 +364,17 @@ public: qreal rotation() const { return m_rotation; } qreal pressure() const { return m_pressure; } + QSizeF ellipseDiameters() const { return m_ellipseDiameters; } QPointingDeviceUniqueId uniqueId() const { return m_uniqueId; } private: qreal m_rotation; qreal m_pressure; + QSizeF m_ellipseDiameters; QPointingDeviceUniqueId m_uniqueId; + friend class QQuickPointerTouchEvent; + Q_DISABLE_COPY(QQuickEventTouchPoint) }; @@ -350,8 +405,12 @@ public: // property accessors public: // helpers for C++ only (during event delivery) virtual QQuickPointerEvent *reset(QEvent *ev) = 0; + virtual void localize(QQuickItem *target) = 0; virtual bool isPressEvent() const = 0; + virtual bool isDoubleClickEvent() const { return false; } + virtual bool isUpdateEvent() const = 0; + virtual bool isReleaseEvent() const = 0; virtual QQuickPointerMouseEvent *asPointerMouseEvent() { return nullptr; } virtual QQuickPointerTouchEvent *asPointerTouchEvent() { return nullptr; } virtual QQuickPointerTabletEvent *asPointerTabletEvent() { return nullptr; } @@ -360,15 +419,18 @@ public: // helpers for C++ only (during event delivery) virtual const QQuickPointerTabletEvent *asPointerTabletEvent() const { return nullptr; } bool isValid() const { return m_event != nullptr; } virtual bool allPointsAccepted() const = 0; + virtual bool allUpdatedPointsAccepted() const = 0; + virtual bool allPointsGrabbed() const = 0; bool isAccepted() { return m_event->isAccepted(); } void setAccepted(bool accepted) { m_event->setAccepted(accepted); } QVector<QPointF> unacceptedPressedPointScenePositions() const; virtual int pointCount() const = 0; virtual QQuickEventPoint *point(int i) const = 0; - virtual QQuickEventPoint *pointById(quint64 pointId) const = 0; - virtual QVector<QQuickItem *> grabbers() const = 0; + virtual QQuickEventPoint *pointById(int pointId) const = 0; + virtual QVector<QObject *> exclusiveGrabbers() const = 0; virtual void clearGrabbers() const = 0; + virtual bool hasExclusiveGrabber(const QQuickPointerHandler *handler) const = 0; ulong timestamp() const { return m_event->timestamp(); } @@ -389,15 +451,22 @@ public: : QQuickPointerEvent(parent, device), m_mousePoint(new QQuickEventPoint(this)) { } QQuickPointerEvent *reset(QEvent *) override; + void localize(QQuickItem *target) override; bool isPressEvent() const override; + bool isDoubleClickEvent() const override; + bool isUpdateEvent() const override; + bool isReleaseEvent() const override; QQuickPointerMouseEvent *asPointerMouseEvent() override { return this; } const QQuickPointerMouseEvent *asPointerMouseEvent() const override { return this; } int pointCount() const override { return 1; } QQuickEventPoint *point(int i) const override; - QQuickEventPoint *pointById(quint64 pointId) const override; + QQuickEventPoint *pointById(int pointId) const override; bool allPointsAccepted() const override; - QVector<QQuickItem *> grabbers() const override; + bool allUpdatedPointsAccepted() const override; + bool allPointsGrabbed() const override; + QVector<QObject *> exclusiveGrabbers() const override; void clearGrabbers() const override; + bool hasExclusiveGrabber(const QQuickPointerHandler *handler) const override; QMouseEvent *asMouseEvent(const QPointF& localPos) const; @@ -418,16 +487,22 @@ public: { } QQuickPointerEvent *reset(QEvent *) override; + void localize(QQuickItem *target) override; bool isPressEvent() const override; + bool isUpdateEvent() const override; + bool isReleaseEvent() const override; QQuickPointerTouchEvent *asPointerTouchEvent() override { return this; } const QQuickPointerTouchEvent *asPointerTouchEvent() const override { return this; } int pointCount() const override { return m_pointCount; } QQuickEventPoint *point(int i) const override; - QQuickEventPoint *pointById(quint64 pointId) const override; + QQuickEventPoint *pointById(int pointId) const override; const QTouchEvent::TouchPoint *touchPointById(int pointId) const; bool allPointsAccepted() const override; - QVector<QQuickItem *> grabbers() const override; + bool allUpdatedPointsAccepted() const override; + bool allPointsGrabbed() const override; + QVector<QObject *> exclusiveGrabbers() const override; void clearGrabbers() const override; + bool hasExclusiveGrabber(const QQuickPointerHandler *handler) const override; QMouseEvent *syntheticMouseEvent(int pointID, QQuickItem *relativeTo) const; QTouchEvent *touchEventForItem(QQuickItem *item, bool isFiltering = false) const; @@ -497,13 +572,6 @@ public: Q_ENUM(CapabilityFlag) Q_FLAG(Capabilities) - QQuickPointerDevice(DeviceType devType, PointerType pType, Capabilities caps, int maxPoints, int buttonCount, const QString &name, qint64 uniqueId = 0) - : m_deviceType(devType), m_pointerType(pType), m_capabilities(caps) - , m_maximumTouchPoints(maxPoints), m_buttonCount(buttonCount), m_name(name) - , m_uniqueId(QPointingDeviceUniqueId::fromNumericId(uniqueId)) - { - } - DeviceType type() const { return m_deviceType; } PointerType pointerType() const { return m_pointerType; } Capabilities capabilities() const { return m_capabilities; } @@ -518,6 +586,17 @@ public: static QQuickPointerDevice *genericMouseDevice(); static QQuickPointerDevice *tabletDevice(qint64); + QVector<QQuickPointerHandler *> &eventDeliveryTargets() { return m_eventDeliveryTargets; } + +private: + QQuickPointerDevice(DeviceType devType, PointerType pType, Capabilities caps, int maxPoints, int buttonCount, const QString &name, qint64 uniqueId = 0) + : m_deviceType(devType), m_pointerType(pType), m_capabilities(caps) + , m_maximumTouchPoints(maxPoints), m_buttonCount(buttonCount), m_name(name) + , m_uniqueId(QPointingDeviceUniqueId::fromNumericId(uniqueId)) + { + } + ~QQuickPointerDevice() { } + private: DeviceType m_deviceType; PointerType m_pointerType; @@ -526,8 +605,10 @@ private: int m_buttonCount; QString m_name; QPointingDeviceUniqueId m_uniqueId; + QVector<QQuickPointerHandler *> m_eventDeliveryTargets; // during delivery, handlers which have already seen the event Q_DISABLE_COPY(QQuickPointerDevice) + friend struct ConstructableQQuickPointerDevice; }; Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickPointerDevice::DeviceTypes) diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp index a3bc7635a1..ce584cd283 100644 --- a/src/quick/items/qquickflickable.cpp +++ b/src/quick/items/qquickflickable.cpp @@ -44,6 +44,7 @@ #include "qquickwindow_p.h" #include "qquickevents_p_p.h" +#include <QtQuick/private/qquickpointerhandler_p.h> #include <QtQuick/private/qquicktransition_p.h> #include <private/qqmlglobal_p.h> @@ -269,6 +270,7 @@ void QQuickFlickablePrivate::init() qmlobject_connect(&velocityTimeline, QQuickTimeLine, SIGNAL(completed()), q, QQuickFlickable, SLOT(velocityTimelineCompleted())) q->setAcceptedMouseButtons(Qt::LeftButton); + q->setAcceptTouchEvents(false); // rely on mouse events synthesized from touch q->setFiltersChildMouseEvents(true); QQuickItemPrivate *viewportPrivate = QQuickItemPrivate::get(contentItem); viewportPrivate->addItemChangeListener(this, QQuickItemPrivate::Geometry); @@ -1547,6 +1549,8 @@ void QQuickFlickablePrivate::replayDelayedPress() // If we have the grab, release before delivering the event if (QQuickWindow *w = q->window()) { + QQuickWindowPrivate *wpriv = QQuickWindowPrivate::get(w); + wpriv->allowChildEventFiltering = false; // don't allow re-filtering during replay replayingPressEvent = true; if (w->mouseGrabberItem() == q) q->ungrabMouse(); @@ -1554,6 +1558,7 @@ void QQuickFlickablePrivate::replayDelayedPress() // Use the event handler that will take care of finding the proper item to propagate the event QCoreApplication::sendEvent(w, mouseEvent.data()); replayingPressEvent = false; + wpriv->allowChildEventFiltering = true; } } } diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 1e71c20ff3..9f2d543387 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -67,6 +67,7 @@ #include <QtQuick/private/qquickstate_p.h> #include <private/qquickitem_p.h> #include <QtQuick/private/qquickaccessibleattached_p.h> +#include <QtQuick/private/qquickpointerhandler_p.h> #include <private/qv4engine_p.h> #include <private/qv4object_p.h> @@ -3189,6 +3190,11 @@ QQuickItemPrivate::QQuickItemPrivate() , antialiasingValid(false) , isTabFence(false) , replayingPressEvent(false) +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + , touchEnabled(true) +#else + , touchEnabled(false) +#endif , dirtyAttributes(0) , nextDirtyItem(0) , prevDirtyItem(0) @@ -3240,7 +3246,14 @@ void QQuickItemPrivate::data_append(QQmlListProperty<QObject> *prop, QObject *o) } else { if (o->inherits("QGraphicsItem")) qWarning("Cannot add a QtQuick 1.0 item (%s) into a QtQuick 2.0 scene!", o->metaObject()->className()); - else { + else if (QQuickPointerHandler *pointerHandler = qmlobject_cast<QQuickPointerHandler *>(o)) { + Q_ASSERT(pointerHandler->parentItem() == that); + // Accept all buttons, and leave filtering to pointerEvent() and/or user JS, + // because there can be multiple handlers... + that->setAcceptedMouseButtons(Qt::AllButtons); + QQuickItemPrivate *p = QQuickItemPrivate::get(that); + p->extra.value().pointerHandlers.append(pointerHandler); + } else { QQuickWindow *thisWindow = qmlobject_cast<QQuickWindow *>(o); QQuickItem *item = that; QQuickWindow *itemWindow = that->window(); @@ -5105,6 +5118,28 @@ void QQuickItemPrivate::deliverShortcutOverrideEvent(QKeyEvent *event) } /*! + \internal + Deliver the \a event to all PointerHandlers which are in the pre-determined + eventDeliveryTargets() vector. If \a avoidExclusiveGrabber is true, it skips + delivery to any handler which is the exclusive grabber of any point within this event + (because delivery to exclusive grabbers is handled separately). +*/ +bool QQuickItemPrivate::handlePointerEvent(QQuickPointerEvent *event, bool avoidExclusiveGrabber) +{ + bool delivered = false; + QVector<QQuickPointerHandler *> &eventDeliveryTargets = event->device()->eventDeliveryTargets(); + if (extra.isAllocated()) { + for (QQuickPointerHandler *handler : extra->pointerHandlers) { + if ((!avoidExclusiveGrabber || !event->hasExclusiveGrabber(handler)) && !eventDeliveryTargets.contains(handler)) { + handler->handlePointerEvent(event); + delivered = true; + } + } + } + return delivered; +} + +/*! Called when \a change occurs for this item. \a value contains extra information relating to the change, when @@ -7189,6 +7224,32 @@ void QQuickItem::setAcceptHoverEvents(bool enabled) d->setHasHoverInChild(enabled); } +/*! + Returns whether touch events are accepted by this item. + + The default value is false. + + If this is false, then the item will not receive any touch events through + the touchEvent() function. +*/ +bool QQuickItem::acceptTouchEvents() const +{ + Q_D(const QQuickItem); + return d->touchEnabled; +} + +/*! + If \a enabled is true, this sets the item to accept touch events; + otherwise, touch events are not accepted by this item. + + \sa acceptTouchEvents() +*/ +void QQuickItem::setAcceptTouchEvents(bool accept) +{ + Q_D(QQuickItem); + d->touchEnabled = accept; +} + void QQuickItemPrivate::setHasCursorInChild(bool hasCursor) { #if QT_CONFIG(cursor) diff --git a/src/quick/items/qquickitem.h b/src/quick/items/qquickitem.h index 4a832bbf6f..25641f16f9 100644 --- a/src/quick/items/qquickitem.h +++ b/src/quick/items/qquickitem.h @@ -293,6 +293,8 @@ public: void setAcceptedMouseButtons(Qt::MouseButtons buttons); bool acceptHoverEvents() const; void setAcceptHoverEvents(bool enabled); + bool acceptTouchEvents() const; + void setAcceptTouchEvents(bool accept); #if QT_CONFIG(cursor) QCursor cursor() const; @@ -449,6 +451,7 @@ private: Q_PRIVATE_SLOT(d_func(), void _q_resourceObjectDeleted(QObject *)) Q_PRIVATE_SLOT(d_func(), quint64 _q_createJSWrapper(QV4::ExecutionEngine *)) + friend class QQuickEventPoint; friend class QQuickWindow; friend class QQuickWindowPrivate; friend class QSGRenderer; diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h index 74d70da2bc..d1aaf6026b 100644 --- a/src/quick/items/qquickitem_p.h +++ b/src/quick/items/qquickitem_p.h @@ -87,6 +87,7 @@ class QQuickItemKeyFilter; class QQuickLayoutMirroringAttached; class QQuickEnterKeyAttached; class QQuickScreenAttached; +class QQuickPointerHandler; class QQuickContents : public QQuickItemChangeListener { @@ -349,6 +350,7 @@ public: QQuickLayoutMirroringAttached* layoutDirectionAttached; QQuickEnterKeyAttached *enterKeyAttached; QQuickItemKeyFilter *keyHandler; + QVector<QQuickPointerHandler *> pointerHandlers; #if QT_CONFIG(quick_shadereffect) mutable QQuickItemLayer *layer; #endif @@ -438,6 +440,7 @@ public: // focus chain and prevents tabbing outside. bool isTabFence:1; bool replayingPressEvent:1; + bool touchEnabled:1; enum DirtyType { TransformOrigin = 0x00000001, @@ -566,6 +569,8 @@ public: #endif void deliverShortcutOverrideEvent(QKeyEvent *); + virtual bool handlePointerEvent(QQuickPointerEvent *, bool avoidExclusiveGrabber = false); + bool isTransparentForPositioner() const; void setTransparentForPositioner(bool trans); diff --git a/src/quick/items/qquickmousearea.cpp b/src/quick/items/qquickmousearea.cpp index d8bad7d793..96f34ef276 100644 --- a/src/quick/items/qquickmousearea.cpp +++ b/src/quick/items/qquickmousearea.cpp @@ -85,6 +85,7 @@ void QQuickMouseAreaPrivate::init() { Q_Q(QQuickMouseArea); q->setAcceptedMouseButtons(Qt::LeftButton); + q->setAcceptTouchEvents(false); // rely on mouse events synthesized from touch q->setFiltersChildMouseEvents(true); if (qmlVisualTouchDebugging()) { q->setFlag(QQuickItem::ItemHasContents); diff --git a/src/quick/items/qquickmultipointtoucharea.cpp b/src/quick/items/qquickmultipointtoucharea.cpp index d4c447a384..54136b1bbf 100644 --- a/src/quick/items/qquickmultipointtoucharea.cpp +++ b/src/quick/items/qquickmultipointtoucharea.cpp @@ -418,6 +418,7 @@ QQuickMultiPointTouchArea::QQuickMultiPointTouchArea(QQuickItem *parent) if (qmlVisualTouchDebugging()) { setFlag(QQuickItem::ItemHasContents); } + setAcceptTouchEvents(true); #ifdef Q_OS_OSX setAcceptHoverEvents(true); // needed to enable touch events on mouse hover. #endif diff --git a/src/quick/items/qquickpincharea.cpp b/src/quick/items/qquickpincharea.cpp index 3f6ce7b8ba..476acd3a3e 100644 --- a/src/quick/items/qquickpincharea.cpp +++ b/src/quick/items/qquickpincharea.cpp @@ -288,6 +288,7 @@ QQuickPinchArea::QQuickPinchArea(QQuickItem *parent) { Q_D(QQuickPinchArea); d->init(); + setAcceptTouchEvents(true); #ifdef Q_OS_OSX setAcceptHoverEvents(true); // needed to enable touch events on mouse hover. #endif diff --git a/src/quick/items/qquickrectangle.cpp b/src/quick/items/qquickrectangle.cpp index 65284eb532..9308553a79 100644 --- a/src/quick/items/qquickrectangle.cpp +++ b/src/quick/items/qquickrectangle.cpp @@ -325,6 +325,9 @@ QQuickRectangle::QQuickRectangle(QQuickItem *parent) : QQuickItem(*(new QQuickRectanglePrivate), parent) { setFlag(ItemHasContents); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + setAcceptTouchEvents(false); +#endif } void QQuickRectangle::doUpdate() diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index ce1016579c..e2dcff7770 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.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. @@ -46,6 +46,7 @@ #include "qquickevents_p_p.h" #include <private/qquickdrag_p.h> +#include <private/qquickpointerhandler_p.h> #include <QtQuick/private/qsgrenderer_p.h> #include <QtQuick/private/qsgtexture_p.h> @@ -499,6 +500,8 @@ QQuickWindowPrivate::QQuickWindowPrivate() , persistentSceneGraph(true) , lastWheelEventAccepted(false) , componentCompleted(true) + , allowChildEventFiltering(true) + , allowDoubleClick(true) , lastFocusReason(Qt::OtherFocusReason) , renderTarget(0) , renderTargetId(0) @@ -652,7 +655,7 @@ bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QQuickPointerEve if (!item->contains(pos)) break; - qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << p.id() << "->" << item; + qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << hex << p.id() << "->" << item; QScopedPointer<QMouseEvent> mousePress(touchToMouseEvent(QEvent::MouseButtonPress, p, event.data(), item, false)); // Send a single press and see if that's accepted @@ -664,7 +667,7 @@ bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QQuickPointerEve if (!q->mouseGrabberItem()) item->grabMouse(); auto pointerEventPoint = pointerEvent->pointById(p.id()); - pointerEventPoint->setGrabber(item); + pointerEventPoint->setGrabberItem(item); if (checkIfDoubleClicked(event->timestamp())) { QScopedPointer<QMouseEvent> mouseDoubleClick(touchToMouseEvent(QEvent::MouseButtonDblClick, p, event.data(), item, false)); @@ -688,7 +691,7 @@ bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QQuickPointerEve QCoreApplication::sendEvent(item, me.data()); event->setAccepted(me->isAccepted()); if (me->isAccepted()) { - qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << p.id() << "->" << mouseGrabberItem; + qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << hex << p.id() << "->" << mouseGrabberItem; } return event->isAccepted(); } else { @@ -744,39 +747,42 @@ void QQuickWindowPrivate::setMouseGrabber(QQuickItem *grabber) if (q->mouseGrabberItem() == grabber) return; - qCDebug(DBG_MOUSE_TARGET) << "grabber" << q->mouseGrabberItem() << "->" << grabber; QQuickItem *oldGrabber = q->mouseGrabberItem(); - bool fromTouch = false; + qCDebug(DBG_MOUSE_TARGET) << "grabber" << oldGrabber << "->" << grabber; if (grabber && touchMouseId != -1 && touchMouseDevice) { // update the touch item for mouse touch id to the new grabber qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << hex << touchMouseId << "->" << q->mouseGrabberItem(); auto point = pointerEventInstance(touchMouseDevice)->pointById(touchMouseId); - if (point) - point->setGrabber(grabber); - fromTouch = true; + if (point) { + auto originalEvent = pointerEventInstance(point->pointerEvent()->device()); + for (int i = 0; i < originalEvent->pointCount(); ++i) + originalEvent->point(i)->cancelExclusiveGrab(); + point->setGrabberItem(grabber); + for (auto handler : point->passiveGrabbers()) + point->cancelPassiveGrab(handler); + } } else { QQuickPointerEvent *event = pointerEventInstance(QQuickPointerDevice::genericMouseDevice()); Q_ASSERT(event->pointCount() == 1); - event->point(0)->setGrabber(grabber); + auto point = event->point(0); + point->setGrabberItem(grabber); + for (auto handler : point->passiveGrabbers()) + point->cancelPassiveGrab(handler); } + if (oldGrabber) { QEvent e(QEvent::UngrabMouse); QSet<QQuickItem *> hasFiltered; - if (!sendFilteredMouseEvent(oldGrabber->parentItem(), oldGrabber, &e, &hasFiltered)) { + if (!sendFilteredMouseEvent(oldGrabber->parentItem(), oldGrabber, &e, &hasFiltered)) oldGrabber->mouseUngrabEvent(); - if (fromTouch) - oldGrabber->touchUngrabEvent(); - } } } -void QQuickWindowPrivate::grabTouchPoints(QQuickItem *grabber, const QVector<int> &ids) +void QQuickWindowPrivate::grabTouchPoints(QObject *grabber, const QVector<int> &ids) { - QSet<QQuickItem*> ungrab; for (int i = 0; i < ids.count(); ++i) { - // FIXME: deprecate this function, we need a device int id = ids.at(i); if (Q_UNLIKELY(id < 0)) { qWarning("ignoring grab of touchpoint %d", id); @@ -784,11 +790,11 @@ void QQuickWindowPrivate::grabTouchPoints(QQuickItem *grabber, const QVector<int } if (id == touchMouseId) { auto point = pointerEventInstance(touchMouseDevice)->pointById(id); - auto touchMouseGrabber = point->grabber(); + auto touchMouseGrabber = point->grabberItem(); if (touchMouseGrabber) { - point->setGrabber(nullptr); + point->setExclusiveGrabber(nullptr); touchMouseGrabber->mouseUngrabEvent(); - ungrab.insert(touchMouseGrabber); + touchMouseGrabber->touchUngrabEvent(); touchMouseDevice = nullptr; touchMouseId = -1; } @@ -800,19 +806,23 @@ void QQuickWindowPrivate::grabTouchPoints(QQuickItem *grabber, const QVector<int auto point = pointerEventInstance(device)->pointById(id); if (!point) continue; - QQuickItem *oldGrabber = point->grabber(); + QObject *oldGrabber = point->exclusiveGrabber(); if (oldGrabber == grabber) continue; - - point->setGrabber(grabber); - if (oldGrabber) - ungrab.insert(oldGrabber); + point->setExclusiveGrabber(grabber); } } - for (QQuickItem *oldGrabber : qAsConst(ungrab)) - oldGrabber->touchUngrabEvent(); } +/*! + Ungrabs all touchpoint grabs and/or the mouse grab from the given item \a grabber. + This should not be called when processing a release event - that's redundant. + It is called in other cases, when the points may not be released, but the item + nevertheless must lose its grab due to becoming disabled, invisible, etc. + QQuickEventPoint::setGrabberItem() calls touchUngrabEvent() when all points are released, + but if not all points are released, it cannot be sure whether to call touchUngrabEvent() + or not; so we have to do it here. +*/ void QQuickWindowPrivate::removeGrabber(QQuickItem *grabber, bool mouse, bool touch) { Q_Q(QQuickWindow); @@ -821,17 +831,19 @@ void QQuickWindowPrivate::removeGrabber(QQuickItem *grabber, bool mouse, bool to setMouseGrabber(nullptr); } if (Q_LIKELY(touch)) { + bool ungrab = false; const auto touchDevices = QQuickPointerDevice::touchDevices(); for (auto device : touchDevices) { auto pointerEvent = pointerEventInstance(device); for (int i = 0; i < pointerEvent->pointCount(); ++i) { - if (pointerEvent->point(i)->grabber() == grabber) { - pointerEvent->point(i)->setGrabber(nullptr); - // FIXME send ungrab event only once - grabber->touchUngrabEvent(); + if (pointerEvent->point(i)->exclusiveGrabber() == grabber) { + pointerEvent->point(i)->setGrabberItem(nullptr); + ungrab = true; } } } + if (ungrab) + grabber->touchUngrabEvent(); } } @@ -1495,12 +1507,12 @@ QQuickItem *QQuickWindow::mouseGrabberItem() const QQuickPointerEvent *event = d->pointerEventInstance(d->touchMouseDevice); auto point = event->pointById(d->touchMouseId); Q_ASSERT(point); - return point->grabber(); + return point->grabberItem(); } QQuickPointerEvent *event = d->pointerEventInstance(QQuickPointerDevice::genericMouseDevice()); Q_ASSERT(event->pointCount()); - return event->point(0)->grabber(); + return event->point(0)->grabberItem(); } @@ -1649,36 +1661,75 @@ QMouseEvent *QQuickWindowPrivate::cloneMouseEvent(QMouseEvent *event, QPointF *t void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEvent) { - Q_Q(QQuickWindow); auto point = pointerEvent->point(0); lastMousePosition = point->scenePos(); - QQuickItem *grabber = point->grabber(); - if (grabber) { - // if the update consists of changing button state, then don't accept it - // unless the button is one in which the item is interested - if (pointerEvent->button() != Qt::NoButton - && grabber->acceptedMouseButtons() - && !(grabber->acceptedMouseButtons() & pointerEvent->button())) { - pointerEvent->setAccepted(false); - return; - } - // send update - QPointF localPos = grabber->mapFromScene(lastMousePosition); - auto me = pointerEvent->asMouseEvent(localPos); - me->accept(); - q->sendEvent(grabber, me); - point->setAccepted(me->isAccepted()); + if (point->exclusiveGrabber()) { + bool mouseIsReleased = (point->state() == QQuickEventPoint::Released && pointerEvent->buttons() == Qt::NoButton); + if (auto grabber = point->grabberItem()) { + if (sendFilteredPointerEvent(pointerEvent, grabber)) + return; + // if the grabber is an Item: + // if the update consists of changing button state, don't accept it unless + // the button is one in which the grabber is interested + if (pointerEvent->button() != Qt::NoButton && grabber->acceptedMouseButtons() + && !(grabber->acceptedMouseButtons() & pointerEvent->button())) { + pointerEvent->setAccepted(false); + return; + } + + // send update + QPointF localPos = grabber->mapFromScene(lastMousePosition); + auto me = pointerEvent->asMouseEvent(localPos); + me->accept(); + QCoreApplication::sendEvent(grabber, me); + point->setAccepted(me->isAccepted()); - // release event, make sure to ungrab if there still is a grabber - if (me->type() == QEvent::MouseButtonRelease && !me->buttons() && q->mouseGrabberItem()) - q->mouseGrabberItem()->ungrabMouse(); + // release event: ungrab if no buttons are pressed anymore + if (mouseIsReleased) + setMouseGrabber(nullptr); + } else { + // if the grabber is not an Item, it must be a PointerHandler + auto handler = point->grabberPointerHandler(); + pointerEvent->localize(handler->parentItem()); + if (!sendFilteredPointerEvent(pointerEvent, handler->parentItem())) + handler->handlePointerEvent(pointerEvent); + if (mouseIsReleased) { + point->setGrabberPointerHandler(nullptr, true); + point->clearPassiveGrabbers(); + } + } } else { - // send initial press bool delivered = false; if (pointerEvent->isPressEvent()) { - QSet<QQuickItem*> hasFiltered; - delivered = deliverPressEvent(pointerEvent, &hasFiltered); + // send initial press + delivered = deliverPressOrReleaseEvent(pointerEvent); + } else if (pointerEvent->device()->type() == QQuickPointerDevice::Mouse) { + // if this is an update or release from an actual mouse, + // and the point wasn't grabbed, deliver only to PointerHandlers: + // passive grabbers first, then the rest + const QVector<QQuickPointerHandler *> &eventDeliveryTargets = pointerEvent->device()->eventDeliveryTargets(); + for (auto handler : point->passiveGrabbers()) { + // a null pointer in passiveGrabbers is unlikely, unless the grabbing handler was deleted dynamically + if (Q_LIKELY(handler) && !eventDeliveryTargets.contains(handler)) { + if (!sendFilteredPointerEvent(pointerEvent, handler->parentItem())) + handler->handlePointerEvent(pointerEvent); + } + } + // If some points weren't grabbed, deliver to non-grabber PointerHandlers in reverse paint order + if (!pointerEvent->allPointsGrabbed() && pointerEvent->buttons()) { + QVector<QQuickItem *> targetItems = pointerTargets(contentItem, point->scenePos(), false, false); + for (QQuickItem *item : targetItems) { + QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); + pointerEvent->localize(item); + if (!sendFilteredPointerEvent(pointerEvent, item)) { + if (itemPrivate->handlePointerEvent(pointerEvent, true)) // avoid re-delivering to grabbers + delivered = true; + } + if (point->exclusiveGrabber()) + break; + } + } } if (!delivered) @@ -1881,19 +1932,16 @@ bool QQuickWindowPrivate::deliverTouchCancelEvent(QTouchEvent *event) Q_Q(QQuickWindow); // A TouchCancel event will typically not contain any points. - // Deliver it to all items that have active touches. + // Deliver it to all items and handlers that have active touches. QQuickPointerEvent *pointerEvent = pointerEventInstance(QQuickPointerDevice::touchDevice(event->device())); - QVector<QQuickItem *> grabbers = pointerEvent->grabbers(); - - for (QQuickItem *grabber: qAsConst(grabbers)) { - q->sendEvent(grabber, event); - } + for (int i = 0; i < pointerEvent->pointCount(); ++i) + pointerEvent->point(i)->cancelExclusiveGrabImpl(event); touchMouseId = -1; touchMouseDevice = nullptr; if (q->mouseGrabberItem()) q->mouseGrabberItem()->ungrabMouse(); - // The next touch event can only be a TouchBegin so clean up. + // The next touch event can only be a TouchBegin, so clean up. pointerEvent->clearGrabbers(); return true; } @@ -2030,8 +2078,6 @@ void QQuickWindow::mouseReleaseEvent(QMouseEvent *event) void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event) { - Q_Q(QQuickWindow); - if (event->source() == Qt::MouseEventSynthesizedBySystem) { event->accept(); return; @@ -2052,7 +2098,8 @@ void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event) case QEvent::MouseButtonDblClick: Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseDoubleClick, event->button(), event->buttons()); - deliverPointerEvent(pointerEventInstance(event)); + if (allowDoubleClick) + deliverPointerEvent(pointerEventInstance(event)); break; case QEvent::MouseMove: Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseMove, @@ -2064,7 +2111,7 @@ void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event) updateCursor(event->windowPos()); #endif - if (!q->mouseGrabberItem()) { + if (!pointerEventInstance(QQuickPointerDevice::genericMouseDevice())->point(0)->exclusiveGrabber()) { QPointF last = lastMousePosition.isNull() ? event->windowPos() : lastMousePosition; lastMousePosition = event->windowPos(); @@ -2075,7 +2122,6 @@ void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event) accepted = clearHover(event->timestamp()); } event->setAccepted(accepted); - return; } deliverPointerEvent(pointerEventInstance(event)); break; @@ -2195,7 +2241,10 @@ void QQuickWindowPrivate::deliverPointerEvent(QQuickPointerEvent *event) // check if item or any of its child items contain the point // FIXME: should this be iterative instead of recursive? -QVector<QQuickItem *> QQuickWindowPrivate::pointerTargets(QQuickItem *item, const QPointF &scenePos, bool checkMouseButtons) const +// If checkMouseButtons is true, it means we are finding targets for a mouse event, so no item for which acceptedMouseButtons() is NoButton will be added. +// If checkAcceptsTouch is true, it means we are finding targets for a touch event, so either acceptTouchEvents() must return true OR +// it must accept a synth. mouse event, thus if acceptTouchEvents() returns false but acceptedMouseButtons() is true, gets added; if not, it doesn't. +QVector<QQuickItem *> QQuickWindowPrivate::pointerTargets(QQuickItem *item, const QPointF &scenePos, bool checkMouseButtons, bool checkAcceptsTouch) const { QVector<QQuickItem *> targets; auto itemPrivate = QQuickItemPrivate::get(item); @@ -2213,13 +2262,16 @@ QVector<QQuickItem *> QQuickWindowPrivate::pointerTargets(QQuickItem *item, cons auto childPrivate = QQuickItemPrivate::get(child); if (!child->isVisible() || !child->isEnabled() || childPrivate->culled) continue; - targets << pointerTargets(child, scenePos, false); + targets << pointerTargets(child, scenePos, checkMouseButtons, checkAcceptsTouch); } - if (item->contains(itemPos) && (!checkMouseButtons || itemPrivate->acceptedMouseButtons())) { - // add this item last - children take precedence - targets << item; - } + bool relevant = item->contains(itemPos); + if (relevant && checkMouseButtons && item->acceptedMouseButtons() == Qt::NoButton) + relevant = false; + if (relevant && checkAcceptsTouch && !(item->acceptTouchEvents() || item->acceptedMouseButtons())) + relevant = false; + if (relevant) + targets << item; // add this item last: children take precedence return targets; } @@ -2249,11 +2301,12 @@ void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event) { qCDebug(DBG_TOUCH) << " - delivering" << event->asTouchEvent(); - QSet<QQuickItem *> hasFiltered; if (event->isPressEvent()) - deliverPressEvent(event, &hasFiltered); - if (!event->allPointsAccepted()) - deliverUpdatedTouchPoints(event, &hasFiltered); + deliverPressOrReleaseEvent(event); + if (!event->allUpdatedPointsAccepted()) + deliverUpdatedTouchPoints(event); + if (event->isReleaseEvent()) + deliverPressOrReleaseEvent(event, true); // Remove released points from itemForTouchPointId bool allReleased = true; @@ -2263,7 +2316,7 @@ void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event) if (point->state() == QQuickEventPoint::Released) { int id = point->pointId(); qCDebug(DBG_TOUCH_TARGET) << "TP" << hex << id << "released"; - point->setGrabber(nullptr); + point->setGrabberItem(nullptr); if (id == touchMouseId) { touchMouseId = -1; touchMouseDevice = nullptr; @@ -2273,29 +2326,92 @@ void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event) } } - if (allReleased && !event->grabbers().isEmpty()) { - qWarning() << "No release received for some grabbers" << event->grabbers(); + if (allReleased && !event->exclusiveGrabbers().isEmpty()) { + qWarning() << "No release received for some grabbers" << event->exclusiveGrabbers(); event->clearGrabbers(); } } // Deliver touch points to existing grabbers -bool QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event, QSet<QQuickItem *> *hasFiltered) -{ - const auto grabbers = event->grabbers(); - for (auto grabber : grabbers) - deliverMatchingPointsToItem(grabber, event, hasFiltered); +void QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event) +{ + const auto grabbers = event->exclusiveGrabbers(); + for (auto grabber : grabbers) { + // The grabber is guaranteed to be either an item or a handler. + QQuickItem *receiver = qmlobject_cast<QQuickItem *>(grabber); + if (!receiver) { + // The grabber is not an item? It's a handler then. Let it have the event first. + QQuickPointerHandler *handler = static_cast<QQuickPointerHandler *>(grabber); + receiver = static_cast<QQuickPointerHandler *>(grabber)->parentItem(); + if (sendFilteredPointerEvent(event, receiver)) + return; + event->localize(receiver); + handler->handlePointerEvent(event); + if (event->allPointsAccepted()) + return; + } + // If the grabber is an item or the grabbing handler didn't handle it, + // then deliver the event to the item (which may have multiple handlers). + deliverMatchingPointsToItem(receiver, event); + } - return false; + // If some points weren't grabbed, deliver only to non-grabber PointerHandlers + if (!event->allPointsGrabbed()) { + int pointCount = event->pointCount(); + + // Deliver to each eventpoint's passive grabbers (but don't visit any handler more than once) + const QVector<QQuickPointerHandler *> &eventDeliveryTargets = event->device()->eventDeliveryTargets(); + for (int i = 0; i < pointCount; ++i) { + QQuickEventPoint *point = event->point(i); + for (auto handler : point->passiveGrabbers()) { + if (Q_LIKELY(handler) && !eventDeliveryTargets.contains(handler)) { + if (sendFilteredPointerEvent(event, handler->parentItem())) + return; + handler->handlePointerEvent(event); + } + } + } + + // If some points weren't grabbed, deliver to non-grabber PointerHandlers in reverse paint order + if (!event->allPointsGrabbed()) { + QVector<QQuickItem *> targetItems; + for (int i = 0; i < pointCount; ++i) { + QQuickEventPoint *point = event->point(i); + if (point->state() == QQuickEventPoint::Pressed) + continue; // presses were delivered earlier; not the responsibility of deliverUpdatedTouchPoints + QVector<QQuickItem *> targetItemsForPoint = pointerTargets(contentItem, point->scenePos(), false, false); + if (targetItems.count()) { + targetItems = mergePointerTargets(targetItems, targetItemsForPoint); + } else { + targetItems = targetItemsForPoint; + } + } + + for (QQuickItem *item: targetItems) { + if (grabbers.contains(item)) + continue; + QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); + event->localize(item); + itemPrivate->handlePointerEvent(event, true); // avoid re-delivering to grabbers + if (event->allPointsGrabbed()) + break; + } + } + } } -// Deliver newly pressed touch points -bool QQuickWindowPrivate::deliverPressEvent(QQuickPointerEvent *event, QSet<QQuickItem *> *hasFiltered) +// Deliver an event containing newly pressed or released touch points +bool QQuickWindowPrivate::deliverPressOrReleaseEvent(QQuickPointerEvent *event, bool handlersOnly) { - const QVector<QPointF> points = event->unacceptedPressedPointScenePositions(); + int pointCount = event->pointCount(); QVector<QQuickItem *> targetItems; - for (QPointF point: points) { - QVector<QQuickItem *> targetItemsForPoint = pointerTargets(contentItem, point, false); + bool isTouchEvent = (event->asPointerTouchEvent() != nullptr); + for (int i = 0; i < pointCount; ++i) { + auto point = event->point(i); + point->setAccepted(false); // because otherwise touchEventForItem will ignore it + if (point->grabberPointerHandler() && point->state() == QQuickEventPoint::Released) + point->setGrabberPointerHandler(nullptr, true); + QVector<QQuickItem *> targetItemsForPoint = pointerTargets(contentItem, point->scenePos(), !isTouchEvent, isTouchEvent); if (targetItems.count()) { targetItems = mergePointerTargets(targetItems, targetItemsForPoint); } else { @@ -2303,8 +2419,14 @@ bool QQuickWindowPrivate::deliverPressEvent(QQuickPointerEvent *event, QSet<QQui } } + if (allowChildEventFiltering && !handlersOnly) { + updateFilteringParentItems(targetItems); + if (sendFilteredPointerEvent(event, nullptr)) + return true; + } + for (QQuickItem *item: targetItems) { - deliverMatchingPointsToItem(item, event, hasFiltered); + deliverMatchingPointsToItem(item, event, handlersOnly); if (event->allPointsAccepted()) break; } @@ -2312,17 +2434,35 @@ bool QQuickWindowPrivate::deliverPressEvent(QQuickPointerEvent *event, QSet<QQui return event->allPointsAccepted(); } -bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent, QSet<QQuickItem *> *hasFiltered) +void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent, bool handlersOnly) { Q_Q(QQuickWindow); + QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); + pointerEvent->localize(item); + + // Let the Item's handlers (if any) have the event first. + // However, double click should never be delivered to handlers. + if (!pointerEvent->isDoubleClickEvent()) { + itemPrivate->handlePointerEvent(pointerEvent); + allowDoubleClick = !(pointerEvent->asPointerMouseEvent() && pointerEvent->isPressEvent() && pointerEvent->allPointsAccepted()); + } + if (handlersOnly) + return; + if (pointerEvent->allPointsAccepted() && !pointerEvent->isReleaseEvent()) + return; + + // If all points are released and the item is not the grabber, it doesn't get the event. + // But if at least one point is still pressed, we might be in a potential gesture-takeover scenario. + if (pointerEvent->isReleaseEvent() && !pointerEvent->isUpdateEvent() + && !pointerEvent->exclusiveGrabbers().contains(item)) + return; // TODO: unite this mouse point delivery with the synthetic mouse event below - if (auto event = pointerEvent->asPointerMouseEvent()) { - if (item->acceptedMouseButtons() & event->button()) { + auto event = pointerEvent->asPointerMouseEvent(); + if (event && item->acceptedMouseButtons() & event->button()) { auto point = event->point(0); if (point->isAccepted()) - return false; - + return; // The only reason to already have a mouse grabber here is // synthetic events - flickable sends one when setPressDelay is used. auto oldMouseGrabber = q->mouseGrabberItem(); @@ -2330,7 +2470,7 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo Q_ASSERT(item->contains(localPos)); // transform is checked already QMouseEvent *me = event->asMouseEvent(localPos); me->accept(); - q->sendEvent(item, me); + QCoreApplication::sendEvent(item, me); if (me->isAccepted()) { auto mouseGrabber = q->mouseGrabberItem(); if (mouseGrabber && mouseGrabber != item && mouseGrabber != oldMouseGrabber) { @@ -2340,33 +2480,25 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo } point->setAccepted(true); } - return me->isAccepted(); - } - return false; + return; } - QQuickPointerTouchEvent *event = pointerEvent->asPointerTouchEvent(); - if (!event) - return false; + QQuickPointerTouchEvent *ptEvent = pointerEvent->asPointerTouchEvent(); + if (!ptEvent) + return; - QScopedPointer<QTouchEvent> touchEvent(event->touchEventForItem(item)); + QScopedPointer<QTouchEvent> touchEvent(ptEvent->touchEventForItem(item)); if (!touchEvent) - return false; + return; - qCDebug(DBG_TOUCH) << " - considering delivering " << touchEvent.data() << " to " << item; + qCDebug(DBG_TOUCH) << "considering delivering " << touchEvent.data() << " to " << item; bool eventAccepted = false; - // First check whether the parent wants to be a filter, - // and if the parent accepts the event we are done. - if (sendFilteredTouchEvent(item->parentItem(), item, event, hasFiltered)) { - // If the touch was accepted (regardless by whom or in what form), - // update acceptedNewPoints - qCDebug(DBG_TOUCH) << " - can't. intercepted " << touchEvent.data() << " to " << item->parentItem() << " instead of " << item; - for (auto point: qAsConst(touchEvent->touchPoints())) { - event->pointById(point.id())->setAccepted(); - } - return true; - } + // If any parent filters the event, we're done. + // updateFilteringParentItems was called when the press occurred, + // and we assume that the filtering relationships don't change between press and release. + if (sendFilteredPointerEvent(pointerEvent, item)) + return; // Deliver the touch event to the given item qCDebug(DBG_TOUCH) << " - actually delivering " << touchEvent.data() << " to " << item; @@ -2374,36 +2506,34 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo eventAccepted = touchEvent->isAccepted(); // If the touch event wasn't accepted, synthesize a mouse event and see if the item wants it. - QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); if (!eventAccepted && (itemPrivate->acceptedMouseButtons() & Qt::LeftButton)) { // send mouse event - if (deliverTouchAsMouse(item, event)) + if (deliverTouchAsMouse(item, ptEvent)) eventAccepted = true; } if (eventAccepted) { // If the touch was accepted (regardless by whom or in what form), // update accepted new points. + bool isPressOrRelease = pointerEvent->isPressEvent() || pointerEvent->isReleaseEvent(); for (auto point: qAsConst(touchEvent->touchPoints())) { - auto pointerEventPoint = event->pointById(point.id()); + auto pointerEventPoint = ptEvent->pointById(point.id()); pointerEventPoint->setAccepted(); - if (point.state() == Qt::TouchPointPressed) - pointerEventPoint->setGrabber(item); + if (isPressOrRelease) + pointerEventPoint->setGrabberItem(item); } } else { // But if the event was not accepted then we know this item // will not be interested in further updates for those touchpoint IDs either. for (auto point: qAsConst(touchEvent->touchPoints())) { if (point.state() == Qt::TouchPointPressed) { - if (event->pointById(point.id())->grabber() == item) { - qCDebug(DBG_TOUCH_TARGET) << "TP" << point.id() << "disassociated"; - event->pointById(point.id())->setGrabber(nullptr); + if (ptEvent->pointById(point.id())->exclusiveGrabber() == item) { + qCDebug(DBG_TOUCH_TARGET) << "TP" << hex << point.id() << "disassociated"; + ptEvent->pointById(point.id())->setGrabberItem(nullptr); } } } } - - return eventAccepted; } #if QT_CONFIG(draganddrop) @@ -2578,38 +2708,86 @@ QQuickItem *QQuickWindowPrivate::findCursorItem(QQuickItem *item, const QPointF } #endif -bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QQuickPointerTouchEvent *event, QSet<QQuickItem *> *hasFiltered) +void QQuickWindowPrivate::updateFilteringParentItems(const QVector<QQuickItem *> &targetItems) { - Q_Q(QQuickWindow); + if (Q_UNLIKELY(DBG_MOUSE_TARGET().isDebugEnabled())) { + // qDebug() << map(&objectName, targetItems) but done the hard way because C++ is still that primitive + QStringList targetNames; + for (QQuickItem *t : targetItems) + targetNames << (QLatin1String(t->metaObject()->className()) + QLatin1Char(' ') + t->objectName()); + qCDebug(DBG_MOUSE_TARGET) << "finding potential filtering parents of" << targetNames; + } + filteringParentItems.clear(); + for (QQuickItem *item : targetItems) { +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + bool acceptsTouchEvents = item->acceptTouchEvents(); +#else + // In versions prior to Qt 6, we can't trust item->acceptTouchEvents() here, because it defaults to true. + bool acceptsTouchEvents = false; +#endif + QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item); + // If the item neither handles events nor has handlers which do, then it will never be a receiver, so filtering is irrelevant. + if (!item->acceptedMouseButtons() && !acceptsTouchEvents && + !(itemPriv->extra.isAllocated() && !itemPriv->extra->pointerHandlers.isEmpty())) + continue; + QQuickItem *parent = item->parentItem(); + while (parent) { + if (parent->filtersChildMouseEvents()) { + bool foundParent = false; + for (const QPair<QQuickItem*,QQuickItem*> existingItemAndParent : filteringParentItems) + if (existingItemAndParent.second == parent) + foundParent = true; + if (!foundParent) + filteringParentItems.append(QPair<QQuickItem*,QQuickItem*>(item, parent)); + } + parent = parent->parentItem(); + } + } +} - if (!target) +bool QQuickWindowPrivate::sendFilteredPointerEvent(QQuickPointerEvent *event, QQuickItem *receiver) +{ + if (!allowChildEventFiltering) return false; - - bool filtered = false; - - QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(target); - if (targetPrivate->filtersChildMouseEvents && !hasFiltered->contains(target)) { - hasFiltered->insert(target); - QScopedPointer<QTouchEvent> targetEvent(event->touchEventForItem(target, true)); - if (targetEvent) { - if (target->childMouseEventFilter(item, targetEvent.data())) { - qCDebug(DBG_TOUCH) << " - first chance intercepted on childMouseEventFilter by " << target; - QVector<int> touchIds; - const int touchPointCount = targetEvent->touchPoints().size(); - touchIds.reserve(touchPointCount); - for (int i = 0; i < touchPointCount; ++i) - touchIds.append(targetEvent->touchPoints().at(i).id()); - target->grabTouchPoints(touchIds); - if (q->mouseGrabberItem()) { - q->mouseGrabberItem()->ungrabMouse(); - touchMouseId = -1; - touchMouseDevice = nullptr; - } - filtered = true; + bool ret = false; + if (QQuickPointerMouseEvent *pme = event->asPointerMouseEvent()) { + for (QPair<QQuickItem *,QQuickItem *> itemAndParent : filteringParentItems) { + QQuickItem *item = receiver ? receiver : itemAndParent.first; + QQuickItem *filteringParent = itemAndParent.second; + if (item == filteringParent) + continue; // a filtering item never needs to filter for itself + QPointF localPos = item->mapFromScene(pme->point(0)->scenePos()); + QMouseEvent *me = pme->asMouseEvent(localPos); + if (filteringParent->childMouseEventFilter(item, me)) { + event->setAccepted(true); + ret = true; } - - for (int i = 0; i < targetEvent->touchPoints().size(); ++i) { - const QTouchEvent::TouchPoint &tp = targetEvent->touchPoints().at(i); + } + } else if (QQuickPointerTouchEvent *pte = event->asPointerTouchEvent()) { + QVarLengthArray<QPair<QQuickPointerHandler *, QQuickEventPoint *>, 32> passiveGrabsToCancel; + for (QPair<QQuickItem *,QQuickItem *> itemAndParent : filteringParentItems) { + QQuickItem *item = receiver ? receiver : itemAndParent.first; + if (item != itemAndParent.first) + continue; + if (!item->acceptTouchEvents() && !item->acceptedMouseButtons()) + continue; // if this item won't accept, parents don't need to filter the touch for it + QQuickItem *filteringParent = itemAndParent.second; + if (item == filteringParent) + continue; // a filtering item never needs to filter for itself + // get a touch event customized for delivery to filteringParent + QScopedPointer<QTouchEvent> filteringParentTouchEvent(pte->touchEventForItem(filteringParent, true)); + if (!filteringParentTouchEvent) + continue; // all points are stationary, or no touchpoints inside that parent + if (filteringParent->childMouseEventFilter(item, filteringParentTouchEvent.data())) { + qCDebug(DBG_TOUCH) << "touch event intercepted by childMouseEventFilter of " << filteringParent; + for (auto point: qAsConst(filteringParentTouchEvent->touchPoints())) + event->pointById(point.id())->setAccepted(); + ret = true; + break; + } + // filteringParent didn't filter the touch event. Give it a chance to filter a synthetic mouse event. + for (int i = 0; i < filteringParentTouchEvent->touchPoints().size(); ++i) { + const QTouchEvent::TouchPoint &tp = filteringParentTouchEvent->touchPoints().at(i); QEvent::Type t; switch (tp.state()) { @@ -2629,29 +2807,29 @@ bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem bool touchMouseUnset = (touchMouseId == -1); // Only deliver mouse event if it is the touchMouseId or it could become the touchMouseId if (touchMouseUnset || touchMouseId == tp.id()) { - // targetEvent is already transformed wrt local position, velocity, etc. - - // FIXME: remove asTouchEvent!!! - QScopedPointer<QMouseEvent> mouseEvent(touchToMouseEvent(t, tp, event->asTouchEvent(), item, false)); + // convert filteringParentTouchEvent (which is already transformed wrt local position, velocity, etc.) + // into a synthetic mouse event, and let childMouseEventFilter() have another chance with that + QScopedPointer<QMouseEvent> mouseEvent(touchToMouseEvent(t, tp, filteringParentTouchEvent.data(), item, false)); // If a filtering item calls QQuickWindow::mouseGrabberItem(), it should // report the touchpoint's grabber. Whenever we send a synthetic mouse event, // touchMouseId and touchMouseDevice must be set, even if it's only temporarily and isn't grabbed. touchMouseId = tp.id(); touchMouseDevice = event->device(); - if (target->childMouseEventFilter(item, mouseEvent.data())) { - qCDebug(DBG_TOUCH) << " - second chance intercepted on childMouseEventFilter by " << target; + if (filteringParent->childMouseEventFilter(item, mouseEvent.data())) { + qCDebug(DBG_TOUCH) << "touch event intercepted as synth mouse event by childMouseEventFilter of " << filteringParent; if (t != QEvent::MouseButtonRelease) { - qCDebug(DBG_TOUCH_TARGET) << "TP" << tp.id() << "->" << target; - if (touchMouseUnset) { - // the point was grabbed as a pure touch point before, now it will be treated as mouse - // but the old receiver still needs to be informed - if (auto oldGrabber = pointerEventInstance(touchMouseDevice)->pointById(tp.id())->grabber()) - oldGrabber->touchUngrabEvent(); - } + qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << hex << tp.id() << "->" << filteringParent; + pointerEventInstance(touchMouseDevice)->pointById(tp.id())->setGrabberItem(filteringParent); touchMouseUnset = false; // We want to leave touchMouseId and touchMouseDevice set - target->grabMouse(); + filteringParent->grabMouse(); + auto pointerEventPoint = pte->pointById(tp.id()); + for (auto handler : pointerEventPoint->passiveGrabbers()) { + QPair<QQuickPointerHandler *, QQuickEventPoint *> grab(handler, pointerEventPoint); + if (!passiveGrabsToCancel.contains(grab)) + passiveGrabsToCancel.append(grab); + } } - filtered = true; + ret = true; } if (touchMouseUnset) { // Now that we're done sending a synth mouse event, and it wasn't grabbed, @@ -2659,14 +2837,16 @@ bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem touchMouseId = -1; touchMouseDevice = nullptr; } - // Only one event can be filtered as a mouse event. - break; + // Only one touchpoint can be treated as a synthetic mouse, so after childMouseEventFilter + // has been called once, we're done with this loop over the touchpoints. + break; } } } + for (auto grab : passiveGrabsToCancel) + grab.second->cancelPassiveGrab(grab.first); } - - return sendFilteredTouchEvent(target->parentItem(), item, event, hasFiltered) || filtered; + return ret; } bool QQuickWindowPrivate::sendFilteredMouseEvent(QQuickItem *target, QQuickItem *item, QEvent *event, QSet<QQuickItem *> *hasFiltered) diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index b3ff5a2b35..c2d2a7a8a4 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -143,11 +143,12 @@ public: bool deliverTouchAsMouse(QQuickItem *item, QQuickPointerEvent *pointerEvent); void translateTouchEvent(QTouchEvent *touchEvent); void setMouseGrabber(QQuickItem *grabber); - void grabTouchPoints(QQuickItem *grabber, const QVector<int> &ids); + void grabTouchPoints(QObject *grabber, const QVector<int> &ids); void removeGrabber(QQuickItem *grabber, bool mouse = true, bool touch = true); static QMouseEvent *cloneMouseEvent(QMouseEvent *event, QPointF *transformedLocalPos = 0); void deliverMouseEvent(QQuickPointerMouseEvent *pointerEvent); bool sendFilteredMouseEvent(QQuickItem *, QQuickItem *, QEvent *, QSet<QQuickItem *> *); + bool sendFilteredPointerEvent(QQuickPointerEvent *event, QQuickItem *receiver); #if QT_CONFIG(wheelevent) bool deliverWheelEvent(QQuickItem *, QWheelEvent *); #endif @@ -171,13 +172,13 @@ public: void deliverPointerEvent(QQuickPointerEvent *); void deliverTouchEvent(QQuickPointerTouchEvent *); bool deliverTouchCancelEvent(QTouchEvent *); - bool deliverPressEvent(QQuickPointerEvent *, QSet<QQuickItem *> *); - bool deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event, QSet<QQuickItem *> *hasFiltered); - bool deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent, QSet<QQuickItem*> *filtered); - bool sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QQuickPointerTouchEvent *event, QSet<QQuickItem*> *filtered); + bool deliverPressOrReleaseEvent(QQuickPointerEvent *, bool handlersOnly = false); + void deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event); + void deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent, bool handlersOnly = false); - QVector<QQuickItem *> pointerTargets(QQuickItem *, const QPointF &, bool checkMouseButtons) const; + QVector<QQuickItem *> pointerTargets(QQuickItem *, const QPointF &, bool checkMouseButtons, bool checkAcceptsTouch) const; QVector<QQuickItem *> mergePointerTargets(const QVector<QQuickItem *> &list1, const QVector<QQuickItem *> &list2) const; + void updateFilteringParentItems(const QVector<QQuickItem *> &targetItems); // hover delivery bool deliverHoverEvent(QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, ulong timestamp, bool &accepted); @@ -226,6 +227,7 @@ public: QList<QSGNode *> cleanupNodeList; QVector<QQuickItem *> itemsToPolish; + QVector<QPair<QQuickItem *,QQuickItem *> > filteringParentItems; // item:parent pairs qreal devicePixelRatio; QMetaObject::Connection physicalDpiChangedConnection; @@ -263,6 +265,9 @@ public: uint lastWheelEventAccepted : 1; bool componentCompleted : 1; + bool allowChildEventFiltering : 1; + bool allowDoubleClick : 1; + Qt::FocusReason lastFocusReason; QOpenGLFramebufferObject *renderTarget; @@ -290,6 +295,14 @@ public: return overThreshold; } + static bool dragOverThreshold(const QQuickEventPoint *point) + { + QPointF delta = point->scenePos() - point->scenePressPos(); + return (QQuickWindowPrivate::dragOverThreshold(delta.x(), Qt::XAxis, point) || + QQuickWindowPrivate::dragOverThreshold(delta.y(), Qt::YAxis, point)); + } + + // data property static void data_append(QQmlListProperty<QObject> *, QObject *); static int data_count(QQmlListProperty<QObject> *); |