diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp index ee71ea8a76318b2701e615fdf006b147e3da9250..3f072359d281333fc7433ad960db97551dd69e9b 100644 --- a/src/quick/items/qquickflickable.cpp +++ b/src/quick/items/qquickflickable.cpp @@ -1380,7 +1380,7 @@ void QQuickFlickablePrivate::replayDelayedPress() // Use the event handler that will take care of finding the proper item to propagate the event replayingPressEvent = true; - QQuickWindowPrivate::get(w)->deliverMouseEvent(mouseEvent.data()); + QCoreApplication::sendEvent(w, mouseEvent.data()); replayingPressEvent = false; } } diff --git a/src/quick/items/qquickpincharea.cpp b/src/quick/items/qquickpincharea.cpp index 234f78c3806c0b0df52afc6153c180927c519adb..d8478806c61d23b3d914affd00fbf51cff73bbbc 100644 --- a/src/quick/items/qquickpincharea.cpp +++ b/src/quick/items/qquickpincharea.cpp @@ -268,8 +268,6 @@ QQuickPinchArea::QQuickPinchArea(QQuickItem *parent) { Q_D(QQuickPinchArea); d->init(); - setAcceptedMouseButtons(Qt::LeftButton); - setFiltersChildMouseEvents(true); #ifdef Q_OS_OSX setAcceptHoverEvents(true); // needed to enable touch events on mouse hover. #endif @@ -413,6 +411,12 @@ void QQuickPinchArea::updatePinch() QTouchEvent::TouchPoint touchPoint1 = d->touchPoints.at(0); QTouchEvent::TouchPoint touchPoint2 = d->touchPoints.at(d->touchPoints. count() >= 2 ? 1 : 0); + if (touchPoint1.state() == Qt::TouchPointPressed) + d->sceneStartPoint1 = touchPoint1.scenePos(); + + if (touchPoint2.state() == Qt::TouchPointPressed) + d->sceneStartPoint2 = touchPoint2.scenePos(); + QRectF bounds = clipRect(); // Pinch is not started unless there are exactly two touch points // AND one or more of the points has just now been pressed (wasn't pressed already) @@ -421,8 +425,6 @@ void QQuickPinchArea::updatePinch() && (touchPoint1.state() & Qt::TouchPointPressed || touchPoint2.state() & Qt::TouchPointPressed) && bounds.contains(touchPoint1.pos()) && bounds.contains(touchPoint2.pos())) { d->id1 = touchPoint1.id(); - d->sceneStartPoint1 = touchPoint1.scenePos(); - d->sceneStartPoint2 = touchPoint2.scenePos(); d->pinchActivated = true; d->initPinch = true; diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 0233e93443c1d83232a79056bfe57578311018b9..12be33d79096766889848883e48aed7edd9ab17b 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -556,7 +556,7 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e // handler spins the event loop all subsequent moves and releases get lost. touchMouseId = p.id(); itemForTouchPointId[touchMouseId] = item; - QScopedPointer<QMouseEvent> mousePress(touchToMouseEvent(QEvent::MouseButtonPress, p, event, item)); + QScopedPointer<QMouseEvent> mousePress(touchToMouseEvent(QEvent::MouseButtonPress, p, event, item, false)); // Send a single press and see if that's accepted if (!mouseGrabberItem) @@ -575,18 +575,21 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e } if (mousePress->isAccepted() && checkIfDoubleClicked(event->timestamp())) { - QScopedPointer<QMouseEvent> mouseDoubleClick(touchToMouseEvent(QEvent::MouseButtonDblClick, p, event, item)); + QScopedPointer<QMouseEvent> mouseDoubleClick(touchToMouseEvent(QEvent::MouseButtonDblClick, p, event, item, false)); QCoreApplication::sendEvent(item, mouseDoubleClick.data()); event->setAccepted(mouseDoubleClick->isAccepted()); if (mouseDoubleClick->isAccepted()) { + touchMouseIdCandidates.clear(); return true; } else { touchMouseId = -1; } } // The event was accepted, we are done. - if (mousePress->isAccepted()) + if (mousePress->isAccepted()) { + touchMouseIdCandidates.clear(); return true; + } // The event was not accepted but touchMouseId was set. if (touchMouseId != -1) return false; @@ -596,7 +599,7 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e } else if (p.id() == touchMouseId) { if (p.state() & Qt::TouchPointMoved) { if (mouseGrabberItem) { - QScopedPointer<QMouseEvent> me(touchToMouseEvent(QEvent::MouseMove, p, event, mouseGrabberItem)); + QScopedPointer<QMouseEvent> me(touchToMouseEvent(QEvent::MouseMove, p, event, mouseGrabberItem, false)); QCoreApplication::sendEvent(item, me.data()); event->setAccepted(me->isAccepted()); if (me->isAccepted()) { @@ -607,7 +610,7 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e // no grabber, check if we care about mouse hover // FIXME: this should only happen once, not recursively... I'll ignore it just ignore hover now. // hover for touch??? - QScopedPointer<QMouseEvent> me(touchToMouseEvent(QEvent::MouseMove, p, event, item)); + QScopedPointer<QMouseEvent> me(touchToMouseEvent(QEvent::MouseMove, p, event, item, false)); if (lastMousePosition.isNull()) lastMousePosition = me->windowPos(); QPointF last = lastMousePosition; @@ -626,7 +629,7 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e // currently handled point was released touchMouseId = -1; if (mouseGrabberItem) { - QScopedPointer<QMouseEvent> me(touchToMouseEvent(QEvent::MouseButtonRelease, p, event, mouseGrabberItem)); + QScopedPointer<QMouseEvent> me(touchToMouseEvent(QEvent::MouseButtonRelease, p, event, mouseGrabberItem, false)); QCoreApplication::sendEvent(item, me.data()); if (mouseGrabberItem) // might have ungrabbed due to event mouseGrabberItem->ungrabMouse(); @@ -1887,7 +1890,8 @@ void QQuickWindowPrivate::reallyDeliverTouchEvent(QTouchEvent *event) // or some item accepted a point and should receive an update if (newPoints.count() > 0 || updatedPoints.count() > 0) { QSet<int> acceptedNewPoints; - event->setAccepted(deliverTouchPoints(contentItem, event, newPoints, &acceptedNewPoints, &updatedPoints)); + QSet<QQuickItem *> hasFiltered; + event->setAccepted(deliverTouchPoints(contentItem, event, newPoints, &acceptedNewPoints, &updatedPoints, &hasFiltered)); } else event->ignore(); @@ -1898,6 +1902,7 @@ void QQuickWindowPrivate::reallyDeliverTouchEvent(QTouchEvent *event) itemForTouchPointId.remove(touchPoints[i].id()); if (touchPoints[i].id() == touchMouseId) touchMouseId = -1; + touchMouseIdCandidates.remove(touchPoints[i].id()); } } } @@ -1910,7 +1915,9 @@ void QQuickWindowPrivate::reallyDeliverTouchEvent(QTouchEvent *event) } // This function recurses and sends the events to the individual items -bool QQuickWindowPrivate::deliverTouchPoints(QQuickItem *item, QTouchEvent *event, const QList<QTouchEvent::TouchPoint> &newPoints, QSet<int> *acceptedNewPoints, QHash<QQuickItem *, QList<QTouchEvent::TouchPoint> > *updatedPoints) +bool QQuickWindowPrivate::deliverTouchPoints(QQuickItem *item, QTouchEvent *event, const QList<QTouchEvent::TouchPoint> &newPoints, + QSet<int> *acceptedNewPoints, QHash<QQuickItem *, + QList<QTouchEvent::TouchPoint> > *updatedPoints, QSet<QQuickItem *> *hasFiltered) { QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); @@ -1929,7 +1936,7 @@ bool QQuickWindowPrivate::deliverTouchPoints(QQuickItem *item, QTouchEvent *even QQuickItem *child = children.at(ii); if (!child->isEnabled() || !child->isVisible() || QQuickItemPrivate::get(child)->culled) continue; - if (deliverTouchPoints(child, event, newPoints, acceptedNewPoints, updatedPoints)) + if (deliverTouchPoints(child, event, newPoints, acceptedNewPoints, updatedPoints, hasFiltered)) return true; } @@ -1979,7 +1986,7 @@ bool QQuickWindowPrivate::deliverTouchPoints(QQuickItem *item, QTouchEvent *even // with only the subset of TouchPoints which are relevant to that item: that's matchingPoints. QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); transformTouchPoints(matchingPoints, itemPrivate->windowToItemTransform()); - deliverMatchingPointsToItem(item, event, acceptedNewPoints, matchingNewPoints, matchingPoints); + deliverMatchingPointsToItem(item, event, acceptedNewPoints, matchingNewPoints, matchingPoints, hasFiltered); } // record the fact that this item has been visited already @@ -1993,7 +2000,7 @@ bool QQuickWindowPrivate::deliverTouchPoints(QQuickItem *item, QTouchEvent *even // only the points that are relevant for this item. Thus the need for // matchingPoints to already be that set of interesting points. // They are all pre-transformed, too. -bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QTouchEvent *event, QSet<int> *acceptedNewPoints, const QSet<int> &matchingNewPoints, const QList<QTouchEvent::TouchPoint> &matchingPoints) +bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QTouchEvent *event, QSet<int> *acceptedNewPoints, const QSet<int> &matchingNewPoints, const QList<QTouchEvent::TouchPoint> &matchingPoints, QSet<QQuickItem *> *hasFiltered) { QScopedPointer<QTouchEvent> touchEvent(touchEventWithPoints(*event, matchingPoints)); touchEvent.data()->setTarget(item); @@ -2001,7 +2008,7 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QTouchEv // First check whether the parent wants to be a filter, // and if the parent accepts the event we are done. - if (sendFilteredTouchEvent(item->parentItem(), item, event)) { + if (sendFilteredTouchEvent(item->parentItem(), item, event, hasFiltered)) { // If the touch was accepted (regardless by whom or in what form), // update acceptedNewPoints foreach (int id, matchingNewPoints) @@ -2021,7 +2028,7 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QTouchEv QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); if (!touchEventAccepted && (itemPrivate->acceptedMouseButtons() & Qt::LeftButton)) { // send mouse event - event->setAccepted(translateTouchToMouse(item, event)); + event->setAccepted(translateTouchToMouse(item, touchEvent.data())); if (event->isAccepted()) { touchEventAccepted = true; } @@ -2271,7 +2278,7 @@ QQuickItem *QQuickWindowPrivate::findCursorItem(QQuickItem *item, const QPointF } #endif -bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QTouchEvent *event) +bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QTouchEvent *event, QSet<QQuickItem *> *hasFiltered) { if (!target) return false; @@ -2279,13 +2286,14 @@ bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem bool filtered = false; QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(target); - if (targetPrivate->filtersChildMouseEvents) { + if (targetPrivate->filtersChildMouseEvents && !hasFiltered->contains(target)) { + hasFiltered->insert(target); QScopedPointer<QTouchEvent> targetEvent(touchEventForItemBounds(target, *event)); if (!targetEvent->touchPoints().isEmpty()) { - QVector<int> touchIds; - for (int i = 0; i < event->touchPoints().size(); ++i) - touchIds.append(event->touchPoints().at(i).id()); if (target->childMouseEventFilter(item, targetEvent.data())) { + QVector<int> touchIds; + for (int i = 0; i < targetEvent->touchPoints().size(); ++i) + touchIds.append(targetEvent->touchPoints().at(i).id()); target->grabTouchPoints(touchIds); if (mouseGrabberItem) { mouseGrabberItem->ungrabMouse(); @@ -2294,38 +2302,54 @@ bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem filtered = true; } - // Only offer a mouse event to the filter if we have one point - if (targetEvent->touchPoints().count() == 1) { + for (int i = 0; i < targetEvent->touchPoints().size(); ++i) { + const QTouchEvent::TouchPoint &tp = targetEvent->touchPoints().at(i); + QEvent::Type t; - const QTouchEvent::TouchPoint &tp = targetEvent->touchPoints().first(); switch (tp.state()) { case Qt::TouchPointPressed: t = QEvent::MouseButtonPress; + if (touchMouseId == -1) { + // We don't want to later filter touches as a mouse event if they were pressed + // while a touchMouseId was already active. + // Remember this touch as a potential to become the touchMouseId. + touchMouseIdCandidates.insert(tp.id()); + } break; case Qt::TouchPointReleased: t = QEvent::MouseButtonRelease; break; + case Qt::TouchPointStationary: + continue; default: - // move or stationary t = QEvent::MouseMove; break; } - // targetEvent is already transformed wrt local position, velocity, etc. - QScopedPointer<QMouseEvent> mouseEvent(touchToMouseEvent(t, targetEvent->touchPoints().first(), event, item, false)); - if (target->childMouseEventFilter(item, mouseEvent.data())) { - itemForTouchPointId[tp.id()] = target; - touchMouseId = tp.id(); - target->grabMouse(); - filtered = true; + // Only deliver mouse event if it is the touchMouseId or it could become the touchMouseId + if ((touchMouseIdCandidates.contains(tp.id()) && touchMouseId == -1) || touchMouseId == tp.id()) { + // targetEvent is already transformed wrt local position, velocity, etc. + QScopedPointer<QMouseEvent> mouseEvent(touchToMouseEvent(t, tp, event, item, false)); + if (target->childMouseEventFilter(item, mouseEvent.data())) { + if (t != QEvent::MouseButtonRelease) { + itemForTouchPointId[tp.id()] = target; + touchMouseId = tp.id(); + target->grabMouse(); + } + touchMouseIdCandidates.clear(); + filtered = true; + } + // Only one event can be filtered as a mouse event. + break; } } } } - return sendFilteredTouchEvent(target->parentItem(), item, event) || filtered; + + return sendFilteredTouchEvent(target->parentItem(), item, event, hasFiltered) || filtered; } -bool QQuickWindowPrivate::sendFilteredMouseEvent(QQuickItem *target, QQuickItem *item, QEvent *event) +bool QQuickWindowPrivate::sendFilteredMouseEvent(QQuickItem *target, QQuickItem *item, QEvent *event, QSet<QQuickItem *> *hasFiltered) { if (!target) return false; @@ -2333,11 +2357,13 @@ bool QQuickWindowPrivate::sendFilteredMouseEvent(QQuickItem *target, QQuickItem bool filtered = false; QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(target); - if (targetPrivate->filtersChildMouseEvents) + if (targetPrivate->filtersChildMouseEvents && !hasFiltered->contains(target)) { + hasFiltered->insert(target); if (target->childMouseEventFilter(item, event)) filtered = true; + } - return sendFilteredMouseEvent(target->parentItem(), item, event) || filtered; + return sendFilteredMouseEvent(target->parentItem(), item, event, hasFiltered) || filtered; } bool QQuickWindowPrivate::dragOverThreshold(qreal d, Qt::Axis axis, QMouseEvent *event, int startDragThreshold) @@ -2478,18 +2504,22 @@ bool QQuickWindow::sendEvent(QQuickItem *item, QEvent *e) case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: case QEvent::MouseButtonDblClick: - case QEvent::MouseMove: - // XXX todo - should sendEvent be doing this? how does it relate to forwarded events? - if (!d->sendFilteredMouseEvent(item->parentItem(), item, e)) { - // accept because qml items by default accept and have to explicitly opt out of accepting - e->accept(); - QCoreApplication::sendEvent(item, e); + case QEvent::MouseMove: { + // XXX todo - should sendEvent be doing this? how does it relate to forwarded events? + QSet<QQuickItem *> hasFiltered; + if (!d->sendFilteredMouseEvent(item->parentItem(), item, e, &hasFiltered)) { + // accept because qml items by default accept and have to explicitly opt out of accepting + e->accept(); + QCoreApplication::sendEvent(item, e); + } } break; - case QEvent::UngrabMouse: - if (!d->sendFilteredMouseEvent(item->parentItem(), item, e)) { - e->accept(); - item->mouseUngrabEvent(); + case QEvent::UngrabMouse: { + QSet<QQuickItem *> hasFiltered; + if (!d->sendFilteredMouseEvent(item->parentItem(), item, e, &hasFiltered)) { + e->accept(); + item->mouseUngrabEvent(); + } } break; #ifndef QT_NO_WHEELEVENT @@ -2511,8 +2541,10 @@ bool QQuickWindow::sendEvent(QQuickItem *item, QEvent *e) break; case QEvent::TouchBegin: case QEvent::TouchUpdate: - case QEvent::TouchEnd: - d->sendFilteredTouchEvent(item->parentItem(), item, static_cast<QTouchEvent *>(e)); + case QEvent::TouchEnd: { + QSet<QQuickItem*> hasFiltered; + d->sendFilteredTouchEvent(item->parentItem(), item, static_cast<QTouchEvent *>(e), &hasFiltered); + } break; default: break; diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 8f0696914e33e76cd6d2c39818b8c7475617c217..93ceefe5af8a3fadf36686bfa80e791ec2c9db0a 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -137,21 +137,21 @@ public: static QMouseEvent *cloneMouseEvent(QMouseEvent *event, QPointF *transformedLocalPos = 0); bool deliverInitialMousePressEvent(QQuickItem *, QMouseEvent *); bool deliverMouseEvent(QMouseEvent *); - bool sendFilteredMouseEvent(QQuickItem *, QQuickItem *, QEvent *); + bool sendFilteredMouseEvent(QQuickItem *, QQuickItem *, QEvent *, QSet<QQuickItem *> *); #ifndef QT_NO_WHEELEVENT bool deliverWheelEvent(QQuickItem *, QWheelEvent *); #endif bool deliverTouchPoints(QQuickItem *, QTouchEvent *, const QList<QTouchEvent::TouchPoint> &, QSet<int> *, - QHash<QQuickItem *, QList<QTouchEvent::TouchPoint> > *); + QHash<QQuickItem *, QList<QTouchEvent::TouchPoint> > *, QSet<QQuickItem*> *filtered); void deliverTouchEvent(QTouchEvent *); void reallyDeliverTouchEvent(QTouchEvent *); bool deliverTouchCancelEvent(QTouchEvent *); void flushDelayedTouchEvent(); bool deliverHoverEvent(QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, bool &accepted); - bool deliverMatchingPointsToItem(QQuickItem *item, QTouchEvent *event, QSet<int> *acceptedNewPoints, const QSet<int> &matchingNewPoints, const QList<QTouchEvent::TouchPoint> &matchingPoints); + bool deliverMatchingPointsToItem(QQuickItem *item, QTouchEvent *event, QSet<int> *acceptedNewPoints, const QSet<int> &matchingNewPoints, const QList<QTouchEvent::TouchPoint> &matchingPoints, QSet<QQuickItem*> *filtered); QTouchEvent *touchEventForItemBounds(QQuickItem *target, const QTouchEvent &originalEvent); QTouchEvent *touchEventWithPoints(const QTouchEvent &event, const QList<QTouchEvent::TouchPoint> &newPoints); - bool sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QTouchEvent *event); + bool sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QTouchEvent *event, QSet<QQuickItem*> *filtered); bool sendHoverEvent(QEvent::Type, QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, bool accepted); bool clearHover(); @@ -239,6 +239,7 @@ public: // Keeps track of which touch point (int) was last accepted by which item QHash<int, QQuickItem *> itemForTouchPointId; + QSet<int> touchMouseIdCandidates; mutable QQuickWindowIncubationController *incubationController; diff --git a/tests/auto/quick/touchmouse/tst_touchmouse.cpp b/tests/auto/quick/touchmouse/tst_touchmouse.cpp index 1d947b4d8c7f0773c57e236238c03dd4d31d5c65..f39a22e131373a01c8b005d59213c067af8615cd 100644 --- a/tests/auto/quick/touchmouse/tst_touchmouse.cpp +++ b/tests/auto/quick/touchmouse/tst_touchmouse.cpp @@ -661,6 +661,7 @@ void tst_TouchMouse::buttonOnDelayedPressFlickable() // but we'll get the delayed mouse press after a delay QTRY_COMPARE(eventItem1->eventList.size(), 1); QCOMPARE(eventItem1->eventList.at(0).type, QEvent::MouseButtonPress); + QCOMPARE(filteredEventList.count(), 1); // eventItem1 should have the mouse grab, and have moved the itemForTouchPointId // for the touchMouseId to the new grabber. @@ -690,8 +691,9 @@ void tst_TouchMouse::buttonOnDelayedPressFlickable() QTest::touchEvent(window, device).release(0, p3, window); QQuickTouchUtils::flush(window); - // We should not have received any synthesised mouse events from Qt gui. - QCOMPARE(filteredEventList.count(), 0); + // We should not have received any synthesised mouse events from Qt gui, + // just the delayed press. + QCOMPARE(filteredEventList.count(), 1); delete window; } @@ -916,6 +918,7 @@ void tst_TouchMouse::pinchOnFlickable() p1 -= QPoint(10, 0); p2 += QPoint(10, 0); pinchSequence.move(0, p1, window).move(1, p2, window).commit(); + QVERIFY(!flickable->isDragging()); QQuickTouchUtils::flush(window); pinchSequence.release(0, p1, window).release(1, p2, window).commit(); QQuickTouchUtils::flush(window);