diff options
Diffstat (limited to 'src/quick/items')
-rw-r--r-- | src/quick/items/qquickevents.cpp | 483 | ||||
-rw-r--r-- | src/quick/items/qquickevents_p_p.h | 42 | ||||
-rw-r--r-- | src/quick/items/qquickitem.cpp | 6 | ||||
-rw-r--r-- | src/quick/items/qquickitem_p.h | 2 | ||||
-rw-r--r-- | src/quick/items/qquickitemsmodule.cpp | 4 | ||||
-rw-r--r-- | src/quick/items/qquickloader.cpp | 10 | ||||
-rw-r--r-- | src/quick/items/qquickrendercontrol.cpp | 4 | ||||
-rw-r--r-- | src/quick/items/qquicktextcontrol.cpp | 3 | ||||
-rw-r--r-- | src/quick/items/qquicktranslate.cpp | 1 | ||||
-rw-r--r-- | src/quick/items/qquickwindow.cpp | 147 | ||||
-rw-r--r-- | src/quick/items/qquickwindow_p.h | 3 |
11 files changed, 647 insertions, 58 deletions
diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 90e72ebde6..8653d758de 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -40,6 +40,7 @@ #include "qquickevents_p_p.h" #include <QtCore/qmap.h> #include <QtGui/private/qguiapplication_p.h> +#include <QtGui/private/qtouchdevice_p.h> #include <QtQuick/private/qquickitem_p.h> #include <QtQuick/private/qquickpointerhandler_p.h> #include <QtQuick/private/qquickwindow_p.h> @@ -447,7 +448,85 @@ Item { \l inverted always returns false. */ -typedef QHash<QTouchDevice *, QQuickPointerDevice *> PointerDeviceForTouchDeviceHash; +/*! + \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. + + \sa PointerEvent, PointerHandler +*/ + +/*! + \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) + + \sa QTouchDevice::DeviceType +*/ + +/*! + \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::PointerEvent::velocity}{velocity} property + \value CapabilityFlag.Scroll + a \l {QtQuick::PointerDevice::DeviceType::Mouse}{Mouse} has a wheel, or the + operating system recognizes scroll gestures on a + \l {QtQuick::PointerDevice::DeviceType::TouchPad}{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 @@ -483,7 +562,7 @@ static const QString pointDeviceName(const QQuickEventPoint *point) } -QQuickPointerDevice *QQuickPointerDevice::touchDevice(QTouchDevice *d) +QQuickPointerDevice *QQuickPointerDevice::touchDevice(const QTouchDevice *d) { if (g_touchDevices->contains(d)) return g_touchDevices->value(d); @@ -531,6 +610,118 @@ QQuickPointerDevice *QQuickPointerDevice::tabletDevice(qint64 id) 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 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 PointerDevice.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; @@ -761,10 +952,15 @@ void QQuickEventPoint::cancelAllGrabs(QQuickPointerHandler *handler) } /*! - Set this point as \a accepted (true) or rejected (false). - Accepting a point is intended to stop event propagation. - It does not imply any kind of grab, passive or exclusive. - TODO explain further under what conditions propagation really does stop... + 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) { @@ -774,6 +970,86 @@ void QQuickEventPoint::setAccepted(bool 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 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 scenePos (\l pos 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 device is not a touchscreen, + these values will be zero. +*/ + QQuickEventTouchPoint::QQuickEventTouchPoint(QQuickPointerTouchEvent *parent) : QQuickEventPoint(parent), m_rotation(0), m_pressure(0) {} @@ -846,6 +1122,24 @@ QVector2D QQuickEventPoint::estimatedVelocity() const } /*! + \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 device property provides more + information about where the event came from. + + \sa PointerHandler + + \image touchpoint-metrics.png +*/ + +/*! \internal \class QQuickPointerEvent @@ -859,6 +1153,68 @@ QVector2D QQuickEventPoint::estimatedVelocity() const 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 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() {} @@ -975,6 +1331,36 @@ void QQuickPointerTouchEvent::localize(QQuickItem *target) point->localizePosition(target); } +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_gesturePoint->reset(state, ev->windowPos(), deviceId << 24, ev->timestamp()); + return this; +} + +void QQuickPointerNativeGestureEvent::localize(QQuickItem *target) +{ + m_gesturePoint->localizePosition(target); +} + QQuickEventPoint *QQuickPointerMouseEvent::point(int i) const { if (i == 0) return m_mousePoint; @@ -987,6 +1373,12 @@ QQuickEventPoint *QQuickPointerTouchEvent::point(int i) const { return nullptr; } +QQuickEventPoint *QQuickPointerNativeGestureEvent::point(int i) const { + if (i == 0) + return m_gesturePoint; + 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) @@ -1068,7 +1460,7 @@ bool QQuickPointerMouseEvent::isUpdateEvent() const bool QQuickPointerMouseEvent::isReleaseEvent() const { auto me = static_cast<QMouseEvent*>(m_event); - return me->type() == QEvent::MouseButtonRelease; + return me && me->type() == QEvent::MouseButtonRelease; } bool QQuickPointerTouchEvent::allPointsAccepted() const { @@ -1207,6 +1599,64 @@ QMouseEvent *QQuickPointerTouchEvent::syntheticMouseEvent(int pointID, QQuickIte } /*! + Returns the exclusive grabber of this event, if any, in a vector. +*/ +QVector<QObject *> QQuickPointerNativeGestureEvent::exclusiveGrabbers() const +{ + QVector<QObject *> result; + if (QObject *grabber = m_gesturePoint->exclusiveGrabber()) + result << grabber; + return result; +} + +/*! + Remove all passive and exclusive grabbers of this event, without notifying. +*/ +void QQuickPointerNativeGestureEvent::clearGrabbers() const { + m_gesturePoint->setGrabberItem(nullptr); + m_gesturePoint->clearPassiveGrabbers(); +} + +/*! + Returns whether the given \a handler is the exclusive grabber of this event. +*/ +bool QQuickPointerNativeGestureEvent::hasExclusiveGrabber(const QQuickPointerHandler *handler) const +{ + return m_gesturePoint->exclusiveGrabber() == handler; +} + +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(); +} + +/*! \internal Returns a pointer to the QQuickEventPoint which has the \a pointId as \l {QQuickEventPoint::pointId}{pointId}. @@ -1228,6 +1678,12 @@ QQuickEventPoint *QQuickPointerTouchEvent::pointById(int pointId) const { return nullptr; } +QQuickEventPoint *QQuickPointerNativeGestureEvent::pointById(int pointId) const { + if (m_gesturePoint && pointId == m_gesturePoint->pointId()) + return m_gesturePoint; + return nullptr; +} + /*! \internal @@ -1346,6 +1802,19 @@ QTouchEvent *QQuickPointerTouchEvent::asTouchEvent() const return static_cast<QTouchEvent *>(m_event); } +bool QQuickPointerNativeGestureEvent::allPointsAccepted() const { + return m_gesturePoint->isAccepted(); +} + +bool QQuickPointerNativeGestureEvent::allUpdatedPointsAccepted() const { + return m_gesturePoint->state() == QQuickEventPoint::Pressed || m_gesturePoint->isAccepted(); +} + +bool QQuickPointerNativeGestureEvent::allPointsGrabbed() const +{ + return m_gesturePoint->exclusiveGrabber() != nullptr; +} + #ifndef QT_NO_DEBUG_STREAM Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickPointerDevice *dev) { diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 2954695b1b..09a63febdc 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -66,6 +66,7 @@ QT_BEGIN_NAMESPACE class QQuickPointerDevice; class QQuickPointerEvent; class QQuickPointerMouseEvent; +class QQuickPointerNativeGestureEvent; class QQuickPointerTabletEvent; class QQuickPointerTouchEvent; class QQuickPointerHandler; @@ -270,7 +271,6 @@ public: Updated = Qt::TouchPointMoved, Stationary = Qt::TouchPointStationary, Released = Qt::TouchPointReleased - // Canceled = Qt::TouchPointReleased << 1 // 0x10 // TODO maybe }; Q_DECLARE_FLAGS(States, State) Q_FLAG(States) @@ -414,9 +414,11 @@ public: // helpers for C++ only (during event delivery) virtual QQuickPointerMouseEvent *asPointerMouseEvent() { return nullptr; } virtual QQuickPointerTouchEvent *asPointerTouchEvent() { return nullptr; } virtual QQuickPointerTabletEvent *asPointerTabletEvent() { return nullptr; } + virtual QQuickPointerNativeGestureEvent *asPointerNativeGestureEvent() { return nullptr; } virtual const QQuickPointerMouseEvent *asPointerMouseEvent() const { return nullptr; } virtual const QQuickPointerTouchEvent *asPointerTouchEvent() const { return nullptr; } virtual const QQuickPointerTabletEvent *asPointerTabletEvent() const { return nullptr; } + virtual const QQuickPointerNativeGestureEvent *asPointerNativeGestureEvent() const { return nullptr; } virtual bool allPointsAccepted() const = 0; virtual bool allUpdatedPointsAccepted() const = 0; virtual bool allPointsGrabbed() const = 0; @@ -516,6 +518,42 @@ private: Q_DISABLE_COPY(QQuickPointerTouchEvent) }; +class Q_QUICK_PRIVATE_EXPORT QQuickPointerNativeGestureEvent : public QQuickPointerEvent +{ + Q_OBJECT + Q_PROPERTY(Qt::NativeGestureType type READ type CONSTANT) + Q_PROPERTY(qreal value READ value CONSTANT) + +public: + QQuickPointerNativeGestureEvent(QObject *parent = nullptr, QQuickPointerDevice *device = nullptr) + : QQuickPointerEvent(parent, device), m_gesturePoint(new QQuickEventPoint(this)) { } + + QQuickPointerEvent *reset(QEvent *) override; + void localize(QQuickItem *target) override; + bool isPressEvent() const override; + bool isUpdateEvent() const override; + bool isReleaseEvent() const override; + QQuickPointerNativeGestureEvent *asPointerNativeGestureEvent() override { return this; } + const QQuickPointerNativeGestureEvent *asPointerNativeGestureEvent() const override { return this; } + int pointCount() const override { return 1; } + QQuickEventPoint *point(int i) const override; + QQuickEventPoint *pointById(int pointId) const override; + bool allPointsAccepted() const override; + bool allUpdatedPointsAccepted() const override; + bool allPointsGrabbed() const override; + QVector<QObject *> exclusiveGrabbers() const override; + void clearGrabbers() const override; + bool hasExclusiveGrabber(const QQuickPointerHandler *handler) const override; + Qt::NativeGestureType type() const; + qreal value() const; + +private: + QQuickEventPoint *m_gesturePoint; + + Q_DISABLE_COPY(QQuickPointerNativeGestureEvent) +}; + + // ### Qt 6: move this to qtbase, replace QTouchDevice and the enums in QTabletEvent class Q_QUICK_PRIVATE_EXPORT QQuickPointerDevice : public QObject { @@ -580,7 +618,7 @@ public: QString name() const { return m_name; } QPointingDeviceUniqueId uniqueId() const { return m_uniqueId; } - static QQuickPointerDevice *touchDevice(QTouchDevice *d); + static QQuickPointerDevice *touchDevice(const QTouchDevice *d); static QList<QQuickPointerDevice *> touchDevices(); static QQuickPointerDevice *genericMouseDevice(); static QQuickPointerDevice *tabletDevice(qint64); diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 993d622087..6579175cc6 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -122,6 +122,7 @@ void debugFocusTree(QQuickItem *item, QQuickItem *scope = 0, int depth = 1) \li \l Rotation \li \l Scale \li \l Translate + \li \l Matrix4x4 \endlist The Transform types let you create and control advanced transformations that can be configured @@ -7972,6 +7973,11 @@ QQuickItemLayer *QQuickItemPrivate::layer() const #endif } +bool QQuickItemPrivate::hasPointerHandlers() const +{ + return extra.isAllocated() && !extra->pointerHandlers.isEmpty(); +} + #if QT_CONFIG(quick_shadereffect) QQuickItemLayer::QQuickItemLayer(QQuickItem *item) : m_item(item) diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h index 387ce326c2..9ed5286f22 100644 --- a/src/quick/items/qquickitem_p.h +++ b/src/quick/items/qquickitem_p.h @@ -279,6 +279,8 @@ public: QQuickItemLayer *layer() const; + bool hasPointerHandlers() const; + // data property static void data_append(QQmlListProperty<QObject> *, QObject *); static int data_count(QQmlListProperty<QObject> *); diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp index 3c87496c83..869fdeadc8 100644 --- a/src/quick/items/qquickitemsmodule.cpp +++ b/src/quick/items/qquickitemsmodule.cpp @@ -402,6 +402,10 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor) qmlRegisterType<QQuickFlickable, 10>(uri, 2, 10, "Flickable"); qmlRegisterType<QQuickTextEdit, 10>(uri, 2, 10, "TextEdit"); qmlRegisterType<QQuickText, 10>(uri, 2, 10, "Text"); + +#if QT_CONFIG(quick_path) + qmlRegisterType<QQuickPathAngleArc, 2>(uri, 2, 11, "PathAngleArc"); +#endif } static void initResources() diff --git a/src/quick/items/qquickloader.cpp b/src/quick/items/qquickloader.cpp index 9f27beb298..27afe5a5db 100644 --- a/src/quick/items/qquickloader.cpp +++ b/src/quick/items/qquickloader.cpp @@ -852,6 +852,7 @@ qreal QQuickLoader::progress() const \qmlproperty bool QtQuick::Loader::asynchronous This property holds whether the component will be instantiated asynchronously. +By default it is \c false. When used in conjunction with the \l source property, loading and compilation will also be performed in a background thread. @@ -915,9 +916,14 @@ void QQuickLoaderPrivate::_q_updateSize(bool loaderGeometryChanged) if (!item) return; - if (loaderGeometryChanged && q->widthValid()) + const bool needToUpdateWidth = loaderGeometryChanged && q->widthValid(); + const bool needToUpdateHeight = loaderGeometryChanged && q->heightValid(); + + if (needToUpdateWidth && needToUpdateHeight) + item->setSize(QSizeF(q->width(), q->height())); + else if (needToUpdateWidth) item->setWidth(q->width()); - if (loaderGeometryChanged && q->heightValid()) + else if (needToUpdateHeight) item->setHeight(q->height()); if (updatingSize) diff --git a/src/quick/items/qquickrendercontrol.cpp b/src/quick/items/qquickrendercontrol.cpp index 7e995936af..58b76fa862 100644 --- a/src/quick/items/qquickrendercontrol.cpp +++ b/src/quick/items/qquickrendercontrol.cpp @@ -393,6 +393,8 @@ QImage QQuickRenderControl::grab() #endif } else if (d->window->rendererInterface()->graphicsApi() == QSGRendererInterface::Software) { QQuickWindowPrivate *cd = QQuickWindowPrivate::get(d->window); + cd->polishItems(); + cd->syncSceneGraph(); QSGSoftwareRenderer *softwareRenderer = static_cast<QSGSoftwareRenderer *>(cd->renderer); if (softwareRenderer) { const qreal dpr = d->window->effectiveDevicePixelRatio(); @@ -402,8 +404,6 @@ QImage QQuickRenderControl::grab() QPaintDevice *prevDev = softwareRenderer->currentPaintDevice(); softwareRenderer->setCurrentPaintDevice(&grabContent); softwareRenderer->markDirty(); - cd->polishItems(); - cd->syncSceneGraph(); d->rc->endSync(); render(); softwareRenderer->setCurrentPaintDevice(prevDev); diff --git a/src/quick/items/qquicktextcontrol.cpp b/src/quick/items/qquicktextcontrol.cpp index 2e23d69e5b..874c02fc99 100644 --- a/src/quick/items/qquicktextcontrol.cpp +++ b/src/quick/items/qquicktextcontrol.cpp @@ -432,6 +432,7 @@ void QQuickTextControlPrivate::selectionChanged(bool forceEmitSelectionChanged / #endif emit q->selectionChanged(); } + q->updateCursorRectangle(true); } void QQuickTextControlPrivate::_q_updateCurrentCharFormatAndSelection() @@ -1150,7 +1151,6 @@ void QQuickTextControlPrivate::mouseMoveEvent(QMouseEvent *e, const QPointF &mou if (interactionFlags & Qt::TextEditable) { if (cursor.position() != oldCursorPos) { emit q->cursorPositionChanged(); - q->updateCursorRectangle(true); } _q_updateCurrentCharFormatAndSelection(); #if QT_CONFIG(im) @@ -1159,7 +1159,6 @@ void QQuickTextControlPrivate::mouseMoveEvent(QMouseEvent *e, const QPointF &mou #endif } else if (cursor.position() != oldCursorPos) { emit q->cursorPositionChanged(); - q->updateCursorRectangle(true); } selectionChanged(true); repaintOldAndNewSelection(oldSelection); diff --git a/src/quick/items/qquicktranslate.cpp b/src/quick/items/qquicktranslate.cpp index 9937c692a5..1c8dd02b92 100644 --- a/src/quick/items/qquicktranslate.cpp +++ b/src/quick/items/qquicktranslate.cpp @@ -467,6 +467,7 @@ public: \instantiates QQuickMatrix4x4 \inqmlmodule QtQuick \ingroup qtquick-visual-transforms + \since 5.3 \brief Provides a way to apply a 4x4 tranformation matrix to an \l Item The Matrix4x4 type provides a way to apply a transformation to an diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 3dfc1295fb..caae188ed8 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1644,10 +1644,15 @@ void QQuickWindow::keyReleaseEvent(QKeyEvent *e) void QQuickWindowPrivate::deliverKeyEvent(QKeyEvent *e) { - Q_Q(QQuickWindow); - - if (activeFocusItem) - q->sendEvent(activeFocusItem, e); + if (activeFocusItem) { + QQuickItem *item = activeFocusItem; + e->accept(); + QCoreApplication::sendEvent(item, e); + while (!e->isAccepted() && (item = item->parentItem())) { + e->accept(); + QCoreApplication::sendEvent(item, e); + } + } } QMouseEvent *QQuickWindowPrivate::cloneMouseEvent(QMouseEvent *event, QPointF *transformedLocalPos) @@ -1664,6 +1669,36 @@ QMouseEvent *QQuickWindowPrivate::cloneMouseEvent(QMouseEvent *event, QPointF *t return me; } +void QQuickWindowPrivate::deliverToPassiveGrabbers(const QVector<QPointer <QQuickPointerHandler> > &passiveGrabbers, + QQuickPointerEvent *pointerEvent) +{ + const QVector<QQuickPointerHandler *> &eventDeliveryTargets = pointerEvent->device()->eventDeliveryTargets(); + QVarLengthArray<QPair<QQuickItem *, bool>, 4> sendFilteredPointerEventResult; + for (auto handler : passiveGrabbers) { + // a null pointer in passiveGrabbers is unlikely, unless the grabbing handler was deleted dynamically + if (Q_LIKELY(handler) && !eventDeliveryTargets.contains(handler)) { + bool alreadyFiltered = false; + QQuickItem *par = handler->parentItem(); + + // see if we already have sent a filter event to the parent + auto it = std::find_if(sendFilteredPointerEventResult.begin(), sendFilteredPointerEventResult.end(), + [par](const QPair<QQuickItem *, bool> &pair) { return pair.first == par; }); + if (it != sendFilteredPointerEventResult.end()) { + // Yes, the event was already filtered to that parent, do not call it again but use + // the result of the previous call to determine if we should call the handler. + alreadyFiltered = it->second; + } else { + alreadyFiltered = sendFilteredPointerEvent(pointerEvent, par); + sendFilteredPointerEventResult << qMakePair<QQuickItem*, bool>(par, alreadyFiltered); + } + if (!alreadyFiltered) + handler->handlePointerEvent(pointerEvent); + } + } +} + + + void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEvent) { auto point = pointerEvent->point(0); @@ -1712,14 +1747,8 @@ void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEven // if this is an update or release from an actual mouse, // and the point wasn't grabbed, deliver only to PointerHandlers: // passive grabbers first, then the rest - const QVector<QQuickPointerHandler *> &eventDeliveryTargets = pointerEvent->device()->eventDeliveryTargets(); - for (auto handler : point->passiveGrabbers()) { - // a null pointer in passiveGrabbers is unlikely, unless the grabbing handler was deleted dynamically - if (Q_LIKELY(handler) && !eventDeliveryTargets.contains(handler)) { - if (!sendFilteredPointerEvent(pointerEvent, handler->parentItem())) - handler->handlePointerEvent(pointerEvent); - } - } + deliverToPassiveGrabbers(point->passiveGrabbers(), pointerEvent); + // If some points weren't grabbed, deliver to non-grabber PointerHandlers in reverse paint order if (!pointerEvent->allPointsGrabbed() && pointerEvent->buttons()) { QVector<QQuickItem *> targetItems = pointerTargets(contentItem, point->scenePosition(), false, false); @@ -1740,9 +1769,6 @@ void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEven // make sure not to accept unhandled events pointerEvent->setAccepted(false); } - // failsafe: never allow any kind of grab to persist after release - if (mouseIsReleased) - pointerEvent->clearGrabbers(); } bool QQuickWindowPrivate::sendHoverEvent(QEvent::Type type, QQuickItem *item, @@ -1915,10 +1941,20 @@ bool QQuickWindowPrivate::deliverNativeGestureEvent(QQuickItem *item, QNativeGes return true; } - QPointF p = item->mapFromScene(event->localPos()); + // Try the Item's pointer handlers first + QQuickPointerEvent *pointerEvent = pointerEventInstance(event); + pointerEvent->localize(item); + if (itemPrivate->handlePointerEvent(pointerEvent, false)) { + if (pointerEvent->allPointsAccepted()) { + event->accept(); + return true; + } + } + // If still not accepted, try direct delivery to the item + QPointF p = item->mapFromScene(event->localPos()); if (item->contains(p)) { - QNativeGestureEvent copy(event->gestureType(), p, event->windowPos(), event->screenPos(), + QNativeGestureEvent copy(event->gestureType(), event->device(), p, event->windowPos(), event->screenPos(), event->value(), 0L, 0L); // TODO can't copy things I can't access event->accept(); item->event(©); @@ -2164,23 +2200,36 @@ void QQuickWindowPrivate::flushFrameSynchronousEvents() } } -QQuickPointerEvent *QQuickWindowPrivate::pointerEventInstance(QQuickPointerDevice *device) const +QQuickPointerEvent *QQuickWindowPrivate::pointerEventInstance(QQuickPointerDevice *device, QEvent::Type eventType) const { - // the list of devices should be very small so a linear search should be ok - for (QQuickPointerEvent *e: pointerEventInstances) { + // Search for a matching reusable event object. + for (QQuickPointerEvent *e : pointerEventInstances) { + // If device can generate native gestures (e.g. a trackpad), there might be two QQuickPointerEvents: + // QQuickPointerNativeGestureEvent and QQuickPointerTouchEvent. Use eventType to disambiguate. + if (eventType == QEvent::NativeGesture && !qobject_cast<QQuickPointerNativeGestureEvent*>(e)) + continue; + // Otherwise we assume there's only one event type per device. + // More disambiguation tests might need to be added above if that changes later. if (e->device() == device) return e; } + // Not found: we have to create a suitable event instance. QQuickPointerEvent *ev = nullptr; QQuickWindow *q = const_cast<QQuickWindow*>(q_func()); switch (device->type()) { case QQuickPointerDevice::Mouse: + // QWindowSystemInterface::handleMouseEvent() does not take a device parameter: + // we assume all mouse events come from one mouse (the "core pointer"). + // So when the event is a mouse event, device == QQuickPointerDevice::genericMouseDevice() ev = new QQuickPointerMouseEvent(q, device); break; case QQuickPointerDevice::TouchPad: case QQuickPointerDevice::TouchScreen: - ev = new QQuickPointerTouchEvent(q, device); + if (eventType == QEvent::NativeGesture) + ev = new QQuickPointerNativeGestureEvent(q, device); + else // assume QEvent::Type is one of TouchBegin/Update/End + ev = new QQuickPointerTouchEvent(q, device); break; default: // TODO tablet event types @@ -2200,29 +2249,29 @@ QQuickPointerEvent *QQuickWindowPrivate::pointerEventInstance(QQuickPointerDevic QQuickPointerEvent *QQuickWindowPrivate::pointerEventInstance(QEvent *event) const { QQuickPointerDevice *dev = nullptr; - QQuickPointerEvent *ev = nullptr; switch (event->type()) { case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: case QEvent::MouseButtonDblClick: case QEvent::MouseMove: dev = QQuickPointerDevice::genericMouseDevice(); - ev = pointerEventInstance(dev); break; case QEvent::TouchBegin: case QEvent::TouchUpdate: case QEvent::TouchEnd: case QEvent::TouchCancel: dev = QQuickPointerDevice::touchDevice(static_cast<QTouchEvent *>(event)->device()); - ev = pointerEventInstance(dev); break; // TODO tablet event types + case QEvent::NativeGesture: + dev = QQuickPointerDevice::touchDevice(static_cast<QNativeGestureEvent *>(event)->device()); + break; default: break; } - Q_ASSERT(ev); - return ev->reset(event); + Q_ASSERT(dev); + return pointerEventInstance(dev, event->type())->reset(event); } void QQuickWindowPrivate::deliverPointerEvent(QQuickPointerEvent *event) @@ -2235,6 +2284,9 @@ void QQuickWindowPrivate::deliverPointerEvent(QQuickPointerEvent *event) skipDelivery.clear(); if (event->asPointerMouseEvent()) { deliverMouseEvent(event->asPointerMouseEvent()); + // failsafe: never allow any kind of grab to persist after release + if (event->isReleaseEvent() && event->buttons() == Qt::NoButton) + event->clearGrabbers(); } else if (event->asPointerTouchEvent()) { deliverTouchEvent(event->asPointerTouchEvent()); } else { @@ -2273,10 +2325,12 @@ QVector<QQuickItem *> QQuickWindowPrivate::pointerTargets(QQuickItem *item, cons } bool relevant = item->contains(itemPos); - if (relevant && checkMouseButtons && item->acceptedMouseButtons() == Qt::NoButton) - relevant = false; - if (relevant && checkAcceptsTouch && !(item->acceptTouchEvents() || item->acceptedMouseButtons())) - relevant = false; + if (!(itemPrivate->hasPointerHandlers())) { + if (relevant && checkMouseButtons && item->acceptedMouseButtons() == Qt::NoButton) + relevant = false; + if (relevant && checkAcceptsTouch && !(item->acceptTouchEvents() || item->acceptedMouseButtons())) + relevant = false; + } if (relevant) targets << item; // add this item last: children take precedence return targets; @@ -2368,16 +2422,9 @@ void QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *eve int pointCount = event->pointCount(); // Deliver to each eventpoint's passive grabbers (but don't visit any handler more than once) - const QVector<QQuickPointerHandler *> &eventDeliveryTargets = event->device()->eventDeliveryTargets(); for (int i = 0; i < pointCount; ++i) { QQuickEventPoint *point = event->point(i); - for (auto handler : point->passiveGrabbers()) { - if (Q_LIKELY(handler) && !eventDeliveryTargets.contains(handler)) { - if (sendFilteredPointerEvent(event, handler->parentItem())) - return; - handler->handlePointerEvent(event); - } - } + deliverToPassiveGrabbers(point->passiveGrabbers(), event); } // If some points weren't grabbed, deliver to non-grabber PointerHandlers in reverse paint order @@ -2740,10 +2787,24 @@ bool QQuickWindowPrivate::sendFilteredPointerEventImpl(QQuickPointerEvent *event if (receiver->acceptedMouseButtons()) { QPointF localPos = receiver->mapFromScene(pme->point(0)->scenePosition()); QMouseEvent *me = pme->asMouseEvent(localPos); + const bool wasAccepted = me->isAccepted(); + me->setAccepted(true); + auto oldMouseGrabber = pme->point(0)->grabberItem(); if (filteringParent->childMouseEventFilter(receiver, me)) { qCDebug(DBG_MOUSE) << "mouse event intercepted by childMouseEventFilter of " << filteringParent; skipDelivery.append(filteringParent); filtered = true; + if (me->isAccepted() && pme->isPressEvent()) { + auto mouseGrabber = pme->point(0)->grabberItem(); + if (mouseGrabber && mouseGrabber != receiver && mouseGrabber != oldMouseGrabber) { + receiver->mouseUngrabEvent(); + } else { + pme->point(0)->setGrabberItem(receiver); + } + } + } else { + // Restore accepted state if the event was not filtered. + me->setAccepted(wasAccepted); } } } else if (QQuickPointerTouchEvent *pte = event->asPointerTouchEvent()) { @@ -2755,13 +2816,12 @@ bool QQuickWindowPrivate::sendFilteredPointerEventImpl(QQuickPointerEvent *event #endif if (acceptsTouchEvents || receiver->acceptedMouseButtons()) { // get a touch event customized for delivery to filteringParent - QScopedPointer<QTouchEvent> filteringParentTouchEvent(pte->touchEventForItem(filteringParent, true)); + QScopedPointer<QTouchEvent> filteringParentTouchEvent(pte->touchEventForItem(receiver, true)); if (filteringParentTouchEvent) { QVarLengthArray<QPair<QQuickPointerHandler *, QQuickEventPoint *>, 32> passiveGrabsToCancel; if (filteringParent->childMouseEventFilter(receiver, filteringParentTouchEvent.data())) { qCDebug(DBG_TOUCH) << "touch event intercepted by childMouseEventFilter of " << filteringParent; skipDelivery.append(filteringParent); - filteringParent->grabMouse(); for (auto point: qAsConst(filteringParentTouchEvent->touchPoints())) { auto pointerEventPoint = pte->pointById(point.id()); for (auto handler : pointerEventPoint->passiveGrabbers()) { @@ -2769,7 +2829,9 @@ bool QQuickWindowPrivate::sendFilteredPointerEventImpl(QQuickPointerEvent *event if (!passiveGrabsToCancel.contains(grab)) passiveGrabsToCancel.append(grab); } - event->pointById(point.id())->setAccepted(); + QQuickEventPoint *pt = event->pointById(point.id()); + pt->setAccepted(); + pt->setGrabberItem(filteringParent); } return true; } else { @@ -2810,7 +2872,8 @@ bool QQuickWindowPrivate::sendFilteredPointerEventImpl(QQuickPointerEvent *event qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << hex << tp.id() << "->" << filteringParent; pointerEventInstance(touchMouseDevice)->pointById(tp.id())->setGrabberItem(filteringParent); touchMouseUnset = false; // We want to leave touchMouseId and touchMouseDevice set - filteringParent->grabMouse(); + if (mouseEvent->isAccepted()) + filteringParent->grabMouse(); auto pointerEventPoint = pte->pointById(tp.id()); for (auto handler : pointerEventPoint->passiveGrabbers()) { QPair<QQuickPointerHandler *, QQuickEventPoint *> grab(handler, pointerEventPoint); diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index c636719258..06891406ab 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -146,6 +146,7 @@ public: void grabTouchPoints(QObject *grabber, const QVector<int> &ids); void removeGrabber(QQuickItem *grabber, bool mouse = true, bool touch = true); static QMouseEvent *cloneMouseEvent(QMouseEvent *event, QPointF *transformedLocalPos = 0); + void deliverToPassiveGrabbers(const QVector<QPointer <QQuickPointerHandler> > &passiveGrabbers, QQuickPointerEvent *pointerEvent); void deliverMouseEvent(QQuickPointerMouseEvent *pointerEvent); bool sendFilteredMouseEvent(QEvent *event, QQuickItem *receiver, QQuickItem *filteringParent); bool sendFilteredPointerEvent(QQuickPointerEvent *event, QQuickItem *receiver, QQuickItem *filteringParent = nullptr); @@ -166,7 +167,7 @@ public: // the device-specific event instances which are reused during event delivery mutable QVector<QQuickPointerEvent *> pointerEventInstances; - QQuickPointerEvent *pointerEventInstance(QQuickPointerDevice *device) const; + QQuickPointerEvent *pointerEventInstance(QQuickPointerDevice *device, QEvent::Type eventType = QEvent::None) const; // delivery of pointer events: QQuickPointerEvent *pointerEventInstance(QEvent *ev) const; |