aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/quick/handlers/qquickmultipointerhandler.cpp36
-rw-r--r--src/quick/handlers/qquickmultipointerhandler_p.h2
-rw-r--r--src/quick/handlers/qquickpinchhandler.cpp6
-rw-r--r--src/quick/handlers/qquickpointerhandler.cpp1
-rw-r--r--src/quick/handlers/qquicktaphandler.cpp7
-rw-r--r--src/quick/items/qquickevents.cpp36
-rw-r--r--src/quick/items/qquickevents_p_p.h9
-rw-r--r--src/quick/items/qquickitem.cpp1
-rw-r--r--src/quick/items/qquickwindow.cpp38
-rw-r--r--src/quick/items/qquickwindow_p.h4
10 files changed, 87 insertions, 53 deletions
diff --git a/src/quick/handlers/qquickmultipointerhandler.cpp b/src/quick/handlers/qquickmultipointerhandler.cpp
index 016210beff..a605b3f12e 100644
--- a/src/quick/handlers/qquickmultipointerhandler.cpp
+++ b/src/quick/handlers/qquickmultipointerhandler.cpp
@@ -66,44 +66,34 @@ bool QQuickMultiPointerHandler::wantsPointerEvent(QQuickPointerEvent *event)
if (!QQuickPointerDeviceHandler::wantsPointerEvent(event))
return false;
- const int pCount = event->pointCount();
- int pointCandidateCount = 0;
-
- // If one or more points are newly pressed, all points are candidates for this handler.
- // In other cases however, do not steal the grab: that is, if a point has a grabber,
- // it's not a candidate for this handler.
- if (event->isPressEvent()) {
- pointCandidateCount = pCount;
- } else {
- for (int i = 0; i < pCount; ++i) {
- QQuickEventPoint *pt = event->point(i);
- QObject *exclusiveGrabber = pt->exclusiveGrabber();
- if (!exclusiveGrabber || exclusiveGrabber == this)
- ++pointCandidateCount;
- }
- }
-
- if (pointCandidateCount < m_requiredPointCount)
- return false;
if (sameAsCurrentPoints(event))
return true;
- const QVector<QQuickEventPoint *> candidatePoints = pointsInsideOrNearTarget(event);
+ const QVector<QQuickEventPoint *> candidatePoints = eligiblePoints(event);
const bool ret = (candidatePoints.size() == m_requiredPointCount);
if (ret)
m_currentPoints = candidatePoints;
return ret;
}
-QVector<QQuickEventPoint *> QQuickMultiPointerHandler::pointsInsideOrNearTarget(QQuickPointerEvent *event)
+QVector<QQuickEventPoint *> QQuickMultiPointerHandler::eligiblePoints(QQuickPointerEvent *event)
{
QVector<QQuickEventPoint *> ret;
int c = event->pointCount();
QRectF targetBounds = target()->mapRectToScene(target()->boundingRect())
.marginsAdded(QMarginsF(m_pointDistanceThreshold, m_pointDistanceThreshold, m_pointDistanceThreshold, m_pointDistanceThreshold));
+ // If one or more points are newly pressed or released, all non-released points are candidates for this handler.
+ // In other cases however, do not steal the grab: that is, if a point has a grabber,
+ // it's not a candidate for this handler.
+ bool stealingAllowed = event->isPressEvent() || event->isReleaseEvent();
for (int i = 0; i < c; ++i) {
QQuickEventPoint *p = event->point(i);
- if (targetBounds.contains(p->scenePos()))
+ if (!stealingAllowed) {
+ QObject *exclusiveGrabber = p->exclusiveGrabber();
+ if (exclusiveGrabber && exclusiveGrabber != this)
+ continue;
+ }
+ if (p->state() != QQuickEventPoint::Released && targetBounds.contains(p->scenePos()))
ret << p;
}
return ret;
@@ -136,6 +126,8 @@ bool QQuickMultiPointerHandler::sameAsCurrentPoints(QQuickPointerEvent *event)
// TODO optimize: either ensure the points are sorted,
// or use std::equal with a predicate
for (int i = 0; ret && i < c; ++i) {
+ if (event->point(i)->state() == QQuickEventPoint::Released)
+ return false;
bool found = false;
int pointId = event->point(i)->pointId();
for (QQuickEventPoint *o : qAsConst(m_currentPoints))
diff --git a/src/quick/handlers/qquickmultipointerhandler_p.h b/src/quick/handlers/qquickmultipointerhandler_p.h
index ce014d658c..1f2d213e56 100644
--- a/src/quick/handlers/qquickmultipointerhandler_p.h
+++ b/src/quick/handlers/qquickmultipointerhandler_p.h
@@ -88,7 +88,7 @@ protected:
bool wantsPointerEvent(QQuickPointerEvent *event) override;
bool sameAsCurrentPoints(QQuickPointerEvent *event);
- QVector<QQuickEventPoint *> pointsInsideOrNearTarget(QQuickPointerEvent *event);
+ QVector<QQuickEventPoint *> eligiblePoints(QQuickPointerEvent *event);
QPointF touchPointCentroid();
QVector2D touchPointCentroidVelocity();
qreal averageTouchPointDistance(const QPointF &ref);
diff --git a/src/quick/handlers/qquickpinchhandler.cpp b/src/quick/handlers/qquickpinchhandler.cpp
index 3af77593e2..465a49a6fd 100644
--- a/src/quick/handlers/qquickpinchhandler.cpp
+++ b/src/quick/handlers/qquickpinchhandler.cpp
@@ -228,7 +228,8 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event)
qCDebug(lcPinchHandler) << point->state() << point->sceneGrabPos() << "->" << point->scenePos();
}
- if (!active()) {
+ bool containsReleasedPoints = event->isReleaseEvent();
+ if (!active() && !containsReleasedPoints) {
// Verify that least one of the points have moved beyond threshold needed to activate the handler
for (QQuickEventPoint *point : qAsConst(m_currentPoints)) {
if (QQuickWindowPrivate::dragOverThreshold(point)) {
@@ -302,7 +303,8 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event)
<< ", rotation" << rotation;
}
- acceptPoints(m_currentPoints);
+ if (!containsReleasedPoints)
+ acceptPoints(m_currentPoints);
emit updated();
}
diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp
index 3774c2d24c..1ed80c0a5b 100644
--- a/src/quick/handlers/qquickpointerhandler.cpp
+++ b/src/quick/handlers/qquickpointerhandler.cpp
@@ -216,6 +216,7 @@ void QQuickPointerHandler::handlePointerEvent(QQuickPointerEvent *event)
pt->cancelExclusiveGrab();
}
}
+ event->device()->eventDeliveryTargets().append(this);
}
bool QQuickPointerHandler::wantsPointerEvent(QQuickPointerEvent *event)
diff --git a/src/quick/handlers/qquicktaphandler.cpp b/src/quick/handlers/qquicktaphandler.cpp
index f79dbb266a..d73e9fd100 100644
--- a/src/quick/handlers/qquicktaphandler.cpp
+++ b/src/quick/handlers/qquicktaphandler.cpp
@@ -270,7 +270,7 @@ void QQuickTapHandler::setPressed(bool press, bool cancel, QQuickEventPoint *poi
else
setExclusiveGrab(point, press);
}
- if (!cancel && !press) {
+ if (!cancel && !press && parentContains(point)) {
if (point->timeHeld() < longPressThreshold()) {
// Assuming here that pointerEvent()->timestamp() is in ms.
qreal ts = point->pointerEvent()->timestamp() / 1000.0;
@@ -301,8 +301,9 @@ void QQuickTapHandler::setPressed(bool press, bool cancel, QQuickEventPoint *poi
void QQuickTapHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabState stateChange, QQuickEventPoint *point)
{
QQuickPointerSingleHandler::onGrabChanged(grabber, stateChange, point);
- if (grabber == this && (stateChange == QQuickEventPoint::CancelGrabExclusive || stateChange == QQuickEventPoint::CancelGrabPassive))
- setPressed(false, true, point);
+ bool isCanceled = stateChange == QQuickEventPoint::CancelGrabExclusive || stateChange == QQuickEventPoint::CancelGrabPassive;
+ if (grabber == this && (isCanceled || point->state() == QQuickEventPoint::Released))
+ setPressed(false, isCanceled, point);
}
void QQuickTapHandler::connectPreRenderSignal(bool conn)
diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp
index 0f613d300b..bde271882c 100644
--- a/src/quick/items/qquickevents.cpp
+++ b/src/quick/items/qquickevents.cpp
@@ -1055,6 +1055,18 @@ bool QQuickPointerMouseEvent::isPressEvent() const
(me->buttons() & me->button()) == me->buttons());
}
+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->type() == QEvent::MouseButtonRelease;
+}
+
bool QQuickPointerTouchEvent::allPointsAccepted() const {
for (int i = 0; i < m_pointCount; ++i) {
if (!m_touchPoints.at(i)->isAccepted())
@@ -1124,6 +1136,16 @@ bool QQuickPointerTouchEvent::isPressEvent() const
return static_cast<QTouchEvent*>(m_event)->touchPointStates() & Qt::TouchPointPressed;
}
+bool QQuickPointerTouchEvent::isUpdateEvent() const
+{
+ return static_cast<QTouchEvent*>(m_event)->touchPointStates() & (Qt::TouchPointMoved | Qt::TouchPointStationary);
+}
+
+bool QQuickPointerTouchEvent::isReleaseEvent() const
+{
+ return static_cast<QTouchEvent*>(m_event)->touchPointStates() & Qt::TouchPointReleased;
+}
+
QVector<QPointF> QQuickPointerEvent::unacceptedPressedPointScenePositions() const
{
QVector<QPointF> points;
@@ -1239,7 +1261,7 @@ QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(QQuickItem *item, bool i
// 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 anyPressInside = false;
+ bool anyPressOrReleaseInside = false;
bool anyGrabber = false;
QMatrix4x4 transformMatrix(QQuickItemPrivate::get(item)->windowToItemTransform());
for (int i = 0; i < m_pointCount; ++i) {
@@ -1250,8 +1272,9 @@ QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(QQuickItem *item, bool i
bool isGrabber = p->exclusiveGrabber() == item;
if (isGrabber)
anyGrabber = true;
- // include points inside the bounds
+ // 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->scenePos()));
+ bool hasAnotherGrabber = p->exclusiveGrabber() && p->exclusiveGrabber() != item;
// filtering: (childMouseEventFilter) include points that are grabbed by children of the target item
bool grabberIsChild = false;
@@ -1265,11 +1288,10 @@ QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(QQuickItem *item, bool i
}
bool filterRelevant = isFiltering && grabberIsChild;
- if (!(isGrabber || isInside || filterRelevant))
+ if (!(isGrabber || (isInside && (!hasAnotherGrabber || isFiltering)) || filterRelevant))
continue;
- bool isPress = p->state() == QQuickEventPoint::Pressed;
- if (isPress && isInside)
- anyPressInside = true;
+ if ((p->state() == QQuickEventPoint::Pressed || p->state() == QQuickEventPoint::Released) && isInside)
+ anyPressOrReleaseInside = true;
const QTouchEvent::TouchPoint *tp = touchPointById(p->pointId());
if (tp) {
eventStates |= tp->state();
@@ -1285,7 +1307,7 @@ QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(QQuickItem *item, bool i
// 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() || (!anyPressInside && !anyGrabber && !isFiltering))
+ if (eventStates == Qt::TouchPointStationary || touchPoints.isEmpty() || (!anyPressOrReleaseInside && !anyGrabber && !isFiltering))
return nullptr;
// if all points have the same state, set the event type accordingly
diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h
index 72d5be54c9..4b3587f18c 100644
--- a/src/quick/items/qquickevents_p_p.h
+++ b/src/quick/items/qquickevents_p_p.h
@@ -272,7 +272,8 @@ public:
Released = Qt::TouchPointReleased
// Canceled = Qt::TouchPointReleased << 1 // 0x10 // TODO maybe
};
- Q_ENUM(State)
+ Q_DECLARE_FLAGS(States, State)
+ Q_FLAG(States)
enum GrabState {
GrabPassive = 0,
@@ -406,6 +407,8 @@ public: // helpers for C++ only (during event delivery)
virtual void localize(QQuickItem *target) = 0;
virtual bool isPressEvent() const = 0;
+ virtual bool isUpdateEvent() const = 0;
+ virtual bool isReleaseEvent() const = 0;
virtual QQuickPointerMouseEvent *asPointerMouseEvent() { return nullptr; }
virtual QQuickPointerTouchEvent *asPointerTouchEvent() { return nullptr; }
virtual QQuickPointerTabletEvent *asPointerTabletEvent() { return nullptr; }
@@ -448,6 +451,8 @@ public:
QQuickPointerEvent *reset(QEvent *) override;
void localize(QQuickItem *target) override;
bool isPressEvent() const override;
+ bool isUpdateEvent() const override;
+ bool isReleaseEvent() const override;
QQuickPointerMouseEvent *asPointerMouseEvent() override { return this; }
const QQuickPointerMouseEvent *asPointerMouseEvent() const override { return this; }
int pointCount() const override { return 1; }
@@ -481,6 +486,8 @@ public:
QQuickPointerEvent *reset(QEvent *) override;
void localize(QQuickItem *target) override;
bool isPressEvent() const override;
+ bool isUpdateEvent() const override;
+ bool isReleaseEvent() const override;
QQuickPointerTouchEvent *asPointerTouchEvent() override { return this; }
const QQuickPointerTouchEvent *asPointerTouchEvent() const override { return this; }
int pointCount() const override { return m_pointCount; }
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp
index 4cb7ef3e22..e959b51bc3 100644
--- a/src/quick/items/qquickitem.cpp
+++ b/src/quick/items/qquickitem.cpp
@@ -5113,7 +5113,6 @@ bool QQuickItemPrivate::handlePointerEvent(QQuickPointerEvent *event, bool avoid
if ((!avoidExclusiveGrabber || !event->hasExclusiveGrabber(handler)) && !eventDeliveryTargets.contains(handler)) {
handler->handlePointerEvent(event);
delivered = true;
- eventDeliveryTargets.append(handler);
}
}
}
diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp
index 9c889d54f5..e4eed8454c 100644
--- a/src/quick/items/qquickwindow.cpp
+++ b/src/quick/items/qquickwindow.cpp
@@ -1703,18 +1703,17 @@ void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEven
bool delivered = false;
if (pointerEvent->isPressEvent()) {
// send initial press
- delivered = deliverPressEvent(pointerEvent);
+ delivered = deliverPressOrReleaseEvent(pointerEvent);
} else if (pointerEvent->device()->type() == QQuickPointerDevice::Mouse) {
// if this is an update or release from an actual mouse,
// and the point wasn't grabbed, deliver only to PointerHandlers:
// passive grabbers first, then the rest
- QVector<QQuickPointerHandler *> &eventDeliveryTargets = pointerEvent->device()->eventDeliveryTargets();
+ 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);
- eventDeliveryTargets.append(handler);
}
}
// If some points weren't grabbed, deliver to non-grabber PointerHandlers in reverse paint order
@@ -2266,9 +2265,11 @@ void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event)
qCDebug(DBG_TOUCH) << " - delivering" << event->asTouchEvent();
if (event->isPressEvent())
- deliverPressEvent(event);
+ deliverPressOrReleaseEvent(event);
if (!event->allUpdatedPointsAccepted())
deliverUpdatedTouchPoints(event);
+ if (event->isReleaseEvent())
+ deliverPressOrReleaseEvent(event, true);
// Remove released points from itemForTouchPointId
bool allReleased = true;
@@ -2322,7 +2323,7 @@ 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)
- QVector<QQuickPointerHandler *> &eventDeliveryTargets = event->device()->eventDeliveryTargets();
+ const QVector<QQuickPointerHandler *> &eventDeliveryTargets = event->device()->eventDeliveryTargets();
for (int i = 0; i < pointCount; ++i) {
QQuickEventPoint *point = event->point(i);
for (auto handler : point->passiveGrabbers()) {
@@ -2330,7 +2331,6 @@ void QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *eve
if (sendFilteredPointerEvent(event, handler->parentItem()))
return;
handler->handlePointerEvent(event);
- eventDeliveryTargets.append(handler);
}
}
}
@@ -2363,8 +2363,8 @@ void QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *eve
}
}
-// Deliver newly pressed touch points
-bool QQuickWindowPrivate::deliverPressEvent(QQuickPointerEvent *event)
+// Deliver an event containing newly pressed or released touch points
+bool QQuickWindowPrivate::deliverPressOrReleaseEvent(QQuickPointerEvent *event, bool handlersOnly)
{
int pointCount = event->pointCount();
QVector<QQuickItem *> targetItems;
@@ -2372,6 +2372,8 @@ bool QQuickWindowPrivate::deliverPressEvent(QQuickPointerEvent *event)
for (int i = 0; i < pointCount; ++i) {
auto point = event->point(i);
point->setAccepted(false); // because otherwise touchEventForItem will ignore it
+ if (point->grabberPointerHandler() && point->state() == QQuickEventPoint::Released)
+ point->setGrabberPointerHandler(nullptr, true);
QVector<QQuickItem *> targetItemsForPoint = pointerTargets(contentItem, point->scenePos(), !isTouchEvent, isTouchEvent);
if (targetItems.count()) {
targetItems = mergePointerTargets(targetItems, targetItemsForPoint);
@@ -2380,14 +2382,14 @@ bool QQuickWindowPrivate::deliverPressEvent(QQuickPointerEvent *event)
}
}
- if (allowChildEventFiltering) {
+ if (allowChildEventFiltering && !handlersOnly) {
updateFilteringParentItems(targetItems);
if (sendFilteredPointerEvent(event, nullptr))
return true;
}
for (QQuickItem *item: targetItems) {
- deliverMatchingPointsToItem(item, event);
+ deliverMatchingPointsToItem(item, event, handlersOnly);
if (event->allPointsAccepted())
break;
}
@@ -2395,7 +2397,7 @@ bool QQuickWindowPrivate::deliverPressEvent(QQuickPointerEvent *event)
return event->allPointsAccepted();
}
-void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent)
+void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent, bool handlersOnly)
{
Q_Q(QQuickWindow);
QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
@@ -2403,7 +2405,15 @@ void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo
// Let the Item's handlers (if any) have the event first.
itemPrivate->handlePointerEvent(pointerEvent);
- if (pointerEvent->allPointsAccepted())
+ if (handlersOnly)
+ return;
+ if (pointerEvent->allPointsAccepted() && !pointerEvent->isReleaseEvent())
+ return;
+
+ // If all points are released and the item is not the grabber, it doesn't get the event.
+ // But if at least one point is still pressed, we might be in a potential gesture-takeover scenario.
+ if (pointerEvent->isReleaseEvent() && !pointerEvent->isUpdateEvent()
+ && !pointerEvent->exclusiveGrabbers().contains(item))
return;
// TODO: unite this mouse point delivery with the synthetic mouse event below
@@ -2464,11 +2474,11 @@ void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo
if (eventAccepted) {
// If the touch was accepted (regardless by whom or in what form),
// update accepted new points.
- bool isPress = pointerEvent->isPressEvent();
+ bool isPressOrRelease = pointerEvent->isPressEvent() || pointerEvent->isReleaseEvent();
for (auto point: qAsConst(touchEvent->touchPoints())) {
auto pointerEventPoint = ptEvent->pointById(point.id());
pointerEventPoint->setAccepted();
- if (isPress)
+ if (isPressOrRelease)
pointerEventPoint->setGrabberItem(item);
}
} else {
diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h
index f66809cc8a..fa39eca9a6 100644
--- a/src/quick/items/qquickwindow_p.h
+++ b/src/quick/items/qquickwindow_p.h
@@ -168,9 +168,9 @@ public:
void deliverPointerEvent(QQuickPointerEvent *);
void deliverTouchEvent(QQuickPointerTouchEvent *);
bool deliverTouchCancelEvent(QTouchEvent *);
- bool deliverPressEvent(QQuickPointerEvent *);
+ bool deliverPressOrReleaseEvent(QQuickPointerEvent *, bool handlersOnly = false);
void deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event);
- void deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent);
+ void deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent, bool handlersOnly = false);
QVector<QQuickItem *> pointerTargets(QQuickItem *, const QPointF &, bool checkMouseButtons, bool checkAcceptsTouch) const;
QVector<QQuickItem *> mergePointerTargets(const QVector<QQuickItem *> &list1, const QVector<QQuickItem *> &list2) const;