aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/items/qquickitem.cpp
diff options
context:
space:
mode:
authorShawn Rutledge <shawn.rutledge@qt.io>2020-07-23 13:56:26 +0200
committerShawn Rutledge <shawn.rutledge@qt.io>2020-09-18 20:56:25 +0200
commita97759a336c597327cb82eebc9f45c793aec32c9 (patch)
tree632bbee8568d38af56974e02df5810afcf48aedc /src/quick/items/qquickitem.cpp
parent39f4d687fc37f48cbc181f42797c42be91b4a345 (diff)
Remove QQuickPointerEvent etc.; deliver QPointerEvents directly
QEventPoint does not have an accessor to get the QPointerEvent that it came from, because that's inconsistent with the idea that QPointerEvent instances are temporary, stack-allocated and movable (the pointer would often be wrong or null, therefore could not be relied upon). So most functions that worked directly with QQuickEventPoint before (which fortunately are still private API) now need to receive the QPointerEvent too, which we choose to pass by pointer. QEventPoint is always passed by reference (const where possible) to be consistent with functions in QPointerEvent that take QEventPoint by reference. QEventPoint::velocity() should be always in scene coordinates now, which saves us the trouble of transforming it to each item's coordinate system during delivery, but means that it will need to be done in handlers or applications sometimes. If we were going to transform it, it would be important to also store the sceneVelocity separately in QEventPoint so that the transformation could be done repeatedly for different items. Task-number: QTBUG-72173 Change-Id: I7ee164d2e6893c4e407fb7d579c75aa32843933a Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
Diffstat (limited to 'src/quick/items/qquickitem.cpp')
-rw-r--r--src/quick/items/qquickitem.cpp165
1 files changed, 138 insertions, 27 deletions
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp
index 8bf5e4f4bf..8e6bb0f224 100644
--- a/src/quick/items/qquickitem.cpp
+++ b/src/quick/items/qquickitem.cpp
@@ -3015,7 +3015,6 @@ void QQuickItemPrivate::derefWindow()
QQuickWindowPrivate *c = QQuickWindowPrivate::get(window);
if (polishScheduled)
c->itemsToPolish.removeOne(q);
- c->removeGrabber(q);
#if QT_CONFIG(cursor)
if (c->cursorItem == q) {
c->cursorItem = nullptr;
@@ -5249,12 +5248,12 @@ void QQuickItemPrivate::deliverShortcutOverrideEvent(QKeyEvent *event)
}
}
-bool QQuickItemPrivate::anyPointerHandlerWants(QQuickEventPoint *point) const
+bool QQuickItemPrivate::anyPointerHandlerWants(const QPointerEvent *event, const QEventPoint &point) const
{
if (!hasPointerHandlers())
return false;
for (QQuickPointerHandler *handler : extra->pointerHandlers) {
- if (handler->wantsEventPoint(point))
+ if (handler->wantsEventPoint(event, point))
return true;
}
return false;
@@ -5267,13 +5266,22 @@ bool QQuickItemPrivate::anyPointerHandlerWants(QQuickEventPoint *point) const
delivery to any handler which is the exclusive grabber of any point within this event
(because delivery to exclusive grabbers is handled separately).
*/
-bool QQuickItemPrivate::handlePointerEvent(QQuickPointerEvent *event, bool avoidExclusiveGrabber)
+bool QQuickItemPrivate::handlePointerEvent(QPointerEvent *event, bool avoidExclusiveGrabber)
{
bool delivered = false;
- QVector<QObject *> &eventDeliveryTargets = QQuickPointerHandlerPrivate::deviceDeliveryTargets(event->device());
if (extra.isAllocated()) {
for (QQuickPointerHandler *handler : extra->pointerHandlers) {
- if ((!avoidExclusiveGrabber || !event->hasExclusiveGrabber(handler)) && !eventDeliveryTargets.contains(handler)) {
+ bool avoidThisHandler = false;
+ if (avoidExclusiveGrabber) {
+ for (auto &p : event->points()) {
+ if (event->exclusiveGrabber(p) == handler) {
+ avoidThisHandler = true;
+ break;
+ }
+ }
+ }
+ if (!avoidThisHandler &&
+ !QQuickPointerHandlerPrivate::deviceDeliveryTargets(event->device()).contains(handler)) {
handler->handlePointerEvent(event);
delivered = true;
}
@@ -7623,6 +7631,8 @@ QQuickPointerHandler *QQuickItemPrivate::effectiveCursorHandler() const
#endif
/*!
+ \obsolete Use QPointerEvent::setExclusiveGrabber()
+
Grabs the mouse input.
This item will receive all mouse events until ungrabMouse() is called.
@@ -7637,18 +7647,21 @@ QQuickPointerHandler *QQuickItemPrivate::effectiveCursorHandler() const
void QQuickItem::grabMouse()
{
Q_D(QQuickItem);
- if (!d->window || d->window->mouseGrabberItem() == this)
+ if (!d->window)
return;
QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(d->window);
- bool fromTouch = windowPriv->isDeliveringTouchAsMouse();
- auto point = fromTouch ?
- windowPriv->pointerEventInstance(windowPriv->touchMouseDevice)->pointById(windowPriv->touchMouseId) :
- windowPriv->pointerEventInstance(QPointingDevice::primaryPointingDevice())->point(0);
- if (point)
- point->setGrabberItem(this);
+ auto eventInDelivery = windowPriv->eventInDelivery();
+ if (!eventInDelivery) {
+ qWarning() << "cannot grab mouse: no event is currently being delivered";
+ return;
+ }
+ auto epd = windowPriv->mousePointData();
+ eventInDelivery->setExclusiveGrabber(epd->eventPoint, this);
}
/*!
+ \obsolete Use QPointerEvent::setExclusiveGrabber()
+
Releases the mouse grab following a call to grabMouse().
Note that this function should only be called when the item wants
@@ -7663,10 +7676,18 @@ void QQuickItem::ungrabMouse()
if (!d->window)
return;
QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(d->window);
- windowPriv->removeGrabber(this, true, windowPriv->isDeliveringTouchAsMouse());
+ auto eventInDelivery = windowPriv->eventInDelivery();
+ if (!eventInDelivery) {
+ // do it the expensive way
+ windowPriv->removeGrabber(this);
+ return;
+ }
+ const auto &eventPoint = windowPriv->mousePointData()->eventPoint;
+ if (eventInDelivery->exclusiveGrabber(eventPoint) != this)
+ return;
+ eventInDelivery->setExclusiveGrabber(eventPoint, this);
}
-
/*!
Returns whether mouse input should exclusively remain with this item.
@@ -7701,32 +7722,31 @@ void QQuickItem::setKeepMouseGrab(bool keep)
}
/*!
+ \obsolete Use QPointerEvent::setExclusiveGrabber().
Grabs the touch points specified by \a ids.
These touch points will be owned by the item until
they are released. Alternatively, the grab can be stolen
by a filtering item like Flickable. Use setKeepTouchGrab()
to prevent the grab from being stolen.
-
- \sa ungrabTouchPoints(), setKeepTouchGrab()
*/
void QQuickItem::grabTouchPoints(const QList<int> &ids)
{
- Q_D(QQuickItem);
- if (!d->window)
+ QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window());
+ auto event = windowPriv->eventInDelivery();
+ if (Q_UNLIKELY(!event)) {
+ qWarning() << "cannot grab: no event is currently being delivered";
return;
- QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(d->window);
- windowPriv->grabTouchPoints(this, ids);
+ }
+ for (auto pt : event->points()) {
+ if (ids.contains(pt.id()))
+ event->setExclusiveGrabber(pt, this);
+ }
}
/*!
+ \obsolete Use QEventPoint::setExclusiveGrabber() instead.
Ungrabs the touch points owned by this item.
-
- \note there is hardly any reason to call this function. It should only be
- called when an item does not want to receive any further events, so no
- move or release events will be delivered after calling this function.
-
- \sa grabTouchPoints()
*/
void QQuickItem::ungrabTouchPoints()
{
@@ -8338,6 +8358,97 @@ QQuickItemLayer *QQuickItemPrivate::layer() const
#endif
}
+/*!
+ \internal
+ Create a modified copy of the given \a event intended for delivery to this
+ item, containing pointers to only the QEventPoint instances that are
+ relevant to this item, and transforming their positions to this item's
+ coordinate system.
+
+ Returns an invalid event with type \l QEvent::None 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
+ \a isFiltering is false.
+
+ When \a 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 QQuickItemPrivate::localizedTouchEvent(const QTouchEvent *event, bool isFiltering)
+{
+ Q_Q(QQuickItem);
+ QList<QEventPoint> touchPoints;
+ QEventPoint::States eventStates;
+
+ bool anyPressOrReleaseInside = false;
+ bool anyStationaryWithModifiedPropertyInside = false;
+ bool anyGrabber = false;
+ for (auto &p : event->points()) {
+ if (p.isAccepted())
+ continue;
+ // include points where item is the grabber
+ auto pointGrabber = event->exclusiveGrabber(p);
+ bool isGrabber = (pointGrabber == q);
+ if (isGrabber)
+ anyGrabber = true;
+ // include points inside the bounds if no other item is the grabber or if the item is filtering
+ const auto localPos = q->mapFromScene(p.scenePosition());
+ bool isInside = q->contains(localPos);
+ bool hasAnotherGrabber = pointGrabber && pointGrabber != q;
+
+ // filtering: (childMouseEventFilter) include points that are grabbed by children of the target item
+ bool grabberIsChild = false;
+ auto parent = qobject_cast<QQuickItem*>(pointGrabber);
+ while (isFiltering && parent) {
+ if (parent == q) {
+ grabberIsChild = true;
+ break;
+ }
+ parent = parent->parentItem();
+ }
+
+ bool filterRelevant = isFiltering && grabberIsChild;
+ if (!(isGrabber || (isInside && (!hasAnotherGrabber || isFiltering)) || filterRelevant))
+ continue;
+ if ((p.state() == QEventPoint::State::Pressed || p.state() == QEventPoint::State::Released) && isInside)
+ anyPressOrReleaseInside = true;
+ QEventPoint pCopy(p);
+ QMutableEventPoint mut = QMutableEventPoint::from(pCopy);
+ if (isInside && mut.stationaryWithModifiedProperty())
+ anyStationaryWithModifiedPropertyInside = true;
+ eventStates |= p.state();
+ mut.setPosition(localPos);
+ touchPoints << mut;
+ }
+
+ // Now touchPoints will have only points which are inside the item.
+ // But if none of them were just pressed inside, and the item has no other reason to care, ignore them anyway.
+ if ((eventStates == QEventPoint::State::Stationary && !anyStationaryWithModifiedPropertyInside) ||
+ touchPoints.isEmpty() || (!anyPressOrReleaseInside && !anyGrabber && !isFiltering))
+ return QTouchEvent(QEvent::None);
+
+ // if all points have the same state, set the event type accordingly
+ QEvent::Type eventType = event->type();
+ switch (eventStates) {
+ case QEventPoint::State::Pressed:
+ eventType = QEvent::TouchBegin;
+ break;
+ case QEventPoint::State::Released:
+ eventType = QEvent::TouchEnd;
+ break;
+ default:
+ eventType = QEvent::TouchUpdate;
+ break;
+ }
+
+ QMutableTouchEvent ret(eventType, event->pointingDevice(), event->modifiers(), touchPoints);
+ ret.setTarget(q);
+ ret.setTimestamp(event->timestamp());
+ ret.accept();
+ return ret;
+}
+
bool QQuickItemPrivate::hasPointerHandlers() const
{
return extra.isAllocated() && !extra->pointerHandlers.isEmpty();