From 6ba732d5f68830c14e313009f67bca58b015e0d4 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 10 Aug 2017 12:05:41 +0200 Subject: PinchHandler: add native pinch gesture support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit macOS generates QNativeGestureEvents for 2-finger trackpad zoom and rotation gestures. Now PinchHandler will react to them in the same way that PinchArea does. Change-Id: I4c7dab1d3561d20897e3671f4eb68d01ea06b9bd Reviewed-by: Jan Arve Sæther --- src/quick/items/qquickwindow.cpp | 45 ++++++++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 11 deletions(-) (limited to 'src/quick/items/qquickwindow.cpp') diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index bd1a5076fd..8c7cbe5117 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1915,10 +1915,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 +2174,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(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(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 +2223,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(event)->device()); - ev = pointerEventInstance(dev); break; // TODO tablet event types + case QEvent::NativeGesture: + dev = QQuickPointerDevice::touchDevice(static_cast(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) -- cgit v1.2.3 From fdcaf8013e7d2701824024a11ef159b2558ab6a6 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Wed, 27 Sep 2017 11:17:56 +0200 Subject: QQuickWindowPrivate::deliverKeyEvent: stop using QQW::sendEvent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To uphold the deprecation, we have to stop using it ourselves. Fixes the warning. Change-Id: Ia9ba24086fbc3a336a950b63ea2745a26b4f2866 Reviewed-by: Jan Arve Sæther --- src/quick/items/qquickwindow.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'src/quick/items/qquickwindow.cpp') diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 8c7cbe5117..9f3f8531e4 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) -- cgit v1.2.3 From 2dd530ca35032465d4e928aa7c8f29563eb0c5da Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Wed, 4 Oct 2017 11:54:53 +0200 Subject: pre-accept the mouse event before childMouseEventFilter; grab after MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In 5.9 a typical filtering sequence would be deliverPressEvent -> deliverMatchingPointsToItem -> QQuickWindow::sendEvent -> sendFilteredMouseEvent -> Test::childMouseEventFilter, let's say it returns true; then because the event is accepted, deliverMatchingPointsToItem grabs the mouse. In 5.10, we rather do deliverPressOrReleaseEvent -> sendFilteredPointerEvent -> sendFilteredPointerEventImpl -> Test::childMouseEventFilter, which returns true; and in this case, setGrabberItem was missing until now. In case of touch rather than mouse, it grabs the touchpoints in this kind of scenario. Also made the failsafe more reliable to ensure that no grabs are retained after release (after seeing that one failing autotest can cause failures in subsequent tests). Done-with: Jan-Arve Sæther Task-number: QTBUG-62631 Task-number: QTBUG-62549 Task-number: QTBUG-62628 Change-Id: I16dafc9aa0de2fc163c524f7f5b109f82d7e84fd Reviewed-by: Jan Arve Sæther --- src/quick/items/qquickwindow.cpp | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) (limited to 'src/quick/items/qquickwindow.cpp') diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 9f3f8531e4..c15079b3d7 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1745,9 +1745,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, @@ -2263,6 +2260,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 { @@ -2768,10 +2768,20 @@ bool QQuickWindowPrivate::sendFilteredPointerEventImpl(QQuickPointerEvent *event if (receiver->acceptedMouseButtons()) { QPointF localPos = receiver->mapFromScene(pme->point(0)->scenePosition()); QMouseEvent *me = pme->asMouseEvent(localPos); + 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 if (QQuickPointerTouchEvent *pte = event->asPointerTouchEvent()) { @@ -2789,7 +2799,6 @@ bool QQuickWindowPrivate::sendFilteredPointerEventImpl(QQuickPointerEvent *event 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()) { @@ -2797,7 +2806,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 { @@ -2838,7 +2849,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 grab(handler, pointerEventPoint); -- cgit v1.2.3 From 6b6ee829c7e0df302eba30ce1f96302c5661279c Mon Sep 17 00:00:00 2001 From: Jan Arve Saether Date: Wed, 4 Oct 2017 11:49:02 +0200 Subject: Fix touch points delivered to the filtering parent The touchpoints delivered to the filtering parent should be the ones that would be delivered to the original receiver (child item). That is the intent of filtering. Task-number: QTBUG-62628 Change-Id: I7ed965c741aaec362f07d65cd6d0789c82ba71e3 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/quick/items/qquickwindow.cpp') diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index c15079b3d7..4c79c40164 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2793,7 +2793,7 @@ bool QQuickWindowPrivate::sendFilteredPointerEventImpl(QQuickPointerEvent *event #endif if (acceptsTouchEvents || receiver->acceptedMouseButtons()) { // get a touch event customized for delivery to filteringParent - QScopedPointer filteringParentTouchEvent(pte->touchEventForItem(filteringParent, true)); + QScopedPointer filteringParentTouchEvent(pte->touchEventForItem(receiver, true)); if (filteringParentTouchEvent) { QVarLengthArray, 32> passiveGrabsToCancel; if (filteringParent->childMouseEventFilter(receiver, filteringParentTouchEvent.data())) { -- cgit v1.2.3 From 6577609b5fb697e0e779f325772087e431877f7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Arve=20S=C3=A6ther?= Date: Wed, 11 Oct 2017 17:11:06 +0200 Subject: Undo pre-accept state if the event was not filtered Since the accepted state should only be considered if an event got filtered, we should make sure that the accepted state gets restored back to what it was before it was pre-accepted if the event was not filtered. This fixes a regression introduced in Qt Location Change-Id: I9cf344c43184d3890ea2d4eff4144a0469b33e7d Task-number: QTBUG-63636 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickwindow.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/quick/items/qquickwindow.cpp') diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 4c79c40164..caa128d6e4 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2768,6 +2768,7 @@ 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)) { @@ -2782,6 +2783,9 @@ bool QQuickWindowPrivate::sendFilteredPointerEventImpl(QQuickPointerEvent *event pme->point(0)->setGrabberItem(receiver); } } + } else { + // Restore accepted state if the event was not filtered. + me->setAccepted(wasAccepted); } } } else if (QQuickPointerTouchEvent *pte = event->asPointerTouchEvent()) { -- cgit v1.2.3 From 32b73196594ade358e7b419b0a7563abcfe7266e Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 26 Sep 2017 14:14:26 +0200 Subject: QQuickWindow: if an Item has PointerHandlers it's a delivery target MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit even if it does not accept mouse or touch itself. Change-Id: I4f1a05231c0ff1e89b8f7f28f0760b3af49c78a8 Reviewed-by: Jan Arve Sæther --- src/quick/items/qquickwindow.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'src/quick/items/qquickwindow.cpp') diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index caa128d6e4..65d69a3bcf 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2301,10 +2301,12 @@ QVector 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; -- cgit v1.2.3 From 5cb76fb3704947cfc4d575695b137460ecc8bbd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Arve=20S=C3=A6ther?= Date: Thu, 19 Oct 2017 14:34:47 +0200 Subject: Fix a bug with TapHandler+DragHandler on top of Flickable If an item that had a TapHandler and a DragHandler was placed inside a Flickable it would call sendFilteredPointerEvent() for each of the handlers, even if they were siblings. This is not ideal, and because of a mechanism in flickable it even caused the DragHandler to not drag the item as expected. This is because of a mechanism in Flickable where the value returned is actually derived from the previous call to filterMouseEvent() (stored in member variable stealGrab). Below are the relevant steps it went through when the mouse drag exceeded the drag threshold (mouse pointer started on the item): Precondition: QQuickFlickablePrivate::stealMouse == false 1. Mouse Drag (which exceeded drag threshold) 2. sendFilteredPointerEvent(tapHandler->parentItem()) returns false. (but since it moved beyond threshold, it would set stealGrab-> true). 3. tapHandler->handlePointerEvent() declined and ungrabbed (due to DragThreshold gesture policy). 4. sendFilteredPointerEvent(dragHandler->parentItem()) returns true (because the previous call to sendFilteredPointerEvent() set stealGrab to true). As a consequence of (4), dragHandler->handlePointerEvent() was not called, and the flickable would start to flick instead of the item starting to drag. Change-Id: Ia99eae91cad0903ebbaedaaafcdf92a25705a922 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickwindow.cpp | 49 +++++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 16 deletions(-) (limited to 'src/quick/items/qquickwindow.cpp') diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 65d69a3bcf..45c14f10a1 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1669,6 +1669,36 @@ QMouseEvent *QQuickWindowPrivate::cloneMouseEvent(QMouseEvent *event, QPointF *t return me; } +void QQuickWindowPrivate::deliverToPassiveGrabbers(const QVector > &passiveGrabbers, + QQuickPointerEvent *pointerEvent) +{ + const QVector &eventDeliveryTargets = pointerEvent->device()->eventDeliveryTargets(); + QVarLengthArray, 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 &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(par, alreadyFiltered); + } + if (!alreadyFiltered) + handler->handlePointerEvent(pointerEvent); + } + } +} + + + void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEvent) { auto point = pointerEvent->point(0); @@ -1717,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 &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 targetItems = pointerTargets(contentItem, point->scenePosition(), false, false); @@ -2398,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 &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 -- cgit v1.2.3