diff options
Diffstat (limited to 'src/quick/items')
-rw-r--r-- | src/quick/items/qquickevents.cpp | 1636 | ||||
-rw-r--r-- | src/quick/items/qquickevents_p_p.h | 448 | ||||
-rw-r--r-- | src/quick/items/qquickflickable.cpp | 14 | ||||
-rw-r--r-- | src/quick/items/qquickitem.cpp | 165 | ||||
-rw-r--r-- | src/quick/items/qquickitem.h | 2 | ||||
-rw-r--r-- | src/quick/items/qquickitem_p.h | 5 | ||||
-rw-r--r-- | src/quick/items/qquickmultipointtoucharea.cpp | 33 | ||||
-rw-r--r-- | src/quick/items/qquickmultipointtoucharea_p.h | 2 | ||||
-rw-r--r-- | src/quick/items/qquickpincharea.cpp | 16 | ||||
-rw-r--r-- | src/quick/items/qquickwindow.cpp | 1004 | ||||
-rw-r--r-- | src/quick/items/qquickwindow_p.h | 55 |
11 files changed, 609 insertions, 2771 deletions
diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 66e850f59c..ab7b407af5 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -51,7 +51,6 @@ QT_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(lcPointerEvents, "qt.quick.pointer.events") -Q_LOGGING_CATEGORY(lcPtrGrab, "qt.quick.pointer.grab") /*! \qmltype KeyEvent @@ -470,1639 +469,4 @@ Item { \l inverted always returns false. */ -/*! - \qmltype PointerDevice - \instantiates QPointingDevice - \inqmlmodule QtQuick - \ingroup qtquick-input-events - - \brief Provides information about a pointing device. - - A pointing device can be a mouse, a touchscreen, or a stylus on a graphics - tablet. - - \sa PointerEvent, PointerHandler -*/ - -/*! - \readonly - \qmlproperty enumeration QtQuick::PointerDevice::type - - This property holds the type of the pointing device. - - Valid values are: - - \value DeviceType.Unknown - the device cannot be identified - \value DeviceType.Mouse - a mouse - \value DeviceType.TouchScreen - a touchscreen providing absolute coordinates - \value DeviceType.TouchPad - a trackpad or touchpad providing relative coordinates - \value DeviceType.Stylus - a pen-like device - \value DeviceType.Airbrush - a stylus with a thumbwheel to adjust - \l {QTabletEvent::tangentialPressure}{tangentialPressure} - \value DeviceType.Puck - a device that is similar to a flat mouse with a - transparent circle with cross-hairs - (same as \l {QTabletEvent::Puck} {Puck}) - \value DeviceType.AllDevices - any of the above; used as a default value for construction - - \sa QPointingDevice::DeviceType -*/ - -/*! - \readonly - \qmlproperty enumeration QtQuick::PointerDevice::pointerType - - This property holds a value indicating what is interacting with - the device. Think of the device as having a planar 2D surface, and - the value of this property as identifying what interacts with the - device. - - There is some redundancy between this property and \l {PointerDevice::type}. - If a tocuchscreen is used, then the device is TouchScreen and - pointerType is Finger (always). - - Valid values are: - - \value PointerDevice.Generic - a mouse or something acting like a mouse (the core pointer on X11) - \value PointerDevice.Finger - the user's finger - \value PointerDevice.Pen - the drawing end of a stylus - \value PointerDevice.Eraser - the other end of the stylus (if it has a virtual eraser on the other end) - \value PointerDevice.Cursor - a cursor in the pre-computer sense of the word - \value PointerDevice.AllPointerTypes - any of the above (used as a default value in constructors) -*/ - -/*! - \readonly - \qmlproperty enumeration QtQuick::PointerDevice::capabilities - - This property holds a bitwise combination of the capabilities of the - pointing device. It tells you under which conditions events are sent, - and which properties of PointerEvent are expected to be valid. - - Valid values are: - - \value Capability.Position - the \l {QtQuick::EventPoint::position}{position} and - \l {QtQuick::EventPoint::scenePosition}{scenePosition} properties - \value Capability.Area - the \l {QtQuick::EventTouchPoint::ellipseDiameters}{ellipseDiameters} property - \value Capability.Pressure - the \l {QtQuick::EventTouchPoint::pressure}{pressure} property - \value Capability.Velocity - the \l {QtQuick::EventPoint::velocity}{velocity} property - \value Capability.Scroll - a \l {QtQuick::PointerDevice::type}{Mouse} has a wheel, or the - operating system recognizes scroll gestures on a - \l {QtQuick::PointerDevice::type}{TouchPad} - \value Capability.Hover - events are sent even when no button is pressed, or the finger or stylus - is not in contact with the surface - \value Capability.Rotation - the \l {QtQuick::EventTouchPoint::rotation}{rotation} property - \value Capability.XTilt - horizontal angle between a stylus and the axis perpendicular to the surface - \value Capability.YTilt - vertical angle between a stylus and the axis perpendicular to the surface - - \sa QPointingDevice::capabilities -*/ - - -// debugging helpers -static const char *pointStateString(const QQuickEventPoint *point) -{ - static const QMetaEnum stateMetaEnum = point->metaObject()->enumerator(point->metaObject()->indexOfEnumerator("State")); - return stateMetaEnum.valueToKey(point->state()); -} - -static const QString pointDeviceName(const QQuickEventPoint *point) -{ - auto device = static_cast<const QQuickPointerEvent *>(point->parent())->device(); - QString deviceName = (device ? device->name() : QLatin1String("null device")); - deviceName.resize(16, u' '); // shorten, and align in case of sequential output - return deviceName; -} - -/*! - \qmltype EventPoint - \qmlabstract - \instantiates QQuickEventPoint - \inqmlmodule QtQuick - \ingroup qtquick-input-events - \brief Provides information about an individual point within a PointerEvent. - - A PointerEvent contains an EventPoint for each point of contact: one corresponding - to the mouse cursor, or one for each finger touching a touchscreen. - - \sa PointerEvent, PointerHandler -*/ - -/*! - \readonly - \qmlproperty point QtQuick::EventPoint::position - - This property holds the coordinates of the position supplied by the event, - relative to the upper-left corner of the Item which has the PointerHandler. - If a contact patch is available from the pointing device, this point - represents its centroid. -*/ - -/*! - \readonly - \qmlproperty point QtQuick::EventPoint::scenePosition - - This property holds the coordinates of the position supplied by the event, - relative to the scene. If a contact patch is available from the - \l {QtQuick::PointerEvent::device} {device}, this point represents its centroid. -*/ - -/*! - \readonly - \qmlproperty point QtQuick::EventPoint::scenePressPosition - - This property holds the scene-relative position at which the press event - (on a touch device) or most recent change in QQuickPointerEvent::buttons() - (on a mouse or tablet stylus) occurred. -*/ - -/*! - \readonly - \qmlproperty point QtQuick::EventPoint::sceneGrabPosition - - This property holds the scene-relative position at which the EventPoint was - located when setGrabber() was called most recently. -*/ - -/*! - \readonly - \qmlproperty vector2d QtQuick::EventPoint::velocity - - This property holds average recent velocity: how fast and in which - direction the event point has been moving recently. -*/ - -/*! - \readonly - \qmlproperty int QtQuick::EventPoint::state - - This property tells what the user is currently doing at this point. - - It can be one of: - \value Pressed - The user's finger is now pressing a touchscreen, button or stylus - which was not pressed already - \value Updated - The touchpoint or position is being moved, with no change in pressed state - \value Stationary - The touchpoint or position is not being moved, and there is also - no change in pressed state - \value Released - The user's finger has now released a touch point, button or stylus - which was pressed -*/ - -/*! - \readonly - \qmlproperty int QtQuick::EventPoint::pointId - - This property holds the ID of the event, if any. - - Touchpoints have automatically-incrementing IDs: each time the user - presses a finger against the touchscreen, it will be a larger number. - In other cases, it will be -1. - - \sa {QtQuick::EventTouchPoint::uniqueId}{uniqueId} -*/ - -/*! - \readonly - \qmlproperty bool QtQuick::EventPoint::accepted - - Setting \a accepted to true prevents the event from being propagated to - Items below the PointerHandler's Item. - - Generally, if the handler acts on the mouse event, then it should be - accepted so that items lower in the stacking order do not also respond to - the same event. -*/ - -/*! - \readonly - \qmlproperty real QtQuick::EventPoint::timeHeld - - This property holds the amount of time in seconds that the button or touchpoint has - been held. It can be used to detect a "long press", and can drive an - animation to show progress toward activation of the "long press" action. -*/ - -void QQuickEventPoint::reset(QEventPoint::State state, const QPointF &scenePos, int pointId, ulong timestamp, const QVector2D &velocity) -{ - m_scenePos = scenePos; - m_pointId = pointId; - m_accept = false; - m_state = static_cast<QQuickEventPoint::State>(state); - m_timestamp = timestamp; - if (state == QEventPoint::State::Pressed) { - m_pressTimestamp = timestamp; - m_scenePressPos = scenePos; - } - m_velocity = (Q_LIKELY(velocity.isNull()) ? estimatedVelocity() : velocity); -} - -void QQuickEventPoint::localizePosition(QQuickItem *target) -{ - if (target) - m_pos = target->mapFromScene(scenePosition()); - else - m_pos = QPointF(); -} - -/*! - If this point has an exclusive grabber, returns a pointer to it; else - returns null, if there is no grabber. The grabber could be either - an Item or a PointerHandler. -*/ -QObject *QQuickEventPoint::exclusiveGrabber() const -{ - return m_exclusiveGrabber.data(); -} - -/*! - Set the given Item or PointerHandler as the exclusive grabber of this point. - If there was already an exclusive grab, it will be canceled. If there - were passive grabbers, they will continue to lurk, but the exclusive grab - is a behavioral override of the passive grab as long as it remains. - If you already know whether the grabber is to be an Item or a PointerHandler, - you should instead call setGrabberItem() or setGrabberPointerHandler(), - because it is slightly more efficient. -*/ -void QQuickEventPoint::setExclusiveGrabber(QObject *grabber) -{ - if (QQuickPointerHandler *phGrabber = qmlobject_cast<QQuickPointerHandler *>(grabber)) - setGrabberPointerHandler(phGrabber, true); - else - setGrabberItem(static_cast<QQuickItem *>(grabber)); -} - -/*! - If the exclusive grabber of this point is an Item, returns a - pointer to that Item; else returns null, if there is no grabber or if - the grabber is a PointerHandler. -*/ -QQuickItem *QQuickEventPoint::grabberItem() const -{ - return (m_grabberIsHandler ? nullptr : static_cast<QQuickItem *>(m_exclusiveGrabber.data())); -} - -/*! - Set the given Item \a grabber as the exclusive grabber of this point. - If there was already an exclusive grab, it will be canceled. If there - were passive grabbers, they will continue to lurk, but the exclusive grab - is a behavioral override of the passive grab as long as it remains. -*/ -void QQuickEventPoint::setGrabberItem(QQuickItem *grabber) -{ - if (grabber != m_exclusiveGrabber.data()) { - QQuickPointerHandler *oldGrabberHandler = grabberPointerHandler(); - if (oldGrabberHandler && !oldGrabberHandler->approveGrabTransition(this, grabber)) - return; - if (Q_UNLIKELY(lcPtrGrab().isDebugEnabled())) { - qCDebug(lcPtrGrab) << pointDeviceName(this) << "point" << Qt::hex << m_pointId << pointStateString(this) << "@" << m_scenePos - << ": grab" << m_exclusiveGrabber << "->" << grabber; - } - QQuickItem *oldGrabberItem = grabberItem(); - m_exclusiveGrabber = QPointer<QObject>(grabber); - m_grabberIsHandler = false; - m_sceneGrabPos = m_scenePos; - if (oldGrabberHandler) { - oldGrabberHandler->onGrabChanged(oldGrabberHandler, (grabber ? CancelGrabExclusive : UngrabExclusive), this); - } else if (oldGrabberItem && oldGrabberItem != grabber && grabber && grabber->window()) { - QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(grabber->window()); - windowPriv->sendUngrabEvent(oldGrabberItem, windowPriv->isDeliveringTouchAsMouse()); - } - if (grabber) { - for (QPointer<QQuickPointerHandler> passiveGrabber : m_passiveGrabbers) - if (passiveGrabber) - passiveGrabber->onGrabChanged(passiveGrabber, OverrideGrabPassive, this); - } - } -} - -/*! - If the exclusive grabber of this point is a PointerHandler, returns a - pointer to that handler; else returns null, if there is no grabber or if - the grabber is an Item. -*/ -QQuickPointerHandler *QQuickEventPoint::grabberPointerHandler() const -{ - return (m_grabberIsHandler ? static_cast<QQuickPointerHandler *>(m_exclusiveGrabber.data()) : nullptr); -} - -/*! - Set the given PointerHandler \a grabber as grabber of this point. If \a - exclusive is true, it will override any other grabs; if false, \a grabber - will be added to the list of passive grabbers of this point. -*/ -void QQuickEventPoint::setGrabberPointerHandler(QQuickPointerHandler *grabber, bool exclusive) -{ - if (Q_UNLIKELY(lcPtrGrab().isDebugEnabled())) { - if (exclusive) { - if (m_exclusiveGrabber != grabber) - qCDebug(lcPtrGrab) << pointDeviceName(this) << "point" << Qt::hex << m_pointId << pointStateString(this) - << ": grab (exclusive)" << m_exclusiveGrabber << "->" << grabber; - } else { - qCDebug(lcPtrGrab) << pointDeviceName(this) << "point" << Qt::hex << m_pointId << pointStateString(this) - << ": grab (passive)" << grabber; - } - } - if (exclusive) { - if (grabber != m_exclusiveGrabber.data()) { - QQuickPointerHandler *oldGrabberHandler = grabberPointerHandler(); - QQuickItem *oldGrabberItem = grabberItem(); - m_exclusiveGrabber = QPointer<QObject>(grabber); - m_grabberIsHandler = true; - m_sceneGrabPos = m_scenePos; - if (grabber) { - grabber->onGrabChanged(grabber, GrabExclusive, this); - for (QPointer<QQuickPointerHandler> passiveGrabber : m_passiveGrabbers) { - if (!passiveGrabber.isNull() && passiveGrabber != grabber) - passiveGrabber->onGrabChanged(grabber, OverrideGrabPassive, this); - } - } - if (oldGrabberHandler) { - oldGrabberHandler->onGrabChanged(oldGrabberHandler, (grabber ? CancelGrabExclusive : UngrabExclusive), this); - } else if (oldGrabberItem) { - if (pointerEvent()->asPointerTouchEvent()) - oldGrabberItem->touchUngrabEvent(); - else if (pointerEvent()->asPointerMouseEvent()) - oldGrabberItem->mouseUngrabEvent(); - } - // touchUngrabEvent() can result in the grabber being set to null (MPTA does that, for example). - // So set it again to ensure that final state is what we want. - m_exclusiveGrabber = QPointer<QObject>(grabber); - m_grabberIsHandler = true; - m_sceneGrabPos = m_scenePos; - } - } else { - if (!grabber) { - qDebug() << "can't set passive grabber to null"; - return; - } - auto ptr = QPointer<QQuickPointerHandler>(grabber); - if (!m_passiveGrabbers.contains(ptr)) { - m_passiveGrabbers.append(ptr); - grabber->onGrabChanged(grabber, GrabPassive, this); - } - } -} - -/*! - If this point has an existing exclusive grabber (Item or PointerHandler), - inform the grabber that its grab is canceled, and remove it as grabber. - This normally happens when the grab is stolen by another Item. -*/ -void QQuickEventPoint::cancelExclusiveGrab() -{ - if (m_exclusiveGrabber.isNull()) - qWarning("cancelGrab: no grabber"); - else - cancelExclusiveGrabImpl(); -} - -void QQuickEventPoint::cancelExclusiveGrabImpl(QTouchEvent *cancelEvent) -{ - if (m_exclusiveGrabber.isNull()) - return; - if (Q_UNLIKELY(lcPtrGrab().isDebugEnabled())) { - qCDebug(lcPtrGrab) << pointDeviceName(this) << "point" << Qt::hex << m_pointId << pointStateString(this) - << ": grab (exclusive)" << m_exclusiveGrabber << "-> nullptr"; - } - if (auto handler = grabberPointerHandler()) { - handler->onGrabChanged(handler, CancelGrabExclusive, this); - } else if (auto item = grabberItem()) { - if (cancelEvent) - QCoreApplication::sendEvent(item, cancelEvent); - else - item->touchUngrabEvent(); - } - m_exclusiveGrabber.clear(); -} - -/*! - If this point has the given \a handler as a passive grabber, - inform the grabber that its grab is canceled, and remove it as grabber. - This normally happens when another Item or PointerHandler does an exclusive grab. -*/ -void QQuickEventPoint::cancelPassiveGrab(QQuickPointerHandler *handler) -{ - if (removePassiveGrabber(handler)) { - if (Q_UNLIKELY(lcPtrGrab().isDebugEnabled())) { - qCDebug(lcPtrGrab) << pointDeviceName(this) << "point" << Qt::hex << m_pointId << pointStateString(this) - << ": grab (passive)" << handler << "removed"; - } - handler->onGrabChanged(handler, CancelGrabPassive, this); - } -} - -/*! - If this point has the given \a handler as a passive grabber, remove it as grabber. - Returns true if it was removed, false if it wasn't a grabber. -*/ -bool QQuickEventPoint::removePassiveGrabber(QQuickPointerHandler *handler) -{ - return m_passiveGrabbers.removeOne(handler); -} - -/*! - If the given \a handler is grabbing this point passively, exclusively - or both, cancel the grab and remove it as grabber. - This normally happens when the handler decides that the behavior of this - point can no longer satisfy the handler's behavioral constraints within - the remainder of the gesture which the user is performing: for example - the handler tries to detect a tap but a drag is occurring instead, or - it tries to detect a drag in one direction but the drag is going in - another direction. In such cases the handler no longer needs or wants - to be informed of any further movements of this point. -*/ -void QQuickEventPoint::cancelAllGrabs(QQuickPointerHandler *handler) -{ - if (m_exclusiveGrabber == handler) { - handler->onGrabChanged(handler, CancelGrabExclusive, this); - m_exclusiveGrabber.clear(); - } - cancelPassiveGrab(handler); -} - -/*! - Sets this point as \a accepted (true) or rejected (false). - - During delivery of the current event to the Items in the scene, each Item - or Pointer Handler should accept the points for which it is taking - responsibility. As soon as all points within the event are accepted, event - propagation stops. However accepting the point does not imply any kind of - grab, passive or exclusive. - - \sa setExclusiveGrabber, QQuickPointerHandler::setPassiveGrab, QQuickPointerHandler::setExclusiveGrab -*/ -void QQuickEventPoint::setAccepted(bool accepted) -{ - if (m_accept != accepted) { - qCDebug(lcPointerEvents) << this << m_accept << "->" << accepted; - m_accept = accepted; - } -} - - -/*! - \qmltype EventTouchPoint - \qmlabstract - \instantiates QQuickEventTouchPoint - \inqmlmodule QtQuick - \ingroup qtquick-input-events - \brief Provides information about an individual touch point within a PointerEvent. - - \sa PointerEvent, PointerHandler -*/ - -/*! - \readonly - \qmlproperty QPointerUniqueId QtQuick::EventTouchPoint::uniqueId - - This property holds the unique ID of the fiducial or stylus in use, if any. - - On touchscreens that can track physical objects (such as knobs or game - pieces) in addition to fingers, each object usually has a unique ID. - Likewise, each stylus that can be used with a graphics tablet usually has a - unique serial number. Qt so far only supports numeric IDs. You can get the - actual number as uniqueId.numeric, but that is a device-specific detail. - In the future, there may be support for non-numeric IDs, so you should - not assume that the number is meaningful. - - If you need to identify specific objects, your application should provide - UI for registering objects and mapping them to functionality: allow the - user to select a meaning, virtual tool, or action, prompt the user to bring - the object into proximity, and store a mapping from uniqueId to its - purpose, for example in \l Settings. -*/ - -/*! - \readonly - \qmlproperty qreal QtQuick::EventTouchPoint::rotation - - This property holds the rotation angle of the stylus on a graphics tablet - or the contact patch of a touchpoint on a touchscreen. - - It is valid only with certain tablet stylus devices and touchscreens that - can measure the rotation angle. Otherwise, it will be zero. -*/ - -/*! - \readonly - \qmlproperty qreal QtQuick::EventTouchPoint::pressure - - This property tells how hard the user is pressing the stylus on a graphics - tablet or the finger against a touchscreen, in the range from \c 0 (no - measurable pressure) to \c 1.0 (maximum pressure which the device can - measure). - - It is valid only with certain tablets and touchscreens that can measure - pressure. Otherwise, it will be \c 1.0 when pressed. -*/ - -/*! - \readonly - \qmlproperty size QtQuick::EventTouchPoint::ellipseDiameters - - This property holds the diameters of the contact patch, if the event - comes from a touchpoint and the \l {QtQuick::PointerEvent::device} {device} - provides this information. - - A touchpoint is modeled as an elliptical area where the finger is - pressed against the touchscreen. (In fact, it could also be - modeled as a bitmap; but in that case we expect an elliptical - bounding estimate to be fitted to the contact patch before the - event is sent.) The harder the user presses, the larger the - contact patch; so, these diameters provide an alternate way of - detecting pressure, in case the device does not include a separate - pressure sensor. The ellipse is centered on - \l {QtQuick::EventPoint::scenePosition} {scenePosition} - (\l {QtQuick::EventPoint::position} {position} in the PointerHandler's - Item's local coordinates). The \l rotation property provides the - rotation of the ellipse, if known. It is expected that if the - \l rotation is zero, the verticalDiameter of the ellipse is the - larger one (the major axis), because of the usual hand position, - reaching upward or outward across the surface. - - If the contact patch is unknown, or the \l {QtQuick::PointerEvent::device} {device} - is not a touchscreen, these values will be zero. -*/ - -QQuickEventTouchPoint::QQuickEventTouchPoint(QQuickPointerTouchEvent *parent) - : QQuickEventPoint(parent), m_rotation(0), m_pressure(0) -{} - -void QQuickEventTouchPoint::reset(const QEventPoint &tp, ulong timestamp) -{ - QQuickEventPoint::reset(tp.state(), tp.scenePosition(), tp.id(), timestamp, tp.velocity()); - m_exclusiveGrabber.clear(); - m_passiveGrabbers.clear(); - m_rotation = tp.rotation(); - m_pressure = tp.pressure(); - m_ellipseDiameters = tp.ellipseDiameters(); - m_uniqueId = tp.uniqueId(); -} - -struct PointVelocityData { - QVector2D velocity; - QPointF pos; - ulong timestamp = 0; -}; - -typedef QMap<quint64, PointVelocityData> PointDataForPointIdMap; -Q_GLOBAL_STATIC(PointDataForPointIdMap, g_previousPointData) -static const int PointVelocityAgeLimit = 500; // milliseconds - -/*! - \internal - Estimates the velocity based on a weighted average of all previous velocities. - The older the velocity is, the less significant it becomes for the estimate. -*/ -QVector2D QQuickEventPoint::estimatedVelocity() const -{ - auto prevPointIt = g_previousPointData->find(m_pointId); - auto end = g_previousPointData->end(); - if (prevPointIt == end) { - // cleanup events older than PointVelocityAgeLimit - for (auto it = g_previousPointData->begin(); it != end; ) { - if (m_timestamp - it->timestamp > PointVelocityAgeLimit) - it = g_previousPointData->erase(it); - else - ++it; - } - prevPointIt = g_previousPointData->insert(m_pointId, PointVelocityData()); - } - - auto &prevPoint = prevPointIt.value(); - const ulong timeElapsed = m_timestamp - prevPoint.timestamp; - if (timeElapsed == 0) // in case we call estimatedVelocity() twice on the same QQuickEventPoint - return m_velocity; - - QVector2D newVelocity; - if (prevPoint.timestamp != 0) - newVelocity = QVector2D(m_scenePos - prevPoint.pos) / timeElapsed; - - // VERY simple kalman filter: does a weighted average - // where the older velocities get less and less significant - static const float KalmanGain = 0.7f; - QVector2D filteredVelocity = newVelocity * KalmanGain + m_velocity * (1.0f - KalmanGain); - - prevPoint.velocity = filteredVelocity; - prevPoint.pos = m_scenePos; - prevPoint.timestamp = m_timestamp; - return filteredVelocity; -} - -/*! - \qmltype PointerEvent - \instantiates QQuickPointerEvent - \inqmlmodule QtQuick - \ingroup qtquick-input-events - - \brief Provides information about an event from a pointing device. - - A PointerEvent is an event describing contact or movement across a surface, - provided by a mouse, a touchpoint (single finger on a touchscreen), or a - stylus on a graphics tablet. The \l {QtQuick::PointerEvent::device} {device} - property provides more information about where the event came from. - - \sa PointerHandler - - \image touchpoint-metrics.png -*/ - -/*! - \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. -*/ - -/*! - \readonly - \qmlproperty enumeration QtQuick::PointerEvent::button - - This property holds the \l {Qt::MouseButton}{button} that caused the event, - if any. If the \l {QtQuick::PointerEvent::device} {device} does not have - buttons, or the event is a hover event, it will be \c Qt.NoButton. -*/ - -/*! - \readonly - \qmlproperty int QtQuick::PointerEvent::buttons - - This property holds the combination of mouse or stylus - \l {Qt::MouseButton}{buttons} pressed when the event was generated. For move - events, this is all buttons that are pressed down. For press events, this - includes the button that caused the event, as well as any others that were - already held. For release events, this excludes the button that caused the - event. -*/ - -/*! - \readonly - \qmlproperty int QtQuick::PointerEvent::modifiers - - This property holds the \l {Qt::KeyboardModifier}{keyboard modifier} flags - that existed immediately before the event occurred. - - It contains a bitwise combination of the following flags: - \value Qt.NoModifier - No modifier key is pressed. - \value Qt.ShiftModifier - A Shift key on the keyboard is pressed. - \value Qt.ControlModifier - A Ctrl key on the keyboard is pressed. - \value Qt.AltModifier - An Alt key on the keyboard is pressed. - \value Qt.MetaModifier - A Meta key on the keyboard is pressed. - \value Qt.KeypadModifier - A keypad button is pressed. - - For example, to react to a Shift key + Left mouse button click: - \qml - Item { - TapHandler { - onTapped: { - if ((event.button == Qt.LeftButton) && (event.modifiers & Qt.ShiftModifier)) - doSomething(); - } - } - } - \endqml -*/ - -/*! - \readonly - \qmlproperty PointerDevice QtQuick::PointerEvent::device - - This property holds the device that generated the event. -*/ - -QQuickPointerEvent::~QQuickPointerEvent() -{} - -QQuickPointerMouseEvent::QQuickPointerMouseEvent(QObject *parent, const QPointingDevice *device) - : QQuickSinglePointEvent(parent, device) -{ - m_point = new QQuickEventPoint(this); -} - -QQuickPointerEvent *QQuickPointerMouseEvent::reset(QEvent *event) -{ - auto ev = static_cast<QMouseEvent*>(event); - m_event = ev; - if (!event) - return this; - - QQuickPointerHandlerPrivate::deviceDeliveryTargets(ev->device()).clear(); - m_button = ev->button(); - m_pressedButtons = ev->buttons(); - QEventPoint::State state = QEventPoint::State::Stationary; - switch (ev->type()) { - case QEvent::MouseButtonPress: - m_point->clearPassiveGrabbers(); - Q_FALLTHROUGH(); - case QEvent::MouseButtonDblClick: - state = QEventPoint::State::Pressed; - break; - case QEvent::MouseButtonRelease: - state = QEventPoint::State::Released; - break; - case QEvent::MouseMove: - state = QEventPoint::State::Updated; - break; - default: - break; - } - m_point->reset(state, ev->scenePosition(), ev->point(0).id(), ev->timestamp()); - return this; -} - -void QQuickSinglePointEvent::localize(QQuickItem *target) -{ - m_point->localizePosition(target); -} - -QQuickPointerEvent *QQuickPointerTouchEvent::reset(QEvent *event) -{ - auto ev = static_cast<QTouchEvent*>(event); - m_event = ev; - if (!event) - return this; - - QQuickPointerHandlerPrivate::deviceDeliveryTargets(ev->device()).clear(); - m_button = Qt::NoButton; - m_pressedButtons = Qt::NoButton; - - const QList<QEventPoint> &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 and on-pressed values are right from one event to the next - struct ToPreserve { - int pointId; // just for double-checking - ulong pressTimestamp; - QPointF scenePressPos; - QPointF sceneGrabPos; - QObject * grabber; - QVector <QPointer <QQuickPointerHandler> > passiveGrabbers; - - ToPreserve() : pointId(0), pressTimestamp(0), grabber(nullptr) {} - }; - QVector<ToPreserve> preserves(newPointCount); // jar of pickled touchpoints, in order of points in the _new_ event - - // Copy stuff we need to preserve, because the order of points might have changed in the event. - // The ID is all that we can rely on (release might remove the first point etc). - for (int i = 0; i < newPointCount; ++i) { - int pid = tps.at(i).id(); - if (auto point = pointById(pid)) { - preserves[i].pointId = pid; - preserves[i].pressTimestamp = point->m_pressTimestamp; - preserves[i].scenePressPos = point->scenePressPosition(); - preserves[i].sceneGrabPos = point->sceneGrabPosition(); - preserves[i].grabber = point->exclusiveGrabber(); - preserves[i].passiveGrabbers = point->passiveGrabbers(); - } - } - - for (int i = 0; i < newPointCount; ++i) { - auto point = m_touchPoints.at(i); - point->reset(tps.at(i), ev->timestamp()); - const auto &preserved = preserves.at(i); - if (point->state() == QQuickEventPoint::Pressed) { - if (preserved.grabber) - qWarning() << "TouchPointPressed without previous release event" << point; - point->setGrabberItem(nullptr); - point->clearPassiveGrabbers(); - } else { - // Restore the grabbers without notifying (don't call onGrabChanged) - Q_ASSERT(preserved.pointId == 0 || preserved.pointId == point->pointId()); - point->m_pressTimestamp = preserved.pressTimestamp; - point->m_scenePressPos = preserved.scenePressPos; - point->m_sceneGrabPos = preserved.sceneGrabPos; - point->m_exclusiveGrabber = preserved.grabber; - point->m_grabberIsHandler = (qmlobject_cast<QQuickPointerHandler *>(point->m_exclusiveGrabber) != nullptr); - point->m_passiveGrabbers = preserved.passiveGrabbers; - } - } - m_pointCount = newPointCount; - return this; -} - -void QQuickPointerTouchEvent::localize(QQuickItem *target) -{ - for (auto point : qAsConst(m_touchPoints)) - point->localizePosition(target); -} - -#if QT_CONFIG(gestures) -QQuickPointerNativeGestureEvent::QQuickPointerNativeGestureEvent(QObject *parent, const QPointingDevice *device) - : QQuickSinglePointEvent(parent, device) -{ - m_point = new QQuickEventPoint(this); -} - -QQuickPointerEvent *QQuickPointerNativeGestureEvent::reset(QEvent *event) -{ - auto ev = static_cast<QNativeGestureEvent*>(event); - m_event = ev; - if (!event) - return this; - - QQuickPointerHandlerPrivate::deviceDeliveryTargets(ev->device()).clear(); - QEventPoint::State state = QEventPoint::State::Updated; - switch (type()) { - case Qt::BeginNativeGesture: - state = QEventPoint::State::Pressed; - break; - case Qt::EndNativeGesture: - state = QEventPoint::State::Released; - break; - default: - break; - } - m_point->reset(state, ev->scenePosition(), ev->point(0).id(), ev->timestamp()); - return this; -} -#endif // QT_CONFIG(gestures) - -QQuickEventPoint *QQuickSinglePointEvent::point(int i) const -{ - if (i == 0) - return m_point; - return nullptr; -} - - -/*! - \qmltype PointerScrollEvent - \instantiates QQuickPointerScrollEvent - \inqmlmodule QtQuick - \ingroup qtquick-input-events - \brief Provides information about a scrolling event, such as from a mouse wheel. - - \sa WheelHandler -*/ - -/*! - \internal - \class QQuickPointerScrollEvent -*/ - -/*! - \readonly - \qmlproperty PointerDevice QtQuick::PointerScrollEvent::device - - This property holds the device that generated the event. -*/ - -/*! - \qmlproperty int QtQuick::PointerScrollEvent::buttons - - This property holds the mouse buttons pressed when the wheel event was generated. - - It contains a bitwise combination of: - \list - \li \l {Qt::LeftButton} {Qt.LeftButton} - \li \l {Qt::RightButton} {Qt.RightButton} - \li \l {Qt::MiddleButton} {Qt.MiddleButton} - \endlist -*/ - -/*! - \readonly - \qmlproperty int QtQuick::PointerScrollEvent::modifiers - - This property holds the \l {Qt::KeyboardModifier}{keyboard modifier} keys - that were pressed immediately before the event occurred. - - It contains a bitwise combination of the following flags: - \value Qt.NoModifier - No modifier key is pressed. - \value Qt.ShiftModifier - A Shift key on the keyboard is pressed. - \value Qt.ControlModifier - A Ctrl key on the keyboard is pressed. - \value Qt.AltModifier - An Alt key on the keyboard is pressed. - \value Qt.MetaModifier - A Meta key on the keyboard is pressed. - \value Qt.KeypadModifier - A keypad button is pressed. - - For example, to react to a Shift key + Left mouse button click: - \qml - Item { - TapHandler { - onTapped: { - if ((event.button == Qt.LeftButton) && (event.modifiers & Qt.ShiftModifier)) - doSomething(); - } - } - } - \endqml -*/ - -/*! - \qmlproperty point QtQuick::PointerScrollEvent::angleDelta - - This property holds the distance that the wheel is rotated in wheel degrees. - The x and y cordinate of this property holds the delta in horizontal and - vertical orientation. - - A positive value indicates that the wheel was rotated up/right; - a negative value indicates that the wheel was rotated down/left. - - Most mouse types work in steps of 15 degrees, in which case the delta value is a - multiple of 120; i.e., 120 units * 1/8 = 15 degrees. -*/ - -/*! - \qmlproperty point QtQuick::PointerScrollEvent::pixelDelta - - This property holds the delta in screen pixels and is available in platforms that - have high-resolution trackpads, such as \macos. - The x and y coordinates of this property hold the delta in horizontal and - vertical orientation. The value should be used directly to scroll content on screen. - - For platforms without high-resolution touchpad support, pixelDelta will - always be (0,0), and angleDelta should be used instead. -*/ - -/*! - \qmlproperty bool QtQuick::PointerScrollEvent::hasAngleDelta - - Returns whether the \l angleDelta property has a non-null value. -*/ - -/*! - \qmlproperty bool QtQuick::PointerScrollEvent::hasPixelDelta - - Returns whether the \l pixelDelta property has a non-null value. -*/ - -/*! - \qmlproperty bool QtQuick::PointerScrollEvent::inverted - - Returns whether the delta values delivered with the event are inverted. - - Normally, a vertical wheel will produce a PointerScrollEvent with positive delta - values if the top of the wheel is rotating away from the hand operating it. - Similarly, a horizontal wheel movement will produce a PointerScrollEvent with - positive delta values if the top of the wheel is moved to the left. - - However, on some platforms this is configurable, so that the same - operations described above will produce negative delta values (but with the - same magnitude). In a QML component (such as a tumbler or a slider) where - it is appropriate to synchronize the movement or rotation of an item with - the direction of the wheel, regardless of the system settings, the wheel - event handler can use the inverted property to decide whether to negate the - \l angleDelta or \l pixelDelta values. - - \note Many platforms provide no such information. On such platforms, - \c inverted always returns false. -*/ -QQuickPointerScrollEvent::QQuickPointerScrollEvent(QObject *parent, const QPointingDevice *device) - : QQuickSinglePointEvent(parent, device) -{ - m_point = new QQuickEventPoint(this); -} - -QQuickPointerEvent *QQuickPointerScrollEvent::reset(QEvent *event) -{ - m_event = static_cast<QPointerEvent*>(event); - if (!event) - return this; -#if QT_CONFIG(wheelevent) - if (event->type() == QEvent::Wheel) { - auto ev = static_cast<QWheelEvent*>(event); - QQuickPointerHandlerPrivate::deviceDeliveryTargets(ev->device()).clear(); - // m_button = Qt::NoButton; - m_pressedButtons = ev->buttons(); - m_angleDelta = QVector2D(ev->angleDelta()); - m_pixelDelta = QVector2D(ev->pixelDelta()); - m_phase = ev->phase(); - m_synthSource = ev->source(); - m_inverted = ev->inverted(); - m_point->reset(QEventPoint::State::Updated, ev->position(), ev->point(0).id(), ev->timestamp()); - } -#endif - // TODO else if (event->type() == QEvent::Scroll) ... - return this; -} - -void QQuickPointerScrollEvent::localize(QQuickItem *target) -{ - m_point->localizePosition(target); -} - -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_exclusiveGrabber(nullptr), m_timestamp(0), m_pressTimestamp(0), - m_state(QQuickEventPoint::Released), m_accept(false), m_grabberIsHandler(false) -{ - Q_UNUSED(m_reserved); -} - -QQuickPointerEvent *QQuickEventPoint::pointerEvent() const -{ - return static_cast<QQuickPointerEvent *>(parent()); -} - -bool QQuickSinglePointEvent::allPointsAccepted() const -{ - return m_point->isAccepted(); -} - -bool QQuickSinglePointEvent::allUpdatedPointsAccepted() const -{ - return m_point->state() == QQuickEventPoint::Pressed || m_point->isAccepted(); -} - -bool QQuickSinglePointEvent::allPointsGrabbed() const -{ - return m_point->exclusiveGrabber() != nullptr; -} - -QMouseEvent *QQuickPointerMouseEvent::asMouseEvent(const QPointF &localPos) const -{ - if (!m_event) - return nullptr; - static_cast<QMutableSinglePointEvent *>(m_event)->mutablePoint().setPosition(localPos); - return static_cast<QMouseEvent *>(m_event); -} - -/*! - Returns the exclusive grabber of this event, if any, in a vector. -*/ -QVector<QObject *> QQuickSinglePointEvent::exclusiveGrabbers() const -{ - QVector<QObject *> result; - if (QObject *grabber = m_point->exclusiveGrabber()) - result << grabber; - return result; -} - -/*! - Remove all passive and exclusive grabbers of this event, without notifying. -*/ -void QQuickSinglePointEvent::clearGrabbers() const -{ - m_point->setGrabberItem(nullptr); - m_point->clearPassiveGrabbers(); -} - -/*! - Returns whether the given \a handler is the exclusive grabber of this event. -*/ -bool QQuickSinglePointEvent::hasExclusiveGrabber(const QQuickPointerHandler *handler) const -{ - return handler && (m_point->exclusiveGrabber() == handler); -} - -bool QQuickPointerMouseEvent::isPressEvent() const -{ - if (!m_event) - return false; - auto me = static_cast<QMouseEvent*>(m_event); - return ((me->type() == QEvent::MouseButtonPress || me->type() == QEvent::MouseButtonDblClick) && - (me->buttons() & me->button()) == me->buttons()); -} - -bool QQuickPointerMouseEvent::isDoubleClickEvent() const -{ - if (!m_event) - return false; - auto me = static_cast<QMouseEvent*>(m_event); - return (me->type() == QEvent::MouseButtonDblClick); -} - -bool QQuickPointerMouseEvent::isUpdateEvent() const -{ - if (!m_event) - return false; - auto me = static_cast<QMouseEvent*>(m_event); - return me->type() == QEvent::MouseMove; -} - -bool QQuickPointerMouseEvent::isReleaseEvent() const -{ - if (!m_event) - return false; - auto me = static_cast<QMouseEvent*>(m_event); - return me && me->type() == QEvent::MouseButtonRelease; -} - -bool QQuickPointerTouchEvent::allPointsAccepted() const -{ - for (int i = 0; i < m_pointCount; ++i) { - if (!m_touchPoints.at(i)->isAccepted()) - return false; - } - return true; -} - -bool QQuickPointerTouchEvent::allUpdatedPointsAccepted() const -{ - for (int i = 0; i < m_pointCount; ++i) { - auto point = m_touchPoints.at(i); - if (point->state() != QQuickEventPoint::Pressed && !point->isAccepted()) - return false; - } - return true; -} - -bool QQuickPointerTouchEvent::allPointsGrabbed() const -{ - for (int i = 0; i < m_pointCount; ++i) { - if (!m_touchPoints.at(i)->exclusiveGrabber()) - return false; - } - return true; -} - -/*! - Returns the exclusive grabbers of all points in this event, if any, in a vector. -*/ -QVector<QObject *> QQuickPointerTouchEvent::exclusiveGrabbers() const -{ - QVector<QObject *> result; - for (int i = 0; i < m_pointCount; ++i) { - if (QObject *grabber = m_touchPoints.at(i)->exclusiveGrabber()) { - if (!result.contains(grabber)) - result << grabber; - } - } - return result; -} - -/*! - Remove all passive and exclusive grabbers of all touchpoints in this event, - without notifying. -*/ -void QQuickPointerTouchEvent::clearGrabbers() const -{ - for (auto point: m_touchPoints) { - point->setGrabberItem(nullptr); - point->clearPassiveGrabbers(); - } -} - -QEventPoint::States QQuickPointerTouchEvent::touchPointStates() const -{ - return m_event - ? static_cast<QTouchEvent*>(m_event)->touchPointStates() - : QEventPoint::States(); -} - -/*! - Returns whether the given \a handler is the exclusive grabber of any - touchpoint within this event. -*/ -bool QQuickPointerTouchEvent::hasExclusiveGrabber(const QQuickPointerHandler *handler) const -{ - for (auto point: m_touchPoints) - if (point->exclusiveGrabber() == handler) - return true; - return false; -} - -bool QQuickPointerTouchEvent::isPressEvent() const -{ - return touchPointStates() & QEventPoint::State::Pressed; -} - -bool QQuickPointerTouchEvent::isUpdateEvent() const -{ - return touchPointStates() & (QEventPoint::State::Updated | QEventPoint::State::Stationary); -} - -bool QQuickPointerTouchEvent::isReleaseEvent() const -{ - return touchPointStates() & QEventPoint::State::Released; -} - -QVector<QPointF> QQuickPointerEvent::unacceptedPressedPointScenePositions() const -{ - QVector<QPointF> points; - for (int i = 0; i < pointCount(); ++i) { - if (!point(i)->isAccepted() && point(i)->state() == QQuickEventPoint::Pressed) - points << point(i)->scenePosition(); - } - 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 QEventPoint *p = touchPointById(pointID); - if (!p) - return nullptr; - QEvent::Type type; - Qt::MouseButton buttons = Qt::LeftButton; - switch (p->state()) { - case QEventPoint::State::Pressed: - type = QEvent::MouseButtonPress; - break; - case QEventPoint::State::Updated: - case QEventPoint::State::Stationary: - type = QEvent::MouseMove; - break; - case QEventPoint::State::Released: - type = QEvent::MouseButtonRelease; - buttons = Qt::NoButton; - break; - default: - Q_ASSERT(false); - return nullptr; - } - m_synthMouseEvent = QMouseEvent(type, relativeTo->mapFromScene(p->scenePosition()), - p->scenePosition(), p->globalPosition(), Qt::LeftButton, buttons, m_event->modifiers(), - Qt::MouseEventSynthesizedByQt, device()); - m_synthMouseEvent.setAccepted(true); - m_synthMouseEvent.setTimestamp(m_event->timestamp()); - // ### Qt 6: try to always have valid velocity in every QEventPoint (either from the platform, or synthesized in QtGui). - // QQuickFlickablePrivate::handleMouseMoveEvent() checks for QInputDevice::Capability::Velocity - // and if it is set, then it does not need to do its own velocity calculations. - // But because m_synthMouseEvent gets the same device() where the (usually) touch event came from, - // the capability won't be set, in practice, until we have the velocity in place. - QMutableSinglePointEvent::from(m_synthMouseEvent).mutablePoint().setVelocity(p->velocity()); - return &m_synthMouseEvent; -} - -#if QT_CONFIG(tabletevent) -QQuickPointerTabletEvent::QQuickPointerTabletEvent(QObject *parent, const QPointingDevice *device) - : QQuickSinglePointEvent(parent, device) -{ - m_point = new QQuickEventTabletPoint(this); -} - -QQuickPointerEvent *QQuickPointerTabletEvent::reset(QEvent *event) -{ - auto ev = static_cast<QTabletEvent*>(event); - m_event = ev; - if (!event) - return this; - - QQuickPointerHandlerPrivate::deviceDeliveryTargets(ev->device()).clear(); - m_button = ev->button(); - m_pressedButtons = ev->buttons(); - static_cast<QQuickEventTabletPoint *>(m_point)->reset(ev); - return this; -} - -QQuickEventTabletPoint::QQuickEventTabletPoint(QQuickPointerTabletEvent *parent) - : QQuickEventPoint(parent) -{ -} - -void QQuickEventTabletPoint::reset(const QTabletEvent *ev) -{ - QEventPoint::State state = QEventPoint::State::Stationary; - switch (ev->type()) { - case QEvent::TabletPress: - state = QEventPoint::State::Pressed; - clearPassiveGrabbers(); - break; - case QEvent::TabletRelease: - state = QEventPoint::State::Released; - break; - case QEvent::TabletMove: - state = QEventPoint::State::Updated; - break; - default: - break; - } - QQuickEventPoint::reset(state, ev->position(), 1, ev->timestamp()); - m_rotation = ev->rotation(); - m_pressure = ev->pressure(); - m_tangentialPressure = ev->tangentialPressure(); - m_tilt = QVector2D(ev->xTilt(), ev->yTilt()); -} - -bool QQuickPointerTabletEvent::isPressEvent() const -{ - auto me = static_cast<QTabletEvent *>(m_event); - return me->type() == QEvent::TabletPress; -} - -bool QQuickPointerTabletEvent::isUpdateEvent() const -{ - auto me = static_cast<QTabletEvent *>(m_event); - return me->type() == QEvent::TabletMove; -} - -bool QQuickPointerTabletEvent::isReleaseEvent() const -{ - auto me = static_cast<QTabletEvent *>(m_event); - return me->type() == QEvent::TabletRelease; -} - -QTabletEvent *QQuickPointerTabletEvent::asTabletEvent() const -{ - return static_cast<QTabletEvent *>(m_event); -} -#endif // QT_CONFIG(tabletevent) - -#if QT_CONFIG(gestures) -bool QQuickPointerNativeGestureEvent::isPressEvent() const -{ - return type() == Qt::BeginNativeGesture; -} - -bool QQuickPointerNativeGestureEvent::isUpdateEvent() const -{ - switch (type()) { - case Qt::BeginNativeGesture: - case Qt::EndNativeGesture: - return false; - default: - return true; - } -} - -bool QQuickPointerNativeGestureEvent::isReleaseEvent() const -{ - return type() == Qt::EndNativeGesture; -} - -Qt::NativeGestureType QQuickPointerNativeGestureEvent::type() const -{ - return static_cast<QNativeGestureEvent *>(m_event)->gestureType(); -} - -qreal QQuickPointerNativeGestureEvent::value() const -{ - return static_cast<QNativeGestureEvent *>(m_event)->value(); -} -#endif // QT_CONFIG(gestures) - -/*! - Returns whether the scroll event has Qt::ScrollBegin phase. On touchpads - which provide phase information, this is true when the fingers are placed - on the touchpad and scrolling begins. On other devices where this - information is not available, it remains false. -*/ -bool QQuickPointerScrollEvent::isPressEvent() const -{ - return phase() == Qt::ScrollBegin; -} - -/*! - Returns true when the scroll event has Qt::ScrollUpdate phase, or when the - phase is unknown. Some multi-touch-capable touchpads and trackpads provide - phase information; whereas ordinary mouse wheels and other types of - trackpads do not, and in such cases this is always true. -*/ -bool QQuickPointerScrollEvent::isUpdateEvent() const -{ - return phase() == Qt::ScrollUpdate || phase() == Qt::NoScrollPhase; -} - -/*! - Returns whether the scroll event has Qt::ScrollBegin phase. On touchpads - which provide phase information, this is true when the fingers are lifted - from the touchpad. On other devices where this information is not - available, it remains false. -*/ -bool QQuickPointerScrollEvent::isReleaseEvent() const -{ - return phase() == Qt::ScrollEnd; -} - -/*! - \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(int pointId) const -*/ -QQuickEventPoint *QQuickSinglePointEvent::pointById(int pointId) const -{ - if (m_point && pointId == m_point->pointId()) - return m_point; - return nullptr; -} - -QQuickEventPoint *QQuickPointerTouchEvent::pointById(int pointId) const -{ - auto it = std::find_if(m_touchPoints.constBegin(), m_touchPoints.constEnd(), - [pointId](const QQuickEventTouchPoint *tp) { return tp->pointId() == pointId; } ); - if (it != m_touchPoints.constEnd()) - return *it; - return nullptr; -} - -/*! - \internal - Returns a pointer to the original TouchPoint which has the same - \l {QEventPoint::id}{id} as \a pointId, if the original event is a - QTouchEvent, and if that point is found. Otherwise, returns nullptr. -*/ -const QEventPoint *QQuickPointerTouchEvent::touchPointById(int pointId) const -{ - const QTouchEvent *ev = asTouchEvent(); - if (!ev) - return nullptr; - const QList<QEventPoint> &tps = ev->touchPoints(); - auto it = std::find_if(tps.constBegin(), tps.constEnd(), - [pointId](QEventPoint const& tp) { return tp.id() == pointId; } ); - // return the pointer to the actual TP in QTouchEvent::_touchPoints - return (it == tps.constEnd() ? nullptr : &*it); -} - -/*! - \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, - or none of the points were pressed inside and the item was not grabbing any of them - and isFiltering is false. When isFiltering is true, it is assumed that the item - cares about all points which are inside its bounds, because most filtering items - need to monitor eventpoint movements until a drag threshold is exceeded or the - requirements for a gesture to be recognized are met in some other way. -*/ -QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(QQuickItem *item, bool isFiltering) const -{ - QList<QEventPoint> touchPoints; - QEventPoint::States eventStates; // TODO maybe avoid accumulating this, since the touchevent ctor doesn't need it - // 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 - - bool anyPressOrReleaseInside = false; - bool anyStationaryWithModifiedPropertyInside = false; - bool anyGrabber = false; - QMatrix4x4 transformMatrix(QQuickItemPrivate::get(item)->windowToItemTransform()); - for (int i = 0; i < m_pointCount; ++i) { - auto p = m_touchPoints.at(i); - if (p->isAccepted()) - continue; - // include points where item is the grabber - bool isGrabber = p->exclusiveGrabber() == item; - if (isGrabber) - anyGrabber = true; - // include points inside the bounds if no other item is the grabber or if the item is filtering - bool isInside = item->contains(item->mapFromScene(p->scenePosition())); - bool hasAnotherGrabber = p->exclusiveGrabber() && p->exclusiveGrabber() != item; - - // filtering: (childMouseEventFilter) include points that are grabbed by children of the target item - bool grabberIsChild = false; - auto parent = p->grabberItem(); - while (isFiltering && parent) { - if (parent == item) { - grabberIsChild = true; - break; - } - parent = parent->parentItem(); - } - - bool filterRelevant = isFiltering && grabberIsChild; - if (!(isGrabber || (isInside && (!hasAnotherGrabber || isFiltering)) || filterRelevant)) - continue; - if ((p->state() == QQuickEventPoint::Pressed || p->state() == QQuickEventPoint::Released) && isInside) - anyPressOrReleaseInside = true; - // we don't use QMutableEventPoint::from() because it's fine to have const here - const QMutableEventPoint *tp = static_cast<const QMutableEventPoint *>(touchPointById(p->pointId())); - if (tp) { - if (isInside && tp->stationaryWithModifiedProperty()) - anyStationaryWithModifiedPropertyInside = true; - eventStates |= tp->state(); - QMutableEventPoint tpCopy(*tp); - tpCopy.setPosition(item->mapFromScene(tp->scenePosition())); - tpCopy.setVelocity(transformMatrix.mapVector(tpCopy.velocity()).toVector2D()); - touchPoints << tpCopy; - } - } - - // Now touchPoints will have only points which are inside the item. - // But if none of them were just pressed inside, and the item has no other reason to care, ignore them anyway. - if ((eventStates == QEventPoint::State::Stationary && !anyStationaryWithModifiedPropertyInside) || - touchPoints.isEmpty() || (!anyPressOrReleaseInside && !anyGrabber && !isFiltering)) - 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 QEventPoint::State::Pressed: - eventType = QEvent::TouchBegin; - break; - case QEventPoint::State::Released: - eventType = QEvent::TouchEnd; - break; - default: - eventType = QEvent::TouchUpdate; - break; - } - - QMutableTouchEvent *touchEvent = new QMutableTouchEvent(eventType, event.pointingDevice(), - event.modifiers(), touchPoints); - touchEvent->setTarget(item); - 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 QQuickPointerEvent *event) -{ - QDebugStateSaver saver(dbg); - dbg.nospace(); - if (!event) { - dbg << "QQuickPointerEvent(0)"; - return dbg; - } - dbg << "QQuickPointerEvent("; - dbg << event->timestamp(); - if (event->device()) { - dbg << " dev:"; - QtDebugUtils::formatQEnum(dbg, event->device()->type()); - dbg << " " << event->device()->name(); - } else { - dbg << " dev: null"; - } - 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(); - if (!event) { - dbg << "QQuickEventPoint(0)"; - return dbg; - } - dbg << "QQuickEventPoint(accepted:" << event->isAccepted() - << " state:"; - QtDebugUtils::formatQEnum(dbg, event->state()); - dbg << " scenePos:" << event->scenePosition() << " id:" << Qt::hex << event->pointId() << Qt::dec - << " 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 b491445323..9b89d8300e 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -68,14 +68,8 @@ QT_BEGIN_NAMESPACE class QPointingDevice; -class QQuickPointerEvent; -class QQuickPointerMouseEvent; -#if QT_CONFIG(gestures) -class QQuickPointerNativeGestureEvent; -#endif -class QQuickPointerScrollEvent; -class QQuickPointerTabletEvent; -class QQuickPointerTouchEvent; +class QPointerEvent; +class QMouseEvent; class QQuickPointerHandler; class QQuickKeyEvent : public QObject @@ -267,441 +261,6 @@ private: bool _accepted = true; }; -class Q_QUICK_PRIVATE_EXPORT QQuickEventPoint : public QObject -{ - Q_OBJECT - Q_PROPERTY(QQuickPointerEvent *event READ pointerEvent CONSTANT) - Q_PROPERTY(QPointF position READ position CONSTANT) - Q_PROPERTY(QPointF scenePosition READ scenePosition CONSTANT) - Q_PROPERTY(QPointF scenePressPosition READ scenePressPosition CONSTANT) - Q_PROPERTY(QPointF sceneGrabPosition READ sceneGrabPosition CONSTANT) - Q_PROPERTY(State state READ state CONSTANT) - Q_PROPERTY(int pointId READ pointId CONSTANT) - Q_PROPERTY(qreal timeHeld READ timeHeld CONSTANT) - Q_PROPERTY(QVector2D velocity READ velocity CONSTANT) - Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted) - Q_PROPERTY(QObject *exclusiveGrabber READ exclusiveGrabber WRITE setExclusiveGrabber) - - QML_NAMED_ELEMENT(EventPoint) - QML_UNCREATABLE("EventPoint is only available as a member of PointerEvent.") - QML_ADDED_IN_VERSION(2, 12) - -public: - enum State { - Pressed = QEventPoint::State::Pressed, - Updated = QEventPoint::State::Updated, - Stationary = QEventPoint::State::Stationary, - Released = QEventPoint::State::Released - }; - Q_DECLARE_FLAGS(States, State) - Q_FLAG(States) - - enum GrabTransition { - GrabPassive = 0x01, - UngrabPassive = 0x02, - CancelGrabPassive = 0x03, - OverrideGrabPassive = 0x04, - GrabExclusive = 0x10, - UngrabExclusive = 0x20, - CancelGrabExclusive = 0x30, - }; - Q_ENUM(GrabTransition) - - QQuickEventPoint(QQuickPointerEvent *parent); - - void reset(QEventPoint::State state, const QPointF &scenePosition, int pointId, ulong timestamp, const QVector2D &velocity = QVector2D()); - void localizePosition(QQuickItem *target); - - QQuickPointerEvent *pointerEvent() const; - QPointF position() const { return m_pos; } - QPointF scenePosition() const { return m_scenePos; } - QPointF scenePressPosition() const { return m_scenePressPos; } - QPointF sceneGrabPosition() const { return m_sceneGrabPos; } - QVector2D velocity() const { return m_velocity; } - State state() const { return m_state; } - int pointId() const { return m_pointId; } - qreal timeHeld() const { return (m_timestamp - m_pressTimestamp) / 1000.0; } - bool isAccepted() const { return m_accept; } - void setAccepted(bool accepted = true); - QObject *exclusiveGrabber() const; - void setExclusiveGrabber(QObject *exclusiveGrabber); - - QQuickItem *grabberItem() const; - void setGrabberItem(QQuickItem *exclusiveGrabber); - - QQuickPointerHandler *grabberPointerHandler() const; - void setGrabberPointerHandler(QQuickPointerHandler *exclusiveGrabber, bool exclusive = false); - - void cancelExclusiveGrab(); - void cancelPassiveGrab(QQuickPointerHandler *handler); - bool removePassiveGrabber(QQuickPointerHandler *handler); - void cancelAllGrabs(QQuickPointerHandler *handler); - - QVector<QPointer <QQuickPointerHandler> > passiveGrabbers() const { return m_passiveGrabbers; } - void setPassiveGrabbers(const QVector<QPointer <QQuickPointerHandler> > &grabbers) { m_passiveGrabbers = grabbers; } - void clearPassiveGrabbers() { m_passiveGrabbers.clear(); } - -protected: - void cancelExclusiveGrabImpl(QTouchEvent *cancelEvent = nullptr); - -private: - QVector2D estimatedVelocity() const; - -protected: - QPointF m_pos; - QPointF m_scenePos; - QPointF m_scenePressPos; - QPointF m_sceneGrabPos; - QVector2D m_velocity; - int m_pointId; - QPointer<QObject> m_exclusiveGrabber; - QVector<QPointer <QQuickPointerHandler> > m_passiveGrabbers; - ulong m_timestamp; - ulong m_pressTimestamp; - State m_state; - bool m_accept : 1; - bool m_grabberIsHandler : 1; - int m_reserved : 29; - - friend class QQuickPointerTouchEvent; - friend class QQuickWindowPrivate; - - 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(QSizeF ellipseDiameters READ ellipseDiameters) - Q_PROPERTY(QPointingDeviceUniqueId uniqueId READ uniqueId) - - QML_NAMED_ELEMENT(EventTouchPoint) - QML_UNCREATABLE("EventTouchPoint is only available as a member of PointerEvent.") - QML_ADDED_IN_VERSION(2, 12) - -public: - QQuickEventTouchPoint(QQuickPointerTouchEvent *parent); - - void reset(const QEventPoint &tp, ulong timestamp); - - qreal rotation() const { return m_rotation; } - qreal pressure() const { return m_pressure; } - QSizeF ellipseDiameters() const { return m_ellipseDiameters; } - QPointingDeviceUniqueId uniqueId() const { return m_uniqueId; } - -private: - qreal m_rotation; - qreal m_pressure; - QSizeF m_ellipseDiameters; - QPointingDeviceUniqueId m_uniqueId; - - friend class QQuickPointerTouchEvent; - - Q_DISABLE_COPY(QQuickEventTouchPoint) -}; - -class Q_QUICK_PRIVATE_EXPORT QQuickPointerEvent : public QObject -{ - Q_OBJECT - Q_PROPERTY(QPointingDevice *device READ device CONSTANT) - Q_PROPERTY(Qt::KeyboardModifiers modifiers READ modifiers CONSTANT) - Q_PROPERTY(Qt::MouseButtons button READ button CONSTANT) - Q_PROPERTY(Qt::MouseButtons buttons READ buttons CONSTANT) - - QML_NAMED_ELEMENT(PointerEvent) - QML_UNCREATABLE("PointerEvent is only available as a parameter of several signals in PointerHandler") - QML_ADDED_IN_VERSION(2, 12) - -public: - QQuickPointerEvent(QObject *parent = nullptr, const QPointingDevice *device = nullptr) - : QObject(parent) - , m_device(device) - , m_pressedButtons(Qt::NoButton) - {} - - ~QQuickPointerEvent() override; - -public: // property accessors - // non-const only because of QML engine limitations (similar to QTBUG-61749) - QPointingDevice *device() const { return const_cast<QPointingDevice *>(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 void localize(QQuickItem *target) = 0; - - virtual bool isPressEvent() const = 0; - virtual bool isDoubleClickEvent() const { return false; } - virtual bool isUpdateEvent() const = 0; - virtual bool isReleaseEvent() const = 0; - virtual QQuickPointerMouseEvent *asPointerMouseEvent() { return nullptr; } - virtual QQuickPointerTouchEvent *asPointerTouchEvent() { return nullptr; } - virtual QQuickPointerTabletEvent *asPointerTabletEvent() { return nullptr; } -#if QT_CONFIG(gestures) - virtual QQuickPointerNativeGestureEvent *asPointerNativeGestureEvent() { return nullptr; } -#endif - virtual QQuickPointerScrollEvent *asPointerScrollEvent() { return nullptr; } - virtual const QQuickPointerMouseEvent *asPointerMouseEvent() const { return nullptr; } - virtual const QQuickPointerTouchEvent *asPointerTouchEvent() const { return nullptr; } - virtual const QQuickPointerTabletEvent *asPointerTabletEvent() const { return nullptr; } -#if QT_CONFIG(gestures) - virtual const QQuickPointerNativeGestureEvent *asPointerNativeGestureEvent() const { return nullptr; } -#endif - virtual const QQuickPointerScrollEvent *asPointerScrollEvent() const { return nullptr; } - virtual bool allPointsAccepted() const = 0; - virtual bool allUpdatedPointsAccepted() const = 0; - virtual bool allPointsGrabbed() const = 0; - bool isAccepted() { return m_event ? m_event->isAccepted() : false; } - void setAccepted(bool accepted) { if (m_event) m_event->setAccepted(accepted); } - QVector<QPointF> unacceptedPressedPointScenePositions() const; - - virtual int pointCount() const = 0; - virtual QQuickEventPoint *point(int i) const = 0; - virtual QQuickEventPoint *pointById(int pointId) const = 0; - virtual QVector<QObject *> exclusiveGrabbers() const = 0; - virtual void clearGrabbers() const = 0; - virtual bool hasExclusiveGrabber(const QQuickPointerHandler *handler) const = 0; - - ulong timestamp() const { return m_event ? m_event->timestamp() : 0; } - -protected: - const QPointingDevice *m_device = nullptr; - QPointerEvent *m_event = nullptr; // original event as received by QQuickWindow - Qt::MouseButton m_button = Qt::NoButton; - Qt::MouseButtons m_pressedButtons; - - friend class QQuickWindowPrivate; - - Q_DISABLE_COPY(QQuickPointerEvent) -}; - -class Q_QUICK_PRIVATE_EXPORT QQuickSinglePointEvent : public QQuickPointerEvent -{ - Q_OBJECT -public: - QQuickSinglePointEvent(QObject *parent, const QPointingDevice *device) - : QQuickPointerEvent(parent, device) { } - - void localize(QQuickItem *target) override; - int pointCount() const override { return 1; } - QQuickEventPoint *point(int i) const override; - QQuickEventPoint *pointById(int pointId) const override; - bool allPointsAccepted() const override; - bool allUpdatedPointsAccepted() const override; - bool allPointsGrabbed() const override; - QVector<QObject *> exclusiveGrabbers() const override; - void clearGrabbers() const override; - bool hasExclusiveGrabber(const QQuickPointerHandler *handler) const override; - -protected: - QQuickEventPoint *m_point = nullptr; - - Q_DISABLE_COPY(QQuickSinglePointEvent) -}; - -class Q_QUICK_PRIVATE_EXPORT QQuickPointerMouseEvent : public QQuickSinglePointEvent -{ - Q_OBJECT - - QML_NAMED_ELEMENT(PointerMouseEvent) - QML_UNCREATABLE("PointerMouseEvent is only available as a parameter of several signals in PointerHandler") - QML_ADDED_IN_VERSION(2, 12) - -public: - QQuickPointerMouseEvent(QObject *parent, const QPointingDevice *device); - - QQuickPointerEvent *reset(QEvent *) override; - bool isPressEvent() const override; - bool isDoubleClickEvent() const override; - bool isUpdateEvent() const override; - bool isReleaseEvent() const override; - QQuickPointerMouseEvent *asPointerMouseEvent() override { return this; } - const QQuickPointerMouseEvent *asPointerMouseEvent() const override { return this; } - - QMouseEvent *asMouseEvent(const QPointF& localPos) const; - - Q_DISABLE_COPY(QQuickPointerMouseEvent) -}; - -class Q_QUICK_PRIVATE_EXPORT QQuickPointerTouchEvent : public QQuickPointerEvent -{ - Q_OBJECT - - QML_NAMED_ELEMENT(PointerTouchEvent) - QML_UNCREATABLE("PointerTouchEvent is only available as a parameter of several signals in PointerHandler") - QML_ADDED_IN_VERSION(2, 12) - -public: - QQuickPointerTouchEvent(QObject *parent = nullptr, const QPointingDevice *device = nullptr) - : QQuickPointerEvent(parent, device) - , m_synthMouseEvent(QEvent::MouseMove, QPointF(), Qt::NoButton, Qt::NoButton, Qt::NoModifier) - {} - - QQuickPointerEvent *reset(QEvent *) override; - void localize(QQuickItem *target) override; - bool isPressEvent() const override; - bool isUpdateEvent() const override; - bool isReleaseEvent() const override; - QQuickPointerTouchEvent *asPointerTouchEvent() override { return this; } - const QQuickPointerTouchEvent *asPointerTouchEvent() const override { return this; } - int pointCount() const override { return m_pointCount; } - QQuickEventPoint *point(int i) const override; - QQuickEventPoint *pointById(int pointId) const override; - const QEventPoint *touchPointById(int pointId) const; - bool allPointsAccepted() const override; - bool allUpdatedPointsAccepted() const override; - bool allPointsGrabbed() const override; - QVector<QObject *> exclusiveGrabbers() const override; - void clearGrabbers() const override; - bool hasExclusiveGrabber(const QQuickPointerHandler *handler) const override; - - QMouseEvent *syntheticMouseEvent(int pointID, QQuickItem *relativeTo) const; - QTouchEvent *touchEventForItem(QQuickItem *item, bool isFiltering = false) const; - - QTouchEvent *asTouchEvent() const; - -private: - QEventPoint::States touchPointStates() const; - - int m_pointCount = 0; - QVector<QQuickEventTouchPoint *> m_touchPoints; - mutable QMouseEvent m_synthMouseEvent; - - Q_DISABLE_COPY(QQuickPointerTouchEvent) -}; - -#if QT_CONFIG(tabletevent) -class Q_QUICK_PRIVATE_EXPORT QQuickEventTabletPoint : public QQuickEventPoint -{ - Q_OBJECT - Q_PROPERTY(qreal rotation READ rotation) - Q_PROPERTY(qreal pressure READ pressure) - Q_PROPERTY(qreal tangentialPressure READ tangentialPressure) - Q_PROPERTY(QVector2D tilt READ tilt) - - QML_NAMED_ELEMENT(EventTabletPoint) - QML_UNCREATABLE("EventTouchPoint is only available as a member of PointerEvent.") - QML_ADDED_IN_VERSION(2, 15) - -public: - QQuickEventTabletPoint(QQuickPointerTabletEvent *parent); - - void reset(const QTabletEvent *e); - - qreal rotation() const { return m_rotation; } - qreal pressure() const { return m_pressure; } - qreal tangentialPressure() const { return m_tangentialPressure; } - QVector2D tilt() const { return m_tilt; } - -private: - qreal m_rotation; - qreal m_pressure; - qreal m_tangentialPressure; - QVector2D m_tilt; - - friend class QQuickPointerTouchEvent; - - Q_DISABLE_COPY(QQuickEventTabletPoint) -}; - -class Q_QUICK_PRIVATE_EXPORT QQuickPointerTabletEvent : public QQuickSinglePointEvent -{ - Q_OBJECT -public: - QQuickPointerTabletEvent(QObject *parent, const QPointingDevice *device); - - QQuickPointerEvent *reset(QEvent *) override; - bool isPressEvent() const override; - bool isUpdateEvent() const override; - bool isReleaseEvent() const override; - QQuickPointerTabletEvent *asPointerTabletEvent() override { return this; } - const QQuickPointerTabletEvent *asPointerTabletEvent() const override { return this; } - const QQuickEventTabletPoint *tabletPoint() const { return static_cast<QQuickEventTabletPoint *>(m_point); } - - QTabletEvent *asTabletEvent() const; - - Q_DISABLE_COPY(QQuickPointerTabletEvent) -}; -#endif // QT_CONFIG(tabletevent) - -#if QT_CONFIG(gestures) -class Q_QUICK_PRIVATE_EXPORT QQuickPointerNativeGestureEvent : public QQuickSinglePointEvent -{ - Q_OBJECT - Q_PROPERTY(Qt::NativeGestureType type READ type CONSTANT) - Q_PROPERTY(qreal value READ value CONSTANT) - -public: - QQuickPointerNativeGestureEvent(QObject *parent, const QPointingDevice *device); - - QQuickPointerEvent *reset(QEvent *) override; - bool isPressEvent() const override; - bool isUpdateEvent() const override; - bool isReleaseEvent() const override; - QQuickPointerNativeGestureEvent *asPointerNativeGestureEvent() override { return this; } - const QQuickPointerNativeGestureEvent *asPointerNativeGestureEvent() const override { return this; } - Qt::NativeGestureType type() const; - qreal value() const; - - Q_DISABLE_COPY(QQuickPointerNativeGestureEvent) -}; -#endif // QT_CONFIG(gestures) - -class Q_QUICK_PRIVATE_EXPORT QQuickPointerScrollEvent : public QQuickSinglePointEvent -{ - Q_OBJECT - Q_PROPERTY(QVector2D angleDelta READ angleDelta CONSTANT) - Q_PROPERTY(QVector2D pixelDelta READ pixelDelta CONSTANT) - Q_PROPERTY(bool hasAngleDelta READ hasAngleDelta CONSTANT) - Q_PROPERTY(bool hasPixelDelta READ hasPixelDelta CONSTANT) - Q_PROPERTY(bool inverted READ isInverted CONSTANT) - - QML_NAMED_ELEMENT(PointerScrollEvent) - QML_UNCREATABLE("PointerScrollEvent is only available via the WheelHandler::wheel signal.") - QML_ADDED_IN_VERSION(2, 14) - -public: - QQuickPointerScrollEvent(QObject *parent, const QPointingDevice *device); - - QQuickPointerEvent *reset(QEvent *) override; - void localize(QQuickItem *target) override; - bool isPressEvent() const override; - bool isUpdateEvent() const override; - bool isReleaseEvent() const override; - QQuickPointerScrollEvent *asPointerScrollEvent() override { return this; } - const QQuickPointerScrollEvent *asPointerScrollEvent() const override { return this; } - QVector2D angleDelta() const { return m_angleDelta; } - QVector2D pixelDelta() const { return m_pixelDelta; } - bool hasAngleDelta() const { return !angleDelta().isNull(); } - bool hasPixelDelta() const { return !pixelDelta().isNull(); } - bool isInverted() const { return m_inverted; } - Qt::ScrollPhase phase() const { return m_phase; } - -private: - // TODO remove this if it's obsolete - Qt::MouseEventSource synthSource() const { return m_synthSource; } - -private: - QVector2D m_angleDelta; - QVector2D m_pixelDelta; - Qt::ScrollPhase m_phase = Qt::NoScrollPhase; - Qt::MouseEventSource m_synthSource = Qt::MouseEventNotSynthesized; - bool m_inverted = false; - - friend class QQuickWindowPrivate; - friend class QQuickWheelHandler; - - Q_DISABLE_COPY(QQuickPointerScrollEvent) -}; - -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) @@ -710,7 +269,6 @@ QML_DECLARE_TYPE(QQuickWheelEvent) QML_DECLARE_TYPE(QQuickCloseEvent) QML_DECLARE_TYPE(QPointingDevice) QML_DECLARE_TYPE(QPointingDeviceUniqueId) -QML_DECLARE_TYPE(QQuickPointerEvent) -Q_DECLARE_METATYPE(QQuickEventPoint::GrabTransition) +QML_DECLARE_TYPE(QPointerEvent) #endif // QQUICKEVENTS_P_P_H diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp index 0a3c1a71c9..d20f23466b 100644 --- a/src/quick/items/qquickflickable.cpp +++ b/src/quick/items/qquickflickable.cpp @@ -1606,19 +1606,7 @@ void QQuickFlickablePrivate::captureDelayedPress(QQuickItem *item, QMouseEvent * if (!isInnermostPressDelay(item)) return; - // delayedPressEvent = QQuickWindowPrivate::cloneMouseEvent(event); // TODO make it OK to tell the truth about the device - - // temporary hack to keep the Qt 5 solution for QTBUG-78818 working the same for now: - // when QQuickWindowPrivate::pointerEventInstance() is called, it has to look like a mouse event from a mouse, - // not a mouse event that actually comes from a touchscreen (which is what it actually is). - // Otherwise a nested MultiPointTouchArea will receive a delayed touch - // (which is what we actually want, later on; but the code isn't ready for that yet). - delayedPressEvent = new QMouseEvent(QEvent::MouseButtonPress, event->position(), - event->scenePosition(), event->globalPosition(), event->button(), event->buttons(), - event->modifiers(), Qt::MouseEventSynthesizedByQt); - QMutableSinglePointEvent::from(delayedPressEvent)->setTimestamp(event->timestamp()); - // end of hack - + delayedPressEvent = QQuickWindowPrivate::cloneMouseEvent(event); delayedPressEvent->setAccepted(false); delayedPressTimer.start(pressDelay, q); } diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 8bf5e4f4bf..8e6bb0f224 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -3015,7 +3015,6 @@ void QQuickItemPrivate::derefWindow() QQuickWindowPrivate *c = QQuickWindowPrivate::get(window); if (polishScheduled) c->itemsToPolish.removeOne(q); - c->removeGrabber(q); #if QT_CONFIG(cursor) if (c->cursorItem == q) { c->cursorItem = nullptr; @@ -5249,12 +5248,12 @@ void QQuickItemPrivate::deliverShortcutOverrideEvent(QKeyEvent *event) } } -bool QQuickItemPrivate::anyPointerHandlerWants(QQuickEventPoint *point) const +bool QQuickItemPrivate::anyPointerHandlerWants(const QPointerEvent *event, const QEventPoint &point) const { if (!hasPointerHandlers()) return false; for (QQuickPointerHandler *handler : extra->pointerHandlers) { - if (handler->wantsEventPoint(point)) + if (handler->wantsEventPoint(event, point)) return true; } return false; @@ -5267,13 +5266,22 @@ bool QQuickItemPrivate::anyPointerHandlerWants(QQuickEventPoint *point) const delivery to any handler which is the exclusive grabber of any point within this event (because delivery to exclusive grabbers is handled separately). */ -bool QQuickItemPrivate::handlePointerEvent(QQuickPointerEvent *event, bool avoidExclusiveGrabber) +bool QQuickItemPrivate::handlePointerEvent(QPointerEvent *event, bool avoidExclusiveGrabber) { bool delivered = false; - QVector<QObject *> &eventDeliveryTargets = QQuickPointerHandlerPrivate::deviceDeliveryTargets(event->device()); if (extra.isAllocated()) { for (QQuickPointerHandler *handler : extra->pointerHandlers) { - if ((!avoidExclusiveGrabber || !event->hasExclusiveGrabber(handler)) && !eventDeliveryTargets.contains(handler)) { + bool avoidThisHandler = false; + if (avoidExclusiveGrabber) { + for (auto &p : event->points()) { + if (event->exclusiveGrabber(p) == handler) { + avoidThisHandler = true; + break; + } + } + } + if (!avoidThisHandler && + !QQuickPointerHandlerPrivate::deviceDeliveryTargets(event->device()).contains(handler)) { handler->handlePointerEvent(event); delivered = true; } @@ -7623,6 +7631,8 @@ QQuickPointerHandler *QQuickItemPrivate::effectiveCursorHandler() const #endif /*! + \obsolete Use QPointerEvent::setExclusiveGrabber() + Grabs the mouse input. This item will receive all mouse events until ungrabMouse() is called. @@ -7637,18 +7647,21 @@ QQuickPointerHandler *QQuickItemPrivate::effectiveCursorHandler() const void QQuickItem::grabMouse() { Q_D(QQuickItem); - if (!d->window || d->window->mouseGrabberItem() == this) + if (!d->window) return; QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(d->window); - bool fromTouch = windowPriv->isDeliveringTouchAsMouse(); - auto point = fromTouch ? - windowPriv->pointerEventInstance(windowPriv->touchMouseDevice)->pointById(windowPriv->touchMouseId) : - windowPriv->pointerEventInstance(QPointingDevice::primaryPointingDevice())->point(0); - if (point) - point->setGrabberItem(this); + auto eventInDelivery = windowPriv->eventInDelivery(); + if (!eventInDelivery) { + qWarning() << "cannot grab mouse: no event is currently being delivered"; + return; + } + auto epd = windowPriv->mousePointData(); + eventInDelivery->setExclusiveGrabber(epd->eventPoint, this); } /*! + \obsolete Use QPointerEvent::setExclusiveGrabber() + Releases the mouse grab following a call to grabMouse(). Note that this function should only be called when the item wants @@ -7663,10 +7676,18 @@ void QQuickItem::ungrabMouse() if (!d->window) return; QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(d->window); - windowPriv->removeGrabber(this, true, windowPriv->isDeliveringTouchAsMouse()); + auto eventInDelivery = windowPriv->eventInDelivery(); + if (!eventInDelivery) { + // do it the expensive way + windowPriv->removeGrabber(this); + return; + } + const auto &eventPoint = windowPriv->mousePointData()->eventPoint; + if (eventInDelivery->exclusiveGrabber(eventPoint) != this) + return; + eventInDelivery->setExclusiveGrabber(eventPoint, this); } - /*! Returns whether mouse input should exclusively remain with this item. @@ -7701,32 +7722,31 @@ void QQuickItem::setKeepMouseGrab(bool keep) } /*! + \obsolete Use QPointerEvent::setExclusiveGrabber(). Grabs the touch points specified by \a ids. These touch points will be owned by the item until they are released. Alternatively, the grab can be stolen by a filtering item like Flickable. Use setKeepTouchGrab() to prevent the grab from being stolen. - - \sa ungrabTouchPoints(), setKeepTouchGrab() */ void QQuickItem::grabTouchPoints(const QList<int> &ids) { - Q_D(QQuickItem); - if (!d->window) + QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window()); + auto event = windowPriv->eventInDelivery(); + if (Q_UNLIKELY(!event)) { + qWarning() << "cannot grab: no event is currently being delivered"; return; - QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(d->window); - windowPriv->grabTouchPoints(this, ids); + } + for (auto pt : event->points()) { + if (ids.contains(pt.id())) + event->setExclusiveGrabber(pt, this); + } } /*! + \obsolete Use QEventPoint::setExclusiveGrabber() instead. Ungrabs the touch points owned by this item. - - \note there is hardly any reason to call this function. It should only be - called when an item does not want to receive any further events, so no - move or release events will be delivered after calling this function. - - \sa grabTouchPoints() */ void QQuickItem::ungrabTouchPoints() { @@ -8338,6 +8358,97 @@ QQuickItemLayer *QQuickItemPrivate::layer() const #endif } +/*! + \internal + Create a modified copy of the given \a event intended for delivery to this + item, containing pointers to only the QEventPoint instances that are + relevant to this item, and transforming their positions to this item's + coordinate system. + + Returns an invalid event with type \l QEvent::None if all points are + stationary, or there are no points inside the item, or none of the points + were pressed inside and the item was not grabbing any of them and + \a isFiltering is false. + + When \a isFiltering is true, it is assumed that the item cares about all + points which are inside its bounds, because most filtering items need to + monitor eventpoint movements until a drag threshold is exceeded or the + requirements for a gesture to be recognized are met in some other way. +*/ +QTouchEvent QQuickItemPrivate::localizedTouchEvent(const QTouchEvent *event, bool isFiltering) +{ + Q_Q(QQuickItem); + QList<QEventPoint> touchPoints; + QEventPoint::States eventStates; + + bool anyPressOrReleaseInside = false; + bool anyStationaryWithModifiedPropertyInside = false; + bool anyGrabber = false; + for (auto &p : event->points()) { + if (p.isAccepted()) + continue; + // include points where item is the grabber + auto pointGrabber = event->exclusiveGrabber(p); + bool isGrabber = (pointGrabber == q); + if (isGrabber) + anyGrabber = true; + // include points inside the bounds if no other item is the grabber or if the item is filtering + const auto localPos = q->mapFromScene(p.scenePosition()); + bool isInside = q->contains(localPos); + bool hasAnotherGrabber = pointGrabber && pointGrabber != q; + + // filtering: (childMouseEventFilter) include points that are grabbed by children of the target item + bool grabberIsChild = false; + auto parent = qobject_cast<QQuickItem*>(pointGrabber); + while (isFiltering && parent) { + if (parent == q) { + grabberIsChild = true; + break; + } + parent = parent->parentItem(); + } + + bool filterRelevant = isFiltering && grabberIsChild; + if (!(isGrabber || (isInside && (!hasAnotherGrabber || isFiltering)) || filterRelevant)) + continue; + if ((p.state() == QEventPoint::State::Pressed || p.state() == QEventPoint::State::Released) && isInside) + anyPressOrReleaseInside = true; + QEventPoint pCopy(p); + QMutableEventPoint mut = QMutableEventPoint::from(pCopy); + if (isInside && mut.stationaryWithModifiedProperty()) + anyStationaryWithModifiedPropertyInside = true; + eventStates |= p.state(); + mut.setPosition(localPos); + touchPoints << mut; + } + + // Now touchPoints will have only points which are inside the item. + // But if none of them were just pressed inside, and the item has no other reason to care, ignore them anyway. + if ((eventStates == QEventPoint::State::Stationary && !anyStationaryWithModifiedPropertyInside) || + touchPoints.isEmpty() || (!anyPressOrReleaseInside && !anyGrabber && !isFiltering)) + return QTouchEvent(QEvent::None); + + // if all points have the same state, set the event type accordingly + QEvent::Type eventType = event->type(); + switch (eventStates) { + case QEventPoint::State::Pressed: + eventType = QEvent::TouchBegin; + break; + case QEventPoint::State::Released: + eventType = QEvent::TouchEnd; + break; + default: + eventType = QEvent::TouchUpdate; + break; + } + + QMutableTouchEvent ret(eventType, event->pointingDevice(), event->modifiers(), touchPoints); + ret.setTarget(q); + ret.setTimestamp(event->timestamp()); + ret.accept(); + return ret; +} + bool QQuickItemPrivate::hasPointerHandlers() const { return extra.isAllocated() && !extra->pointerHandlers.isEmpty(); diff --git a/src/quick/items/qquickitem.h b/src/quick/items/qquickitem.h index 3f75eca5e1..775bc6edbe 100644 --- a/src/quick/items/qquickitem.h +++ b/src/quick/items/qquickitem.h @@ -462,7 +462,7 @@ private: Q_PRIVATE_SLOT(d_func(), void _q_resourceObjectDeleted(QObject *)) Q_PRIVATE_SLOT(d_func(), quint64 _q_createJSWrapper(QV4::ExecutionEngine *)) - friend class QQuickEventPoint; + friend class QEventPoint; friend class QQuickWindow; friend class QQuickWindowPrivate; friend class QSGRenderer; diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h index 2461ea29f7..68c5d59dfe 100644 --- a/src/quick/items/qquickitem_p.h +++ b/src/quick/items/qquickitem_p.h @@ -281,6 +281,7 @@ public: QQuickItemLayer *layer() const; + QTouchEvent localizedTouchEvent(const QTouchEvent *event, bool isFiltering); bool hasPointerHandlers() const; bool hasHoverHandlers() const; virtual void addPointerHandler(QQuickPointerHandler *h); @@ -613,8 +614,8 @@ public: #endif void deliverShortcutOverrideEvent(QKeyEvent *); - bool anyPointerHandlerWants(QQuickEventPoint *point) const; - virtual bool handlePointerEvent(QQuickPointerEvent *, bool avoidExclusiveGrabber = false); + bool anyPointerHandlerWants(const QPointerEvent *event, const QEventPoint &point) const; + virtual bool handlePointerEvent(QPointerEvent *, bool avoidExclusiveGrabber = false); virtual void setVisible(bool visible); diff --git a/src/quick/items/qquickmultipointtoucharea.cpp b/src/quick/items/qquickmultipointtoucharea.cpp index cebfe74df0..8b666628e3 100644 --- a/src/quick/items/qquickmultipointtoucharea.cpp +++ b/src/quick/items/qquickmultipointtoucharea.cpp @@ -45,6 +45,7 @@ #include <private/qquickwindow_p.h> #include <private/qguiapplication_p.h> #include <QtGui/private/qevent_p.h> +#include <QtGui/private/qpointingdevice_p.h> #include <QEvent> #include <QMouseEvent> #include <QDebug> @@ -553,20 +554,19 @@ void QQuickMultiPointTouchArea::touchEvent(QTouchEvent *event) } } -void QQuickMultiPointTouchArea::grabGesture() +void QQuickMultiPointTouchArea::grabGesture(QPointingDevice *dev) { _stealMouse = true; grabMouse(); setKeepMouseGrab(true); - QList<int> ids; - ids.reserve(_touchPoints.size()); + QPointingDevicePrivate *devPriv = QPointingDevicePrivate::get(dev); for (auto it = _touchPoints.keyBegin(), end = _touchPoints.keyEnd(); it != end; ++it) { if (*it != -1) // -1 might be the mouse-point, but we already grabbed the mouse above. - ids.append(*it); + if (auto pt = devPriv->queryPointById(*it)) + pt->exclusiveGrabber = this; } - grabTouchPoints(ids); setKeepTouchGrab(true); } @@ -579,13 +579,17 @@ void QQuickMultiPointTouchArea::updateTouchData(QEvent *event) clearTouchLists(); QList<QEventPoint> touchPoints; QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window()); + QPointingDevice *dev = nullptr; switch (event->type()) { case QEvent::TouchBegin: case QEvent::TouchUpdate: - case QEvent::TouchEnd: - touchPoints = static_cast<QTouchEvent*>(event)->touchPoints(); + case QEvent::TouchEnd: { + QTouchEvent* te = static_cast<QTouchEvent*>(event); + touchPoints = te->points(); + dev = const_cast<QPointingDevice *>(te->pointingDevice()); break; + } case QEvent::MouseButtonPress: _mouseQpaTouchPoint = QEventPoint(windowPriv->touchMouseId); _touchMouseDevice = windowPriv->touchMouseDevice; @@ -593,7 +597,8 @@ void QQuickMultiPointTouchArea::updateTouchData(QEvent *event) case QEvent::MouseMove: case QEvent::MouseButtonRelease: { QMouseEvent *me = static_cast<QMouseEvent*>(event); - _mouseQpaTouchPoint = me->point(0); + _mouseQpaTouchPoint = me->points().first(); + dev = const_cast<QPointingDevice *>(me->pointingDevice()); if (event->type() == QEvent::MouseButtonPress) { addTouchPoint(me); started = true; @@ -623,7 +628,7 @@ void QQuickMultiPointTouchArea::updateTouchData(QEvent *event) } } if (numTouchPoints >= _minimumTouchPoints && numTouchPoints <= _maximumTouchPoints) { - for (const QEventPoint &p : qAsConst(touchPoints)) { + for (QEventPoint &p : touchPoints) { QEventPoint::State touchPointState = p.state(); int id = p.id(); if (touchPointState & QEventPoint::State::Released) { @@ -633,7 +638,7 @@ void QQuickMultiPointTouchArea::updateTouchData(QEvent *event) addTouchPoint(&p); started = true; } else if ((touchPointState & QEventPoint::State::Updated) || - QMutableEventPoint::from(const_cast<QEventPoint &>(p)).stationaryWithModifiedProperty()) { + QMutableEventPoint::from(p).stationaryWithModifiedProperty()) { // React to a stationary point with a property change (velocity, pressure) as if the point moved. (QTBUG-77142) QQuickTouchPoint* dtp = static_cast<QQuickTouchPoint*>(_touchPoints.value(id)); Q_ASSERT(dtp); @@ -668,8 +673,8 @@ void QQuickMultiPointTouchArea::updateTouchData(QEvent *event) QQuickGrabGestureEvent event; event._touchPoints = _touchPoints.values(); emit gestureStarted(&event); - if (event.wantsGrab()) - grabGesture(); + if (event.wantsGrab() && dev) + grabGesture(dev); } } @@ -957,7 +962,7 @@ bool QQuickMultiPointTouchArea::childMouseEventFilter(QQuickItem *receiver, QEve _lastFilterableTouchPointIds.clear(); Q_FALLTHROUGH(); case QEvent::TouchUpdate: - for (auto tp : static_cast<QTouchEvent*>(event)->touchPoints()) { + for (const auto &tp : static_cast<QTouchEvent*>(event)->points()) { if (tp.state() == QEventPoint::State::Pressed) _lastFilterableTouchPointIds << tp.id(); } @@ -998,7 +1003,7 @@ bool QQuickMultiPointTouchArea::shouldFilter(QEvent *event) case QEvent::TouchUpdate: case QEvent::TouchEnd: { QTouchEvent *te = static_cast<QTouchEvent*>(event); - for (const QEventPoint &point : te->touchPoints()) { + for (const QEventPoint &point : te->points()) { if (contains(mapFromScene(point.scenePosition()))) { containsPoint = true; break; diff --git a/src/quick/items/qquickmultipointtoucharea_p.h b/src/quick/items/qquickmultipointtoucharea_p.h index 693ceea50c..16e490adab 100644 --- a/src/quick/items/qquickmultipointtoucharea_p.h +++ b/src/quick/items/qquickmultipointtoucharea_p.h @@ -281,7 +281,7 @@ protected: bool sendMouseEvent(QMouseEvent *event); bool shouldFilter(QEvent *event); - void grabGesture(); + void grabGesture(QPointingDevice *dev); QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *) override; #ifdef Q_OS_OSX void hoverEnterEvent(QHoverEvent *event) override; diff --git a/src/quick/items/qquickpincharea.cpp b/src/quick/items/qquickpincharea.cpp index c021e5181c..6a5dda483d 100644 --- a/src/quick/items/qquickpincharea.cpp +++ b/src/quick/items/qquickpincharea.cpp @@ -335,10 +335,9 @@ void QQuickPinchArea::touchEvent(QTouchEvent *event) case QEvent::TouchBegin: case QEvent::TouchUpdate: d->touchPoints.clear(); - for (int i = 0; i < event->touchPoints().count(); ++i) { - if (!(event->touchPoints().at(i).state() & QEventPoint::State::Released)) { - d->touchPoints << event->touchPoints().at(i); - } + for (auto &tp : event->points()) { + if (!(tp.state() & QEventPoint::State::Released)) + d->touchPoints << tp; } updatePinch(); break; @@ -639,11 +638,12 @@ bool QQuickPinchArea::childMouseEventFilter(QQuickItem *i, QEvent *e) clearPinch(); Q_FALLTHROUGH(); case QEvent::TouchUpdate: { - QTouchEvent *touch = static_cast<QTouchEvent*>(e); + const auto &points = static_cast<QTouchEvent*>(e)->points(); d->touchPoints.clear(); - for (int i = 0; i < touch->touchPoints().count(); ++i) - if (!(touch->touchPoints().at(i).state() & QEventPoint::State::Released)) - d->touchPoints << touch->touchPoints().at(i); + for (auto &tp : points) { + if (!(tp.state() & QEventPoint::State::Released)) + d->touchPoints << tp; + } updatePinch(); } e->setAccepted(d->inPinch); diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 96527d955b..06c2df41a9 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -97,9 +97,12 @@ QT_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(DBG_TOUCH, "qt.quick.touch") +Q_LOGGING_CATEGORY(lcTouchCmprs, "qt.quick.touch.compression") Q_LOGGING_CATEGORY(DBG_TOUCH_TARGET, "qt.quick.touch.target") Q_LOGGING_CATEGORY(DBG_MOUSE, "qt.quick.mouse") Q_LOGGING_CATEGORY(DBG_MOUSE_TARGET, "qt.quick.mouse.target") +Q_LOGGING_CATEGORY(lcPtr, "qt.quick.pointer") +Q_LOGGING_CATEGORY(lcPtrGrab, "qt.quick.pointer.grab") Q_LOGGING_CATEGORY(lcTablet, "qt.quick.tablet") Q_LOGGING_CATEGORY(lcWheelTarget, "qt.quick.wheel.target") Q_LOGGING_CATEGORY(lcGestureTarget, "qt.quick.gesture.target") @@ -855,23 +858,18 @@ QQmlListProperty<QObject> QQuickWindowPrivate::data() QQuickWindowPrivate::data_removeLast); } -QMouseEvent *QQuickWindowPrivate::touchToMouseEvent(QEvent::Type type, const QEventPoint &p, QTouchEvent *event, QQuickItem *item, bool transformNeeded) +QMouseEvent QQuickWindowPrivate::touchToMouseEvent(QEvent::Type type, const QEventPoint &p, QTouchEvent *event, QQuickItem *item) { + Q_UNUSED(item) Q_ASSERT(QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents)); - // The touch point local position and velocity are not yet transformed. - QMouseEvent *me = new QMouseEvent(type, transformNeeded ? item->mapFromScene(p.scenePosition()) : p.position(), p.scenePosition(), p.globalPosition(), - Qt::LeftButton, (type == QEvent::MouseButtonRelease ? Qt::NoButton : Qt::LeftButton), event->modifiers(), - Qt::MouseEventSynthesizedByQt, event->pointingDevice()); - me->setAccepted(true); - me->setTimestamp(event->timestamp()); - QVector2D transformedVelocity = p.velocity(); - if (transformNeeded) { - QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); - QMatrix4x4 transformMatrix(itemPrivate->windowToItemTransform()); - transformedVelocity = transformMatrix.mapVector(p.velocity()).toVector2D(); - } - QMutableSinglePointEvent::from(me)->mutablePoint().setVelocity(transformedVelocity); - return me; + QMutableSinglePointEvent ret(type, event->pointingDevice(), p, + (type == QEvent::MouseMove ? Qt::NoButton : Qt::LeftButton), + (type == QEvent::MouseButtonRelease ? Qt::NoButton : Qt::LeftButton), + event->modifiers(), Qt::MouseEventSynthesizedByQt); + ret.setAccepted(true); + ret.setTimestamp(event->timestamp()); + static_assert(sizeof(QMutableSinglePointEvent) == sizeof(QMouseEvent)); + return *reinterpret_cast<QMouseEvent*>(&ret); } bool QQuickWindowPrivate::checkIfDoubleTapped(ulong newPressEventTimestamp, QPoint newPressPos) @@ -900,18 +898,37 @@ bool QQuickWindowPrivate::checkIfDoubleTapped(ulong newPressEventTimestamp, QPoi return doubleClicked; } +QPointerEvent *QQuickWindowPrivate::eventInDelivery() const +{ + if (eventsInDelivery.isEmpty()) + return nullptr; + return eventsInDelivery.top(); +} + +/*! \internal + A helper function for the benefit of obsolete APIs like QQuickItem::grabMouse() + that don't have the currently-being-delivered event in context. + Returns the device the currently-being-delivered event comse from. +*/ +QPointingDevicePrivate::EventPointData *QQuickWindowPrivate::mousePointData() +{ + if (eventsInDelivery.isEmpty()) + return nullptr; + auto devPriv = QPointingDevicePrivate::get(const_cast<QPointingDevice*>(eventsInDelivery.top()->pointingDevice())); + return devPriv->pointById(isDeliveringTouchAsMouse() ? touchMouseId : 0); +} + void QQuickWindowPrivate::cancelTouchMouseSynthesis() { - qCDebug(DBG_TOUCH_TARGET); + qCDebug(DBG_TOUCH_TARGET) << "id" << touchMouseId << "on" << touchMouseDevice; touchMouseId = -1; touchMouseDevice = nullptr; } -bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QQuickPointerEvent *pointerEvent) +bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QTouchEvent *pointerEvent) { Q_ASSERT(QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents)); - Q_Q(QQuickWindow); - auto device = pointerEvent->device(); + auto device = pointerEvent->pointingDevice(); // A touch event from a trackpad is likely to be followed by a mouse or gesture event, so mouse event synth is redundant if (device->type() == QInputDevice::DeviceType::TouchPad && device->capabilities().testFlag(QInputDevice::Capability::MouseEmulation)) { @@ -920,15 +937,14 @@ bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QQuickPointerEve } // FIXME: make this work for mouse events too and get rid of the asTouchEvent in here. - Q_ASSERT(pointerEvent->asPointerTouchEvent()); - QScopedPointer<QTouchEvent> event(pointerEvent->asPointerTouchEvent()->touchEventForItem(item)); - if (event.isNull()) + QTouchEvent event = QQuickItemPrivate::get(item)->localizedTouchEvent(pointerEvent, false); + if (!event.points().count()) return false; // 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. - for (auto &p : event->points()) { + for (auto &p : event.points()) { // A new touch point if (touchMouseId == -1 && p.state() & QEventPoint::State::Pressed) { QPointF pos = item->mapFromScene(p.scenePosition()); @@ -938,26 +954,25 @@ bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QQuickPointerEve break; qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << Qt::hex << p.id() << "->" << item; - QScopedPointer<QMouseEvent> mousePress(touchToMouseEvent(QEvent::MouseButtonPress, p, event.data(), item, false)); + QMouseEvent mousePress = touchToMouseEvent(QEvent::MouseButtonPress, p, &event, item); // Send a single press and see if that's accepted - QCoreApplication::sendEvent(item, mousePress.data()); - event->setAccepted(mousePress->isAccepted()); - if (mousePress->isAccepted()) { + QCoreApplication::sendEvent(item, &mousePress); + event.setAccepted(mousePress.isAccepted()); + if (mousePress.isAccepted()) { touchMouseDevice = device; touchMouseId = p.id(); - if (!q->mouseGrabberItem()) - item->grabMouse(); - if (auto pointerEventPoint = pointerEvent->pointById(p.id())) - pointerEventPoint->setGrabberItem(item); + const auto &pt = mousePress.point(0); + if (!mousePress.exclusiveGrabber(pt)) + mousePress.setExclusiveGrabber(pt, item); - if (checkIfDoubleTapped(event->timestamp(), p.globalPosition().toPoint())) { + if (checkIfDoubleTapped(event.timestamp(), p.globalPosition().toPoint())) { // since we synth the mouse event from from touch, we respect the // QPlatformTheme::TouchDoubleTapDistance instead of QPlatformTheme::MouseDoubleClickDistance - QScopedPointer<QMouseEvent> mouseDoubleClick(touchToMouseEvent(QEvent::MouseButtonDblClick, p, event.data(), item, false)); - QCoreApplication::sendEvent(item, mouseDoubleClick.data()); - event->setAccepted(mouseDoubleClick->isAccepted()); - if (!mouseDoubleClick->isAccepted()) + QMouseEvent mouseDoubleClick = touchToMouseEvent(QEvent::MouseButtonDblClick, p, &event, item); + QCoreApplication::sendEvent(item, &mouseDoubleClick); + event.setAccepted(mouseDoubleClick.isAccepted()); + if (!mouseDoubleClick.isAccepted()) cancelTouchMouseSynthesis(); } @@ -974,52 +989,49 @@ bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QQuickPointerEve if (moveDelta.x() >= doubleTapDistance || moveDelta.y() >= doubleTapDistance) touchMousePressTimestamp = 0; // Got dragged too far, dismiss the double tap } - if (QQuickItem *mouseGrabberItem = q->mouseGrabberItem()) { - QScopedPointer<QMouseEvent> me(touchToMouseEvent(QEvent::MouseMove, p, event.data(), mouseGrabberItem, false)); - QCoreApplication::sendEvent(item, me.data()); - event->setAccepted(me->isAccepted()); - if (me->isAccepted()) { + if (QQuickItem *mouseGrabberItem = qmlobject_cast<QQuickItem *>(pointerEvent->exclusiveGrabber(p))) { + QMouseEvent me = touchToMouseEvent(QEvent::MouseMove, p, &event, mouseGrabberItem); + QCoreApplication::sendEvent(item, &me); + event.setAccepted(me.isAccepted()); + if (me.isAccepted()) qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << Qt::hex << p.id() << "->" << mouseGrabberItem; - } - return event->isAccepted(); + return event.isAccepted(); } else { // no grabber, check if we care about mouse hover // FIXME: this should only happen once, not recursively... I'll ignore it just ignore hover now. // hover for touch??? - QScopedPointer<QMouseEvent> me(touchToMouseEvent(QEvent::MouseMove, p, event.data(), item, false)); + QMouseEvent me = touchToMouseEvent(QEvent::MouseMove, p, &event, item); if (lastMousePosition.isNull()) - lastMousePosition = me->scenePosition(); + lastMousePosition = me.scenePosition(); QPointF last = lastMousePosition; - lastMousePosition = me->scenePosition(); + lastMousePosition = me.scenePosition(); - bool accepted = me->isAccepted(); - bool delivered = deliverHoverEvent(contentItem, me->scenePosition(), last, me->modifiers(), me->timestamp(), accepted); - if (!delivered) { - //take care of any exits - accepted = clearHover(me->timestamp()); - } - me->setAccepted(accepted); + bool accepted = me.isAccepted(); + bool delivered = deliverHoverEvent(contentItem, me.scenePosition(), last, me.modifiers(), me.timestamp(), accepted); + // take care of any exits + if (!delivered) + clearHover(me.timestamp()); break; } } else if (p.state() & QEventPoint::State::Released) { // currently handled point was released - if (QQuickItem *mouseGrabberItem = q->mouseGrabberItem()) { - QScopedPointer<QMouseEvent> me(touchToMouseEvent(QEvent::MouseButtonRelease, p, event.data(), mouseGrabberItem, false)); - QCoreApplication::sendEvent(item, me.data()); + if (QQuickItem *mouseGrabberItem = qmlobject_cast<QQuickItem *>(pointerEvent->exclusiveGrabber(p))) { + QMouseEvent me = touchToMouseEvent(QEvent::MouseButtonRelease, p, &event, mouseGrabberItem); + QCoreApplication::sendEvent(item, &me); if (item->acceptHoverEvents() && p.globalPosition() != QGuiApplicationPrivate::lastCursorPosition) { QPointF localMousePos(qInf(), qInf()); if (QWindow *w = item->window()) localMousePos = item->mapFromScene(w->mapFromGlobal(QGuiApplicationPrivate::lastCursorPosition.toPoint())); QMouseEvent mm(QEvent::MouseMove, localMousePos, QGuiApplicationPrivate::lastCursorPosition, - Qt::NoButton, Qt::NoButton, event->modifiers()); + Qt::NoButton, Qt::NoButton, event.modifiers()); QCoreApplication::sendEvent(item, &mm); } - if (q->mouseGrabberItem()) // might have ungrabbed due to event - q->mouseGrabberItem()->ungrabMouse(); + if (pointerEvent->exclusiveGrabber(p) == mouseGrabberItem) // might have ungrabbed due to event + pointerEvent->setExclusiveGrabber(p, nullptr); cancelTouchMouseSynthesis(); - return me->isAccepted(); + return me.isAccepted(); } } break; @@ -1028,74 +1040,34 @@ bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QQuickPointerEve return false; } -void QQuickWindowPrivate::grabTouchPoints(QObject *grabber, const QVector<int> &ids) -{ - QQuickPointerEvent *ev = nullptr; - for (int i = 0; i < ids.count(); ++i) { - int id = ids.at(i); - if (Q_UNLIKELY(id < 0)) { - qWarning("ignoring grab of touchpoint %d", id); - continue; - } - if (id == touchMouseId) { - auto point = pointerEventInstance(touchMouseDevice)->pointById(id); - auto touchMouseGrabber = point->grabberItem(); - if (touchMouseGrabber) { - point->setExclusiveGrabber(nullptr); - touchMouseGrabber->mouseUngrabEvent(); - touchMouseGrabber->touchUngrabEvent(); - cancelTouchMouseSynthesis(); - } - qCDebug(DBG_MOUSE_TARGET) << "grabTouchPoints: mouse grabber changed due to grabTouchPoints:" << touchMouseGrabber << "-> null"; - } - - // optimization to avoid the loop over devices below: - // all ids are probably from the same event, so we don't have to search - if (ev) { - auto point = ev->pointById(id); - if (point && point->exclusiveGrabber() != grabber) { - point->setExclusiveGrabber(grabber); - continue; // next id in the ids loop - } - } - // search all devices for a QQuickPointerEvent instance that is delivering the point with id - const auto devs = QPointingDevice::devices(); - for (auto device : devs) { - if (device->type() != QInputDevice::DeviceType::TouchScreen) - continue; - QQuickPointerEvent *pev = pointerEventInstance(static_cast<const QPointingDevice *>(device)); - auto point = pev->pointById(id); - if (point) { - ev = pev; - if (point->exclusiveGrabber() != grabber) - point->setExclusiveGrabber(grabber); - break; // out of touchDevices loop - } - } - } -} - /*! Ungrabs all touchpoint grabs and/or the mouse grab from the given item \a grabber. This should not be called when processing a release event - that's redundant. It is called in other cases, when the points may not be released, but the item nevertheless must lose its grab due to becoming disabled, invisible, etc. - QQuickEventPoint::setGrabberItem() calls touchUngrabEvent() when all points are released, + QPointerEvent::setExclusiveGrabber() calls touchUngrabEvent() when all points are released, but if not all points are released, it cannot be sure whether to call touchUngrabEvent() or not; so we have to do it here. */ void QQuickWindowPrivate::removeGrabber(QQuickItem *grabber, bool mouse, bool touch) { Q_Q(QQuickWindow); - if (Q_LIKELY(mouse) && q->mouseGrabberItem() == grabber) { - bool fromTouch = isDeliveringTouchAsMouse(); - auto point = fromTouch ? - pointerEventInstance(touchMouseDevice)->pointById(touchMouseId) : - pointerEventInstance(QPointingDevice::primaryPointingDevice())->point(0); - QQuickItem *oldGrabber = point->grabberItem(); + if (eventsInDelivery.isEmpty()) { + // do it the expensive way + for (auto dev : knownPointingDevices) { + auto devPriv = QPointingDevicePrivate::get(const_cast<QPointingDevice *>(dev)); + devPriv->removeGrabber(grabber); + } + return; + } + auto eventInDelivery = eventsInDelivery.top(); + if (Q_LIKELY(mouse) && q->mouseGrabberItem() == grabber && eventInDelivery) { + const bool fromTouch = isDeliveringTouchAsMouse(); + auto point = eventInDelivery->pointById(fromTouch ? touchMouseId : 0); + Q_ASSERT(point); + QQuickItem *oldGrabber = qobject_cast<QQuickItem *>(eventInDelivery->exclusiveGrabber(*point)); qCDebug(DBG_MOUSE_TARGET) << "removeGrabber" << oldGrabber << "-> null"; - point->setGrabberItem(nullptr); - sendUngrabEvent(oldGrabber, fromTouch); + eventInDelivery->setExclusiveGrabber(*point, nullptr); } if (Q_LIKELY(touch)) { bool ungrab = false; @@ -1103,14 +1075,9 @@ void QQuickWindowPrivate::removeGrabber(QQuickItem *grabber, bool mouse, bool to for (auto device : touchDevices) { if (device->type() != QInputDevice::DeviceType::TouchScreen) continue; - if (auto pointerEvent = queryPointerEventInstance(static_cast<const QPointingDevice *>(device))) { - for (int i = 0; i < pointerEvent->pointCount(); ++i) { - if (pointerEvent->point(i)->exclusiveGrabber() == grabber) { - pointerEvent->point(i)->setGrabberItem(nullptr); - ungrab = true; - } - } - } + if (QPointingDevicePrivate::get(const_cast<QPointingDevice *>(static_cast<const QPointingDevice *>(device)))-> + removeExclusiveGrabber(eventInDelivery, grabber)) + ungrab = true; } if (ungrab) grabber->touchUngrabEvent(); @@ -1691,8 +1658,6 @@ QQuickWindow::~QQuickWindow() QQuickRootItem *root = d->contentItem; d->contentItem = nullptr; delete root; - qDeleteAll(d->pointerEventInstances); - d->pointerEventInstances.clear(); d->renderJobMutex.lock(); qDeleteAll(d->beforeSynchronizingJobs); @@ -1897,38 +1862,21 @@ QObject *QQuickWindow::focusObject() const /*! - Returns the item which currently has the mouse grab. + \obsolete Use QPointerEvent::exclusiveGrabber() + Returns the item which currently has the mouse grab. */ -// TODO deprecate this, or take seat name as an argument QQuickItem *QQuickWindow::mouseGrabberItem() const { Q_D(const QQuickWindow); - - if (d->isDeliveringTouchAsMouse()) { - if (QQuickPointerEvent *event = d->queryPointerEventInstance(d->touchMouseDevice)) { - auto point = event->pointById(d->touchMouseId); - return point ? point->grabberItem() : nullptr; - } - } else { - const QPointingDevice *mouse = QPointingDevice::primaryPointingDevice(); - if (mouse->type() != QInputDevice::DeviceType::Mouse) { - // TODO don't assume the first mouse is the core pointer (but so far there is normally only one) - for (const auto *dev : QInputDevice::devices()) { - if (dev->type() == QInputDevice::DeviceType::Mouse) { - mouse = static_cast<const QPointingDevice *>(dev); - break; - } - } - } - if (QQuickPointerEvent *event = d->queryPointerEventInstance(mouse)) { - Q_ASSERT(event->pointCount()); - return event->point(0)->grabberItem(); - } + auto epd = const_cast<QQuickWindowPrivate *>(d)->mousePointData(); + if (!epd && d->eventsInDelivery.isEmpty()) { + qCDebug(DBG_MOUSE, "mouse grabber ambiguous: no event is currently being delivered"); + return qmlobject_cast<QQuickItem *>(QPointingDevicePrivate::get(QPointingDevice::primaryPointingDevice())-> + firstPointExclusiveGrabber()); } - return nullptr; + return qobject_cast<QQuickItem *>(epd->exclusiveGrabber); } - bool QQuickWindowPrivate::clearHover(ulong timestamp) { Q_Q(QQuickWindow); @@ -1940,20 +1888,6 @@ bool QQuickWindowPrivate::clearHover(ulong timestamp) bool accepted = false; for (QQuickItem* item : qAsConst(hoverItems)) { accepted = sendHoverEvent(QEvent::HoverLeave, item, pos, pos, QGuiApplication::keyboardModifiers(), timestamp, true) || accepted; -#if QT_CONFIG(cursor) - QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); - if (itemPrivate->hasPointerHandlers()) { - pos = q->mapFromGlobal(QCursor::pos()); - const auto dev = QPointingDevice::primaryPointingDevice(); - QQuickPointerEvent *pointerEvent = pointerEventInstance(dev, QEvent::MouseMove); - pointerEvent->point(0)->reset(QEventPoint::State::Updated, pos, int(dev->systemId()), timestamp, QVector2D()); - pointerEvent->point(0)->setAccepted(true); - pointerEvent->localize(item); - for (QQuickPointerHandler *h : itemPrivate->extra->pointerHandlers) - if (QQuickHoverHandler *hh = qmlobject_cast<QQuickHoverHandler *>(h)) - hh->handlePointerEvent(pointerEvent); - } -#endif } hoverItems.clear(); return accepted; @@ -2037,7 +1971,7 @@ bool QQuickWindow::event(QEvent *e) } #if QT_CONFIG(gestures) case QEvent::NativeGesture: - d->deliverSinglePointEventUntilAccepted(d->pointerEventInstance(e)); + d->deliverSinglePointEventUntilAccepted(static_cast<QPointerEvent *>(e)); break; #endif case QEvent::ShortcutOverride: @@ -2123,20 +2057,21 @@ void QQuickWindowPrivate::deliverKeyEvent(QKeyEvent *e) QMouseEvent *QQuickWindowPrivate::cloneMouseEvent(QMouseEvent *event, QPointF *transformedLocalPos) { QMouseEvent *me = new QMouseEvent(*event); - QMutableEventPoint &point = QMutableSinglePointEvent::from(me)->mutablePoint(); + QMutableEventPoint &point = QMutableEventPoint::from(event->point(0)); point.setTimestamp(event->timestamp()); point.setPosition(transformedLocalPos ? *transformedLocalPos : event->position()); return me; } -void QQuickWindowPrivate::deliverToPassiveGrabbers(const QVector<QPointer <QQuickPointerHandler> > &passiveGrabbers, - QQuickPointerEvent *pointerEvent) +void QQuickWindowPrivate::deliverToPassiveGrabbers(const QVector<QPointer <QObject> > &passiveGrabbers, + QPointerEvent *pointerEvent) { const QVector<QObject *> &eventDeliveryTargets = QQuickPointerHandlerPrivate::deviceDeliveryTargets(pointerEvent->device()); QVarLengthArray<QPair<QQuickItem *, bool>, 4> sendFilteredPointerEventResult; hasFiltered.clear(); - for (auto handler : passiveGrabbers) { + for (auto o : passiveGrabbers) { + QQuickPointerHandler *handler = qobject_cast<QQuickPointerHandler *>(o); // a null pointer in passiveGrabbers is unlikely, unless the grabbing handler was deleted dynamically if (Q_LIKELY(handler) && !eventDeliveryTargets.contains(handler)) { bool alreadyFiltered = false; @@ -2154,97 +2089,13 @@ void QQuickWindowPrivate::deliverToPassiveGrabbers(const QVector<QPointer <QQuic sendFilteredPointerEventResult << qMakePair(par, alreadyFiltered); } if (!alreadyFiltered) { - pointerEvent->localize(handler->parentItem()); + localizePointerEvent(pointerEvent, handler->parentItem()); handler->handlePointerEvent(pointerEvent); } } } } - - -void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEvent) -{ - Q_Q(QQuickWindow); - auto point = pointerEvent->point(0); - lastMousePosition = point->scenePosition(); - const bool mouseIsReleased = (point->state() == QQuickEventPoint::Released && pointerEvent->buttons() == Qt::NoButton); - QQuickItem *grabberItem = point->grabberItem(); - if (!grabberItem && isDeliveringTouchAsMouse()) - grabberItem = q->mouseGrabberItem(); - - if (grabberItem) { - bool handled = false; - hasFiltered.clear(); - if (sendFilteredPointerEvent(pointerEvent, grabberItem)) - handled = true; - // if the grabber is an Item: - // if the update consists of changing button state, don't accept it unless - // the button is one in which the grabber is interested - Qt::MouseButtons acceptedButtons = grabberItem->acceptedMouseButtons(); - if (!handled && pointerEvent->button() != Qt::NoButton && acceptedButtons - && !(acceptedButtons & pointerEvent->button())) { - pointerEvent->setAccepted(false); - handled = true; - } - - // send update - if (!handled) { - QPointF localPos = grabberItem->mapFromScene(lastMousePosition); - auto me = pointerEvent->asMouseEvent(localPos); - me->accept(); - QCoreApplication::sendEvent(grabberItem, me); - point->setAccepted(me->isAccepted()); - } - - // release event: ungrab if no buttons are pressed anymore - if (mouseIsReleased) - removeGrabber(grabberItem, true, isDeliveringTouchAsMouse()); - deliverToPassiveGrabbers(point->passiveGrabbers(), pointerEvent); - } else if (auto handler = point->grabberPointerHandler()) { - pointerEvent->localize(handler->parentItem()); - hasFiltered.clear(); - if (!sendFilteredPointerEvent(pointerEvent, handler->parentItem())) - handler->handlePointerEvent(pointerEvent); - if (mouseIsReleased) - point->setGrabberPointerHandler(nullptr, true); - deliverToPassiveGrabbers(point->passiveGrabbers(), pointerEvent); - } else { - bool delivered = false; - if (pointerEvent->isPressEvent()) { - // send initial press - delivered = deliverPressOrReleaseEvent(pointerEvent); - } else if (pointerEvent->device()->type() == QInputDevice::DeviceType::Mouse) { - // if this is an update or release from an actual mouse, - // and the point wasn't grabbed, deliver only to PointerHandlers: - // passive grabbers first, then the rest - deliverToPassiveGrabbers(point->passiveGrabbers(), pointerEvent); - - // If some points weren't grabbed, deliver to non-grabber PointerHandlers in reverse paint order - if (!pointerEvent->allPointsGrabbed() && pointerEvent->buttons()) { - QVector<QQuickItem *> targetItems = pointerTargets(contentItem, point, false, false); - for (QQuickItem *item : targetItems) { - QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); - if (!itemPrivate->extra.isAllocated() || itemPrivate->extra->pointerHandlers.isEmpty()) - continue; - pointerEvent->localize(item); - hasFiltered.clear(); - if (!sendFilteredPointerEvent(pointerEvent, item)) { - if (itemPrivate->handlePointerEvent(pointerEvent, true)) // avoid re-delivering to grabbers - delivered = true; - } - if (point->exclusiveGrabber()) - break; - } - } - } - - if (!delivered) - // make sure not to accept unhandled events - pointerEvent->setAccepted(false); - } -} - bool QQuickWindowPrivate::sendHoverEvent(QEvent::Type type, QQuickItem *item, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, ulong timestamp, @@ -2291,14 +2142,13 @@ bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &sce } if (itemPrivate->hasPointerHandlers()) { - const auto dev = QPointingDevice::primaryPointingDevice(); - QQuickPointerEvent *pointerEvent = pointerEventInstance(dev, QEvent::MouseMove); - pointerEvent->point(0)->reset(QEventPoint::State::Updated, scenePos, dev->systemId(), timestamp, QVector2D()); - pointerEvent->point(0)->setAccepted(true); - pointerEvent->localize(item); + const QPointF localPos = item->mapFromScene(scenePos); + QMouseEvent hoverEvent(QEvent::MouseMove, localPos, scenePos, q->mapToGlobal(scenePos), Qt::NoButton, Qt::NoButton, modifiers); + hoverEvent.setTimestamp(timestamp); + hoverEvent.setAccepted(true); for (QQuickPointerHandler *h : itemPrivate->extra->pointerHandlers) if (QQuickHoverHandler *hh = qmlobject_cast<QQuickHoverHandler *>(h)) - hh->handlePointerEvent(pointerEvent); + hh->handlePointerEvent(&hoverEvent); } if (itemPrivate->hoverEnabled) { @@ -2355,49 +2205,26 @@ bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &sce // Simple delivery of non-mouse, non-touch Pointer Events: visit the items and handlers // in the usual reverse-paint-order until propagation is stopped -bool QQuickWindowPrivate::deliverSinglePointEventUntilAccepted(QQuickPointerEvent *event) +bool QQuickWindowPrivate::deliverSinglePointEventUntilAccepted(QPointerEvent *event) { - Q_ASSERT(event->pointCount() == 1); - QQuickEventPoint *point = event->point(0); - QVector<QQuickItem *> targetItems = pointerTargets(contentItem, point, false, false); + Q_ASSERT(event->points().count() == 1); + QEventPoint &point = event->point(0); + QVector<QQuickItem *> targetItems = pointerTargets(contentItem, event, point, false, false); + point.setAccepted(false); for (QQuickItem *item : targetItems) { QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); - event->localize(item); + localizePointerEvent(event, item); // Let Pointer Handlers have the first shot itemPrivate->handlePointerEvent(event); - if (point->isAccepted()) + if (point.isAccepted()) + return true; + event->accept(); + QCoreApplication::sendEvent(item, event); + if (event->isAccepted()) { + qCDebug(lcWheelTarget) << event << "->" << item; return true; - QPointF g = item->window()->mapToGlobal(point->scenePosition().toPoint()); -#if QT_CONFIG(wheelevent) - // Let the Item have a chance to handle it - if (QQuickPointerScrollEvent *pse = event->asPointerScrollEvent()) { - QWheelEvent wheel(point->position(), g, pse->pixelDelta().toPoint(), pse->angleDelta().toPoint(), - pse->buttons(), pse->modifiers(), pse->phase(), - pse->isInverted(), pse->synthSource()); - wheel.setTimestamp(pse->timestamp()); - wheel.accept(); - QCoreApplication::sendEvent(item, &wheel); - if (wheel.isAccepted()) { - qCDebug(lcWheelTarget) << &wheel << "->" << item; - event->setAccepted(true); - return true; - } - } -#endif -#if QT_CONFIG(gestures) - if (QQuickPointerNativeGestureEvent *pnge = event->asPointerNativeGestureEvent()) { - QNativeGestureEvent nge(pnge->type(), pnge->device(), point->position(), point->scenePosition(), g, - pnge->value(), 0L, 0L); // TODO can't copy things I can't access - nge.accept(); - QCoreApplication::sendEvent(item, &nge); - if (nge.isAccepted()) { - qCDebug(lcGestureTarget) << &nge << "->" << item; - event->setAccepted(true); - return true; - } } -#endif // gestures } return false; // it wasn't handled @@ -2411,14 +2238,14 @@ void QQuickWindow::wheelEvent(QWheelEvent *event) Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseWheel, event->angleDelta().x(), event->angleDelta().y()); - qCDebug(DBG_MOUSE) << "QQuickWindow::wheelEvent()" << event->pixelDelta() << event->angleDelta() << event->phase(); + qCDebug(DBG_MOUSE) << event; //if the actual wheel event was accepted, accept the compatibility wheel event and return early if (d->lastWheelEventAccepted && event->angleDelta().isNull() && event->phase() == Qt::ScrollUpdate) return; event->ignore(); - d->deliverPointerEvent(d->pointerEventInstance(event)); + d->deliverSinglePointEventUntilAccepted(event); d->lastWheelEventAccepted = event->isAccepted(); } #endif // wheelevent @@ -2430,37 +2257,123 @@ void QQuickWindow::tabletEvent(QTabletEvent *event) Q_D(QQuickWindow); qCDebug(lcTablet) << event; // TODO Qt 6: make sure TabletEnterProximity and TabletLeaveProximity are delivered here - d->deliverPointerEvent(d->pointerEventInstance(event)); + d->deliverPointerEvent(event); } #endif // tabletevent bool QQuickWindowPrivate::deliverTouchCancelEvent(QTouchEvent *event) { qCDebug(DBG_TOUCH) << event; - Q_Q(QQuickWindow); - if (QQuickItem *grabber = q->mouseGrabberItem()) - sendUngrabEvent(grabber, true); - cancelTouchMouseSynthesis(); + if (isDeliveringTouchAsMouse()) { + if (QQuickItem *grabber = qmlobject_cast<QQuickItem *>(event->exclusiveGrabber(touchMouseId))) + sendUngrabEvent(grabber, true); + } - // A TouchCancel event will typically not contain any points. + // An incoming TouchCancel event will typically not contain any points, + // but sendTouchCancelEvent() adds the points that have grabbers to the event. // Deliver it to all items and handlers that have active touches. - QQuickPointerEvent *pointerEvent = pointerEventInstance(event->pointingDevice()); - for (int i = 0; i < pointerEvent->pointCount(); ++i) - pointerEvent->point(i)->cancelExclusiveGrabImpl(event); + const_cast<QPointingDevicePrivate *>(QPointingDevicePrivate::get(event->pointingDevice()))-> + sendTouchCancelEvent(event); - // The next touch event can only be a TouchBegin, so clean up. - pointerEvent->clearGrabbers(); return true; } void QQuickWindowPrivate::deliverDelayedTouchEvent() { // Deliver and delete delayedTouch. - // Set delayedTouch to 0 before delivery to avoid redelivery in case of + // Set delayedTouch to nullptr 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()); - deliverPointerEvent(pointerEventInstance(e.data())); + qCDebug(lcTouchCmprs) << "delivering" << e.data(); + deliverPointerEvent(e.data()); +} + +bool QQuickWindowPrivate::allUpdatedPointsAccepted(const QPointerEvent *ev) +{ + for (auto &point : ev->points()) { + if (point.state() != QEventPoint::State::Pressed && !point.isAccepted()) + return false; + } + return true; +} + +/*! \internal + Localize \a ev for delivery to \a dest. + + Unlike QMutableTouchEvent::localized(), this modifies the QEventPoint + instances in \a ev, which is more efficient than making a copy. +*/ +void QQuickWindowPrivate::localizePointerEvent(QPointerEvent *ev, const QQuickItem *dest) +{ + for (int i = 0; i < ev->pointCount(); ++i) { + auto &point = QMutableEventPoint::from(ev->point(i)); + QMutableEventPoint::from(point).setPosition(dest->mapFromScene(point.scenePosition())); + } +} + +QList<QObject *> QQuickWindowPrivate::exclusiveGrabbers(QPointerEvent *ev) +{ + QList<QObject *> result; + for (const QEventPoint &point : ev->points()) { + if (QObject *grabber = ev->exclusiveGrabber(point)) { + if (!result.contains(grabber)) + result << grabber; + } + } + return result; +} + +bool QQuickWindowPrivate::isMouseEvent(const QPointerEvent *ev) +{ + switch (ev->type()) { + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseButtonDblClick: + case QEvent::MouseMove: + return true; + default: + return false; + } +} + +bool QQuickWindowPrivate::isTouchEvent(const QPointerEvent *ev) +{ + switch (ev->type()) { + case QEvent::TouchBegin: + case QEvent::TouchUpdate: + case QEvent::TouchEnd: + case QEvent::TouchCancel: + return true; + default: + return false; + } +} + +bool QQuickWindowPrivate::isTabletEvent(const QPointerEvent *ev) +{ + switch (ev->type()) { + case QEvent::TabletPress: + case QEvent::TabletMove: + case QEvent::TabletRelease: + case QEvent::TabletEnterProximity: + case QEvent::TabletLeaveProximity: + return true; + default: + return false; + } +} + +/*! \internal + Add the given \a point. +*/ +void QMutableTouchEvent::addPoint(const QEventPoint &point) +{ + m_points.append(point); + auto &added = m_points.last(); + if (!added.device()) + QMutableEventPoint::from(added).setDevice(pointingDevice()); + m_touchPointStates |= point.state(); } bool QQuickWindowPrivate::compressTouchEvent(QTouchEvent *event) @@ -2468,13 +2381,14 @@ bool QQuickWindowPrivate::compressTouchEvent(QTouchEvent *event) Q_Q(QQuickWindow); QEventPoint::States states = event->touchPointStates(); if (states.testFlag(QEventPoint::State::Pressed) || states.testFlag(QEventPoint::State::Released)) { - // we can only compress something that isn't a press or release + // we can only compress an event that doesn't include any pressed or released points return false; } if (!delayedTouch) { delayedTouch.reset(new QMutableTouchEvent(event->type(), event->pointingDevice(), event->modifiers(), event->points())); delayedTouch->setTimestamp(event->timestamp()); + qCDebug(lcTouchCmprs) << "delayed" << delayedTouch.data(); if (renderControl) QQuickRenderControlPrivate::get(renderControl)->maybeUpdate(); else if (windowManager) @@ -2510,6 +2424,7 @@ bool QQuickWindowPrivate::compressTouchEvent(QTouchEvent *event) // TODO optimize, or move event compression elsewhere delayedTouch.reset(new QMutableTouchEvent(event->type(), event->pointingDevice(), event->modifiers(), tpts)); delayedTouch->setTimestamp(event->timestamp()); + qCDebug(lcTouchCmprs) << "coalesced" << delayedTouch.data(); return true; } } @@ -2529,6 +2444,7 @@ bool QQuickWindowPrivate::compressTouchEvent(QTouchEvent *event) void QQuickWindowPrivate::handleTouchEvent(QTouchEvent *event) { translateTouchEvent(event); + // TODO remove: touch and mouse should be independent until we come to touch->mouse synth if (event->pointCount()) { auto &point = event->point(0); if (point.state() == QEventPoint::State::Released) { @@ -2543,14 +2459,16 @@ void QQuickWindowPrivate::handleTouchEvent(QTouchEvent *event) static bool qquickwindow_no_touch_compression = qEnvironmentVariableIsSet("QML_NO_TOUCH_COMPRESSION"); if (qquickwindow_no_touch_compression || pointerEventRecursionGuard) { - deliverPointerEvent(pointerEventInstance(event)); + deliverPointerEvent(event); return; } if (!compressTouchEvent(event)) { - if (delayedTouch) + if (delayedTouch) { deliverDelayedTouchEvent(); - deliverPointerEvent(pointerEventInstance(event)); + qCDebug(lcTouchCmprs) << "resuming delivery" << event; + } + deliverPointerEvent(event); } } @@ -2591,12 +2509,12 @@ void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event) case QEvent::MouseButtonPress: Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMousePress, event->button(), event->buttons()); - deliverPointerEvent(pointerEventInstance(event)); + deliverPointerEvent(event); break; case QEvent::MouseButtonRelease: Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseRelease, event->button(), event->buttons()); - deliverPointerEvent(pointerEventInstance(event)); + deliverPointerEvent(event); #if QT_CONFIG(cursor) updateCursor(event->scenePosition()); #endif @@ -2605,7 +2523,7 @@ void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event) Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseDoubleClick, event->button(), event->buttons()); if (allowDoubleClick) - deliverPointerEvent(pointerEventInstance(event)); + deliverPointerEvent(event); break; case QEvent::MouseMove: { Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseMove, @@ -2616,8 +2534,7 @@ void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event) #if QT_CONFIG(cursor) updateCursor(event->scenePosition()); #endif - auto pointerEvent = pointerEventInstance(event->pointingDevice()); - if (!pointerEvent->pointCount() || !pointerEvent->point(0)->exclusiveGrabber()) { + if (!event->points().count() || !event->exclusiveGrabber(event->point(0))) { QPointF last = lastMousePosition.isNull() ? event->scenePosition() : lastMousePosition; lastMousePosition = event->scenePosition(); @@ -2629,7 +2546,7 @@ void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event) } event->setAccepted(accepted); } - deliverPointerEvent(pointerEventInstance(event)); + deliverPointerEvent(event); break; } default: @@ -2660,6 +2577,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. + // TODO do this for each known mouse device or come up with a different strategy if (!q->mouseGrabberItem() && !lastMousePosition.isNull() && dirtyItemList) { bool accepted = false; bool delivered = deliverHoverEvent(contentItem, lastMousePosition, lastMousePosition, QGuiApplication::keyboardModifiers(), 0, accepted); @@ -2669,136 +2587,71 @@ void QQuickWindowPrivate::flushFrameSynchronousEvents() #endif } -QQuickPointerEvent *QQuickWindowPrivate::queryPointerEventInstance(const QPointingDevice *device, QEvent::Type eventType) const +void QQuickWindowPrivate::onGrabChanged(QObject *grabber, QPointingDevice::GrabTransition transition, + const QPointerEvent *event, const QEventPoint &point) { - // Search for a matching reusable event object. - for (QQuickPointerEvent *e : pointerEventInstances) { - // If device can generate native gestures (e.g. a trackpad), there might be multiple QQuickPointerEvents: - // QQuickPointerNativeGestureEvent, QQuickPointerScrollEvent, and QQuickPointerTouchEvent. - // Use eventType to disambiguate. -#if QT_CONFIG(gestures) - if ((eventType == QEvent::NativeGesture) != bool(e->asPointerNativeGestureEvent())) - continue; -#endif - if ((eventType == QEvent::Wheel) != bool(e->asPointerScrollEvent())) - continue; - // Otherwise we assume there's only one event type per device. - // More disambiguation tests might need to be added above if that changes later. - if (e->device() == device) - return e; - } - return nullptr; -} - -QQuickPointerEvent *QQuickWindowPrivate::pointerEventInstance(const QPointingDevice *device, QEvent::Type eventType) const -{ - QQuickPointerEvent *ev = queryPointerEventInstance(device, eventType); - if (ev) - return ev; - QQuickWindow *q = const_cast<QQuickWindow*>(q_func()); - switch (device->type()) { - case QInputDevice::DeviceType::Mouse: - // QWindowSystemInterface::handleMouseEvent() does not take a device parameter: - // we assume all mouse events come from one mouse (the "core pointer"). - // So when the event is a mouse event, device == QPointingDevice::primaryPointingDevice() - if (eventType == QEvent::Wheel) - ev = new QQuickPointerScrollEvent(q, device); - else - ev = new QQuickPointerMouseEvent(q, device); + qCDebug(lcPtrGrab) << grabber << transition << event << point; + // event can be null, if the signal was emitted from QPointingDevicePrivate::removeGrabber(grabber) + switch (transition) { + case QPointingDevice::UngrabExclusive: + if (point.device()->type() == QInputDevice::DeviceType::Mouse || isDeliveringTouchAsMouse()) + sendUngrabEvent(qobject_cast<QQuickItem *>(grabber), isDeliveringTouchAsMouse()); break; - case QInputDevice::DeviceType::TouchPad: - case QInputDevice::DeviceType::TouchScreen: -#if QT_CONFIG(gestures) - if (eventType == QEvent::NativeGesture) - ev = new QQuickPointerNativeGestureEvent(q, device); - else // assume QEvent::Type is one of TouchBegin/Update/End -#endif - ev = new QQuickPointerTouchEvent(q, device); - break; -#if QT_CONFIG(tabletevent) - case QInputDevice::DeviceType::Stylus: - case QPointingDevice::DeviceType::Airbrush: - case QPointingDevice::DeviceType::Puck: - ev = new QQuickPointerTabletEvent(q, device); - break; -#endif default: break; } - pointerEventInstances << ev; - return ev; } -/*! - \internal - Returns a QQuickPointerEvent instance suitable for wrapping and delivering \a event. - - There is a unique instance per QPointingDevice, which is determined - from \a event's device. -*/ -QQuickPointerEvent *QQuickWindowPrivate::pointerEventInstance(QEvent *event) const +void QQuickWindowPrivate::ensureDeviceConnected(const QPointingDevice *dev) { - const QPointingDevice *dev = nullptr; - switch (event->type()) { - case QEvent::MouseButtonPress: - case QEvent::MouseButtonRelease: - case QEvent::MouseButtonDblClick: - case QEvent::MouseMove: - case QEvent::Wheel: - case QEvent::TouchBegin: - case QEvent::TouchUpdate: - case QEvent::TouchEnd: - case QEvent::TouchCancel: -#if QT_CONFIG(tabletevent) - case QEvent::TabletPress: - case QEvent::TabletMove: - case QEvent::TabletRelease: - case QEvent::TabletEnterProximity: - case QEvent::TabletLeaveProximity: -#endif -#if QT_CONFIG(gestures) - case QEvent::NativeGesture: -#endif - dev = static_cast<QPointerEvent *>(event)->pointingDevice(); - break; - default: - break; - } - - Q_ASSERT(dev); - return pointerEventInstance(dev, event->type())->reset(event); + if (knownPointingDevices.contains(dev)) + return; + knownPointingDevices.append(dev); + connect(dev, &QPointingDevice::grabChanged, this, &QQuickWindowPrivate::onGrabChanged); } -void QQuickWindowPrivate::deliverPointerEvent(QQuickPointerEvent *event) +void QQuickWindowPrivate::deliverPointerEvent(QPointerEvent *event) { - Q_Q(QQuickWindow); // 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; + eventsInDelivery.push(event); skipDelivery.clear(); - if (event->asPointerMouseEvent()) { - deliverMouseEvent(event->asPointerMouseEvent()); - // failsafe: never allow any kind of grab to persist after release - if (event->isReleaseEvent() && event->buttons() == Qt::NoButton) { - QQuickItem *oldGrabber = q->mouseGrabberItem(); - event->clearGrabbers(); - sendUngrabEvent(oldGrabber, false); + QQuickPointerHandlerPrivate::deviceDeliveryTargets(event->pointingDevice()).clear(); + qCDebug(lcPtr) << "delivering" << event; + for (int i = 0; i < event->pointCount(); ++i) + event->point(i).setAccepted(false); + + if (event->isBeginEvent()) { + ensureDeviceConnected(event->pointingDevice()); + if (!deliverPressOrReleaseEvent(event)) + event->setAccepted(false); + } + if (!allUpdatedPointsAccepted(event)) + deliverUpdatedPoints(event); + if (event->isEndEvent()) + deliverPressOrReleaseEvent(event, true); + + // failsafe: never allow any kind of grab to persist after release + if (event->isEndEvent()) { + if (isTouchEvent(event)) { + for (int i = 0; i < event->pointCount(); ++i) { + auto &point = event->point(i); + event->setExclusiveGrabber(point, nullptr); + event->clearPassiveGrabbers(point); + } + // never allow touch->mouse synthesis to persist either + cancelTouchMouseSynthesis(); + } else if (static_cast<QSinglePointEvent *>(event)->buttons() == Qt::NoButton) { + auto &firstPt = event->point(0); + event->setExclusiveGrabber(firstPt, nullptr); + event->clearPassiveGrabbers(firstPt); } - } else if (event->asPointerTouchEvent()) { - deliverTouchEvent(event->asPointerTouchEvent()); - } else { - deliverSinglePointEventUntilAccepted(event); - // If any handler got interested in the tablet event, we don't want to receive a synth-mouse event from QtGui - // TODO Qt 6: QTabletEvent will be accepted by default, like other events - if (event->asPointerTabletEvent() && - (!event->point(0)->passiveGrabbers().isEmpty() || event->point(0)->exclusiveGrabber())) - event->setAccepted(true); } - event->reset(nullptr); - + eventsInDelivery.pop(); --pointerEventRecursionGuard; } @@ -2807,11 +2660,12 @@ void QQuickWindowPrivate::deliverPointerEvent(QQuickPointerEvent *event) // If checkMouseButtons is true, it means we are finding targets for a mouse event, so no item for which acceptedMouseButtons() is NoButton will be added. // If checkAcceptsTouch is true, it means we are finding targets for a touch event, so either acceptTouchEvents() must return true OR // it must accept a synth. mouse event, thus if acceptTouchEvents() returns false but acceptedMouseButtons() is true, gets added; if not, it doesn't. -QVector<QQuickItem *> QQuickWindowPrivate::pointerTargets(QQuickItem *item, QQuickEventPoint *point, bool checkMouseButtons, bool checkAcceptsTouch) const +QVector<QQuickItem *> QQuickWindowPrivate::pointerTargets(QQuickItem *item, const QPointerEvent *event, const QEventPoint &point, + bool checkMouseButtons, bool checkAcceptsTouch) const { QVector<QQuickItem *> targets; auto itemPrivate = QQuickItemPrivate::get(item); - QPointF itemPos = item->mapFromScene(point->scenePosition()); + QPointF itemPos = item->mapFromScene(point.scenePosition()); bool relevant = item->contains(itemPos); // if the item clips, we can potentially return early if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) { @@ -2821,7 +2675,7 @@ QVector<QQuickItem *> QQuickWindowPrivate::pointerTargets(QQuickItem *item, QQui if (itemPrivate->hasPointerHandlers()) { if (!relevant) - if (itemPrivate->anyPointerHandlerWants(point)) + if (itemPrivate->anyPointerHandlerWants(event, point)) relevant = true; } else { if (relevant && checkMouseButtons && item->acceptedMouseButtons() == Qt::NoButton) @@ -2844,7 +2698,7 @@ QVector<QQuickItem *> QQuickWindowPrivate::pointerTargets(QQuickItem *item, QQui continue; if (child != item) - targets << pointerTargets(child, point, checkMouseButtons, checkAcceptsTouch); + targets << pointerTargets(child, event, point, checkMouseButtons, checkAcceptsTouch); else targets << child; } @@ -2874,45 +2728,14 @@ QVector<QQuickItem *> QQuickWindowPrivate::mergePointerTargets(const QVector<QQu return targets; } -void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event) -{ - qCDebug(DBG_TOUCH) << " - delivering" << event->asTouchEvent(); - - if (event->isPressEvent()) - deliverPressOrReleaseEvent(event); - if (!event->allUpdatedPointsAccepted()) - deliverUpdatedTouchPoints(event); - if (event->isReleaseEvent()) - deliverPressOrReleaseEvent(event, true); - - // 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() == QQuickEventPoint::Released) { - int id = point->pointId(); - qCDebug(DBG_TOUCH_TARGET) << "TP" << Qt::hex << id << "released"; - point->setGrabberItem(nullptr); - if (id == touchMouseId) - cancelTouchMouseSynthesis(); - } else { - allReleased = false; - } - } - - if (allReleased) { - if (Q_UNLIKELY(!event->exclusiveGrabbers().isEmpty())) - qWarning() << "No release received for some grabbers" << event->exclusiveGrabbers(); - event->clearGrabbers(); - } -} - -// Deliver touch points to existing grabbers -void QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event) +/*! \internal + Deliver updated points to existing grabbers. +*/ +void QQuickWindowPrivate::deliverUpdatedPoints(QPointerEvent *event) { bool done = false; - const auto grabbers = event->exclusiveGrabbers(); + const auto grabbers = exclusiveGrabbers(event); + hasFiltered.clear(); for (auto grabber : grabbers) { // The grabber is guaranteed to be either an item or a handler. QQuickItem *receiver = qmlobject_cast<QQuickItem *>(grabber); @@ -2923,22 +2746,19 @@ void QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *eve hasFiltered.clear(); if (sendFilteredPointerEvent(event, receiver)) done = true; - event->localize(receiver); + localizePointerEvent(event, receiver); handler->handlePointerEvent(event); } if (done) break; // If the grabber is an item or the grabbing handler didn't handle it, // then deliver the event to the item (which may have multiple handlers). - deliverMatchingPointsToItem(receiver, event); + deliverMatchingPointsToItem(receiver, true, event); } // Deliver to each eventpoint's passive grabbers (but don't visit any handler more than once) - int pointCount = event->pointCount(); - for (int i = 0; i < pointCount; ++i) { - QQuickEventPoint *point = event->point(i); - deliverToPassiveGrabbers(point->passiveGrabbers(), event); - } + for (auto &point : event->points()) + deliverToPassiveGrabbers(event->passiveGrabbers(point), event); if (done) return; @@ -2946,11 +2766,10 @@ void QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *eve // If some points weren't grabbed, deliver only to non-grabber PointerHandlers in reverse paint order if (!event->allPointsGrabbed()) { QVector<QQuickItem *> targetItems; - for (int i = 0; i < pointCount; ++i) { - QQuickEventPoint *point = event->point(i); - if (point->state() == QQuickEventPoint::Pressed) + for (auto &point : event->points()) { + if (point.state() == QEventPoint::Pressed) continue; // presses were delivered earlier; not the responsibility of deliverUpdatedTouchPoints - QVector<QQuickItem *> targetItemsForPoint = pointerTargets(contentItem, point, false, false); + QVector<QQuickItem *> targetItemsForPoint = pointerTargets(contentItem, event, point, false, false); if (targetItems.count()) { targetItems = mergePointerTargets(targetItems, targetItemsForPoint); } else { @@ -2961,7 +2780,7 @@ void QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *eve if (grabbers.contains(item)) continue; QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); - event->localize(item); + localizePointerEvent(event, item); itemPrivate->handlePointerEvent(event, true); // avoid re-delivering to grabbers if (event->allPointsGrabbed()) break; @@ -2970,13 +2789,12 @@ void QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *eve } // Deliver an event containing newly pressed or released touch points -bool QQuickWindowPrivate::deliverPressOrReleaseEvent(QQuickPointerEvent *event, bool handlersOnly) +bool QQuickWindowPrivate::deliverPressOrReleaseEvent(QPointerEvent *event, bool handlersOnly) { - int pointCount = event->pointCount(); QVector<QQuickItem *> targetItems; - bool isTouchEvent = (event->asPointerTouchEvent() != nullptr); - if (isTouchEvent && event->isPressEvent() && isDeliveringTouchAsMouse()) { - if (const QQuickEventPoint *point = pointerEventInstance(touchMouseDevice)->pointById(touchMouseId)) { + const bool isTouch = isTouchEvent(event); + if (isTouch && event->isBeginEvent() && isDeliveringTouchAsMouse()) { + if (auto point = const_cast<QPointingDevicePrivate *>(QPointingDevicePrivate::get(touchMouseDevice))->queryPointById(touchMouseId)) { // When a second point is pressed, if the first point's existing // grabber was a pointer handler while a filtering parent is filtering // the same first point _as mouse_: we're starting over with delivery, @@ -2986,20 +2804,17 @@ bool QQuickWindowPrivate::deliverPressOrReleaseEvent(QQuickPointerEvent *event, // synth-mouse and perhaps grab it. Ideally we would always do this // when a new touchpoint is pressed, but this compromise fixes // QTBUG-70998 and avoids breaking tst_FlickableInterop::touchDragSliderAndFlickable - if (point->grabberPointerHandler()) + if (qobject_cast<QQuickPointerHandler *>(event->exclusiveGrabber(point->eventPoint))) cancelTouchMouseSynthesis(); } else { qCWarning(DBG_TOUCH_TARGET) << "during delivery of touch press, synth-mouse ID" << Qt::hex << touchMouseId << "is missing from" << event; } } - for (int i = 0; i < pointCount; ++i) { - auto point = event->point(i); - if (point->state() == QQuickEventPoint::Pressed && !event->isDoubleClickEvent()) - point->clearPassiveGrabbers(); - point->setAccepted(false); // because otherwise touchEventForItem will ignore it - if (point->grabberPointerHandler() && point->state() == QQuickEventPoint::Released) - point->setGrabberPointerHandler(nullptr, true); - QVector<QQuickItem *> targetItemsForPoint = pointerTargets(contentItem, point, !isTouchEvent, isTouchEvent); + for (int i = 0; i < event->pointCount(); ++i) { + auto &point = event->point(i); + if (point.state() == QEventPoint::Pressed && event->type() != QEvent::MouseButtonDblClick) + event->clearPassiveGrabbers(point); + QVector<QQuickItem *> targetItemsForPoint = pointerTargets(contentItem, event, point, !isTouch, isTouch); if (targetItems.count()) { targetItems = mergePointerTargets(targetItems, targetItemsForPoint); } else { @@ -3008,15 +2823,11 @@ bool QQuickWindowPrivate::deliverPressOrReleaseEvent(QQuickPointerEvent *event, } for (QQuickItem *item : targetItems) { - if (!event->m_event) { - qWarning("event went missing during delivery! (nested sendEvent() is not allowed)"); - break; - } hasFiltered.clear(); if (!handlersOnly && sendFilteredPointerEvent(event, item)) { if (event->isAccepted()) { for (int i = 0; i < event->pointCount(); ++i) - event->point(i)->setAccepted(); + event->point(i).setAccepted(); return true; } skipDelivery.append(item); @@ -3026,11 +2837,7 @@ bool QQuickWindowPrivate::deliverPressOrReleaseEvent(QQuickPointerEvent *event, // nor to any item which already had a chance to filter. if (skipDelivery.contains(item)) continue; - if (!event->m_event) { - qWarning("event went missing during delivery! (nested sendEvent() is not allowed)"); - break; - } - deliverMatchingPointsToItem(item, event, handlersOnly); + deliverMatchingPointsToItem(item, false, event, handlersOnly); if (event->allPointsAccepted()) handlersOnly = true; } @@ -3038,77 +2845,78 @@ bool QQuickWindowPrivate::deliverPressOrReleaseEvent(QQuickPointerEvent *event, return event->allPointsAccepted(); } -void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent, bool handlersOnly) +void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, bool isGrabber, QPointerEvent *pointerEvent, bool handlersOnly) { - Q_Q(QQuickWindow); QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); - pointerEvent->localize(item); + localizePointerEvent(pointerEvent, item); + bool isMouse = isMouseEvent(pointerEvent); // Let the Item's handlers (if any) have the event first. // However, double click should never be delivered to handlers. - if (!pointerEvent->isDoubleClickEvent()) { + if (pointerEvent->type() != QEvent::MouseButtonDblClick) { bool wasAccepted = pointerEvent->allPointsAccepted(); itemPrivate->handlePointerEvent(pointerEvent); - allowDoubleClick = wasAccepted || !(pointerEvent->asPointerMouseEvent() && pointerEvent->isPressEvent() && pointerEvent->allPointsAccepted()); + allowDoubleClick = wasAccepted || !(isMouse && pointerEvent->isBeginEvent() && pointerEvent->allPointsAccepted()); } if (handlersOnly) return; // If all points are released and the item is not the grabber, it doesn't get the event. // But if at least one point is still pressed, we might be in a potential gesture-takeover scenario. - if (pointerEvent->isReleaseEvent() && !pointerEvent->isUpdateEvent() - && !pointerEvent->exclusiveGrabbers().contains(item)) + if (pointerEvent->isEndEvent() && !pointerEvent->isUpdateEvent() + && !exclusiveGrabbers(pointerEvent).contains(item)) return; // TODO: unite this mouse point delivery with the synthetic mouse event below - auto event = pointerEvent->asPointerMouseEvent(); - if (event && item->acceptedMouseButtons() & event->button()) { - auto point = event->point(0); - // The only reason to already have a mouse grabber here is - // synthetic events - flickable sends one when setPressDelay is used. - auto oldMouseGrabber = q->mouseGrabberItem(); - QPointF localPos = item->mapFromScene(point->scenePosition()); - QMouseEvent *me = event->asMouseEvent(localPos); - me->accept(); - QCoreApplication::sendEvent(item, me); - if (me->isAccepted()) { - auto mouseGrabber = q->mouseGrabberItem(); - if (mouseGrabber && mouseGrabber != item && mouseGrabber != oldMouseGrabber) { - item->mouseUngrabEvent(); - } else if (item->isEnabled() && item->isVisible()) { - item->grabMouse(); - } - point->setAccepted(true); + if (isMouse && (isGrabber || (item->acceptedMouseButtons() & static_cast<QSinglePointEvent *>(pointerEvent)->button()))) { + // The only reason to already have a mouse grabber here is + // synthetic events - flickable sends one when setPressDelay is used. + auto oldMouseGrabber = pointerEvent->exclusiveGrabber(pointerEvent->point(0)); + pointerEvent->accept(); + if (isGrabber && sendFilteredPointerEvent(pointerEvent, item)) + return; + localizePointerEvent(pointerEvent, item); + QCoreApplication::sendEvent(item, pointerEvent); + if (pointerEvent->isAccepted()) { + auto &point = pointerEvent->point(0); + auto mouseGrabber = pointerEvent->exclusiveGrabber(point); + if (mouseGrabber && mouseGrabber != item && mouseGrabber != oldMouseGrabber) { + // we don't need item->mouseUngrabEvent() because QQuickWindowPrivate::onGrabChanged does it + } else if (item->isEnabled() && item->isVisible()) { + pointerEvent->setExclusiveGrabber(point, item); } + point.setAccepted(true); + } return; } - QQuickPointerTouchEvent *ptEvent = pointerEvent->asPointerTouchEvent(); - if (!ptEvent) + if (!isTouchEvent(pointerEvent)) return; - QScopedPointer<QTouchEvent> touchEvent(ptEvent->touchEventForItem(item)); - if (!touchEvent) - return; - - qCDebug(DBG_TOUCH) << "considering delivering " << touchEvent.data() << " to " << item; bool eventAccepted = false; + QTouchEvent touchEvent = QQuickItemPrivate::get(item)->localizedTouchEvent(static_cast<QTouchEvent *>(pointerEvent), false); + if (touchEvent.type() == QEvent::None) + return; // no points inside this item - // If any parent filters the event, we're done. - hasFiltered.clear(); - if (sendFilteredPointerEvent(pointerEvent, item)) - return; + if (item->acceptTouchEvents()) { + qCDebug(DBG_TOUCH) << "considering delivering" << &touchEvent << " to " << item; - // Deliver the touch event to the given item - qCDebug(DBG_TOUCH) << " - actually delivering " << touchEvent.data() << " to " << item; - QCoreApplication::sendEvent(item, touchEvent.data()); - eventAccepted = touchEvent->isAccepted(); + // If any parent filters the event, we're done. + hasFiltered.clear(); + if (sendFilteredPointerEvent(pointerEvent, item)) + return; + + // Deliver the touch event to the given item + qCDebug(DBG_TOUCH) << "actually delivering" << &touchEvent << " to " << item; + QCoreApplication::sendEvent(item, &touchEvent); + eventAccepted = touchEvent.isAccepted(); + } // If the touch event wasn't accepted, synthesize a mouse event and see if the item wants it. if (Q_LIKELY(QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents))) { if (!eventAccepted && (itemPrivate->acceptedMouseButtons() & Qt::LeftButton)) { // send mouse event - if (deliverTouchAsMouse(item, ptEvent)) + if (deliverTouchAsMouse(item, &touchEvent)) eventAccepted = true; } } @@ -3116,24 +2924,21 @@ void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo if (eventAccepted) { // If the touch was accepted (regardless by whom or in what form), // update accepted new points. - bool isPressOrRelease = pointerEvent->isPressEvent() || pointerEvent->isReleaseEvent(); - for (const auto &point: touchEvent->points()) { - if (auto pointerEventPoint = ptEvent->pointById(point.id())) { - pointerEventPoint->setAccepted(); - if (isPressOrRelease) - pointerEventPoint->setGrabberItem(item); - } + bool isPressOrRelease = pointerEvent->isBeginEvent() || pointerEvent->isEndEvent(); + for (int i = 0; i < touchEvent.pointCount(); ++i) { + auto &point = QMutableEventPoint::from(touchEvent.point(i)); + point.setAccepted(); + if (isPressOrRelease) + pointerEvent->setExclusiveGrabber(point, item); } } else { // But if the event was not accepted then we know this item // will not be interested in further updates for those touchpoint IDs either. - for (const auto &point: touchEvent->points()) { + for (const auto &point: touchEvent.points()) { if (point.state() == QEventPoint::State::Pressed) { - if (auto *tp = ptEvent->pointById(point.id())) { - if (tp->exclusiveGrabber() == item) { - qCDebug(DBG_TOUCH_TARGET) << "TP" << Qt::hex << point.id() << "disassociated"; - tp->setGrabberItem(nullptr); - } + if (pointerEvent->exclusiveGrabber(point) == item) { + qCDebug(DBG_TOUCH_TARGET) << "TP" << Qt::hex << point.id() << "disassociated"; + pointerEvent->setExclusiveGrabber(point, nullptr); } } } @@ -3342,11 +3147,7 @@ QPair<QQuickItem*, QQuickPointerHandler*> QQuickWindowPrivate::findCursorItemAnd } if (itemPrivate->hasCursorHandler) { if (auto handler = itemPrivate->effectiveCursorHandler()) { - QQuickPointerEvent *pointerEvent = pointerEventInstance(QPointingDevice::primaryPointingDevice(), QEvent::MouseMove); - pointerEvent->point(0)->reset(QEventPoint::State::Updated, scenePos, 0, 0); - pointerEvent->point(0)->setAccepted(true); - pointerEvent->localize(item); - if (handler->parentContains(pointerEvent->point(0))) + if (handler->parentContains(scenePos)) return {item, handler}; } } @@ -3361,12 +3162,12 @@ QPair<QQuickItem*, QQuickPointerHandler*> QQuickWindowPrivate::findCursorItemAnd } #endif -bool QQuickWindowPrivate::sendFilteredPointerEvent(QQuickPointerEvent *event, QQuickItem *receiver, QQuickItem *filteringParent) +bool QQuickWindowPrivate::sendFilteredPointerEvent(QPointerEvent *event, QQuickItem *receiver, QQuickItem *filteringParent) { return sendFilteredPointerEventImpl(event, receiver, filteringParent ? filteringParent : receiver->parentItem()); } -bool QQuickWindowPrivate::sendFilteredPointerEventImpl(QQuickPointerEvent *event, QQuickItem *receiver, QQuickItem *filteringParent) +bool QQuickWindowPrivate::sendFilteredPointerEventImpl(QPointerEvent *event, QQuickItem *receiver, QQuickItem *filteringParent) { if (!allowChildEventFiltering) return false; @@ -3375,58 +3176,58 @@ bool QQuickWindowPrivate::sendFilteredPointerEventImpl(QQuickPointerEvent *event bool filtered = false; if (filteringParent->filtersChildMouseEvents() && !hasFiltered.contains(filteringParent)) { hasFiltered.append(filteringParent); - if (QQuickPointerMouseEvent *pme = event->asPointerMouseEvent()) { + if (isMouseEvent(event)) { + auto me = static_cast<QMouseEvent *>(event); if (receiver->acceptedMouseButtons()) { - QPointF localPos = receiver->mapFromScene(pme->point(0)->scenePosition()); - QMouseEvent *me = pme->asMouseEvent(localPos); const bool wasAccepted = me->isAccepted(); - me->setAccepted(true); - auto oldMouseGrabber = pme->point(0)->grabberItem(); - if (filteringParent->childMouseEventFilter(receiver, me)) { + Q_ASSERT(event->pointCount()); + localizePointerEvent(event, receiver); + event->setAccepted(true); + auto oldMouseGrabber = event->exclusiveGrabber(event->point(0)); + if (filteringParent->childMouseEventFilter(receiver, const_cast<QMouseEvent *>(me))) { qCDebug(DBG_MOUSE) << "mouse event intercepted by childMouseEventFilter of " << filteringParent; skipDelivery.append(filteringParent); filtered = true; - if (me->isAccepted() && pme->isPressEvent()) { - auto mouseGrabber = pme->point(0)->grabberItem(); + if (me->isAccepted() && me->isBeginEvent()) { + auto &point = event->point(0); + auto mouseGrabber = event->exclusiveGrabber(point); if (mouseGrabber && mouseGrabber != receiver && mouseGrabber != oldMouseGrabber) { receiver->mouseUngrabEvent(); } else { - pme->point(0)->setGrabberItem(receiver); + event->setExclusiveGrabber(point, receiver); } } } else { // Restore accepted state if the event was not filtered. - me->setAccepted(wasAccepted); + const_cast<QMouseEvent *>(me)->setAccepted(wasAccepted); } } - } else if (QQuickPointerTouchEvent *pte = event->asPointerTouchEvent()) { -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + } else if (isTouchEvent(event)) { + auto te = static_cast<QTouchEvent *>(event); bool acceptsTouchEvents = receiver->acceptTouchEvents(); -#else - // In versions prior to Qt 6, we can't trust item->acceptTouchEvents() here, because it defaults to true. - bool acceptsTouchEvents = false; -#endif - auto device = pte->device(); + auto device = te->device(); if (device->type() == QInputDevice::DeviceType::TouchPad && device->capabilities().testFlag(QInputDevice::Capability::MouseEmulation)) { qCDebug(DBG_TOUCH_TARGET) << "skipping filtering of synth-mouse event from" << device; } else if (acceptsTouchEvents || receiver->acceptedMouseButtons()) { // get a touch event customized for delivery to filteringParent - QScopedPointer<QTouchEvent> filteringParentTouchEvent(pte->touchEventForItem(receiver, true)); - if (filteringParentTouchEvent) { - if (filteringParent->childMouseEventFilter(receiver, filteringParentTouchEvent.data())) { + // TODO should not be necessary? because QQuickWindowPrivate::deliverMatchingPointsToItem() does it + QTouchEvent filteringParentTouchEvent = + QQuickItemPrivate::get(receiver)->localizedTouchEvent(te, true); + if (filteringParentTouchEvent.type() != QEvent::None) { + if (filteringParent->childMouseEventFilter(receiver, &filteringParentTouchEvent)) { qCDebug(DBG_TOUCH) << "touch event intercepted by childMouseEventFilter of " << filteringParent; skipDelivery.append(filteringParent); - for (const auto &point: filteringParentTouchEvent->points()) { - QQuickEventPoint *pt = event->pointById(point.id()); - pt->setAccepted(); - pt->setGrabberItem(filteringParent); + for (qsizetype i = 0; i < filteringParentTouchEvent.pointCount(); ++i) { + auto &point = QMutableEventPoint::from(filteringParentTouchEvent.point(i)); + point.setAccepted(); + te->setExclusiveGrabber(point, filteringParent); } return true; - } else if (Q_LIKELY(QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents))) { + } + else if (Q_LIKELY(QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents))) { // filteringParent didn't filter the touch event. Give it a chance to filter a synthetic mouse event. - for (auto &tp : filteringParentTouchEvent->points()) { - + for (auto &tp : filteringParentTouchEvent.points()) { QEvent::Type t; switch (tp.state()) { case QEventPoint::State::Pressed: @@ -3447,21 +3248,20 @@ bool QQuickWindowPrivate::sendFilteredPointerEventImpl(QQuickPointerEvent *event if (touchMouseUnset || touchMouseId == tp.id()) { // convert filteringParentTouchEvent (which is already transformed wrt local position, velocity, etc.) // into a synthetic mouse event, and let childMouseEventFilter() have another chance with that - QScopedPointer<QMouseEvent> mouseEvent(touchToMouseEvent(t, tp, filteringParentTouchEvent.data(), receiver, false)); + QMouseEvent mouseEvent = touchToMouseEvent(t, tp, &filteringParentTouchEvent, receiver); // If a filtering item calls QQuickWindow::mouseGrabberItem(), it should // report the touchpoint's grabber. Whenever we send a synthetic mouse event, // touchMouseId and touchMouseDevice must be set, even if it's only temporarily and isn't grabbed. touchMouseId = tp.id(); - touchMouseDevice = event->device(); - const QPointingDevice *dev = touchMouseDevice; - if (filteringParent->childMouseEventFilter(receiver, mouseEvent.data())) { + touchMouseDevice = event->pointingDevice(); + if (filteringParent->childMouseEventFilter(receiver, &mouseEvent)) { qCDebug(DBG_TOUCH) << "touch event intercepted as synth mouse event by childMouseEventFilter of " << filteringParent; skipDelivery.append(filteringParent); if (t != QEvent::MouseButtonRelease) { qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << Qt::hex << tp.id() << "->" << filteringParent; - pointerEventInstance(dev)->pointById(tp.id())->setGrabberItem(filteringParent); + filteringParentTouchEvent.setExclusiveGrabber(tp, filteringParent); touchMouseUnset = false; // We want to leave touchMouseId and touchMouseDevice set - if (mouseEvent->isAccepted()) + if (mouseEvent.isAccepted()) filteringParent->grabMouse(); } filtered = true; diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 2046fb0462..632cf1b6da 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -64,8 +64,10 @@ #include <QtCore/qmutex.h> #include <QtCore/qwaitcondition.h> #include <QtCore/qrunnable.h> +#include <QtCore/qstack.h> #include <QtGui/private/qevent_p.h> +#include <QtGui/private/qpointingdevice_p.h> #include <QtGui/private/qwindow_p.h> #include <QtGui/qevent.h> #include <QtGui/qstylehints.h> @@ -150,28 +152,33 @@ public: #if QT_CONFIG(quick_draganddrop) QQuickDragGrabber *dragGrabber; #endif - int touchMouseId; + QStack<QPointerEvent *> eventsInDelivery; + + int touchMouseId; // only for obsolete stuff like QQuickItem::grabMouse() + // TODO get rid of these const QPointingDevice *touchMouseDevice; - bool checkIfDoubleTapped(ulong newPressEventTimestamp, QPoint newPressPos); ulong touchMousePressTimestamp; QPoint touchMousePressPos; // in screen coordiantes + bool isDeliveringTouchAsMouse() const { return touchMouseId != -1 && touchMouseDevice; } void cancelTouchMouseSynthesis(); + bool checkIfDoubleTapped(ulong newPressEventTimestamp, QPoint newPressPos); + QPointingDevicePrivate::EventPointData *mousePointData(); + QPointerEvent *eventInDelivery() const; + // Mouse positions are saved in widget coordinates QPointF lastMousePosition; - bool deliverTouchAsMouse(QQuickItem *item, QQuickPointerEvent *pointerEvent); - bool isDeliveringTouchAsMouse() const { return touchMouseId != -1 && touchMouseDevice; } + bool deliverTouchAsMouse(QQuickItem *item, QTouchEvent *pointerEvent); void translateTouchEvent(QTouchEvent *touchEvent); - void grabTouchPoints(QObject *grabber, const QVector<int> &ids); void removeGrabber(QQuickItem *grabber, bool mouse = true, bool touch = true); void sendUngrabEvent(QQuickItem *grabber, bool touch); + void onGrabChanged(QObject *grabber, QPointingDevice::GrabTransition transition, const QPointerEvent *event, const QEventPoint &point); static QMouseEvent *cloneMouseEvent(QMouseEvent *event, QPointF *transformedLocalPos = nullptr); - void deliverToPassiveGrabbers(const QVector<QPointer <QQuickPointerHandler> > &passiveGrabbers, QQuickPointerEvent *pointerEvent); - void deliverMouseEvent(QQuickPointerMouseEvent *pointerEvent); + void deliverToPassiveGrabbers(const QVector<QPointer<QObject> > &passiveGrabbers, QPointerEvent *pointerEvent); bool sendFilteredMouseEvent(QEvent *event, QQuickItem *receiver, QQuickItem *filteringParent); - bool sendFilteredPointerEvent(QQuickPointerEvent *event, QQuickItem *receiver, QQuickItem *filteringParent = nullptr); - bool sendFilteredPointerEventImpl(QQuickPointerEvent *event, QQuickItem *receiver, QQuickItem *filteringParent); - bool deliverSinglePointEventUntilAccepted(QQuickPointerEvent *); + bool sendFilteredPointerEvent(QPointerEvent *event, QQuickItem *receiver, QQuickItem *filteringParent = nullptr); + bool sendFilteredPointerEventImpl(QPointerEvent *event, QQuickItem *receiver, QQuickItem *filteringParent); + bool deliverSinglePointEventUntilAccepted(QPointerEvent *); // entry point of events to the window void handleTouchEvent(QTouchEvent *); @@ -180,22 +187,25 @@ public: void flushFrameSynchronousEvents(); void deliverDelayedTouchEvent(); - // the device-specific event instances which are reused during event delivery - mutable QVector<QQuickPointerEvent *> pointerEventInstances; - QQuickPointerEvent *queryPointerEventInstance(const QPointingDevice *device, QEvent::Type eventType = QEvent::None) const; - QQuickPointerEvent *pointerEventInstance(const QPointingDevice *device, QEvent::Type eventType = QEvent::None) const; + // utility functions that used to be in QQuickPointerEvent et al. + bool allUpdatedPointsAccepted(const QPointerEvent *ev); + void localizePointerEvent(QPointerEvent *ev, const QQuickItem *dest); + QList<QObject *> exclusiveGrabbers(QPointerEvent *ev); + static bool isMouseEvent(const QPointerEvent *ev); + static bool isTouchEvent(const QPointerEvent *ev); + static bool isTabletEvent(const QPointerEvent *ev); // delivery of pointer events: - QMouseEvent *touchToMouseEvent(QEvent::Type type, const QEventPoint &p, QTouchEvent *event, QQuickItem *item, bool transformNeeded = true); - QQuickPointerEvent *pointerEventInstance(QEvent *ev) const; - void deliverPointerEvent(QQuickPointerEvent *); - void deliverTouchEvent(QQuickPointerTouchEvent *); + QMouseEvent touchToMouseEvent(QEvent::Type type, const QEventPoint &p, QTouchEvent *event, QQuickItem *item); + void ensureDeviceConnected(const QPointingDevice *dev); + void deliverPointerEvent(QPointerEvent *); bool deliverTouchCancelEvent(QTouchEvent *); - bool deliverPressOrReleaseEvent(QQuickPointerEvent *, bool handlersOnly = false); - void deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event); - void deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent, bool handlersOnly = false); + bool deliverPressOrReleaseEvent(QPointerEvent *, bool handlersOnly = false); + void deliverUpdatedPoints(QPointerEvent *event); + void deliverMatchingPointsToItem(QQuickItem *item, bool isGrabber, QPointerEvent *pointerEvent, bool handlersOnly = false); - QVector<QQuickItem *> pointerTargets(QQuickItem *, QQuickEventPoint *point, bool checkMouseButtons, bool checkAcceptsTouch) const; + QVector<QQuickItem *> pointerTargets(QQuickItem *, const QPointerEvent *event, const QEventPoint &point, + bool checkMouseButtons, bool checkAcceptsTouch) const; QVector<QQuickItem *> mergePointerTargets(const QVector<QQuickItem *> &list1, const QVector<QQuickItem *> &list2) const; // hover delivery @@ -277,6 +287,7 @@ public: QQuickRenderControl *renderControl; QScopedPointer<QQuickAnimatorController> animationController; QScopedPointer<QMutableTouchEvent> delayedTouch; + QList<const QPointingDevice *> knownPointingDevices; int pointerEventRecursionGuard; |