diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/quick/items/qquickevents.cpp | 501 | ||||
-rw-r--r-- | src/quick/items/qquickevents_p_p.h | 300 | ||||
-rw-r--r-- | src/quick/items/qquickitem.cpp | 2 | ||||
-rw-r--r-- | src/quick/items/qquickmousearea.cpp | 8 | ||||
-rw-r--r-- | src/quick/items/qquickmultipointtoucharea.cpp | 31 | ||||
-rw-r--r-- | src/quick/items/qquickwindow.cpp | 844 | ||||
-rw-r--r-- | src/quick/items/qquickwindow_p.h | 67 |
7 files changed, 1217 insertions, 536 deletions
diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 14c0adf393..420bbad0b1 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -38,9 +38,15 @@ ****************************************************************************/ #include "qquickevents_p_p.h" +#include <QtGui/private/qguiapplication_p.h> +#include <QtQuick/private/qquickitem_p.h> +#include <QtQuick/private/qquickwindow_p.h> +#include <private/qdebug_p.h> QT_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcPointerEvents, "qt.quick.pointer.events") + /*! \qmltype KeyEvent \instantiates QQuickKeyEvent @@ -437,4 +443,499 @@ Item { \l inverted always returns false. */ +typedef QHash<QTouchDevice *, QQuickPointerDevice *> PointerDeviceForTouchDeviceHash; +Q_GLOBAL_STATIC(PointerDeviceForTouchDeviceHash, g_touchDevices) + +Q_GLOBAL_STATIC_WITH_ARGS(QQuickPointerDevice, g_genericMouseDevice, + (QQuickPointerDevice::Mouse, + QQuickPointerDevice::GenericPointer, + QQuickPointerDevice::Position | QQuickPointerDevice::Scroll | QQuickPointerDevice::Hover, + 1, 3, QLatin1String("core pointer"), 0)) + +typedef QHash<qint64, QQuickPointerDevice *> PointerDeviceForDeviceIdHash; +Q_GLOBAL_STATIC(PointerDeviceForDeviceIdHash, g_tabletDevices) + +QQuickPointerDevice *QQuickPointerDevice::touchDevice(QTouchDevice *d) +{ + if (g_touchDevices->contains(d)) + return g_touchDevices->value(d); + + QQuickPointerDevice::DeviceType type = QQuickPointerDevice::TouchScreen; + QString name; + int maximumTouchPoints = 10; + QQuickPointerDevice::Capabilities caps = QQuickPointerDevice::Capabilities(QTouchDevice::Position); + if (d) { + QQuickPointerDevice::Capabilities caps = + static_cast<QQuickPointerDevice::Capabilities>(static_cast<int>(d->capabilities()) & 0x0F); + if (d->type() == QTouchDevice::TouchPad) { + type = QQuickPointerDevice::TouchPad; + caps |= QQuickPointerDevice::Scroll; + } + name = d->name(); + maximumTouchPoints = d->maximumTouchPoints(); + } else { + qWarning() << "QQuickWindowPrivate::touchDevice: creating touch device from nullptr device in QTouchEvent"; + } + + QQuickPointerDevice *dev = new QQuickPointerDevice(type, QQuickPointerDevice::Finger, + caps, maximumTouchPoints, 0, name, 0); + g_touchDevices->insert(d, dev); + return dev; +} + +QList<QQuickPointerDevice*> QQuickPointerDevice::touchDevices() +{ + return g_touchDevices->values(); +} + +QQuickPointerDevice *QQuickPointerDevice::genericMouseDevice() +{ + return g_genericMouseDevice; +} + +QQuickPointerDevice *QQuickPointerDevice::tabletDevice(qint64 id) +{ + auto it = g_tabletDevices->find(id); + if (it != g_tabletDevices->end()) + return it.value(); + + // ### Figure out how to populate the tablet devices + return nullptr; +} + +void QQuickEventPoint::reset(Qt::TouchPointState state, QPointF scenePos, quint64 pointId, ulong timestamp) +{ + m_scenePos = scenePos; + m_pointId = pointId; + m_valid = true; + m_accept = false; + m_state = state; + m_timestamp = timestamp; + if (state == Qt::TouchPointPressed) + m_pressTimestamp = timestamp; + // TODO calculate velocity +} + +QQuickItem *QQuickEventPoint::grabber() const +{ + return m_grabber.data(); +} + +void QQuickEventPoint::setGrabber(QQuickItem *grabber) +{ + m_grabber = QPointer<QQuickItem>(grabber); +} + +void QQuickEventPoint::setAccepted(bool accepted) +{ + if (m_accept != accepted) { + qCDebug(lcPointerEvents) << this << m_accept << "->" << accepted; + m_accept = accepted; + } +} + +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); + m_rotation = tp.rotation(); + m_pressure = tp.pressure(); + m_uniqueId = tp.uniqueId(); +} + +/*! + \internal + \class QQuickPointerEvent + + QQuickPointerEvent is used as a long-lived object to store data related to + an event from a pointing device, such as a mouse, touch or tablet event, + during event delivery. It also provides properties which may be used later + to expose the event to QML, the same as is done with QQuickMouseEvent, + QQuickTouchPoint, QQuickKeyEvent, etc. Since only one event can be + delivered at a time, this class is effectively a singleton. We don't worry + about the QObject overhead because the instances are long-lived: we don't + dynamically create and destroy objects of this type for each event. +*/ + +QQuickPointerEvent::~QQuickPointerEvent() +{} + +QQuickPointerEvent *QQuickPointerMouseEvent::reset(QEvent *event) { + auto ev = static_cast<QMouseEvent*>(event); + m_device = QQuickPointerDevice::genericMouseDevice(); + m_event = ev; + m_button = ev->button(); + m_pressedButtons = ev->buttons(); + Qt::TouchPointState state = Qt::TouchPointStationary; + switch (ev->type()) { + case QEvent::MouseButtonPress: + case QEvent::MouseButtonDblClick: + state = Qt::TouchPointPressed; + break; + case QEvent::MouseButtonRelease: + state = Qt::TouchPointReleased; + break; + case QEvent::MouseMove: + state = Qt::TouchPointMoved; + break; + default: + break; + } + m_mousePoint->reset(state, ev->windowPos(), 0, ev->timestamp()); // mouse is 0 + return this; +} + +QQuickPointerEvent *QQuickPointerTouchEvent::reset(QEvent *event) { + auto ev = static_cast<QTouchEvent*>(event); + m_device = QQuickPointerDevice::touchDevice(ev->device()); + m_event = ev; + m_button = Qt::NoButton; + m_pressedButtons = Qt::NoButton; + + const QList<QTouchEvent::TouchPoint> &tps = ev->touchPoints(); + int newPointCount = tps.count(); + m_touchPoints.reserve(newPointCount); + + 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. + // 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); + } + + for (int i = 0; i < newPointCount; ++i) { + auto point = m_touchPoints.at(i); + point->reset(tps.at(i), ev->timestamp()); + point->setGrabber(grabbers.at(i)); + } + m_pointCount = newPointCount; + return this; +} + +QQuickEventPoint *QQuickPointerMouseEvent::point(int i) const { + if (i == 0) + return m_mousePoint; + return nullptr; +} + +QQuickEventPoint *QQuickPointerTouchEvent::point(int i) const { + if (i >= 0 && i < m_pointCount) + return m_touchPoints.at(i); + return nullptr; +} + +QQuickEventPoint::QQuickEventPoint(QQuickPointerEvent *parent) + : QObject(parent), m_pointId(0), m_grabber(nullptr), m_timestamp(0), m_pressTimestamp(0), + m_state(Qt::TouchPointReleased), m_valid(false), m_accept(false) +{ + Q_UNUSED(m_reserved); +} + +QQuickPointerEvent *QQuickEventPoint::pointerEvent() const +{ + return static_cast<QQuickPointerEvent *>(parent()); +} + +bool QQuickPointerMouseEvent::allPointsAccepted() const { + return m_mousePoint->isAccepted(); +} + +QMouseEvent *QQuickPointerMouseEvent::asMouseEvent(const QPointF &localPos) const +{ + auto event = static_cast<QMouseEvent *>(m_event); + event->setLocalPos(localPos); + return event; +} + +QVector<QQuickItem *> QQuickPointerMouseEvent::grabbers() const +{ + QVector<QQuickItem *> result; + if (QQuickItem *grabber = m_mousePoint->grabber()) + result << grabber; + return result; +} + +void QQuickPointerMouseEvent::clearGrabbers() const { + m_mousePoint->setGrabber(nullptr); +} + +bool QQuickPointerMouseEvent::isPressEvent() const +{ + auto me = static_cast<QMouseEvent*>(m_event); + return ((me->type() == QEvent::MouseButtonPress || me->type() == QEvent::MouseButtonDblClick) && + (me->buttons() & me->button()) == me->buttons()); +} + +bool QQuickPointerTouchEvent::allPointsAccepted() const { + for (int i = 0; i < m_pointCount; ++i) { + if (!m_touchPoints.at(i)->isAccepted()) + return false; + } + return true; +} + +QVector<QQuickItem *> QQuickPointerTouchEvent::grabbers() const +{ + QVector<QQuickItem *> result; + for (int i = 0; i < m_pointCount; ++i) { + auto point = m_touchPoints.at(i); + if (QQuickItem *grabber = point->grabber()) { + if (!result.contains(grabber)) + result << grabber; + } + } + return result; +} + +void QQuickPointerTouchEvent::clearGrabbers() const { + for (auto point: m_touchPoints) + point->setGrabber(nullptr); +} + +bool QQuickPointerTouchEvent::isPressEvent() const +{ + return static_cast<QTouchEvent*>(m_event)->touchPointStates() & Qt::TouchPointPressed; +} + +QVector<QPointF> QQuickPointerEvent::unacceptedPointScenePositions() const +{ + QVector<QPointF> points; + for (int i = 0; i < pointCount(); ++i) { + if (!point(i)->isAccepted()) + points << point(i)->scenePos(); + } + return points; +} + +QVector<QPointF> QQuickPointerEvent::unacceptedPressedPointScenePositions() const +{ + QVector<QPointF> points; + for (int i = 0; i < pointCount(); ++i) { + if (!point(i)->isAccepted() && point(i)->state() == Qt::TouchPointPressed) + points << point(i)->scenePos(); + } + return points; +} + +/*! + \internal + Populate the reusable synth-mouse event from one touchpoint. + It's required that isTouchEvent() be true when this is called. + If the touchpoint cannot be found, this returns nullptr. + Ownership of the event is NOT transferred to the caller. +*/ +QMouseEvent *QQuickPointerTouchEvent::syntheticMouseEvent(int pointID, QQuickItem *relativeTo) const { + const QTouchEvent::TouchPoint *p = touchPointById(pointID); + if (!p) + return nullptr; + QEvent::Type type; + Qt::MouseButton buttons = Qt::LeftButton; + switch (p->state()) { + case Qt::TouchPointPressed: + type = QEvent::MouseButtonPress; + break; + case Qt::TouchPointMoved: + case Qt::TouchPointStationary: + type = QEvent::MouseMove; + break; + case Qt::TouchPointReleased: + type = QEvent::MouseButtonRelease; + buttons = Qt::NoButton; + break; + default: + Q_ASSERT(false); + return nullptr; + } + m_synthMouseEvent = QMouseEvent(type, relativeTo->mapFromScene(p->scenePos()), + p->scenePos(), p->screenPos(), Qt::LeftButton, buttons, m_event->modifiers()); + m_synthMouseEvent.setAccepted(true); + m_synthMouseEvent.setTimestamp(m_event->timestamp()); + // In the future we will try to always have valid velocity in every QQuickEventPoint. + // QQuickFlickablePrivate::handleMouseMoveEvent() checks for QTouchDevice::Velocity + // and if it is set, then it does not need to do its own velocity calculations. + // That's probably the only usecase for this, so far. Some day Flickable should handle + // pointer events, and then passing touchpoint velocity via QMouseEvent will be obsolete. + // Conveniently (by design), QTouchDevice::Velocity == QQuickPointerDevice.Velocity + // so that we don't need to convert m_device->capabilities(). + if (m_device) + QGuiApplicationPrivate::setMouseEventCapsAndVelocity(&m_synthMouseEvent, m_device->capabilities(), p->velocity()); + QGuiApplicationPrivate::setMouseEventSource(&m_synthMouseEvent, Qt::MouseEventSynthesizedByQt); + return &m_synthMouseEvent; +} + +/*! + \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 +*/ + + + +QQuickEventPoint *QQuickPointerMouseEvent::pointById(quint64 pointId) const { + if (m_mousePoint && pointId == m_mousePoint->pointId()) + return m_mousePoint; + return nullptr; +} + +QQuickEventPoint *QQuickPointerTouchEvent::pointById(quint64 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()) + return *it; + return nullptr; +} + + +/*! + \internal + Returns a pointer to the original TouchPoint which has the same + \l {QTouchEvent::TouchPoint::id}{id} as \a pointId, if the original event is a + QTouchEvent, and if that point is found. Otherwise, returns nullptr. +*/ +const QTouchEvent::TouchPoint *QQuickPointerTouchEvent::touchPointById(int pointId) const { + const QTouchEvent *ev = asTouchEvent(); + if (!ev) + return nullptr; + const QList<QTouchEvent::TouchPoint> &tps = ev->touchPoints(); + auto it = std::find_if(tps.constBegin(), tps.constEnd(), + [pointId](QTouchEvent::TouchPoint const& tp) { return tp.id() == pointId; } ); + // return the pointer to the actual TP in QTouchEvent::_touchPoints + return (it == tps.constEnd() ? nullptr : it.operator->()); +} + +/*! + \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. +*/ +QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(QQuickItem *item, bool isFiltering) const +{ + QList<QTouchEvent::TouchPoint> touchPoints; + Qt::TouchPointStates eventStates; + // TODO maybe add QQuickItem::mapVector2DFromScene(QVector2D) to avoid needing QQuickItemPrivate here + // 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 + + QMatrix4x4 transformMatrix(QQuickItemPrivate::get(item)->windowToItemTransform()); + for (int i = 0; i < m_pointCount; ++i) { + auto p = m_touchPoints.at(i); + if (p->isAccepted()) + continue; + bool isGrabber = p->grabber() == item; + bool isPressInside = p->state() == Qt::TouchPointPressed && item->contains(item->mapFromScene(p->scenePos())); + if (!(isGrabber || isPressInside || isFiltering)) + continue; + + const QTouchEvent::TouchPoint *tp = touchPointById(p->pointId()); + if (tp) { + eventStates |= tp->state(); + QTouchEvent::TouchPoint tpCopy = *tp; + tpCopy.setPos(item->mapFromScene(tpCopy.scenePos())); + tpCopy.setLastPos(item->mapFromScene(tpCopy.lastScenePos())); + tpCopy.setStartPos(item->mapFromScene(tpCopy.startScenePos())); + tpCopy.setRect(item->mapRectFromScene(tpCopy.sceneRect())); + tpCopy.setVelocity(transformMatrix.mapVector(tpCopy.velocity()).toVector2D()); + touchPoints << tpCopy; + } + } + + if (eventStates == Qt::TouchPointStationary || touchPoints.isEmpty()) + return nullptr; + + // if all points have the same state, set the event type accordingly + const QTouchEvent &event = *asTouchEvent(); + QEvent::Type eventType = event.type(); + switch (eventStates) { + case Qt::TouchPointPressed: + eventType = QEvent::TouchBegin; + break; + case Qt::TouchPointReleased: + eventType = QEvent::TouchEnd; + break; + default: + eventType = QEvent::TouchUpdate; + break; + } + + QTouchEvent *touchEvent = new QTouchEvent(eventType); + touchEvent->setWindow(event.window()); + touchEvent->setTarget(item); + touchEvent->setDevice(event.device()); + touchEvent->setModifiers(event.modifiers()); + touchEvent->setTouchPoints(touchPoints); + touchEvent->setTouchPointStates(eventStates); + touchEvent->setTimestamp(event.timestamp()); + touchEvent->accept(); + return touchEvent; +} + +QTouchEvent *QQuickPointerTouchEvent::asTouchEvent() const +{ + return static_cast<QTouchEvent *>(m_event); +} + +#ifndef QT_NO_DEBUG_STREAM + +Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickPointerDevice *dev) { + QDebugStateSaver saver(dbg); + dbg.nospace(); + dbg << "QQuickPointerDevice("<< dev->name() << ' '; + QtDebugUtils::formatQEnum(dbg, dev->type()); + dbg << ' '; + QtDebugUtils::formatQEnum(dbg, dev->pointerType()); + dbg << " caps:"; + QtDebugUtils::formatQFlags(dbg, dev->capabilities()); + if (dev->type() == QQuickPointerDevice::TouchScreen || + dev->type() == QQuickPointerDevice::TouchPad) + dbg << " maxTouchPoints:" << dev->maximumTouchPoints(); + else + dbg << " buttonCount:" << dev->buttonCount(); + dbg << ')'; + return dbg; +} + +Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickPointerEvent *event) { + QDebugStateSaver saver(dbg); + dbg.nospace(); + dbg << "QQuickPointerEvent(dev:"; + QtDebugUtils::formatQEnum(dbg, event->device()->type()); + if (event->buttons() != Qt::NoButton) { + dbg << " buttons:"; + QtDebugUtils::formatQEnum(dbg, event->buttons()); + } + dbg << " ["; + int c = event->pointCount(); + for (int i = 0; i < c; ++i) + dbg << event->point(i) << ' '; + dbg << "])"; + return dbg; +} + +Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickEventPoint *event) { + QDebugStateSaver saver(dbg); + dbg.nospace(); + dbg << "QQuickEventPoint(valid:" << event->isValid() << " accepted:" << event->isAccepted() + << " state:"; + QtDebugUtils::formatQEnum(dbg, event->state()); + dbg << " scenePos:" << event->scenePos() << " id:" << event->pointId() + << " timeHeld:" << event->timeHeld() << ')'; + return dbg; +} + +#endif + QT_END_NAMESPACE diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 065e025152..f3e6fdddff 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -55,12 +55,20 @@ #include <qqml.h> #include <QtCore/qobject.h> +#include <QtCore/qpointer.h> #include <QtGui/qvector2d.h> #include <QtGui/qevent.h> #include <QtGui/qkeysequence.h> +#include <QtQuick/qquickitem.h> QT_BEGIN_NAMESPACE +class QQuickPointerDevice; +class QQuickPointerEvent; +class QQuickPointerMouseEvent; +class QQuickPointerTabletEvent; +class QQuickPointerTouchEvent; + class QQuickKeyEvent : public QObject { Q_OBJECT @@ -238,11 +246,303 @@ private: bool _accepted; }; +class Q_QUICK_PRIVATE_EXPORT QQuickEventPoint : public QObject +{ + Q_OBJECT + Q_PROPERTY(QPointF scenePos READ scenePos) + Q_PROPERTY(Qt::TouchPointState state READ state) + Q_PROPERTY(quint64 pointId READ pointId) + Q_PROPERTY(qreal timeHeld READ timeHeld) + Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted) + Q_PROPERTY(QQuickItem *grabber READ grabber WRITE setGrabber) + +public: + QQuickEventPoint(QQuickPointerEvent *parent); + + void reset(Qt::TouchPointState state, QPointF scenePos, quint64 pointId, ulong timestamp); + + void invalidate() { m_valid = false; } + + QQuickPointerEvent *pointerEvent() const; + QPointF scenePos() const { return m_scenePos; } + Qt::TouchPointState state() const { return m_state; } + quint64 pointId() const { return m_pointId; } + bool isValid() const { return m_valid; } + 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); + +private: + QPointF m_scenePos; + quint64 m_pointId; + QPointer<QQuickItem> m_grabber; + ulong m_timestamp; + ulong m_pressTimestamp; + Qt::TouchPointState m_state; + bool m_valid : 1; + bool m_accept : 1; + int m_reserved : 30; + + Q_DISABLE_COPY(QQuickEventPoint) +}; + +class Q_QUICK_PRIVATE_EXPORT QQuickEventTouchPoint : public QQuickEventPoint +{ + Q_OBJECT + Q_PROPERTY(qreal rotation READ rotation) + Q_PROPERTY(qreal pressure READ pressure) + Q_PROPERTY(QPointerUniqueId uniqueId READ uniqueId) + +public: + QQuickEventTouchPoint(QQuickPointerTouchEvent *parent); + + void reset(const QTouchEvent::TouchPoint &tp, ulong timestamp); + + qreal rotation() const { return m_rotation; } + qreal pressure() const { return m_pressure; } + QPointerUniqueId uniqueId() const { return m_uniqueId; } + +private: + qreal m_rotation; + qreal m_pressure; + QPointerUniqueId m_uniqueId; + + Q_DISABLE_COPY(QQuickEventTouchPoint) +}; + +class Q_QUICK_PRIVATE_EXPORT QQuickPointerEvent : public QObject +{ + Q_OBJECT + Q_PROPERTY(const QQuickPointerDevice *device READ device) + Q_PROPERTY(Qt::KeyboardModifiers modifiers READ modifiers) + Q_PROPERTY(Qt::MouseButtons button READ button) + Q_PROPERTY(Qt::MouseButtons buttons READ buttons) + +public: + QQuickPointerEvent(QObject *parent = nullptr) + : QObject(parent) + , m_device(nullptr) + , m_event(nullptr) + , m_button(Qt::NoButton) + , m_pressedButtons(Qt::NoButton) + , m_synthMouseEvent(QEvent::MouseMove, QPointF(), Qt::NoButton, Qt::NoButton, Qt::NoModifier) { } + + virtual ~QQuickPointerEvent(); + +public: // property accessors + QQuickPointerDevice *device() const { return m_device; } + Qt::KeyboardModifiers modifiers() const { return m_event ? m_event->modifiers() : Qt::NoModifier; } + Qt::MouseButton button() const { return m_button; } + Qt::MouseButtons buttons() const { return m_pressedButtons; } + +public: // helpers for C++ only (during event delivery) + virtual QQuickPointerEvent *reset(QEvent *ev) = 0; + + virtual bool isPressEvent() const = 0; + virtual QQuickPointerMouseEvent *asPointerMouseEvent() { return nullptr; } + virtual QQuickPointerTouchEvent *asPointerTouchEvent() { return nullptr; } + virtual QQuickPointerTabletEvent *asPointerTabletEvent() { return nullptr; } + virtual const QQuickPointerMouseEvent *asPointerMouseEvent() const { return nullptr; } + virtual const QQuickPointerTouchEvent *asPointerTouchEvent() const { return nullptr; } + virtual const QQuickPointerTabletEvent *asPointerTabletEvent() const { return nullptr; } + bool isValid() const { return m_event != nullptr; } + virtual bool allPointsAccepted() const = 0; + bool isAccepted() { return m_event->isAccepted(); } + void setAccepted(bool accepted) { m_event->setAccepted(accepted); } + QVector<QPointF> unacceptedPointScenePositions() const; + 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 void clearGrabbers() const = 0; + + ulong timestamp() const { return m_event->timestamp(); } + +protected: + + QQuickPointerDevice *m_device; + QInputEvent *m_event; // original event as received by QQuickWindow + Qt::MouseButton m_button; + Qt::MouseButtons m_pressedButtons; + mutable QMouseEvent m_synthMouseEvent; + + Q_DISABLE_COPY(QQuickPointerEvent) +}; + +class Q_QUICK_PRIVATE_EXPORT QQuickPointerMouseEvent : public QQuickPointerEvent +{ +public: + QQuickPointerMouseEvent(QObject *parent = nullptr) + : QQuickPointerEvent(parent), m_mousePoint(new QQuickEventPoint(this)) { } + + QQuickPointerEvent *reset(QEvent *) override; + bool isPressEvent() 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; + bool allPointsAccepted() const override; + QVector<QQuickItem *> grabbers() const override; + void clearGrabbers() const override; + + QMouseEvent *asMouseEvent(const QPointF& localPos) const; + +private: + QQuickEventPoint *m_mousePoint; + + Q_DISABLE_COPY(QQuickPointerMouseEvent) +}; + +class Q_QUICK_PRIVATE_EXPORT QQuickPointerTouchEvent : public QQuickPointerEvent +{ +public: + QQuickPointerTouchEvent(QObject *parent = nullptr) + : QQuickPointerEvent(parent), m_pointCount(0) { } + + QQuickPointerEvent *reset(QEvent *) override; + bool isPressEvent() 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; + const QTouchEvent::TouchPoint *touchPointById(int pointId) const; + bool allPointsAccepted() const override; + QVector<QQuickItem *> grabbers() const override; + void clearGrabbers() const override; + + QMouseEvent *syntheticMouseEvent(int pointID, QQuickItem *relativeTo) const; + QTouchEvent *touchEventForItem(QQuickItem *item, bool isFiltering = false) const; + + QTouchEvent *asTouchEvent() const; + +private: + int m_pointCount; + QVector<QQuickEventTouchPoint *> m_touchPoints; + + Q_DISABLE_COPY(QQuickPointerTouchEvent) +}; + +// ### Qt 6: move this to qtbase, replace QTouchDevice and the enums in QTabletEvent +class Q_QUICK_PRIVATE_EXPORT QQuickPointerDevice : public QObject +{ + Q_OBJECT + Q_PROPERTY(DeviceType type READ type CONSTANT) + Q_PROPERTY(PointerType pointerType READ pointerType CONSTANT) + Q_PROPERTY(Capabilities capabilities READ capabilities CONSTANT) + Q_PROPERTY(int maximumTouchPoints READ maximumTouchPoints CONSTANT) + Q_PROPERTY(int buttonCount READ buttonCount CONSTANT) + Q_PROPERTY(QString name READ name CONSTANT) + Q_PROPERTY(qint64 uniqueId READ uniqueId CONSTANT) + +public: + enum DeviceType { + UnknownDevice = 0x0000, + Mouse = 0x0001, + TouchScreen = 0x0002, + TouchPad = 0x0004, + Puck = 0x0008, + Stylus = 0x0010, + Airbrush = 0x0020, + AllDevices = 0x003F + }; + Q_DECLARE_FLAGS(DeviceTypes, DeviceType) + Q_ENUM(DeviceType) + Q_FLAG(DeviceTypes) + + enum PointerType { + GenericPointer = 0x0001, + Finger = 0x0002, + Pen = 0x0004, + Eraser = 0x0008, + Cursor = 0x0010, + AllPointerTypes = 0x001F + }; + Q_DECLARE_FLAGS(PointerTypes, PointerType) + Q_ENUM(PointerType) + Q_FLAG(PointerTypes) + + enum CapabilityFlag { + Position = QTouchDevice::Position, + Area = QTouchDevice::Area, + Pressure = QTouchDevice::Pressure, + Velocity = QTouchDevice::Velocity, + // some bits reserved in case we need more of QTouchDevice::Capabilities + Scroll = 0x0100, // mouse has a wheel, or there is OS-level scroll gesture recognition (dubious?) + Hover = 0x0200, + Rotation = 0x0400, + XTilt = 0x0800, + YTilt = 0x1000 + }; + Q_DECLARE_FLAGS(Capabilities, CapabilityFlag) + 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(uniqueId), m_event(nullptr) + { + if (m_deviceType == Mouse) { + m_event = new QQuickPointerMouseEvent; + } else if (m_deviceType == TouchScreen || m_deviceType == TouchPad) { + m_event = new QQuickPointerTouchEvent; + } else { + Q_ASSERT(false); + } + } + + ~QQuickPointerDevice() { delete m_event; } + DeviceType type() const { return m_deviceType; } + PointerType pointerType() const { return m_pointerType; } + Capabilities capabilities() const { return m_capabilities; } + bool hasCapability(CapabilityFlag cap) { return m_capabilities & cap; } + int maximumTouchPoints() const { return m_maximumTouchPoints; } + int buttonCount() const { return m_buttonCount; } + QString name() const { return m_name; } + qint64 uniqueId() const { return m_uniqueId; } + QQuickPointerEvent *pointerEvent() const { return m_event; } + + static QQuickPointerDevice *touchDevice(QTouchDevice *d); + static QList<QQuickPointerDevice *> touchDevices(); + static QQuickPointerDevice *genericMouseDevice(); + static QQuickPointerDevice *tabletDevice(qint64); + +private: + DeviceType m_deviceType; + PointerType m_pointerType; + Capabilities m_capabilities; + int m_maximumTouchPoints; + int m_buttonCount; + QString m_name; + qint64 m_uniqueId; + // the device-specific event instance which is reused during event delivery + QQuickPointerEvent *m_event; + + Q_DISABLE_COPY(QQuickPointerDevice) +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickPointerDevice::DeviceTypes) +Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickPointerDevice::PointerTypes) +Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickPointerDevice::Capabilities) + +Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug, const QQuickPointerDevice *); +Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug, const QQuickPointerEvent *); +Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug, const QQuickEventPoint *); +//Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug, const QQuickEventTouchPoint *); TODO maybe + QT_END_NAMESPACE QML_DECLARE_TYPE(QQuickKeyEvent) QML_DECLARE_TYPE(QQuickMouseEvent) QML_DECLARE_TYPE(QQuickWheelEvent) QML_DECLARE_TYPE(QQuickCloseEvent) +QML_DECLARE_TYPE(QQuickPointerDevice) +QML_DECLARE_TYPE(QPointerUniqueId) +QML_DECLARE_TYPE(QQuickPointerEvent) #endif // QQUICKEVENTS_P_P_H diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 8905faf973..84f9b0f169 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -1426,7 +1426,7 @@ void QQuickKeysAttached::inputMethodEvent(QInputMethodEvent *event, bool post) d->inIM = true; for (QQuickItem *targetItem : qAsConst(d->targets)) { if (targetItem && targetItem->isVisible() && (targetItem->flags() & QQuickItem::ItemAcceptsInputMethod)) { - d->item->window()->sendEvent(targetItem, event); + QCoreApplication::sendEvent(targetItem, event); if (event->isAccepted()) { d->imeItem = targetItem; d->inIM = false; diff --git a/src/quick/items/qquickmousearea.cpp b/src/quick/items/qquickmousearea.cpp index 33cc6c9a63..0118d882af 100644 --- a/src/quick/items/qquickmousearea.cpp +++ b/src/quick/items/qquickmousearea.cpp @@ -405,8 +405,7 @@ bool QQuickMouseAreaPrivate::propagateHelper(QQuickMouseEvent *ev, QQuickItem *i /*! \qmlsignal QtQuick::MouseArea::canceled() - This signal is emitted when mouse events have been canceled, either because an event was not accepted, or - because another item stole the mouse event handling. + This signal is emitted when mouse events have been canceled, because another item stole the mouse event handling. This signal is for advanced use: it is useful when there is more than one MouseArea that is handling input, or when there is a MouseArea inside a \l Flickable. In the latter @@ -1198,6 +1197,11 @@ bool QQuickMouseArea::setPressed(Qt::MouseButton button, bool p, Qt::MouseEventS emit mouseXChanged(&me); me.setPosition(d->lastPos); emit mouseYChanged(&me); + + if (!me.isAccepted()) { + d->pressed = Qt::NoButton; + } + if (!oldPressed) { emit pressedChanged(); emit containsPressChanged(); diff --git a/src/quick/items/qquickmultipointtoucharea.cpp b/src/quick/items/qquickmultipointtoucharea.cpp index ac5598767a..89bff6e057 100644 --- a/src/quick/items/qquickmultipointtoucharea.cpp +++ b/src/quick/items/qquickmultipointtoucharea.cpp @@ -448,20 +448,12 @@ void QQuickMultiPointTouchArea::touchEvent(QTouchEvent *event) } } updateTouchData(event); - if (event->type() == QEvent::TouchEnd) { - //TODO: move to window - _stealMouse = false; - setKeepMouseGrab(false); - setKeepTouchGrab(false); - ungrabTouchPoints(); - } + if (event->type() == QEvent::TouchEnd) + ungrab(); break; } case QEvent::TouchCancel: - _stealMouse = false; - setKeepMouseGrab(false); - setKeepTouchGrab(false); - ungrabTouchPoints(); + ungrab(); break; default: QQuickItem::touchEvent(event); @@ -784,13 +776,12 @@ void QQuickMultiPointTouchArea::mouseReleaseEvent(QMouseEvent *event) void QQuickMultiPointTouchArea::ungrab() { + _stealMouse = false; + setKeepMouseGrab(false); + setKeepTouchGrab(false); + ungrabTouchPoints(); + if (_touchPoints.count()) { - QQuickWindow *c = window(); - if (c && c->mouseGrabberItem() == this) { - _stealMouse = false; - setKeepMouseGrab(false); - } - setKeepTouchGrab(false); foreach (QObject *obj, _touchPoints) static_cast<QQuickTouchPoint*>(obj)->setPressed(false); emit canceled(_touchPoints.values()); @@ -881,11 +872,7 @@ bool QQuickMultiPointTouchArea::childMouseEventFilter(QQuickItem *i, QEvent *eve if (!shouldFilter(event)) return false; updateTouchData(event); - //TODO: verify this behavior - _stealMouse = false; - setKeepMouseGrab(false); - setKeepTouchGrab(false); - ungrabTouchPoints(); + ungrab(); } break; default: diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index a2c82c30a0..ae3b272e72 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -471,7 +471,6 @@ void QQuickWindowPrivate::renderSceneGraph(const QSize &size) QQuickWindowPrivate::QQuickWindowPrivate() : contentItem(0) , activeFocusItem(0) - , mouseGrabberItem(0) #ifndef QT_NO_CURSOR , cursorItem(0) #endif @@ -479,6 +478,7 @@ QQuickWindowPrivate::QQuickWindowPrivate() , dragGrabber(0) #endif , touchMouseId(-1) + , touchMouseDevice(nullptr) , touchMousePressTimestamp(0) , dirtyItemList(0) , devicePixelRatio(0) @@ -486,7 +486,7 @@ QQuickWindowPrivate::QQuickWindowPrivate() , renderer(0) , windowManager(0) , renderControl(0) - , touchRecursionGuard(0) + , pointerEventRecursionGuard(0) , customRenderStage(0) , clearColor(Qt::white) , clearBeforeRendering(true) @@ -622,8 +622,11 @@ bool QQuickWindowPrivate::checkIfDoubleClicked(ulong newPressEventTimestamp) return doubleClicked; } -bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *event) +bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QTouchEvent *event) { + Q_Q(QQuickWindow); + auto device = QQuickPointerDevice::touchDevice(event->device()); + // For each point, check if it is accepted, if not, try the next point. // Any of the fingers can become the mouse one. // This can happen because a mouse area might not accept an event at some point but another. @@ -637,46 +640,32 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e if (!item->contains(pos)) break; - // Store the id already here and restore it to -1 if the event does not get - // accepted. Cannot defer setting the new value because otherwise if the event - // handler spins the event loop all subsequent moves and releases get lost. - touchMouseId = p.id(); - itemForTouchPointId[touchMouseId] = item; + // FIXME: this is a bit backwards, should just have the pointer event passed into the function + auto pointerEventPoint = device->pointerEvent()->pointById(p.id()); + pointerEventPoint->setGrabber(item); qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << p.id() << "->" << item; QScopedPointer<QMouseEvent> mousePress(touchToMouseEvent(QEvent::MouseButtonPress, p, event, item, false)); // Send a single press and see if that's accepted - if (!mouseGrabberItem) - item->grabMouse(); - item->grabTouchPoints(QVector<int>() << touchMouseId); - QCoreApplication::sendEvent(item, mousePress.data()); event->setAccepted(mousePress->isAccepted()); - if (!mousePress->isAccepted()) { - touchMouseId = -1; - if (itemForTouchPointId.value(p.id()) == item) { - qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << p.id() << "disassociated"; - itemForTouchPointId.remove(p.id()); + if (mousePress->isAccepted()) { + touchMouseDevice = device; + touchMouseId = p.id(); + if (!q->mouseGrabberItem()) + item->grabMouse(); + item->grabTouchPoints(QVector<int>() << touchMouseId); + + if (checkIfDoubleClicked(event->timestamp())) { + QScopedPointer<QMouseEvent> mouseDoubleClick(touchToMouseEvent(QEvent::MouseButtonDblClick, p, event, item, false)); + QCoreApplication::sendEvent(item, mouseDoubleClick.data()); + event->setAccepted(mouseDoubleClick->isAccepted()); + if (!mouseDoubleClick->isAccepted()) { + touchMouseId = -1; + touchMouseDevice = nullptr; + } } - if (mouseGrabberItem == item) - item->ungrabMouse(); - } - - if (mousePress->isAccepted() && checkIfDoubleClicked(event->timestamp())) { - QScopedPointer<QMouseEvent> mouseDoubleClick(touchToMouseEvent(QEvent::MouseButtonDblClick, p, event, item, false)); - QCoreApplication::sendEvent(item, mouseDoubleClick.data()); - event->setAccepted(mouseDoubleClick->isAccepted()); - if (mouseDoubleClick->isAccepted()) { - touchMouseIdCandidates.clear(); - return true; - } else { - touchMouseId = -1; - } - } - // The event was accepted, we are done. - if (mousePress->isAccepted()) { - touchMouseIdCandidates.clear(); return true; } // The event was not accepted but touchMouseId was set. @@ -685,15 +674,16 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e // try the next point // Touch point was there before and moved - } else if (p.id() == touchMouseId) { + } else if (touchMouseDevice == device && p.id() == touchMouseId) { if (p.state() & Qt::TouchPointMoved) { - if (mouseGrabberItem) { + if (QQuickItem *mouseGrabberItem = q->mouseGrabberItem()) { QScopedPointer<QMouseEvent> me(touchToMouseEvent(QEvent::MouseMove, p, event, mouseGrabberItem, false)); QCoreApplication::sendEvent(item, me.data()); event->setAccepted(me->isAccepted()); if (me->isAccepted()) { qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << p.id() << "->" << mouseGrabberItem; - itemForTouchPointId[p.id()] = mouseGrabberItem; // N.B. the mouseGrabberItem may be different after returning from sendEvent() + auto pointerEventPoint = device->pointerEvent()->pointById(p.id()); + pointerEventPoint->setGrabber(q->mouseGrabberItem()); // N.B. the mouseGrabberItem may be different after returning from sendEvent() return true; } } else { @@ -718,7 +708,8 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e } else if (p.state() & Qt::TouchPointReleased) { // currently handled point was released touchMouseId = -1; - if (mouseGrabberItem) { + touchMouseDevice = nullptr; + if (QQuickItem *mouseGrabberItem = q->mouseGrabberItem()) { QScopedPointer<QMouseEvent> me(touchToMouseEvent(QEvent::MouseButtonRelease, p, event, mouseGrabberItem, false)); QCoreApplication::sendEvent(item, me.data()); @@ -730,8 +721,8 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e Qt::NoButton, Qt::NoButton, event->modifiers()); QCoreApplication::sendEvent(item, &mm); } - if (mouseGrabberItem) // might have ungrabbed due to event - mouseGrabberItem->ungrabMouse(); + if (q->mouseGrabberItem()) // might have ungrabbed due to event + q->mouseGrabberItem()->ungrabMouse(); return me->isAccepted(); } } @@ -744,25 +735,29 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e void QQuickWindowPrivate::setMouseGrabber(QQuickItem *grabber) { Q_Q(QQuickWindow); - if (mouseGrabberItem == grabber) + if (q->mouseGrabberItem() == grabber) return; - qCDebug(DBG_MOUSE_TARGET) << "grabber" << mouseGrabberItem << "->" << grabber; - QQuickItem *oldGrabber = mouseGrabberItem; - mouseGrabberItem = grabber; + qCDebug(DBG_MOUSE_TARGET) << "grabber" << q->mouseGrabberItem() << "->" << grabber; + QQuickItem *oldGrabber = q->mouseGrabberItem(); + + QQuickPointerEvent *event = QQuickPointerDevice::genericMouseDevice()->pointerEvent(); + Q_ASSERT(event->pointCount() == 1); + event->point(0)->setGrabber(grabber); - if (touchMouseId != -1) { + if (grabber && touchMouseId != -1 && touchMouseDevice) { // update the touch item for mouse touch id to the new grabber - itemForTouchPointId.remove(touchMouseId); - if (grabber) { - qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << touchMouseId << "->" << mouseGrabberItem; - itemForTouchPointId[touchMouseId] = grabber; - } + qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << touchMouseId << "->" << q->mouseGrabberItem(); + auto point = touchMouseDevice->pointerEvent()->pointById(touchMouseId); + if (point) + point->setGrabber(grabber); } if (oldGrabber) { - QEvent ev(QEvent::UngrabMouse); - q->sendEvent(oldGrabber, &ev); + QEvent e(QEvent::UngrabMouse); + QSet<QQuickItem *> hasFiltered; + if (!sendFilteredMouseEvent(oldGrabber->parentItem(), oldGrabber, &e, &hasFiltered)) + oldGrabber->mouseUngrabEvent(); } } @@ -771,20 +766,24 @@ void QQuickWindowPrivate::grabTouchPoints(QQuickItem *grabber, const QVector<int Q_Q(QQuickWindow); QSet<QQuickItem*> ungrab; for (int i = 0; i < ids.count(); ++i) { - QQuickItem *oldGrabber = itemForTouchPointId.value(ids.at(i)); - if (oldGrabber == grabber) - continue; + // FIXME: deprecate this function, we need a device + for (auto device: QQuickPointerDevice::touchDevices()) { + auto point = device->pointerEvent()->pointById(ids.at(i)); + if (!point) + continue; + QQuickItem *oldGrabber = point->grabber(); + if (oldGrabber == grabber) + continue; - itemForTouchPointId[ids.at(i)] = grabber; - if (oldGrabber) - ungrab.insert(oldGrabber); + point->setGrabber(grabber); + if (oldGrabber) + ungrab.insert(oldGrabber); + } - QQuickItem *originalMouseGrabberItem = mouseGrabberItem; + QQuickItem *mouseGrabberItem = q->mouseGrabberItem(); if (touchMouseId == ids.at(i) && mouseGrabberItem && mouseGrabberItem != grabber) { qCDebug(DBG_MOUSE_TARGET) << "grabTouchPoints: grabber" << mouseGrabberItem << "-> null"; - mouseGrabberItem = 0; - QEvent ev(QEvent::UngrabMouse); - q->sendEvent(originalMouseGrabberItem, &ev); + setMouseGrabber(nullptr); } } foreach (QQuickItem *oldGrabber, ungrab) @@ -795,35 +794,23 @@ void QQuickWindowPrivate::removeGrabber(QQuickItem *grabber, bool mouse, bool to { Q_Q(QQuickWindow); if (Q_LIKELY(touch)) { - QMutableHashIterator<int, QQuickItem *> itemTouchMapIt(itemForTouchPointId); - while (itemTouchMapIt.hasNext()) { - if (itemTouchMapIt.next().value() == grabber) { - itemTouchMapIt.remove(); - grabber->touchUngrabEvent(); + for (auto device: QQuickPointerDevice::touchDevices()) { + auto pointerEvent = device->pointerEvent(); + 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 (Q_LIKELY(mouse) && mouseGrabberItem == grabber) { - qCDebug(DBG_MOUSE_TARGET) << "removeGrabber" << mouseGrabberItem << "-> null"; - mouseGrabberItem = 0; - QEvent ev(QEvent::UngrabMouse); - q->sendEvent(grabber, &ev); + if (Q_LIKELY(mouse) && q->mouseGrabberItem() == grabber) { + qCDebug(DBG_MOUSE_TARGET) << "removeGrabber" << q->mouseGrabberItem() << "-> null"; + setMouseGrabber(nullptr); } } -void QQuickWindowPrivate::transformTouchPoints(QList<QTouchEvent::TouchPoint> &touchPoints, const QTransform &transform) -{ - QMatrix4x4 transformMatrix(transform); - for (int i=0; i<touchPoints.count(); i++) { - QTouchEvent::TouchPoint &touchPoint = touchPoints[i]; - touchPoint.setRect(transform.mapRect(touchPoint.sceneRect())); - touchPoint.setStartPos(transform.map(touchPoint.startScenePos())); - touchPoint.setLastPos(transform.map(touchPoint.lastScenePos())); - touchPoint.setVelocity(transformMatrix.mapVector(touchPoint.velocity()).toVector2D()); - } -} - - /*! Translates the data in \a touchEvent to this window. This method leaves the item local positions in \a touchEvent untouched (these are filled in later). @@ -837,9 +824,6 @@ void QQuickWindowPrivate::translateTouchEvent(QTouchEvent *touchEvent) touchPoint.setSceneRect(touchPoint.rect()); touchPoint.setStartScenePos(touchPoint.startPos()); touchPoint.setLastScenePos(touchPoint.lastPos()); - - if (i == 0) - lastMousePosition = touchPoint.pos().toPoint(); } touchEvent->setTouchPoints(touchPoints); } @@ -950,17 +934,16 @@ void QQuickWindowPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, Q } // Now that all the state is changed, emit signals & events - // We must do this last, as this process may result in further changes to - // focus. + // We must do this last, as this process may result in further changes to focus. if (oldActiveFocusItem) { QFocusEvent event(QEvent::FocusOut, reason); - q->sendEvent(oldActiveFocusItem, &event); + QCoreApplication::sendEvent(oldActiveFocusItem, &event); } // Make sure that the FocusOut didn't result in another focus change. if (sendFocusIn && activeFocusItem == newActiveFocusItem) { QFocusEvent event(QEvent::FocusIn, reason); - q->sendEvent(newActiveFocusItem, &event); + QCoreApplication::sendEvent(newActiveFocusItem, &event); } if (activeFocusItem != currentActiveFocusItem) @@ -1047,13 +1030,13 @@ void QQuickWindowPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem *item, // focus. if (oldActiveFocusItem) { QFocusEvent event(QEvent::FocusOut, reason); - q->sendEvent(oldActiveFocusItem, &event); + QCoreApplication::sendEvent(oldActiveFocusItem, &event); } // Make sure that the FocusOut didn't result in another focus change. if (newActiveFocusItem && activeFocusItem == newActiveFocusItem) { QFocusEvent event(QEvent::FocusIn, reason); - q->sendEvent(newActiveFocusItem, &event); + QCoreApplication::sendEvent(newActiveFocusItem, &event); } if (activeFocusItem != currentActiveFocusItem) @@ -1482,9 +1465,9 @@ QObject *QQuickWindow::focusObject() const */ QQuickItem *QQuickWindow::mouseGrabberItem() const { - Q_D(const QQuickWindow); - - return d->mouseGrabberItem; + QQuickPointerEvent *event = QQuickPointerDevice::genericMouseDevice()->pointerEvent(); + Q_ASSERT(event->pointCount()); + return event->point(0)->grabber(); } @@ -1529,7 +1512,7 @@ bool QQuickWindow::event(QEvent *e) break; case QEvent::Leave: d->clearHover(); - d->lastMousePosition = QPoint(); + d->lastMousePosition = QPointF(); break; #ifndef QT_NO_DRAGANDDROP case QEvent::DragEnter: @@ -1555,8 +1538,8 @@ bool QQuickWindow::event(QEvent *e) if (d->activeFocusItem) qGuiApp->inputMethod()->commit(); #endif - if (d->mouseGrabberItem) - d->mouseGrabberItem->ungrabMouse(); + if (mouseGrabberItem()) + mouseGrabberItem()->ungrabMouse(); break; case QEvent::UpdateRequest: { if (d->windowManager) @@ -1570,7 +1553,7 @@ bool QQuickWindow::event(QEvent *e) #endif case QEvent::ShortcutOverride: if (d->activeFocusItem) - sendEvent(d->activeFocusItem, static_cast<QKeyEvent *>(e)); + QCoreApplication::sendEvent(d->activeFocusItem, e); return true; default: break; @@ -1622,78 +1605,44 @@ QMouseEvent *QQuickWindowPrivate::cloneMouseEvent(QMouseEvent *event, QPointF *t return me; } -bool QQuickWindowPrivate::deliverInitialMousePressEvent(QQuickItem *item, QMouseEvent *event) +void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEvent) { Q_Q(QQuickWindow); - - QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); - - if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) { - QPointF p = item->mapFromScene(event->windowPos()); - if (!item->contains(p)) - return false; - } - - QList<QQuickItem *> children = itemPrivate->paintOrderChildItems(); - for (int ii = children.count() - 1; ii >= 0; --ii) { - QQuickItem *child = children.at(ii); - if (!child->isVisible() || !child->isEnabled() || QQuickItemPrivate::get(child)->culled) - continue; - if (deliverInitialMousePressEvent(child, event)) - return true; - } - - if (itemPrivate->acceptedMouseButtons() & event->button()) { - QPointF localPos = item->mapFromScene(event->windowPos()); - if (item->contains(localPos)) { - QScopedPointer<QMouseEvent> me(cloneMouseEvent(event, &localPos)); - me->accept(); - item->grabMouse(); - q->sendEvent(item, me.data()); - event->setAccepted(me->isAccepted()); - if (me->isAccepted()) - return true; - if (mouseGrabberItem) - mouseGrabberItem->ungrabMouse(); + 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; } - } - return false; -} - -bool QQuickWindowPrivate::deliverMouseEvent(QMouseEvent *event) -{ - Q_Q(QQuickWindow); - - lastMousePosition = event->windowPos(); - - if (!mouseGrabberItem && - event->type() == QEvent::MouseButtonPress && - (event->buttons() & event->button()) == event->buttons()) { - if (deliverInitialMousePressEvent(contentItem, event)) - event->accept(); - else - event->ignore(); - return event->isAccepted(); - } + // send update + QPointF localPos = grabber->mapFromScene(lastMousePosition); + auto me = pointerEvent->asMouseEvent(localPos); + me->accept(); + q->sendEvent(grabber, me); + point->setAccepted(me->isAccepted()); - if (mouseGrabberItem) { - if (event->button() != Qt::NoButton - && mouseGrabberItem->acceptedMouseButtons() - && !(mouseGrabberItem->acceptedMouseButtons() & event->button())) { - event->ignore(); - return false; + // release event, make sure to ungrab if there still is a grabber + if (me->type() == QEvent::MouseButtonRelease && !me->buttons() && q->mouseGrabberItem()) + q->mouseGrabberItem()->ungrabMouse(); + } else { + // send initial press + bool delivered = false; + if (pointerEvent->isPressEvent()) { + QSet<QQuickItem*> hasFiltered; + delivered = deliverPressEvent(pointerEvent, &hasFiltered); } - QPointF localPos = mouseGrabberItem->mapFromScene(event->windowPos()); - QScopedPointer<QMouseEvent> me(cloneMouseEvent(event, &localPos)); - me->accept(); - q->sendEvent(mouseGrabberItem, me.data()); - event->setAccepted(me->isAccepted()); - if (me->isAccepted()) - return true; - } - return false; + if (!delivered) + // make sure not to accept unhandled events + pointerEvent->setAccepted(false); + } } bool QQuickWindowPrivate::sendHoverEvent(QEvent::Type type, QQuickItem *item, @@ -1701,7 +1650,6 @@ bool QQuickWindowPrivate::sendHoverEvent(QEvent::Type type, QQuickItem *item, Qt::KeyboardModifiers modifiers, ulong timestamp, bool accepted) { - Q_Q(QQuickWindow); const QTransform transform = QQuickItemPrivate::get(item)->windowToItemTransform(); //create copy of event @@ -1714,7 +1662,7 @@ bool QQuickWindowPrivate::sendHoverEvent(QEvent::Type type, QQuickItem *item, return true; } - q->sendEvent(item, &hoverEvent); + QCoreApplication::sendEvent(item, &hoverEvent); return hoverEvent.isAccepted(); } @@ -1798,7 +1746,6 @@ bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &sce #ifndef QT_NO_WHEELEVENT bool QQuickWindowPrivate::deliverWheelEvent(QQuickItem *item, QWheelEvent *event) { - Q_Q(QQuickWindow); QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) { @@ -1822,7 +1769,7 @@ bool QQuickWindowPrivate::deliverWheelEvent(QQuickItem *item, QWheelEvent *event QWheelEvent wheel(p, p, event->pixelDelta(), event->angleDelta(), event->delta(), event->orientation(), event->buttons(), event->modifiers(), event->phase(), event->source(), event->inverted()); wheel.accept(); - q->sendEvent(item, &wheel); + QCoreApplication::sendEvent(item, &wheel); if (wheel.isAccepted()) { event->accept(); return true; @@ -1889,20 +1836,22 @@ bool QQuickWindowPrivate::deliverTouchCancelEvent(QTouchEvent *event) { qCDebug(DBG_TOUCH) << event; Q_Q(QQuickWindow); + // A TouchCancel event will typically not contain any points. // Deliver it to all items that have active touches. - QSet<QQuickItem *> cancelDelivered; - foreach (QQuickItem *item, itemForTouchPointId) { - if (cancelDelivered.contains(item)) - continue; - cancelDelivered.insert(item); - q->sendEvent(item, event); + QQuickPointerEvent *pointerEvent = QQuickPointerDevice::touchDevice(event->device())->pointerEvent(); + QVector<QQuickItem *> grabbers = pointerEvent->grabbers(); + + for (QQuickItem *grabber: qAsConst(grabbers)) { + q->sendEvent(grabber, event); } touchMouseId = -1; - if (mouseGrabberItem) - mouseGrabberItem->ungrabMouse(); + touchMouseDevice = nullptr; + if (q->mouseGrabberItem()) + q->mouseGrabberItem()->ungrabMouse(); + // The next touch event can only be a TouchBegin so clean up. - itemForTouchPointId.clear(); + pointerEvent->clearGrabbers(); return true; } @@ -1912,7 +1861,7 @@ void QQuickWindowPrivate::deliverDelayedTouchEvent() // Set delayedTouch to 0 before delivery to avoid redelivery in case of // event loop recursions (e.g if it the touch starts a dnd session). QScopedPointer<QTouchEvent> e(delayedTouch.take()); - deliverTouchEvent(e.data()); + deliverPointerEvent(pointerEventInstance(e.data())); } static bool qquickwindow_no_touch_compression = qEnvironmentVariableIsSet("QML_NO_TOUCH_COMPRESSION"); @@ -1988,17 +1937,20 @@ bool QQuickWindowPrivate::compressTouchEvent(QTouchEvent *event) void QQuickWindowPrivate::handleTouchEvent(QTouchEvent *event) { translateTouchEvent(event); + if (event->touchPoints().size()) + lastMousePosition = event->touchPoints().at(0).pos(); + qCDebug(DBG_TOUCH) << event; - if (qquickwindow_no_touch_compression || touchRecursionGuard) { - deliverTouchEvent(event); + if (qquickwindow_no_touch_compression || pointerEventRecursionGuard) { + deliverPointerEvent(pointerEventInstance(event)); return; } if (!compressTouchEvent(event)) { if (delayedTouch) deliverDelayedTouchEvent(); - deliverTouchEvent(event); + deliverPointerEvent(pointerEventInstance(event)); } } @@ -2029,6 +1981,8 @@ void QQuickWindow::mouseReleaseEvent(QMouseEvent *event) void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event) { + Q_Q(QQuickWindow); + if (event->source() == Qt::MouseEventSynthesizedBySystem) { event->accept(); return; @@ -2039,33 +1993,17 @@ void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event) case QEvent::MouseButtonPress: Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMousePress, event->button(), event->buttons()); - deliverMouseEvent(event); + deliverPointerEvent(pointerEventInstance(event)); break; case QEvent::MouseButtonRelease: Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseRelease, event->button(), event->buttons()); - if (!mouseGrabberItem) { - event->ignore(); - return; - } - - deliverMouseEvent(event); - if (mouseGrabberItem && !event->buttons()) - mouseGrabberItem->ungrabMouse(); + deliverPointerEvent(pointerEventInstance(event)); break; case QEvent::MouseButtonDblClick: Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseDoubleClick, event->button(), event->buttons()); - - if (!mouseGrabberItem && (event->buttons() & event->button()) == event->buttons()) { - if (deliverInitialMousePressEvent(contentItem, event)) - event->accept(); - else - event->ignore(); - return; - } - - deliverMouseEvent(event); + deliverPointerEvent(pointerEventInstance(event)); break; case QEvent::MouseMove: Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseMove, @@ -2077,10 +2015,8 @@ void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event) updateCursor(event->windowPos()); #endif - if (!mouseGrabberItem) { - if (lastMousePosition.isNull()) - lastMousePosition = event->windowPos(); - QPointF last = lastMousePosition; + if (!q->mouseGrabberItem()) { + QPointF last = lastMousePosition.isNull() ? event->windowPos() : lastMousePosition; lastMousePosition = event->windowPos(); bool accepted = event->isAccepted(); @@ -2092,7 +2028,7 @@ void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event) event->setAccepted(accepted); return; } - deliverMouseEvent(event); + deliverPointerEvent(pointerEventInstance(event)); break; default: Q_ASSERT(false); @@ -2102,6 +2038,8 @@ void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event) void QQuickWindowPrivate::flushFrameSynchronousEvents() { + Q_Q(QQuickWindow); + if (delayedTouch) { deliverDelayedTouchEvent(); @@ -2116,7 +2054,7 @@ void QQuickWindowPrivate::flushFrameSynchronousEvents() // For instance, during animation (including the case of a ListView // whose delegates contain MouseAreas), a MouseArea needs to know // whether it has moved into a position where it is now under the cursor. - if (!mouseGrabberItem && !lastMousePosition.isNull()) { + if (!q->mouseGrabberItem() && !lastMousePosition.isNull()) { bool accepted = false; bool delivered = deliverHoverEvent(contentItem, lastMousePosition, lastMousePosition, QGuiApplication::keyboardModifiers(), 0, accepted); if (!delivered) @@ -2124,167 +2062,209 @@ void QQuickWindowPrivate::flushFrameSynchronousEvents() } } -void QQuickWindowPrivate::deliverTouchEvent(QTouchEvent *event) -{ - qCDebug(DBG_TOUCH) << " - delivering" << event; +/*! + \internal + Returns a QQuickPointerEvent instance suitable for wrapping and delivering \a event. - // If users spin the eventloop as a result of touch delivery, we disable - // touch compression and send events directly. This is because we consider - // the usecase a bit evil, but we at least don't want to lose events. - ++touchRecursionGuard; - - // List of all items that received an event before - // When we have TouchBegin this is and will stay empty - QHash<QQuickItem *, QList<QTouchEvent::TouchPoint> > updatedPoints; - - // Figure out who accepted a touch point last and put it in updatedPoints - // Add additional item to newPoints - const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints(); - QList<QTouchEvent::TouchPoint> newPoints; - for (int i=0; i<touchPoints.count(); i++) { - const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i); - if (touchPoint.state() == Qt::TouchPointPressed) { - newPoints << touchPoint; - } else { - // TouchPointStationary is relevant only to items which - // are also receiving touch points with some other state. - // But we have not yet decided which points go to which item, - // so for now we must include all non-new points in updatedPoints. - if (itemForTouchPointId.contains(touchPoint.id())) { - QQuickItem *item = itemForTouchPointId.value(touchPoint.id()); - if (item) - updatedPoints[item].append(touchPoint); - } - } + There is a unique instance per QQuickPointerDevice, which is determined + from \a event's device. +*/ +QQuickPointerEvent *QQuickWindowPrivate::pointerEventInstance(QEvent *event) +{ + QQuickPointerDevice *dev = nullptr; + switch (event->type()) { + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseButtonDblClick: + case QEvent::MouseMove: + dev = QQuickPointerDevice::genericMouseDevice(); + break; + case QEvent::TouchBegin: + case QEvent::TouchUpdate: + case QEvent::TouchEnd: + case QEvent::TouchCancel: + dev = QQuickPointerDevice::touchDevice(static_cast<QTouchEvent *>(event)->device()); + break; + // TODO tablet event types + default: + break; } + Q_ASSERT(dev); + return dev->pointerEvent()->reset(event); +} - // Deliver the event, but only if there is at least one new point - // or some item accepted a point and should receive an update - if (newPoints.count() > 0 || updatedPoints.count() > 0) { - QSet<int> acceptedNewPoints; - QSet<QQuickItem *> hasFiltered; - event->setAccepted(deliverTouchPoints(contentItem, event, newPoints, &acceptedNewPoints, &updatedPoints, &hasFiltered)); - } else - event->ignore(); - - // Remove released points from itemForTouchPointId - if (event->touchPointStates() & Qt::TouchPointReleased) { - for (int i=0; i<touchPoints.count(); i++) { - if (touchPoints[i].state() == Qt::TouchPointReleased) { - qCDebug(DBG_TOUCH_TARGET) << "TP" << touchPoints[i].id() << "released"; - itemForTouchPointId.remove(touchPoints[i].id()); - if (touchPoints[i].id() == touchMouseId) - touchMouseId = -1; - touchMouseIdCandidates.remove(touchPoints[i].id()); - } - } - } +void QQuickWindowPrivate::deliverPointerEvent(QQuickPointerEvent *event) +{ + // If users spin the eventloop as a result of event delivery, we disable + // event compression and send events directly. This is because we consider + // the usecase a bit evil, but we at least don't want to lose events. + ++pointerEventRecursionGuard; - if (event->type() == QEvent::TouchEnd) { - if (!itemForTouchPointId.isEmpty()) { - qWarning() << "No release received for" << itemForTouchPointId.size() - << "touch points over" << itemForTouchPointId.begin().value() - << "on touch end."; - itemForTouchPointId.clear(); - } + if (event->asPointerMouseEvent()) { + deliverMouseEvent(event->asPointerMouseEvent()); + } else if (event->asPointerTouchEvent()) { + deliverTouchEvent(event->asPointerTouchEvent()); + } else { + Q_ASSERT(false); } - --touchRecursionGuard; + --pointerEventRecursionGuard; } -// This function recurses and sends the events to the individual items -bool QQuickWindowPrivate::deliverTouchPoints(QQuickItem *item, QTouchEvent *event, const QList<QTouchEvent::TouchPoint> &newPoints, - QSet<int> *acceptedNewPoints, QHash<QQuickItem *, - QList<QTouchEvent::TouchPoint> > *updatedPoints, QSet<QQuickItem *> *hasFiltered) +// 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 { - QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); - + QVector<QQuickItem *> targets; + auto itemPrivate = QQuickItemPrivate::get(item); + QPointF itemPos = item->mapFromScene(scenePos); + // if the item clips, we can potentially return early if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) { - for (int i=0; i<newPoints.count(); i++) { - QPointF p = item->mapFromScene(newPoints[i].scenePos()); - if (!item->contains(p)) - return false; - } + if (!item->contains(itemPos)) + return targets; } - // Check if our children want the event (or parts of it) - // This is the only point where touch event delivery recurses! + // recurse for children QList<QQuickItem *> children = itemPrivate->paintOrderChildItems(); for (int ii = children.count() - 1; ii >= 0; --ii) { QQuickItem *child = children.at(ii); - if (!child->isEnabled() || !child->isVisible() || QQuickItemPrivate::get(child)->culled) + auto childPrivate = QQuickItemPrivate::get(child); + if (!child->isVisible() || !child->isEnabled() || childPrivate->culled) continue; - if (deliverTouchPoints(child, event, newPoints, acceptedNewPoints, updatedPoints, hasFiltered)) - return true; + targets << pointerTargets(child, scenePos, false); } - // None of the children accepted the event, so check the given item itself. - // First, construct matchingPoints as a list of TouchPoints which the - // given item might be interested in. Any newly-pressed point which is - // inside the item's bounds will be interesting, and also any updated point - // which was already accepted by that item when it was first pressed. - // (A point which was already accepted is effectively "grabbed" by the item.) - - // set of IDs of "interesting" new points - QSet<int> matchingNewPoints; - // set of points which this item has previously accepted, for starters - QList<QTouchEvent::TouchPoint> matchingPoints = (*updatedPoints)[item]; - // now add the new points which are inside this item's bounds - if (newPoints.count() > 0 && acceptedNewPoints->count() < newPoints.count()) { - for (int i = 0; i < newPoints.count(); i++) { - if (acceptedNewPoints->contains(newPoints[i].id())) - continue; - QPointF p = item->mapFromScene(newPoints[i].scenePos()); - if (item->contains(p)) { - matchingNewPoints.insert(newPoints[i].id()); - matchingPoints << newPoints[i]; - } + if (item->contains(itemPos) && (!checkMouseButtons || itemPrivate->acceptedMouseButtons())) { + // add this item last - children take precedence + targets << item; + } + return targets; +} + +// return the joined lists +// list1 has priority, common items come last +QVector<QQuickItem *> QQuickWindowPrivate::mergePointerTargets(const QVector<QQuickItem *> &list1, const QVector<QQuickItem *> &list2) const +{ + QVector<QQuickItem *> targets = list1; + // start at the end of list2 + // if item not in list, append it + // if item found, move to next one, inserting before the last found one + int insertPosition = targets.length(); + for (int i = list2.length() - 1; i >= 0; --i) { + int newInsertPosition = targets.lastIndexOf(list2.at(i), insertPosition); + if (newInsertPosition >= 0) { + Q_ASSERT(newInsertPosition <= insertPosition); + insertPosition = newInsertPosition; } + // check for duplicates, only insert if the item isn't there already + if (insertPosition == targets.size() || list2.at(i) != targets.at(insertPosition)) + targets.insert(insertPosition, list2.at(i)); } - // If there are no matching new points, and the existing points are all stationary, - // there's no need to send an event to this item. This is required by a test in - // tst_qquickwindow::touchEvent_basic: - // a single stationary press on an item shouldn't cause an event - if (matchingNewPoints.isEmpty()) { - bool stationaryOnly = true; - - foreach (const QTouchEvent::TouchPoint &tp, matchingPoints) { - if (tp.state() != Qt::TouchPointStationary) { - stationaryOnly = false; - break; + return targets; +} + +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); + + // Remove released points from itemForTouchPointId + bool allReleased = true; + int pointCount = event->pointCount(); + for (int i = 0; i < pointCount; ++i) { + QQuickEventPoint *point = event->point(i); + if (point->state() == Qt::TouchPointReleased) { + int id = point->pointId(); + qCDebug(DBG_TOUCH_TARGET) << "TP" << id << "released"; + point->setGrabber(nullptr); + if (id == touchMouseId) { + touchMouseId = -1; + touchMouseDevice = nullptr; } + } else { + allReleased = false; } + } - if (stationaryOnly) - matchingPoints.clear(); + if (allReleased && !event->grabbers().isEmpty()) { + qWarning() << "No release received for some grabbers" << event->grabbers(); + event->clearGrabbers(); } +} - if (!matchingPoints.isEmpty()) { - // Now we know this item might be interested in the event. Copy and send it, but - // with only the subset of TouchPoints which are relevant to that item: that's matchingPoints. - QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); - transformTouchPoints(matchingPoints, itemPrivate->windowToItemTransform()); - deliverMatchingPointsToItem(item, event, acceptedNewPoints, matchingNewPoints, matchingPoints, hasFiltered); +// Deliver touch points to existing grabbers +bool QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event, QSet<QQuickItem *> *hasFiltered) +{ + for (auto grabber: event->grabbers()) { + deliverMatchingPointsToItem(grabber, event, hasFiltered); } - // record the fact that this item has been visited already - updatedPoints->remove(item); + return false; +} + +// Deliver newly pressed touch points +bool QQuickWindowPrivate::deliverPressEvent(QQuickPointerEvent *event, QSet<QQuickItem *> *hasFiltered) +{ + const QVector<QPointF> points = event->unacceptedPressedPointScenePositions(); + QVector<QQuickItem *> targetItems; + for (QPointF point: points) { + QVector<QQuickItem *> targetItemsForPoint = pointerTargets(contentItem, point, false); + if (targetItems.count()) { + targetItems = mergePointerTargets(targetItems, targetItemsForPoint); + } else { + targetItems = targetItemsForPoint; + } + } - // recursion is done only if ALL touch points have been delivered - return (acceptedNewPoints->count() == newPoints.count() && updatedPoints->isEmpty()); + for (QQuickItem *item: targetItems) { + deliverMatchingPointsToItem(item, event, hasFiltered); + if (event->allPointsAccepted()) + break; + } + + return event->allPointsAccepted(); } -// touchEventForItem has no means to generate a touch event that contains -// only the points that are relevant for this item. Thus the need for -// matchingPoints to already be that set of interesting points. -// They are all pre-transformed, too. -bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QTouchEvent *event, QSet<int> *acceptedNewPoints, const QSet<int> &matchingNewPoints, const QList<QTouchEvent::TouchPoint> &matchingPoints, QSet<QQuickItem *> *hasFiltered) +bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent, QSet<QQuickItem *> *hasFiltered) { - QScopedPointer<QTouchEvent> touchEvent(touchEventWithPoints(*event, matchingPoints)); - touchEvent.data()->setTarget(item); - bool touchEventAccepted = false; + Q_Q(QQuickWindow); + + // TODO: unite this mouse point delivery with the synthetic mouse event below + if (auto event = pointerEvent->asPointerMouseEvent()) { + if (item->acceptedMouseButtons() & event->button()) { + auto point = event->point(0); + if (point->isAccepted()) + return false; + QPointF localPos = item->mapFromScene(point->scenePos()); + Q_ASSERT(item->contains(localPos)); // transform is checked already + QMouseEvent *me = event->asMouseEvent(localPos); + me->accept(); + q->sendEvent(item, me); + if (me->isAccepted()) { + if (!q->mouseGrabberItem()) + item->grabMouse(); + point->setAccepted(true); + } + return me->isAccepted(); + } + return false; + } + + QQuickPointerTouchEvent *event = pointerEvent->asPointerTouchEvent(); + if (!event) + return false; + + QScopedPointer<QTouchEvent> touchEvent(event->touchEventForItem(item)); + if (!touchEvent) + return false; 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. @@ -2292,114 +2272,53 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QTouchEv // 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; - foreach (int id, matchingNewPoints) - acceptedNewPoints->insert(id); + for (auto point: qAsConst(touchEvent->touchPoints())) { + event->pointById(point.id())->setAccepted(); + } return true; } - // Since it can change in sendEvent, update itemForTouchPointId now - foreach (int id, matchingNewPoints) { - qCDebug(DBG_TOUCH_TARGET) << "TP" << id << "->" << item; - itemForTouchPointId[id] = item; - } - // Deliver the touch event to the given item qCDebug(DBG_TOUCH) << " - actually delivering " << touchEvent.data() << " to " << item; QCoreApplication::sendEvent(item, touchEvent.data()); - touchEventAccepted = touchEvent->isAccepted(); + 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 (!touchEventAccepted && (itemPrivate->acceptedMouseButtons() & Qt::LeftButton)) { + if (!eventAccepted && (itemPrivate->acceptedMouseButtons() & Qt::LeftButton)) { // send mouse event - event->setAccepted(translateTouchToMouse(item, touchEvent.data())); - if (event->isAccepted()) { - touchEventAccepted = true; - } + if (deliverTouchAsMouse(item, touchEvent.data())) + eventAccepted = true; } - if (touchEventAccepted) { + if (eventAccepted) { // If the touch was accepted (regardless by whom or in what form), - // update acceptedNewPoints. - foreach (int id, matchingNewPoints) - acceptedNewPoints->insert(id); + // update accepted new points. + for (auto point: qAsConst(touchEvent->touchPoints())) { + auto pointerEventPoint = event->pointById(point.id()); + pointerEventPoint->setAccepted(); + if (point.state() == Qt::TouchPointPressed) + pointerEventPoint->setGrabber(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. - foreach (int id, matchingNewPoints) - if (itemForTouchPointId[id] == item) { - qCDebug(DBG_TOUCH_TARGET) << "TP" << id << "disassociated"; - itemForTouchPointId.remove(id); - } - } - - return touchEventAccepted; -} - -// create touch event containing only points inside the target item -QTouchEvent *QQuickWindowPrivate::touchEventForItem(QQuickItem *target, const QTouchEvent &originalEvent, bool alwaysCheckBounds) -{ - const QList<QTouchEvent::TouchPoint> &touchPoints = originalEvent.touchPoints(); - QList<QTouchEvent::TouchPoint> pointsInBounds; - // if all points are stationary, the list of points should be empty to signal a no-op - if (originalEvent.touchPointStates() != Qt::TouchPointStationary) { - for (int i = 0; i < touchPoints.count(); ++i) { - const QTouchEvent::TouchPoint &tp = touchPoints.at(i); - // Touch presses are relevant to the target item only if they occur inside its bounds. - // Touch updates and releases are relevant if they occur inside, or if we want to - // finish the sequence because the press occurred inside. - if (tp.state() == Qt::TouchPointPressed || alwaysCheckBounds) { - QPointF p = target->mapFromScene(tp.scenePos()); - if (target->contains(p)) - pointsInBounds.append(tp); - } else { - pointsInBounds.append(tp); + 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); + } } } - transformTouchPoints(pointsInBounds, QQuickItemPrivate::get(target)->windowToItemTransform()); - } - - QTouchEvent* touchEvent = touchEventWithPoints(originalEvent, pointsInBounds); - touchEvent->setTarget(target); - return touchEvent; -} - -// copy a touch event's basic properties but give it new touch points -QTouchEvent *QQuickWindowPrivate::touchEventWithPoints(const QTouchEvent &event, const QList<QTouchEvent::TouchPoint> &newPoints) -{ - Qt::TouchPointStates eventStates; - for (int i=0; i<newPoints.count(); i++) - eventStates |= newPoints[i].state(); - // if all points have the same state, set the event type accordingly - QEvent::Type eventType = event.type(); - switch (eventStates) { - case Qt::TouchPointPressed: - eventType = QEvent::TouchBegin; - break; - case Qt::TouchPointReleased: - eventType = QEvent::TouchEnd; - break; - default: - eventType = QEvent::TouchUpdate; - break; } - QTouchEvent *touchEvent = new QTouchEvent(eventType); - touchEvent->setWindow(event.window()); - touchEvent->setTarget(event.target()); - touchEvent->setDevice(event.device()); - touchEvent->setModifiers(event.modifiers()); - touchEvent->setTouchPoints(newPoints); - touchEvent->setTouchPointStates(eventStates); - touchEvent->setTimestamp(event.timestamp()); - touchEvent->accept(); - return touchEvent; + return eventAccepted; } #ifndef QT_NO_DRAGANDDROP void QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *event) { - Q_Q(QQuickWindow); grabber->resetTarget(); QQuickDragGrabber::iterator grabItem = grabber->begin(); if (grabItem != grabber->end()) { @@ -2415,7 +2334,7 @@ void QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *e e->mouseButtons(), e->keyboardModifiers()); QQuickDropEventEx::copyActions(&translatedEvent, *e); - q->sendEvent(**grabItem, &translatedEvent); + QCoreApplication::sendEvent(**grabItem, &translatedEvent); e->setAccepted(translatedEvent.isAccepted()); e->setDropAction(translatedEvent.dropAction()); grabber->setTarget(**grabItem); @@ -2424,7 +2343,7 @@ void QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *e if (event->type() != QEvent::DragMove) { // Either an accepted drop or a leave. QDragLeaveEvent leaveEvent; for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem)) - q->sendEvent(**grabItem, &leaveEvent); + QCoreApplication::sendEvent(**grabItem, &leaveEvent); return; } else for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem)) { QDragMoveEvent *moveEvent = static_cast<QDragMoveEvent *>(event); @@ -2440,18 +2359,18 @@ void QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *e moveEvent->mouseButtons(), moveEvent->keyboardModifiers()); QQuickDropEventEx::copyActions(&translatedEvent, *moveEvent); - q->sendEvent(**grabItem, &translatedEvent); + QCoreApplication::sendEvent(**grabItem, &translatedEvent); ++grabItem; } else { QDragLeaveEvent leaveEvent; - q->sendEvent(**grabItem, &leaveEvent); + QCoreApplication::sendEvent(**grabItem, &leaveEvent); grabItem = grabber->release(grabItem); } } return; } else { QDragLeaveEvent leaveEvent; - q->sendEvent(**grabItem, &leaveEvent); + QCoreApplication::sendEvent(**grabItem, &leaveEvent); } } } @@ -2470,7 +2389,6 @@ void QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *e bool QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQuickItem *item, QDragMoveEvent *event) { - Q_Q(QQuickWindow); bool accepted = false; QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); if (!item->isVisible() || !item->isEnabled() || QQuickItemPrivate::get(item)->culled) @@ -2505,7 +2423,7 @@ bool QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQuickIte event->keyboardModifiers(), event->type()); QQuickDropEventEx::copyActions(&translatedEvent, *event); - q->sendEvent(item, &translatedEvent); + QCoreApplication::sendEvent(item, &translatedEvent); if (event->type() == QEvent::DragEnter) { if (translatedEvent.isAccepted()) { grabber->grab(item); @@ -2568,8 +2486,10 @@ QQuickItem *QQuickWindowPrivate::findCursorItem(QQuickItem *item, const QPointF } #endif -bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QTouchEvent *event, QSet<QQuickItem *> *hasFiltered) +bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QQuickPointerTouchEvent *event, QSet<QQuickItem *> *hasFiltered) { + Q_Q(QQuickWindow); + if (!target) return false; @@ -2578,8 +2498,8 @@ bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(target); if (targetPrivate->filtersChildMouseEvents && !hasFiltered->contains(target)) { hasFiltered->insert(target); - QScopedPointer<QTouchEvent> targetEvent(touchEventForItem(target, *event)); - if (!targetEvent->touchPoints().isEmpty()) { + 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; @@ -2588,9 +2508,10 @@ bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem for (int i = 0; i < touchPointCount; ++i) touchIds.append(targetEvent->touchPoints().at(i).id()); target->grabTouchPoints(touchIds); - if (mouseGrabberItem) { - mouseGrabberItem->ungrabMouse(); + if (q->mouseGrabberItem()) { + q->mouseGrabberItem()->ungrabMouse(); touchMouseId = -1; + touchMouseDevice = nullptr; } filtered = true; } @@ -2602,12 +2523,6 @@ bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem switch (tp.state()) { case Qt::TouchPointPressed: t = QEvent::MouseButtonPress; - if (touchMouseId == -1) { - // We don't want to later filter touches as a mouse event if they were pressed - // while a touchMouseId was already active. - // Remember this touch as a potential to become the touchMouseId. - touchMouseIdCandidates.insert(tp.id()); - } break; case Qt::TouchPointReleased: t = QEvent::MouseButtonRelease; @@ -2620,18 +2535,20 @@ bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem } // Only deliver mouse event if it is the touchMouseId or it could become the touchMouseId - if ((touchMouseIdCandidates.contains(tp.id()) && touchMouseId == -1) || touchMouseId == tp.id()) { + if (touchMouseId == -1 || touchMouseId == tp.id()) { // targetEvent is already transformed wrt local position, velocity, etc. - QScopedPointer<QMouseEvent> mouseEvent(touchToMouseEvent(t, tp, event, item, false)); + + // FIXME: remove asTouchEvent!!! + QScopedPointer<QMouseEvent> mouseEvent(touchToMouseEvent(t, tp, event->asTouchEvent(), item, false)); if (target->childMouseEventFilter(item, mouseEvent.data())) { qCDebug(DBG_TOUCH) << " - second chance intercepted on childMouseEventFilter by " << target; if (t != QEvent::MouseButtonRelease) { qCDebug(DBG_TOUCH_TARGET) << "TP" << tp.id() << "->" << target; - itemForTouchPointId[tp.id()] = target; touchMouseId = tp.id(); + touchMouseDevice = event->device(); + touchMouseDevice->pointerEvent()->pointById(tp.id())->setGrabber(target); target->grabMouse(); } - touchMouseIdCandidates.clear(); filtered = true; } // Only one event can be filtered as a mouse event. @@ -2785,8 +2702,13 @@ void QQuickWindowPrivate::contextCreationFailureMessage(const QSurfaceFormat &fo /*! Propagates an event \a e to a QQuickItem \a item on the window. + Use \l QCoreApplication::sendEvent() directly instead. + The return value is currently not used. + + \deprecated */ +// ### Qt6: remove bool QQuickWindow::sendEvent(QQuickItem *item, QEvent *e) { Q_D(QQuickWindow); @@ -2808,9 +2730,6 @@ bool QQuickWindow::sendEvent(QQuickItem *item, QEvent *e) QCoreApplication::sendEvent(item, e); } break; - case QEvent::ShortcutOverride: - QCoreApplication::sendEvent(item, e); - break; case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: case QEvent::MouseButtonDblClick: @@ -2824,41 +2743,8 @@ bool QQuickWindow::sendEvent(QQuickItem *item, QEvent *e) } } break; - case QEvent::UngrabMouse: { - QSet<QQuickItem *> hasFiltered; - if (!d->sendFilteredMouseEvent(item->parentItem(), item, e, &hasFiltered)) { - e->accept(); - item->mouseUngrabEvent(); - } - } - break; -#ifndef QT_NO_WHEELEVENT - case QEvent::Wheel: -#endif -#ifndef QT_NO_DRAGANDDROP - case QEvent::DragEnter: - case QEvent::DragMove: - case QEvent::DragLeave: - case QEvent::Drop: -#endif - case QEvent::FocusIn: - case QEvent::FocusOut: - case QEvent::HoverEnter: - case QEvent::HoverLeave: - case QEvent::HoverMove: - case QEvent::TouchCancel: - QCoreApplication::sendEvent(item, e); - break; - case QEvent::TouchBegin: - case QEvent::TouchUpdate: - case QEvent::TouchEnd: { - QSet<QQuickItem*> hasFiltered; - QTouchEvent *ev = static_cast<QTouchEvent *>(e); - qCDebug(DBG_TOUCH) << " - sendEvent for " << ev << " to " << item->parentItem() << " and " << item; - d->sendFilteredTouchEvent(item->parentItem(), item, ev, &hasFiltered); - } - break; default: + QCoreApplication::sendEvent(item, e); break; } diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 40a361a897..90e75fb751 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -53,6 +53,7 @@ #include "qquickitem.h" #include "qquickwindow.h" +#include "qquickevents_p_p.h" #include <QtQuick/private/qsgcontext_p.h> @@ -68,13 +69,19 @@ QT_BEGIN_NAMESPACE -//Make it easy to identify and customize the root item if needed - +class QOpenGLVertexArrayObjectHelper; class QQuickAnimatorController; -class QSGRenderLoop; -class QQuickRenderControl; class QQuickDragGrabber; +class QQuickItemPrivate; +class QQuickPointerDevice; +class QQuickRenderControl; +class QQuickWindowIncubationController; +class QQuickWindowPrivate; +class QQuickWindowRenderLoop; +class QSGRenderLoop; +class QTouchEvent; +//Make it easy to identify and customize the root item if needed class QQuickRootItem : public QQuickItem { Q_OBJECT @@ -85,15 +92,6 @@ public Q_SLOTS: void setHeight(int h) {QQuickItem::setHeight(qreal(h));} }; -class QQuickItemPrivate; -class QQuickWindowPrivate; - -class QTouchEvent; -class QQuickWindowRenderLoop; -class QQuickWindowIncubationController; - -class QOpenGLVertexArrayObjectHelper; - class Q_QUICK_PRIVATE_EXPORT QQuickCustomRenderStage { public: @@ -127,7 +125,6 @@ public: void deliverKeyEvent(QKeyEvent *e); // Keeps track of the item currently receiving mouse events - QQuickItem *mouseGrabberItem; #ifndef QT_NO_CURSOR QQuickItem *cursorItem; #endif @@ -135,20 +132,19 @@ public: QQuickDragGrabber *dragGrabber; #endif int touchMouseId; + QQuickPointerDevice *touchMouseDevice; bool checkIfDoubleClicked(ulong newPressEventTimestamp); ulong touchMousePressTimestamp; // Mouse positions are saved in widget coordinates QPointF lastMousePosition; - bool translateTouchToMouse(QQuickItem *item, QTouchEvent *event); + bool deliverTouchAsMouse(QQuickItem *item, QTouchEvent *event); void translateTouchEvent(QTouchEvent *touchEvent); void setMouseGrabber(QQuickItem *grabber); void grabTouchPoints(QQuickItem *grabber, const QVector<int> &ids); void removeGrabber(QQuickItem *grabber, bool mouse = true, bool touch = true); - static void transformTouchPoints(QList<QTouchEvent::TouchPoint> &touchPoints, const QTransform &transform); static QMouseEvent *cloneMouseEvent(QMouseEvent *event, QPointF *transformedLocalPos = 0); - bool deliverInitialMousePressEvent(QQuickItem *, QMouseEvent *); - bool deliverMouseEvent(QMouseEvent *); + void deliverMouseEvent(QQuickPointerMouseEvent *pointerEvent); bool sendFilteredMouseEvent(QQuickItem *, QQuickItem *, QEvent *, QSet<QQuickItem *> *); #ifndef QT_NO_WHEELEVENT bool deliverWheelEvent(QQuickItem *, QWheelEvent *); @@ -156,23 +152,33 @@ public: #ifndef QT_NO_GESTURES bool deliverNativeGestureEvent(QQuickItem *, QNativeGestureEvent *); #endif - bool deliverTouchPoints(QQuickItem *, QTouchEvent *, const QList<QTouchEvent::TouchPoint> &, QSet<int> *, - QHash<QQuickItem *, QList<QTouchEvent::TouchPoint> > *, QSet<QQuickItem*> *filtered); + + // entry point of events to the window void handleTouchEvent(QTouchEvent *); void handleMouseEvent(QMouseEvent *); - void deliverTouchEvent(QTouchEvent *); bool compressTouchEvent(QTouchEvent *); - bool deliverTouchCancelEvent(QTouchEvent *); - void deliverDelayedTouchEvent(); void flushFrameSynchronousEvents(); + void deliverDelayedTouchEvent(); + + // delivery of pointer events: + QQuickPointerEvent *pointerEventInstance(QEvent *ev); + 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); + + QVector<QQuickItem *> pointerTargets(QQuickItem *, const QPointF &, bool checkMouseButtons) const; + QVector<QQuickItem *> mergePointerTargets(const QVector<QQuickItem *> &list1, const QVector<QQuickItem *> &list2) const; + + // hover delivery bool deliverHoverEvent(QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, ulong timestamp, bool &accepted); - bool deliverMatchingPointsToItem(QQuickItem *item, QTouchEvent *event, QSet<int> *acceptedNewPoints, const QSet<int> &matchingNewPoints, const QList<QTouchEvent::TouchPoint> &matchingPoints, QSet<QQuickItem*> *filtered); - static QTouchEvent *touchEventForItem(QQuickItem *target, const QTouchEvent &originalEvent, bool alwaysCheckBounds = false); - static QTouchEvent *touchEventWithPoints(const QTouchEvent &event, const QList<QTouchEvent::TouchPoint> &newPoints); - bool sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QTouchEvent *event, QSet<QQuickItem*> *filtered); bool sendHoverEvent(QEvent::Type, QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, ulong timestamp, bool accepted); bool clearHover(ulong timestamp = 0); + #ifndef QT_NO_DRAGANDDROP void deliverDragEvent(QQuickDragGrabber *, QEvent *); bool deliverDragEvent(QQuickDragGrabber *, QQuickItem *, QDragMoveEvent *); @@ -237,7 +243,8 @@ public: QQuickRenderControl *renderControl; QQuickAnimatorController *animationController; QScopedPointer<QTouchEvent> delayedTouch; - int touchRecursionGuard; + + int pointerEventRecursionGuard; QQuickCustomRenderStage *customRenderStage; QColor clearColor; @@ -258,10 +265,6 @@ public: QOpenGLVertexArrayObjectHelper *vaoHelper; - // Keeps track of which touch point (int) was last accepted by which item - QHash<int, QQuickItem *> itemForTouchPointId; - QSet<int> touchMouseIdCandidates; - mutable QQuickWindowIncubationController *incubationController; static bool defaultAlphaBuffer; |