From 5119ae6d364a5ac738894d0e60131f21eaf403f2 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Tue, 16 Feb 2010 13:28:11 +0100 Subject: Added a new flag to fine-tune gesture propagation policy When a graphicsobject subscribes to a gesture using the IgnoredGesturesPropagateToParent flag, normal propagation rules do not apply to the gesture, and instead all gestures of the given type that are started over the item will propagate to parent items only. Task-number: QTBUG-7400 Reviewed-by: Andreas --- src/corelib/global/qnamespace.h | 3 +- src/gui/graphicsview/qgraphicsscene.cpp | 78 +++++++++++++++++++++++++++--- tests/auto/gestures/tst_gestures.cpp | 86 +++++++++++++++++++++++++++++++++ 3 files changed, 160 insertions(+), 7 deletions(-) diff --git a/src/corelib/global/qnamespace.h b/src/corelib/global/qnamespace.h index 35ff8e7..6ee8ae9 100644 --- a/src/corelib/global/qnamespace.h +++ b/src/corelib/global/qnamespace.h @@ -1725,7 +1725,8 @@ public: enum GestureFlag { DontStartGestureOnChildren = 0x01, - ReceivePartialGestures = 0x02 + ReceivePartialGestures = 0x02, + IgnoredGesturesPropagateToParent = 0x04 }; Q_DECLARE_FLAGS(GestureFlags, GestureFlag) diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp index 9a36d46..3280e86 100644 --- a/src/gui/graphicsview/qgraphicsscene.cpp +++ b/src/gui/graphicsview/qgraphicsscene.cpp @@ -5936,6 +5936,9 @@ void QGraphicsScenePrivate::gestureEventHandler(QGestureEvent *event) typedef QHash > GesturesPerItem; GesturesPerItem gesturesPerItem; + // gestures that are only supposed to propagate to parent items. + QSet parentPropagatedGestures; + QSet startedGestures; foreach (QGesture *gesture, allGestures) { QGraphicsObject *target = gestureTargets.value(gesture, 0); @@ -5946,6 +5949,10 @@ void QGraphicsScenePrivate::gestureEventHandler(QGestureEvent *event) startedGestures.insert(gesture); } else { gesturesPerItem[target].append(gesture); + Qt::GestureFlags flags = + target->QGraphicsItem::d_func()->gestureContext.value(gesture->gestureType()); + if (flags & Qt::IgnoredGesturesPropagateToParent) + parentPropagatedGestures.insert(gesture); } } @@ -6035,6 +6042,10 @@ void QGraphicsScenePrivate::gestureEventHandler(QGestureEvent *event) Q_ASSERT(!gestureTargets.contains(g)); gestureTargets.insert(g, receiver); gesturesPerItem[receiver].append(g); + Qt::GestureFlags flags = + receiver->QGraphicsItem::d_func()->gestureContext.value(g->gestureType()); + if (flags & Qt::IgnoredGesturesPropagateToParent) + parentPropagatedGestures.insert(g); } it = ignoredConflictedGestures.begin(); e = ignoredConflictedGestures.end(); @@ -6044,13 +6055,17 @@ void QGraphicsScenePrivate::gestureEventHandler(QGestureEvent *event) Q_ASSERT(!gestureTargets.contains(g)); gestureTargets.insert(g, receiver); gesturesPerItem[receiver].append(g); + Qt::GestureFlags flags = + receiver->QGraphicsItem::d_func()->gestureContext.value(g->gestureType()); + if (flags & Qt::IgnoredGesturesPropagateToParent) + parentPropagatedGestures.insert(g); } DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:" << "Started gestures:" << normalGestures.keys() << "All gestures:" << gesturesPerItem.values(); - // deliver all events + // deliver all gesture events QList alreadyIgnoredGestures; QHash > itemIgnoredGestures; QList targetItems = gesturesPerItem.keys(); @@ -6070,9 +6085,26 @@ void QGraphicsScenePrivate::gestureEventHandler(QGestureEvent *event) foreach(QGesture *g, alreadyIgnoredGestures) { QMap::iterator contextit = gid->gestureContext.find(g->gestureType()); - bool deliver = contextit != gid->gestureContext.end() && - (g->state() == Qt::GestureStarted || - (contextit.value() & Qt::ReceivePartialGestures)); + bool deliver = false; + if (contextit != gid->gestureContext.end()) { + if (g->state() == Qt::GestureStarted) { + deliver = true; + } else { + const Qt::GestureFlags flags = contextit.value(); + if (flags & Qt::ReceivePartialGestures) { + QGraphicsObject *originalTarget = gestureTargets.value(g); + Q_ASSERT(originalTarget); + QGraphicsItemPrivate *otd = originalTarget->QGraphicsItem::d_func(); + const Qt::GestureFlags originalTargetFlags = otd->gestureContext.value(g->gestureType()); + if (originalTargetFlags & Qt::IgnoredGesturesPropagateToParent) { + // only deliver to parents of the original target item + deliver = item->isAncestorOf(originalTarget); + } else { + deliver = true; + } + } + } + } if (deliver) gestures += g; } @@ -6100,18 +6132,52 @@ void QGraphicsScenePrivate::gestureEventHandler(QGestureEvent *event) << "item has ignored the event, will propagate." << item << ignoredGestures; itemIgnoredGestures[item] += ignoredGestures; + alreadyIgnoredGestures = ignoredGestures.toList(); + + // remove gestures that are supposed to be propagated to + // parent items only. + QSet parentGestures; + for (QSet::iterator it = ignoredGestures.begin(); + it != ignoredGestures.end();) { + if (parentPropagatedGestures.contains(*it)) { + parentGestures.insert(*it); + it = ignoredGestures.erase(it); + } else { + ++it; + } + } + + QSet itemsSet = targetItems.toSet(); + + foreach(QGesture *g, parentGestures) { + // get the original target for the gesture + QGraphicsItem *item = gestureTargets.value(g, 0); + Q_ASSERT(item); + const Qt::GestureType gestureType = g->gestureType(); + // iterate through parent items of the original gesture + // target item and collect potential receivers + do { + if (QGraphicsObject *obj = item->toGraphicsObject()) { + if (item->d_func()->gestureContext.contains(gestureType)) + itemsSet.insert(obj); + } + if (item->isPanel()) + break; + } while ((item = item->parentItem())); + } + QMap conflictedGestures; QList > itemsForConflictedGestures; QHash normalGestures; getGestureTargets(ignoredGestures, viewport, &conflictedGestures, &itemsForConflictedGestures, &normalGestures); - QSet itemsSet = targetItems.toSet(); for (int k = 0; k < itemsForConflictedGestures.size(); ++k) itemsSet += itemsForConflictedGestures.at(k).toSet(); + targetItems = itemsSet.toList(); + qSort(targetItems.begin(), targetItems.end(), qt_closestItemFirst); - alreadyIgnoredGestures = conflictedGestures.values(); DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:" << "new targets:" << targetItems; i = -1; // start delivery again diff --git a/tests/auto/gestures/tst_gestures.cpp b/tests/auto/gestures/tst_gestures.cpp index 952136b..e6de590 100644 --- a/tests/auto/gestures/tst_gestures.cpp +++ b/tests/auto/gestures/tst_gestures.cpp @@ -333,6 +333,7 @@ private slots: void unregisterRecognizer(); void autoCancelGestures(); void autoCancelGestures2(); + void graphicsViewParentPropagation(); }; tst_Gestures::tst_Gestures() @@ -698,6 +699,9 @@ public: bool acceptGestureOverride; QSet ignoredGestures; + QSet ignoredStartedGestures; + QSet ignoredUpdatedGestures; + QSet ignoredFinishedGestures; QRectF size; int instanceNumber; @@ -710,6 +714,9 @@ public: events.clear(); overrideEvents.clear(); ignoredGestures.clear(); + ignoredStartedGestures.clear(); + ignoredUpdatedGestures.clear(); + ignoredFinishedGestures.clear(); } protected: @@ -732,6 +739,24 @@ protected: QGestureEvent *e = static_cast(event); foreach(Qt::GestureType type, ignoredGestures) e->ignore(e->gesture(type)); + foreach(QGesture *g, e->gestures()) { + switch (g->state()) { + case Qt::GestureStarted: + if (ignoredStartedGestures.contains(g->gestureType())) + e->ignore(g); + break; + case Qt::GestureUpdated: + if (ignoredUpdatedGestures.contains(g->gestureType())) + e->ignore(g); + break; + case Qt::GestureFinished: + if (ignoredFinishedGestures.contains(g->gestureType())) + e->ignore(g); + break; + default: + break; + } + } } else if (event->type() == QEvent::GestureOverride) { ++gestureOverrideEventsReceived; eventsPtr = &overrideEvents; @@ -1471,5 +1496,66 @@ void tst_Gestures::autoCancelGestures2() QCOMPARE(parent->events.all.count(), 2); } +void tst_Gestures::graphicsViewParentPropagation() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + view.setWindowFlags(Qt::X11BypassWindowManagerHint); + + GestureItem *item0 = new GestureItem("item0"); + scene.addItem(item0); + item0->setPos(0, 0); + item0->grabGesture(CustomGesture::GestureType); + item0->setZValue(1); + + GestureItem *item1 = new GestureItem("item1"); + scene.addItem(item1); + item1->setPos(0, 0); + item1->setZValue(5); + + GestureItem *item1_c1 = new GestureItem("item1_child1"); + item1_c1->setParentItem(item1); + item1_c1->setPos(0, 0); + + GestureItem *item1_c1_c1 = new GestureItem("item1_child1_child1"); + item1_c1_c1->setParentItem(item1_c1); + item1_c1_c1->setPos(0, 0); + + view.show(); + QTest::qWaitForWindowShown(&view); + view.ensureVisible(scene.sceneRect()); + + view.viewport()->grabGesture(CustomGesture::GestureType, Qt::DontStartGestureOnChildren); + item0->grabGesture(CustomGesture::GestureType, Qt::ReceivePartialGestures | Qt::IgnoredGesturesPropagateToParent); + item1->grabGesture(CustomGesture::GestureType, Qt::ReceivePartialGestures | Qt::IgnoredGesturesPropagateToParent); + item1_c1->grabGesture(CustomGesture::GestureType, Qt::IgnoredGesturesPropagateToParent); + item1_c1_c1->grabGesture(CustomGesture::GestureType, Qt::ReceivePartialGestures | Qt::IgnoredGesturesPropagateToParent); + + item0->ignoredUpdatedGestures << CustomGesture::GestureType; + item0->ignoredFinishedGestures << CustomGesture::GestureType; + item1->ignoredUpdatedGestures << CustomGesture::GestureType; + item1->ignoredFinishedGestures << CustomGesture::GestureType; + item1_c1->ignoredUpdatedGestures << CustomGesture::GestureType; + item1_c1->ignoredFinishedGestures << CustomGesture::GestureType; + item1_c1_c1->ignoredUpdatedGestures << CustomGesture::GestureType; + item1_c1_c1->ignoredFinishedGestures << CustomGesture::GestureType; + + static const int TotalGestureEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialStartedThreshold + 1; + + CustomEvent event; + event.hotSpot = mapToGlobal(QPointF(10, 10), item1_c1, &view); + event.hasHotSpot = true; + sendCustomGesture(&event, item0, &scene); + + QCOMPARE(item1_c1_c1->gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(item1_c1_c1->gestureOverrideEventsReceived, 1); + QCOMPARE(item1_c1->gestureEventsReceived, 0); + QCOMPARE(item1_c1->gestureOverrideEventsReceived, 1); + QCOMPARE(item1->gestureEventsReceived, TotalGestureEventsCount-1); + QCOMPARE(item1->gestureOverrideEventsReceived, 1); + QCOMPARE(item0->gestureEventsReceived, 0); + QCOMPARE(item0->gestureOverrideEventsReceived, 1); +} + QTEST_MAIN(tst_Gestures) #include "tst_gestures.moc" -- cgit v0.12