diff options
Diffstat (limited to 'src/quick/items/qquickevents.cpp')
-rw-r--r-- | src/quick/items/qquickevents.cpp | 1636 |
1 files changed, 0 insertions, 1636 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 |