diff options
Diffstat (limited to 'src/quick/items/qquickevents.cpp')
-rw-r--r-- | src/quick/items/qquickevents.cpp | 1646 |
1 files changed, 70 insertions, 1576 deletions
diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 8303c3fed1..d6d012a021 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -1,55 +1,20 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qquickevents_p_p.h" #include <QtCore/qmap.h> #include <QtGui/private/qguiapplication_p.h> -#include <QtGui/private/qtouchdevice_p.h> +#include <QtGui/private/qinputdevice_p.h> +#include <QtGui/private/qevent_p.h> #include <QtQuick/private/qquickitem_p.h> #include <QtQuick/private/qquickpointerhandler_p.h> +#include <QtQuick/private/qquickpointerhandler_p_p.h> #include <QtQuick/private/qquickwindow_p.h> #include <private/qdebug_p.h> QT_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(lcPointerEvents, "qt.quick.pointer.events") -Q_LOGGING_CATEGORY(lcPointerGrab, "qt.quick.pointer.grab") /*! \qmltype KeyEvent @@ -64,7 +29,7 @@ Q_LOGGING_CATEGORY(lcPointerGrab, "qt.quick.pointer.grab") \qml Item { focus: true - Keys.onPressed: { if (event.key == Qt.Key_Enter) state = 'ShowDetails'; } + Keys.onPressed: (event)=> { if (event.key == Qt.Key_Enter) state = 'ShowDetails'; } } \endqml */ @@ -132,22 +97,21 @@ Item { This property holds the keyboard modifier flags that existed immediately before the event occurred. - It contains a bitwise combination of: - \list - \li \l {Qt::NoModifier} {Qt.NoModifier} - No modifier key is pressed. - \li \l {Qt::ShiftModifier} {Qt.ShiftModifier} - A Shift key on the keyboard is pressed. - \li \l {Qt::ControlModifier} {Qt.ControlModifier} - A Ctrl key on the keyboard is pressed. - \li \l {Qt::AltModifier} {Qt.AltModifier} - An Alt key on the keyboard is pressed. - \li \l {Qt::MetaModifier} {Qt.MetaModifier} - A Meta key on the keyboard is pressed. - \li \l {Qt::KeypadModifier} {Qt.KeypadModifier} - A keypad button is pressed. - \li \l {Qt::GroupSwitchModifier} {Qt.GroupSwitchModifier} - X11 only. A Mode_switch key on the keyboard is pressed. - \endlist + It contains a bitwise combination of numeric values (the same as in Qt::KeyboardModifier): + + \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. + \value Qt.GroupSwitchModifier X11 only. A Mode_switch key on the keyboard is pressed. For example, to react to a Shift key + Enter key combination: \qml Item { focus: true - Keys.onPressed: { + Keys.onPressed: (event)=> { if ((event.key == Qt.Key_Enter) && (event.modifiers & Qt.ShiftModifier)) doSomething(); } @@ -156,15 +120,15 @@ Item { */ /*! - \qmlmethod bool QtQuick::KeyEvent::matches(StandardKey key) + \qmlmethod bool QtQuick::KeyEvent::matches(StandardKey matchKey) \since 5.2 - Returns \c true if the key event matches the given standard \a key; otherwise returns \c false. + Returns \c true if the key event matches the given standard \a matchKey; otherwise returns \c false. \qml Item { focus: true - Keys.onPressed: { + Keys.onPressed: (event)=> { if (event.matches(StandardKey.Undo)) myModel.undo(); else if (event.matches(StandardKey.Redo)) @@ -175,6 +139,17 @@ Item { \sa QKeySequence::StandardKey */ +#if QT_CONFIG(shortcut) +bool QQuickKeyEvent::matches(QKeySequence::StandardKey matchKey) const +{ + // copying QKeyEvent::matches + uint searchkey = (modifiers() | key()) & ~(Qt::KeypadModifier | Qt::GroupSwitchModifier); + + const QList<QKeySequence> bindings = QKeySequence::keyBindings(matchKey); + return bindings.contains(QKeySequence(searchkey)); +} +#endif + /*! \qmltype MouseEvent @@ -266,7 +241,7 @@ Item { For example, to react to a Shift key + Left mouse button click: \qml MouseArea { - onClicked: { + onClicked: (mouse)=> { if ((mouse.button == Qt.LeftButton) && (mouse.modifiers & Qt.ShiftModifier)) doSomething(); } @@ -277,6 +252,7 @@ Item { /*! \qmlproperty int QtQuick::MouseEvent::source \since 5.7 + \deprecated [6.2] Use \l {Qt Quick Input Handlers}{input handlers} with \l {PointerDeviceHandler::acceptedDevices}{acceptedDevices} set. This property holds the source of the mouse event. @@ -310,8 +286,9 @@ Item { For example, to react only to events which come from an actual mouse: \qml MouseArea { - onPressed: if (mouse.source !== Qt.MouseEventNotSynthesized) { - mouse.accepted = false + onPressed: (mouse)=> { + if (mouse.source !== Qt.MouseEventNotSynthesized) + mouse.accepted = false } onClicked: doSomething() @@ -351,10 +328,9 @@ Item { \ingroup qtquick-input-events \brief Provides information about a mouse wheel event. - The position of the mouse can be found via the - \l {Item::x} {x} and \l {Item::y} {y} properties. + The position of the mouse can be found via the \l x and \l y properties. - \sa MouseArea + \sa WheelHandler, MouseArea */ /*! @@ -367,16 +343,20 @@ Item { \qmlproperty real QtQuick::WheelEvent::y These properties hold the coordinates of the position supplied by the wheel event. + + \sa QWheelEvent::position() */ /*! \qmlproperty bool QtQuick::WheelEvent::accepted - Setting \a accepted to true prevents the wheel event from being - propagated to items below this item. + Setting \a accepted to \c true prevents the wheel event from being + propagated to items below the receiving item or handler. - Generally, if the item acts on the wheel event then it should be accepted + Generally, if the item acts on the wheel event, it should be accepted so that items lower in the stacking order do not also respond to the same event. + + \sa QWheelEvent::accepted */ /*! @@ -390,32 +370,39 @@ Item { \li \l {Qt::RightButton} {Qt.RightButton} \li \l {Qt::MiddleButton} {Qt.MiddleButton} \endlist + + \sa QWheelEvent::buttons() */ /*! \qmlproperty point QtQuick::WheelEvent::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. + This property holds the relative amount that the wheel was rotated, in + eighths of a degree. The \c x and \c y coordinates of this property hold + the delta in horizontal and vertical orientations, respectively. 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. + Most mouse types work in steps of \c 15 degrees, in which case the delta value is a + multiple of \c 120; i.e., \c {120 units * 1/8 = 15 degrees}. + + \sa QWheelEvent::angleDelta() */ /*! \qmlproperty point QtQuick::WheelEvent::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 cordinate of this property holds the delta in horizontal and - vertical orientation. The value should be used directly to scroll content on screen. + have high-resolution \l {QInputDevice::DeviceType::TouchPad}{trackpads}, such as \macos. + The \c x and \c y coordinates of this property hold the delta in horizontal + and vertical orientations, respectively. The values can be used directly to + scroll content on screen. + + For platforms without \l {QInputDevice::Capability::PixelScroll}{high-resolution trackpad} + support, pixelDelta will always be \c {(0,0)}, and \l angleDelta should be used instead. - For platforms without high-resolution trackpad support, pixelDelta will always be (0,0), - and angleDelta should be used instead. + \sa QWheelEvent::pixelDelta() */ /*! @@ -436,18 +423,20 @@ Item { For example, to react to a Control key pressed during the wheel event: \qml - MouseArea { - onWheel: { + WheelHandler { + onWheel: (wheel)=> { if (wheel.modifiers & Qt.ControlModifier) { adjustZoom(wheel.angleDelta.y / 120); } } } \endqml + + \sa QWheelEvent::modifiers() */ /*! - \qmlproperty int QtQuick::WheelEvent::inverted + \qmlproperty bool QtQuick::WheelEvent::inverted Returns whether the delta values delivered with the event are inverted. @@ -465,1506 +454,11 @@ Item { negate the angleDelta or pixelDelta values. \note Many platforms provide no such information. On such platforms - \l inverted always returns false. -*/ - -/*! - \qmltype PointerDevice - \instantiates QQuickPointerDevice - \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. + \c inverted always returns \c false. - \sa PointerEvent, PointerHandler + \sa QWheelEvent::inverted() */ -/*! - \readonly - \qmlproperty enumeration QtQuick::PointerDevice::type - - This property holds the type of the pointing device. - - Valid values are: - - \value DeviceType.UnknownDevice - 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 QTouchDevice::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.GenericPointer - 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 CapabilityFlag.Position - the \l {QtQuick::EventPoint::position}{position} and - \l {QtQuick::EventPoint::scenePosition}{scenePosition} properties - \value CapabilityFlag.Area - the \l {QtQuick::EventTouchPoint::ellipseDiameters}{ellipseDiameters} property - \value CapabilityFlag.Pressure - the \l {QtQuick::EventTouchPoint::pressure}{pressure} property - \value CapabilityFlag.Velocity - the \l {QtQuick::EventPoint::velocity}{velocity} property - \value CapabilityFlag.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 CapabilityFlag.Hover - events are sent even when no button is pressed, or the finger or stylus - is not in contact with the surface - \value CapabilityFlag.Rotation - the \l {QtQuick::EventTouchPoint::rotation}{rotation} property - \value CapabilityFlag.XTilt - horizontal angle between a stylus and the axis perpendicular to the surface - \value CapabilityFlag.YTilt - vertical angle between a stylus and the axis perpendicular to the surface - - \sa QTouchDevice::capabilities -*/ - -typedef QHash<const QTouchDevice *, QQuickPointerDevice *> PointerDeviceForTouchDeviceHash; -Q_GLOBAL_STATIC(PointerDeviceForTouchDeviceHash, g_touchDevices) - -struct ConstructableQQuickPointerDevice : public QQuickPointerDevice -{ - ConstructableQQuickPointerDevice(DeviceType devType, PointerType pType, Capabilities caps, - int maxPoints, int buttonCount, const QString &name, - qint64 uniqueId = 0) - : QQuickPointerDevice(devType, pType, caps, maxPoints, buttonCount, name, uniqueId) {} - -}; -Q_GLOBAL_STATIC_WITH_ARGS(ConstructableQQuickPointerDevice, g_genericMouseDevice, - (QQuickPointerDevice::Mouse, - QQuickPointerDevice::GenericPointer, - QQuickPointerDevice::Position | QQuickPointerDevice::Scroll | QQuickPointerDevice::Hover, - 1, 3, QLatin1String("core pointer"), 0)) - -typedef QHash<qint64, QQuickPointerDevice *> PointerDeviceForDeviceIdHash; -Q_GLOBAL_STATIC(PointerDeviceForDeviceIdHash, g_tabletDevices) - -// debugging helpers -static const char *pointStateString(const QQuickEventPoint *point) -{ - static const QMetaEnum stateMetaEnum = point->metaObject()->enumerator(point->metaObject()->indexOfEnumerator("State")); - return stateMetaEnum.valueToKey(point->state()); -} - -static const QString pointDeviceName(const QQuickEventPoint *point) -{ - auto device = static_cast<const QQuickPointerEvent *>(point->parent())->device(); - QString deviceName = (device ? device->name() : QLatin1String("null device")); - deviceName.resize(16, ' '); // shorten, and align in case of sequential output - return deviceName; -} - - -QQuickPointerDevice *QQuickPointerDevice::touchDevice(const QTouchDevice *d) -{ - if (g_touchDevices->contains(d)) - return g_touchDevices->value(d); - - QQuickPointerDevice::DeviceType type = QQuickPointerDevice::TouchScreen; - QString name; - int maximumTouchPoints = 10; - QQuickPointerDevice::Capabilities caps = QQuickPointerDevice::Capabilities(QTouchDevice::Position); - if (d) { - caps = static_cast<QQuickPointerDevice::Capabilities>(static_cast<int>(d->capabilities()) & 0xFF); - if (d->type() == QTouchDevice::TouchPad) { - type = QQuickPointerDevice::TouchPad; - caps |= QQuickPointerDevice::Scroll; - } - name = d->name(); - maximumTouchPoints = d->maximumTouchPoints(); - } else { - qWarning() << "QQuickWindowPrivate::touchDevice: creating touch device from nullptr device in QTouchEvent"; - } - - QQuickPointerDevice *dev = new QQuickPointerDevice(type, QQuickPointerDevice::Finger, - caps, maximumTouchPoints, 0, name, 0); - g_touchDevices->insert(d, dev); - return dev; -} - -const QTouchDevice *QQuickPointerDevice::qTouchDevice() const -{ - return g_touchDevices->key(const_cast<QQuickPointerDevice *>(this)); -} - -QList<QQuickPointerDevice*> QQuickPointerDevice::touchDevices() -{ - return g_touchDevices->values(); -} - -QQuickPointerDevice *QQuickPointerDevice::genericMouseDevice() -{ - return g_genericMouseDevice; -} - -QQuickPointerDevice *QQuickPointerDevice::tabletDevice(qint64 id) -{ - auto it = g_tabletDevices->find(id); - if (it != g_tabletDevices->end()) - return it.value(); - - // ### Figure out how to populate the tablet devices - return nullptr; -} - -/*! - \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(Qt::TouchPointState 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 == Qt::TouchPointPressed) { - 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(lcPointerGrab().isDebugEnabled())) { - qCDebug(lcPointerGrab) << pointDeviceName(this) << "point" << 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(lcPointerGrab().isDebugEnabled())) { - if (exclusive) { - if (m_exclusiveGrabber != grabber) - qCDebug(lcPointerGrab) << pointDeviceName(this) << "point" << hex << m_pointId << pointStateString(this) - << ": grab (exclusive)" << m_exclusiveGrabber << "->" << grabber; - } else { - qCDebug(lcPointerGrab) << pointDeviceName(this) << "point" << hex << m_pointId << pointStateString(this) - << ": grab (passive)" << grabber; - } - } - if (exclusive) { - if (grabber != m_exclusiveGrabber.data()) { - 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(lcPointerGrab().isDebugEnabled())) { - qCDebug(lcPointerGrab) << pointDeviceName(this) << "point" << hex << m_pointId << pointStateString(this) - << ": grab (exclusive)" << m_exclusiveGrabber << "-> nullptr"; - } - if (auto handler = grabberPointerHandler()) { - handler->onGrabChanged(handler, CancelGrabExclusive, this); - } else if (auto item = grabberItem()) { - if (cancelEvent) - QCoreApplication::sendEvent(item, cancelEvent); - else - item->touchUngrabEvent(); - } - m_exclusiveGrabber.clear(); -} - -/*! - If this point has the given \a handler as a passive grabber, - inform the grabber that its grab is canceled, and remove it as grabber. - This normally happens when another Item or PointerHandler does an exclusive grab. -*/ -void QQuickEventPoint::cancelPassiveGrab(QQuickPointerHandler *handler) -{ - if (removePassiveGrabber(handler)) { - if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) { - qCDebug(lcPointerGrab) << pointDeviceName(this) << "point" << hex << m_pointId << pointStateString(this) - << ": grab (passive)" << handler << "removed"; - } - handler->onGrabChanged(handler, CancelGrabPassive, this); - } -} - -/*! - 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 QTouchEvent::TouchPoint &tp, ulong timestamp) -{ - QQuickEventPoint::reset(tp.state(), tp.scenePos(), tp.id(), timestamp, tp.velocity()); - m_exclusiveGrabber.clear(); - m_passiveGrabbers.clear(); - m_rotation = tp.rotation(); - m_pressure = tp.pressure(); - m_ellipseDiameters = tp.ellipseDiameters(); - m_uniqueId = tp.uniqueId(); -} - -struct PointVelocityData { - QVector2D velocity; - QPointF pos; - ulong timestamp = 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() -{} - -QQuickPointerEvent *QQuickPointerMouseEvent::reset(QEvent *event) -{ - auto ev = static_cast<QMouseEvent*>(event); - m_event = ev; - if (!event) - return this; - - m_device = QQuickPointerDevice::genericMouseDevice(); - m_device->eventDeliveryTargets().clear(); - m_button = ev->button(); - m_pressedButtons = ev->buttons(); - Qt::TouchPointState state = Qt::TouchPointStationary; - switch (ev->type()) { - case QEvent::MouseButtonPress: - m_point->clearPassiveGrabbers(); - Q_FALLTHROUGH(); - case QEvent::MouseButtonDblClick: - state = Qt::TouchPointPressed; - break; - case QEvent::MouseButtonRelease: - state = Qt::TouchPointReleased; - break; - case QEvent::MouseMove: - state = Qt::TouchPointMoved; - break; - default: - break; - } - m_point->reset(state, ev->windowPos(), quint64(1) << 24, ev->timestamp()); // mouse has device ID 1 - 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; - - m_device = QQuickPointerDevice::touchDevice(ev->device()); - m_device->eventDeliveryTargets().clear(); - m_button = Qt::NoButton; - m_pressedButtons = Qt::NoButton; - - const QList<QTouchEvent::TouchPoint> &tps = ev->touchPoints(); - int newPointCount = tps.count(); - m_touchPoints.reserve(newPointCount); - - for (int i = m_touchPoints.size(); i < newPointCount; ++i) - m_touchPoints.insert(i, new QQuickEventTouchPoint(this)); - - // Make sure the grabbers 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) -QQuickPointerEvent *QQuickPointerNativeGestureEvent::reset(QEvent *event) -{ - auto ev = static_cast<QNativeGestureEvent*>(event); - m_event = ev; - if (!event) - return this; - - m_device = QQuickPointerDevice::touchDevice(ev->device()); - m_device->eventDeliveryTargets().clear(); - Qt::TouchPointState state = Qt::TouchPointMoved; - switch (type()) { - case Qt::BeginNativeGesture: - state = Qt::TouchPointPressed; - break; - case Qt::EndNativeGesture: - state = Qt::TouchPointReleased; - break; - default: - break; - } - quint64 deviceId = QTouchDevicePrivate::get(const_cast<QTouchDevice *>(ev->device()))->id; // a bit roundabout since QTouchDevice::mTouchDeviceId is protected - m_point->reset(state, ev->windowPos(), deviceId << 24, ev->timestamp()); - return this; -} -#endif // QT_CONFIG(gestures) - -QQuickEventPoint *QQuickSinglePointEvent::point(int i) const -{ - if (i == 0) - return m_point; - return nullptr; -} - -QQuickPointerEvent *QQuickPointerScrollEvent::reset(QEvent *event) -{ - m_event = static_cast<QInputEvent*>(event); - if (!event) - return this; -#if QT_CONFIG(wheelevent) - if (event->type() == QEvent::Wheel) { - auto ev = static_cast<QWheelEvent*>(event); - m_device = QQuickPointerDevice::genericMouseDevice(); - m_device->eventDeliveryTargets().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(Qt::TouchPointMoved, ev->posF(), quint64(1) << 24, ev->timestamp()); // mouse has device ID 1 - } -#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 -{ - auto event = static_cast<QMouseEvent *>(m_event); - event->setLocalPos(localPos); - return 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 -{ - 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 -{ - auto me = static_cast<QMouseEvent*>(m_event); - return (me->type() == QEvent::MouseButtonDblClick); -} - -bool QQuickPointerMouseEvent::isUpdateEvent() const -{ - auto me = static_cast<QMouseEvent*>(m_event); - return me->type() == QEvent::MouseMove; -} - -bool QQuickPointerMouseEvent::isReleaseEvent() const -{ - auto me = static_cast<QMouseEvent*>(m_event); - return me && 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(); - } -} - -Qt::TouchPointStates QQuickPointerTouchEvent::touchPointStates() const -{ - return m_event - ? static_cast<QTouchEvent*>(m_event)->touchPointStates() - : Qt::TouchPointStates(); -} - -/*! - 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() & Qt::TouchPointPressed; -} - -bool QQuickPointerTouchEvent::isUpdateEvent() const -{ - return touchPointStates() & (Qt::TouchPointMoved | Qt::TouchPointStationary); -} - -bool QQuickPointerTouchEvent::isReleaseEvent() const -{ - return touchPointStates() & Qt::TouchPointReleased; -} - -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 QTouchEvent::TouchPoint *p = touchPointById(pointID); - if (!p) - return nullptr; - QEvent::Type type; - Qt::MouseButton buttons = Qt::LeftButton; - switch (p->state()) { - case Qt::TouchPointPressed: - type = QEvent::MouseButtonPress; - break; - case Qt::TouchPointMoved: - case Qt::TouchPointStationary: - type = QEvent::MouseMove; - break; - case Qt::TouchPointReleased: - type = QEvent::MouseButtonRelease; - buttons = Qt::NoButton; - break; - default: - Q_ASSERT(false); - return nullptr; - } - m_synthMouseEvent = QMouseEvent(type, relativeTo->mapFromScene(p->scenePos()), - p->scenePos(), p->screenPos(), Qt::LeftButton, buttons, m_event->modifiers()); - m_synthMouseEvent.setAccepted(true); - m_synthMouseEvent.setTimestamp(m_event->timestamp()); - // In the future we will try to always have valid velocity in every QQuickEventPoint. - // QQuickFlickablePrivate::handleMouseMoveEvent() checks for QTouchDevice::Velocity - // and if it is set, then it does not need to do its own velocity calculations. - // That's probably the only usecase for this, so far. Some day Flickable should handle - // pointer events, and then passing touchpoint velocity via QMouseEvent will be obsolete. - // Conveniently (by design), QTouchDevice::Velocity == QQuickPointerDevice.Velocity - // so that we don't need to convert m_device->capabilities(). - if (m_device) - QGuiApplicationPrivate::setMouseEventCapsAndVelocity(&m_synthMouseEvent, m_device->capabilities(), p->velocity()); - QGuiApplicationPrivate::setMouseEventSource(&m_synthMouseEvent, Qt::MouseEventSynthesizedByQt); - return &m_synthMouseEvent; -} - -#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 {QTouchEvent::TouchPoint::id}{id} as \a pointId, if the original event is a - QTouchEvent, and if that point is found. Otherwise, returns nullptr. -*/ -const QTouchEvent::TouchPoint *QQuickPointerTouchEvent::touchPointById(int pointId) const -{ - const QTouchEvent *ev = asTouchEvent(); - if (!ev) - return nullptr; - const QList<QTouchEvent::TouchPoint> &tps = ev->touchPoints(); - auto it = std::find_if(tps.constBegin(), tps.constEnd(), - [pointId](QTouchEvent::TouchPoint const& tp) { return tp.id() == pointId; } ); - // return the pointer to the actual TP in QTouchEvent::_touchPoints - return (it == tps.constEnd() ? nullptr : it.operator->()); -} - -/*! - \internal - Make a new QTouchEvent, giving it a subset of the original touch points. - - Returns a nullptr if all points are stationary, or there are no points inside the item, - 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<QTouchEvent::TouchPoint> touchPoints; - Qt::TouchPointStates eventStates; - // TODO maybe add QQuickItem::mapVector2DFromScene(QVector2D) to avoid needing QQuickItemPrivate here - // Or else just document that velocity is always scene-relative and is not scaled and rotated with the item - // but that would require changing tst_qquickwindow::touchEvent_velocity(): it expects transformed velocity - - bool anyPressOrReleaseInside = false; - bool anyGrabber = false; - QMatrix4x4 transformMatrix(QQuickItemPrivate::get(item)->windowToItemTransform()); - for (int i = 0; i < m_pointCount; ++i) { - auto p = m_touchPoints.at(i); - if (p->isAccepted()) - continue; - // include points where item is the grabber - bool isGrabber = p->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; - const QTouchEvent::TouchPoint *tp = touchPointById(p->pointId()); - if (tp) { - eventStates |= tp->state(); - QTouchEvent::TouchPoint tpCopy = *tp; - tpCopy.setPos(item->mapFromScene(tpCopy.scenePos())); - tpCopy.setLastPos(item->mapFromScene(tpCopy.lastScenePos())); - tpCopy.setStartPos(item->mapFromScene(tpCopy.startScenePos())); - tpCopy.setRect(item->mapRectFromScene(tpCopy.sceneRect())); - tpCopy.setVelocity(transformMatrix.mapVector(tpCopy.velocity()).toVector2D()); - touchPoints << tpCopy; - } - } - - // Now touchPoints will have only points which are inside the item. - // But if none of them were just pressed inside, and the item has no other reason to care, ignore them anyway. - if (eventStates == Qt::TouchPointStationary || touchPoints.isEmpty() || (!anyPressOrReleaseInside && !anyGrabber && !isFiltering)) - return nullptr; - - // if all points have the same state, set the event type accordingly - const QTouchEvent &event = *asTouchEvent(); - QEvent::Type eventType = event.type(); - switch (eventStates) { - case Qt::TouchPointPressed: - eventType = QEvent::TouchBegin; - break; - case Qt::TouchPointReleased: - eventType = QEvent::TouchEnd; - break; - default: - eventType = QEvent::TouchUpdate; - break; - } - - QTouchEvent *touchEvent = new QTouchEvent(eventType); - touchEvent->setWindow(event.window()); - touchEvent->setTarget(item); - touchEvent->setDevice(event.device()); - touchEvent->setModifiers(event.modifiers()); - touchEvent->setTouchPoints(touchPoints); - touchEvent->setTouchPointStates(eventStates); - touchEvent->setTimestamp(event.timestamp()); - touchEvent->accept(); - return touchEvent; -} - -QTouchEvent *QQuickPointerTouchEvent::asTouchEvent() const -{ - return static_cast<QTouchEvent *>(m_event); -} - -#ifndef QT_NO_DEBUG_STREAM - -Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickPointerDevice *dev) -{ - QDebugStateSaver saver(dbg); - dbg.nospace(); - if (!dev) { - dbg << "QQuickPointerDevice(0)"; - return dbg; - } - dbg << "QQuickPointerDevice("<< dev->name() << ' '; - QtDebugUtils::formatQEnum(dbg, dev->type()); - dbg << ' '; - QtDebugUtils::formatQEnum(dbg, dev->pointerType()); - dbg << " caps:"; - QtDebugUtils::formatQFlags(dbg, dev->capabilities()); - if (dev->type() == QQuickPointerDevice::TouchScreen || - dev->type() == QQuickPointerDevice::TouchPad) - dbg << " maxTouchPoints:" << dev->maximumTouchPoints(); - else - dbg << " buttonCount:" << dev->buttonCount(); - dbg << ')'; - return dbg; -} - -Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickPointerEvent *event) -{ - QDebugStateSaver saver(dbg); - dbg.nospace(); - dbg << "QQuickPointerEvent("; - dbg << event->timestamp(); - dbg << " dev:"; - QtDebugUtils::formatQEnum(dbg, event->device()->type()); - if (event->buttons() != Qt::NoButton) { - dbg << " buttons:"; - QtDebugUtils::formatQEnum(dbg, event->buttons()); - } - dbg << " ["; - int c = event->pointCount(); - for (int i = 0; i < c; ++i) - dbg << event->point(i) << ' '; - dbg << "])"; - return dbg; -} - -Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickEventPoint *event) -{ - QDebugStateSaver saver(dbg); - dbg.nospace(); - dbg << "QQuickEventPoint(accepted:" << event->isAccepted() - << " state:"; - QtDebugUtils::formatQEnum(dbg, event->state()); - dbg << " scenePos:" << event->scenePosition() << " id:" << hex << event->pointId() << dec - << " timeHeld:" << event->timeHeld() << ')'; - return dbg; -} - -#endif - QT_END_NAMESPACE + +#include "moc_qquickevents_p_p.cpp" |