From cb78b25d039bfa82281c458cb329c6e5f3d4f836 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Wed, 28 Apr 2021 09:34:24 +0200 Subject: Use QQDeliveryAgent::sceneTransform, if set, in QQuickItem::mapFromGlobal During dragging of a QEventPoint, Flickable computes the drag delta as pos - mapFromGlobal(point.globalPressPosition()) We cannot use only the global position delta, because the Flickable might be transformed in 2D (by setting rotation on it or in a parent, as in tst_qquickflickable::clickAndDragWhenTransformed) or in 3D (by mapping it onto a Model object). So we really need QQuickItem::mapFromGlobal() to actually work regardless how many of these transformations are in place. This is just the beginning: we have a lot of these mapFrom/To functions; but it's enough for the Flickable in the quick3d/dynamictexture example to work better. Without this fix, if you tried to drag a yellow note on the door panel, at the very first drag ListView saw a large delta and considered its drag threshold exceeded immediately, whereas the DragHandler on the note saw a very small delta; so ListView grabbed and DragHandler did not steal it: it relies on having "first dibs". When the drag threshold is exceeded, Flickable merely plans to grab on the next event rather than grabbing immediately, and therefore a child has a chance to grab first. Therefore it's normally OK for DragHandler to simply become the first exclusive grabber when the drag threshold is exceeded, and not steal the grab from another item (although grabPermissions can be changed to allow stealing if necessary). However this means that we continue to enforce the drag threshold in local (transformed) coordinates: if Flickable should wait until the user drags 10 pixels, but it's scaled to half-size, it will start dragging after only 5 pixels of movement, for example. Task-number: QTBUG-92944 Change-Id: Id01cc833ca80850ca18a965adf47f435e43e20ed Reviewed-by: Andy Nichols (cherry picked from commit f83efd3feb564cf8b929b9dfde6787853a561069) Reviewed-by: Qt Cherry-pick Bot --- src/quick/handlers/qquickpointerhandler.cpp | 3 ++- src/quick/items/qquickitem.cpp | 13 ++++++++++++- src/quick/util/qquickdeliveryagent.cpp | 29 ++++++++++++++++++++++++++++- src/quick/util/qquickdeliveryagent_p.h | 1 + src/quick/util/qquickdeliveryagent_p_p.h | 1 + 5 files changed, 44 insertions(+), 3 deletions(-) diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp index 11c29ee812..254f91cfe1 100644 --- a/src/quick/handlers/qquickpointerhandler.cpp +++ b/src/quick/handlers/qquickpointerhandler.cpp @@ -302,7 +302,8 @@ void QQuickPointerHandler::onGrabChanged(QQuickPointerHandler *grabber, QPointin */ void QQuickPointerHandler::setPassiveGrab(QPointerEvent *event, const QEventPoint &point, bool grab) { - qCDebug(lcPointerHandlerGrab) << point << grab; + qCDebug(lcPointerHandlerGrab) << this << point << grab << "via" + << QQuickDeliveryAgentPrivate::currentOrItemDeliveryAgent(parentItem()); if (grab) { event->addPassiveGrabber(point, this); } else { diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 464076cbf8..05e50d8e75 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -8178,6 +8178,12 @@ QPointF QQuickItem::mapFromScene(const QPointF &point) const treated only as a hint. So, the resulting window position may differ from what is expected. + \note If this item is in a subscene, e.g. mapped onto a 3D + \l [QtQuick3D QML] {Model}{Model} object, the UV mapping is incorporated + into this transformation, so that it really goes from screen coordinates to + this item's coordinates, as long as \a point is actually within this item's bounds. + The other mapping functions do not yet work that way. + \since 5.7 \sa {Concepts - Visual Coordinates in Qt Quick} @@ -8185,7 +8191,12 @@ QPointF QQuickItem::mapFromScene(const QPointF &point) const QPointF QQuickItem::mapFromGlobal(const QPointF &point) const { Q_D(const QQuickItem); - return mapFromScene(d->globalToWindowTransform().map(point)); + QPointF scenePoint = d->globalToWindowTransform().map(point); + if (auto da = QQuickDeliveryAgentPrivate::currentOrItemDeliveryAgent(this)) { + if (auto sceneTransform = da->sceneTransform()) + scenePoint = sceneTransform->map(scenePoint); + } + return mapFromScene(scenePoint); } /*! diff --git a/src/quick/util/qquickdeliveryagent.cpp b/src/quick/util/qquickdeliveryagent.cpp index a38ac410d4..3b045ed8e4 100644 --- a/src/quick/util/qquickdeliveryagent.cpp +++ b/src/quick/util/qquickdeliveryagent.cpp @@ -625,6 +625,21 @@ void QQuickDeliveryAgentPrivate::updateFocusItemTransform() #endif } +/*! \internal + If called during event delivery, returns the agent that is delivering the + event, without checking whether \a item is reachable from there. + Otherwise returns QQuickItemPrivate::deliveryAgent() (the delivery agent for + the narrowest subscene containing \a item), or \c null if \a item is \c null. +*/ +QQuickDeliveryAgent *QQuickDeliveryAgentPrivate::currentOrItemDeliveryAgent(const QQuickItem *item) +{ + if (currentEventDeliveryAgent) + return currentEventDeliveryAgent; + if (item) + return QQuickItemPrivate::get(const_cast(item))->deliveryAgent(); + return nullptr; +} + /*! \internal QQuickDeliveryAgent delivers events to a tree of Qt Quick Items, beginning with the given root item, which is usually QQuickWindow::rootItem() but @@ -649,9 +664,21 @@ QQuickItem *QQuickDeliveryAgent::rootItem() const return d->rootItem; } +/*! \internal + Returns the object that was set in setSceneTransform(): a functor that + transforms from scene coordinates in the parent scene to scene coordinates + within this DA's subscene, or \c null if none was set. +*/ +QQuickDeliveryAgent::Transform *QQuickDeliveryAgent::sceneTransform() const +{ + Q_D(const QQuickDeliveryAgent); + return d->sceneTransform; +} + /*! \internal QQuickDeliveryAgent takes ownership of the given \a transform, which - encapsulates the ability to transform viewport coordinates to rootItem coordinates. + encapsulates the ability to transform parent scene coordinates to rootItem + (subscene) coordinates. */ void QQuickDeliveryAgent::setSceneTransform(QQuickDeliveryAgent::Transform *transform) { diff --git a/src/quick/util/qquickdeliveryagent_p.h b/src/quick/util/qquickdeliveryagent_p.h index 9b73d9b9b9..df8c97b368 100644 --- a/src/quick/util/qquickdeliveryagent_p.h +++ b/src/quick/util/qquickdeliveryagent_p.h @@ -81,6 +81,7 @@ public: QQuickItem *rootItem() const; + Transform *sceneTransform() const; void setSceneTransform(Transform *transform); bool event(QEvent *ev) override; diff --git a/src/quick/util/qquickdeliveryagent_p_p.h b/src/quick/util/qquickdeliveryagent_p_p.h index ad1eece36e..f931cfd70b 100644 --- a/src/quick/util/qquickdeliveryagent_p_p.h +++ b/src/quick/util/qquickdeliveryagent_p_p.h @@ -121,6 +121,7 @@ public: static bool subsceneAgentsExist; // QQuickDeliveryAgent::event() sets this to the one that's currently (trying to) handle the event static QQuickDeliveryAgent *currentEventDeliveryAgent; + static QQuickDeliveryAgent *currentOrItemDeliveryAgent(const QQuickItem *item); Qt::FocusReason lastFocusReason = Qt::OtherFocusReason; int pointerEventRecursionGuard = 0; -- cgit v1.2.3