From 8932e80d0c8879a1e720fef825ed0d9c4e384a01 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Sat, 29 Aug 2020 16:59:21 +0200 Subject: Add more QPointerEvent functions needed in Qt Quick Change-Id: I87a874477b89eb3f5951930f03e305d896a24c2e Reviewed-by: Volker Hilsheimer --- src/gui/kernel/qevent.cpp | 59 ++++++++++++++++++++++++ src/gui/kernel/qevent.h | 31 +++++++++++++ src/gui/kernel/qevent_p.h | 6 +++ src/gui/kernel/qpointingdevice.cpp | 93 ++++++++++++++++++++++++++++++++++++++ src/gui/kernel/qpointingdevice.h | 1 + src/gui/kernel/qpointingdevice_p.h | 5 ++ 6 files changed, 195 insertions(+) diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp index 811d2d1f20..6f4208fe84 100644 --- a/src/gui/kernel/qevent.cpp +++ b/src/gui/kernel/qevent.cpp @@ -605,6 +605,44 @@ QPointerEvent::~QPointerEvent() { } +/*! + Returns the point whose \l {QEventPoint::id()}{id} matches the given \a id, + or \c nullptr if no such point is found. +*/ +QEventPoint *QPointerEvent::pointById(int id) +{ + for (auto &p : m_points) { + if (p.id() == id) + return &p; + } + return nullptr; +} + +/*! + Returns \c true if every point in points() has an exclusiveGrabber(). +*/ +bool QPointerEvent::allPointsGrabbed() const +{ + for (const auto &p : points()) { + if (exclusiveGrabber(p) && passiveGrabbers(p).isEmpty()) + return false; + } + return true; +} + +/*! + Returns \c true if isPointAccepted() is \c true for every point in + points(); otherwise \c false. +*/ +bool QPointerEvent::allPointsAccepted() const +{ + for (const auto &p : points()) { + if (!p.isAccepted()) + return false; + } + return true; +} + /*! Returns the source device from which this event originates. @@ -844,6 +882,27 @@ QSinglePointEvent::QSinglePointEvent(QEvent::Type type, const QPointingDevice *d m_points.append(mut); } +/*! \internal + Constructs a single-point event with the given \a point, which must be an instance + (or copy of one) that already exists in QPointingDevicePrivate::activePoints. + Unlike the other constructor, it does not modify the given \a point in any way. + This is useful when synthesizing a QMouseEvent from one point taken from a QTouchEvent, for example. + + \sa QMutableSinglePointEvent() +*/ +QSinglePointEvent::QSinglePointEvent(QEvent::Type type, const QPointingDevice *dev, const QEventPoint &point, + Qt::MouseButton button, Qt::MouseButtons buttons, + Qt::KeyboardModifiers modifiers, Qt::MouseEventSource source) + : QPointerEvent(type, dev, modifiers), + m_button(button), + m_mouseState(buttons), + m_source(source), + m_doubleClick(false), + m_reserved(0) +{ + m_points << point; +} + /*! Returns \c true if this event represents a \l {button()}{button} being pressed. */ diff --git a/src/gui/kernel/qevent.h b/src/gui/kernel/qevent.h index 7f9a780959..ab87fc3c4b 100644 --- a/src/gui/kernel/qevent.h +++ b/src/gui/kernel/qevent.h @@ -63,8 +63,11 @@ QT_BEGIN_NAMESPACE class QFile; class QAction; +class QMouseEvent; class QPointerEvent; class QScreen; +class QTabletEvent; +class QTouchEvent; #if QT_CONFIG(gestures) class QGesture; #endif @@ -94,6 +97,19 @@ struct QEventPointPrivate; class Q_GUI_EXPORT QEventPoint { Q_GADGET + Q_PROPERTY(const QPointingDevice *device READ device) + Q_PROPERTY(int id READ id) + Q_PROPERTY(QPointingDeviceUniqueId uniqueId READ uniqueId) + Q_PROPERTY(State state READ state) + Q_PROPERTY(ulong timestamp READ timestamp) + Q_PROPERTY(qreal timeHeld READ timeHeld) + Q_PROPERTY(qreal pressure READ pressure) + Q_PROPERTY(qreal rotation READ rotation) + Q_PROPERTY(QSizeF ellipseDiameters READ ellipseDiameters) + Q_PROPERTY(QVector2D velocity READ velocity) + Q_PROPERTY(QPointF position READ position) + Q_PROPERTY(QPointF scenePosition READ scenePosition) + Q_PROPERTY(QPointF globalPosition READ globalPosition) public: enum State : quint8 { Unknown = Qt::TouchPointUnknownState, @@ -192,9 +208,12 @@ public: qsizetype pointCount() const { return m_points.count(); } QEventPoint &point(qsizetype i) { return m_points[i]; } const QList &points() const { return m_points; } + QEventPoint *pointById(int id); + bool allPointsGrabbed() const; virtual bool isPressEvent() const { return false; } virtual bool isUpdateEvent() const { return false; } virtual bool isReleaseEvent() const { return false; } + bool allPointsAccepted() const; QObject *exclusiveGrabber(const QEventPoint &point) const; void setExclusiveGrabber(const QEventPoint &point, QObject *exclusiveGrabber); QList> passiveGrabbers(const QEventPoint &point) const; @@ -229,6 +248,10 @@ public: bool isReleaseEvent() const override; protected: + QSinglePointEvent(Type type, const QPointingDevice *dev, const QEventPoint &point, + Qt::MouseButton button, Qt::MouseButtons buttons, + Qt::KeyboardModifiers modifiers, Qt::MouseEventSource source); + Qt::MouseButton m_button = Qt::NoButton; Qt::MouseButtons m_mouseState = Qt::NoButton; quint32 m_source : 8; // actually Qt::MouseEventSource @@ -346,6 +369,12 @@ protected: #if QT_CONFIG(wheelevent) class Q_GUI_EXPORT QWheelEvent : public QSinglePointEvent { + Q_GADGET + Q_PROPERTY(const QPointingDevice *device READ pointingDevice) + Q_PROPERTY(QPoint pixelDelta READ pixelDelta) + Q_PROPERTY(QPoint angleDelta READ angleDelta) + Q_PROPERTY(Qt::ScrollPhase phase READ phase) + Q_PROPERTY(bool inverted READ inverted) public: enum { DefaultDeltasPerStep = 120 }; @@ -360,6 +389,8 @@ public: inline Qt::ScrollPhase phase() const { return Qt::ScrollPhase(m_phase); } inline bool inverted() const { return m_invertedScrolling; } + inline bool isInverted() const { return m_invertedScrolling; } + inline bool hasPixelDelta() const { return !m_pixelDelta.isNull(); } Qt::MouseEventSource source() const { return Qt::MouseEventSource(m_source); } diff --git a/src/gui/kernel/qevent_p.h b/src/gui/kernel/qevent_p.h index 2bad2c0f5b..23a171e6ab 100644 --- a/src/gui/kernel/qevent_p.h +++ b/src/gui/kernel/qevent_p.h @@ -199,6 +199,12 @@ static_assert(sizeof(QMutableTouchEvent) == sizeof(QTouchEvent)); class Q_GUI_EXPORT QMutableSinglePointEvent : public QSinglePointEvent { public: + QMutableSinglePointEvent(Type type, const QPointingDevice *device, const QEventPoint &point, + Qt::MouseButton button = Qt::NoButton, Qt::MouseButtons buttons = Qt::NoButton, + Qt::KeyboardModifiers modifiers = Qt::NoModifier, + Qt::MouseEventSource source = Qt::MouseEventSynthesizedByQt) : + QSinglePointEvent(type, device, point, button, buttons, modifiers, source) { } + static QMutableSinglePointEvent *from(QSinglePointEvent *e) { return static_cast(e); } static QMutableSinglePointEvent &from(QSinglePointEvent &e) { return static_cast(e); } diff --git a/src/gui/kernel/qpointingdevice.cpp b/src/gui/kernel/qpointingdevice.cpp index 094193db4d..227c4ef2cc 100644 --- a/src/gui/kernel/qpointingdevice.cpp +++ b/src/gui/kernel/qpointingdevice.cpp @@ -354,6 +354,33 @@ const QPointingDevice *QPointingDevicePrivate::queryTabletDevice(QInputDevice::D return nullptr; } +/*! + \internal + First, ensure that the \a cancelEvent's QTouchEvent::points() list contains + all points that have exclusive grabs. Then send the event to each object + that has an exclusive grab of any of the points. +*/ +void QPointingDevicePrivate::sendTouchCancelEvent(QTouchEvent *cancelEvent) +{ + // An incoming TouchCancel event will typically not contain any points, but + // QQuickPointerHandler::onGrabChanged needs to be called for each point + // that has an exclusive grabber. Adding those points to the event makes it + // an easy iteration there. + if (cancelEvent->points().isEmpty()) { + for (auto &epd : activePoints.values()) { + if (epd.exclusiveGrabber) + QMutableTouchEvent::from(cancelEvent)->addPoint(epd.eventPoint); + } + } + for (auto &epd : activePoints.values()) { + if (epd.exclusiveGrabber) + QCoreApplication::sendEvent(epd.exclusiveGrabber, cancelEvent); + // The next touch event can only be a TouchBegin, so clean up. + cancelEvent->setExclusiveGrabber(epd.eventPoint, nullptr); + cancelEvent->clearPassiveGrabbers(epd.eventPoint); + } +} + /*! \internal Returns the active EventPointData instance with the given \a id, if available, or \c nullptr if not. @@ -422,6 +449,19 @@ QWindow *QPointingDevicePrivate::firstActiveWindow() const return nullptr; } +/*! \internal + Return the exclusive grabber of the first point in activePoints. + This is mainly for autotests that try to verify the "current" grabber + outside the context of event delivery, which is something that the rest + of the codebase should not be doing. +*/ +QObject *QPointingDevicePrivate::firstPointExclusiveGrabber() const +{ + if (activePoints.isEmpty()) + return nullptr; + return activePoints.values().first().exclusiveGrabber; +} + void QPointingDevicePrivate::setExclusiveGrabber(const QPointerEvent *event, const QEventPoint &point, QObject *exclusiveGrabber) { Q_Q(QPointingDevice); @@ -446,6 +486,22 @@ void QPointingDevicePrivate::setExclusiveGrabber(const QPointerEvent *event, con emit q->grabChanged(exclusiveGrabber, QPointingDevice::GrabExclusive, event, point); } +/*! + \internal + Call QEventPoint::setExclusiveGrabber(nullptr) on each active point that has a grabber. +*/ +bool QPointingDevicePrivate::removeExclusiveGrabber(const QPointerEvent *event, const QObject *grabber) +{ + bool ret = false; + for (auto &pt : activePoints.values()) { + if (pt.exclusiveGrabber == grabber) { + setExclusiveGrabber(event, pt.eventPoint, nullptr); + ret = true; + } + } + return ret; +} + bool QPointingDevicePrivate::addPassiveGrabber(const QPointerEvent *event, const QEventPoint &point, QObject *grabber) { Q_Q(QPointingDevice); @@ -505,6 +561,43 @@ void QPointingDevicePrivate::clearPassiveGrabbers(const QPointerEvent *event, co persistentPoint->passiveGrabbers.clear(); } +/*! + \internal + Removes the given \a grabber as both passive and exclusive grabber from all + points in activePoints where it's currently found. If \a cancel is \c true, + the transition emitted from the grabChanged() signal will be + \c CancelGrabExclusive or \c CancelGrabPassive. Otherwise it will be + \c UngrabExclusive or \c UngrabPassive. + + \note This function provides a way to work around the limitation that we + normally change grabbers only during event delivery; but it's also more expensive. +*/ +void QPointingDevicePrivate::removeGrabber(QObject *grabber, bool cancel) +{ + Q_Q(QPointingDevice); + for (auto ap : activePoints) { + auto &epd = ap.second; + if (epd.exclusiveGrabber.data() == grabber) { + qCDebug(lcPointerGrab) << name << "point" << epd.eventPoint.id() << epd.eventPoint.state() + << "@" << epd.eventPoint.scenePosition() + << ": grab" << grabber << "-> nullptr"; + epd.exclusiveGrabber.clear(); + emit q->grabChanged(grabber, + cancel ? QPointingDevice::CancelGrabExclusive : QPointingDevice::UngrabExclusive, + nullptr, epd.eventPoint); + } + int pi = epd.passiveGrabbers.indexOf(grabber); + if (pi >= 0) { + qCDebug(lcPointerGrab) << name << "point" << epd.eventPoint.id() << epd.eventPoint.state() + << ": removing passive grabber" << grabber; + epd.passiveGrabbers.removeAt(pi); + emit q->grabChanged(grabber, + cancel ? QPointingDevice::CancelGrabPassive : QPointingDevice::UngrabPassive, + nullptr, epd.eventPoint); + } + } +} + /*! \internal Finds the device instance belonging to the drawing or eraser end of a particular stylus, diff --git a/src/gui/kernel/qpointingdevice.h b/src/gui/kernel/qpointingdevice.h index 476931432b..30c7c8ca1a 100644 --- a/src/gui/kernel/qpointingdevice.h +++ b/src/gui/kernel/qpointingdevice.h @@ -50,6 +50,7 @@ class QDebug; class QEventPoint; class QPointerEvent; class QPointingDevicePrivate; +class QPointerEvent; class QScreen; class Q_GUI_EXPORT QPointingDeviceUniqueId diff --git a/src/gui/kernel/qpointingdevice_p.h b/src/gui/kernel/qpointingdevice_p.h index 811f5ba01b..871c39939f 100644 --- a/src/gui/kernel/qpointingdevice_p.h +++ b/src/gui/kernel/qpointingdevice_p.h @@ -77,6 +77,8 @@ public: activePoints.reserve(maxPoints); } + void sendTouchCancelEvent(QTouchEvent *cancelEvent); + /*! \internal This struct (stored in activePoints) holds persistent state between event deliveries. */ @@ -91,10 +93,13 @@ public: QObject *firstActiveTarget() const; QWindow *firstActiveWindow() const; + QObject *firstPointExclusiveGrabber() const; void setExclusiveGrabber(const QPointerEvent *event, const QEventPoint &point, QObject *exclusiveGrabber); + bool removeExclusiveGrabber(const QPointerEvent *event, const QObject *grabber); bool addPassiveGrabber(const QPointerEvent *event, const QEventPoint &point, QObject *grabber); bool removePassiveGrabber(const QPointerEvent *event, const QEventPoint &point, QObject *grabber); void clearPassiveGrabbers(const QPointerEvent *event, const QEventPoint &point); + void removeGrabber(QObject *grabber, bool cancel = false); using EventPointMap = QFlatMap; mutable EventPointMap activePoints; -- cgit v1.2.3