diff options
author | Denis Dzyubenko <denis.dzyubenko@nokia.com> | 2009-10-15 19:00:24 (GMT) |
---|---|---|
committer | Denis Dzyubenko <denis.dzyubenko@nokia.com> | 2009-10-22 14:59:18 (GMT) |
commit | 0b61c5e284462376afab15ac9189d759b859ec46 (patch) | |
tree | d959820f8e4f3664d45ee971fee508422ab116ee /src | |
parent | c5c1b878891b5ace5a71b95ea62229e26722fdba (diff) | |
download | Qt-0b61c5e284462376afab15ac9189d759b859ec46.zip Qt-0b61c5e284462376afab15ac9189d759b859ec46.tar.gz Qt-0b61c5e284462376afab15ac9189d759b859ec46.tar.bz2 |
Improving gesture event delivery for widgets.
Reviewed-by: trustme
Diffstat (limited to 'src')
-rw-r--r-- | src/gui/kernel/qapplication.cpp | 1 | ||||
-rw-r--r-- | src/gui/kernel/qevent.h | 2 | ||||
-rw-r--r-- | src/gui/kernel/qgesturemanager.cpp | 214 | ||||
-rw-r--r-- | src/gui/kernel/qgesturemanager_p.h | 12 |
4 files changed, 148 insertions, 81 deletions
diff --git a/src/gui/kernel/qapplication.cpp b/src/gui/kernel/qapplication.cpp index aee8afc..af1c1c8 100644 --- a/src/gui/kernel/qapplication.cpp +++ b/src/gui/kernel/qapplication.cpp @@ -4204,6 +4204,7 @@ bool QApplication::notify(QObject *receiver, QEvent *e) w = w->parentWidget(); } gestureEvent->m_accept = eventAccepted; + gestureEvent->gestures_ = allGestures; } else { res = d->notify_helper(receiver, e); } diff --git a/src/gui/kernel/qevent.h b/src/gui/kernel/qevent.h index 6cba5fb..1ba2d41 100644 --- a/src/gui/kernel/qevent.h +++ b/src/gui/kernel/qevent.h @@ -856,6 +856,8 @@ public: private: QList<QGesture *> gestures_; QWidget *widget_; + + friend class QApplication; }; QT_END_NAMESPACE diff --git a/src/gui/kernel/qgesturemanager.cpp b/src/gui/kernel/qgesturemanager.cpp index 8928d1c..f8e1e49 100644 --- a/src/gui/kernel/qgesturemanager.cpp +++ b/src/gui/kernel/qgesturemanager.cpp @@ -122,7 +122,7 @@ QGesture *QGestureManager::getState(QObject *object, Qt::GestureType type) Q_ASSERT(qobject_cast<QGraphicsObject *>(object)); } - QWeakPointer<QGesture> state = + QGesture *state = objectGestures.value(QGestureManager::ObjectGesture(object, type)); if (!state) { QGestureRecognizer *recognizer = recognizers.value(type); @@ -130,21 +130,25 @@ QGesture *QGestureManager::getState(QObject *object, Qt::GestureType type) state = recognizer->createGesture(object); if (!state) return 0; - if (state.data()->gestureType() == Qt::CustomGesture) { + if (state->gestureType() == Qt::CustomGesture) { // if the recognizer didn't fill in the gesture type, then this // is a custom gesture with autogenerated it and we fill it. - state.data()->d_func()->gestureType = type; + state->d_func()->gestureType = type; +#if defined(GESTURE_DEBUG) + state->setObjectName(QString::number((int)type)); +#endif } objectGestures.insert(QGestureManager::ObjectGesture(object, type), state); - gestureToRecognizer[state.data()] = recognizer; + gestureToRecognizer[state] = recognizer; + gestureOwners[state] = object; } } - return state.data(); + return state; } bool QGestureManager::filterEventThroughContexts(const QMap<QObject *, Qt::GestureType> &contexts, - QObject *receiver, QEvent *event) + QEvent *event) { QSet<QGesture *> triggeredGestures; QSet<QGesture *> finishedGestures; @@ -152,6 +156,9 @@ bool QGestureManager::filterEventThroughContexts(const QMap<QObject *, QSet<QGesture *> canceledGestures; QSet<QGesture *> notGestures; + // TODO: sort contexts by the gesture type and check if one of the contexts + // is already active. + // filter the event through recognizers typedef QMap<QObject *, Qt::GestureType>::const_iterator ContextIterator; for (ContextIterator cit = contexts.begin(), ce = contexts.end(); cit != ce; ++cit) { @@ -237,7 +244,7 @@ bool QGestureManager::filterEventThroughContexts(const QMap<QObject *, foreach (QGesture *gesture, notStarted) gesture->d_func()->state = Qt::GestureStarted; QSet<QGesture *> undeliveredGestures; - deliverEvents(notStarted, receiver, &undeliveredGestures); + deliverEvents(notStarted, &undeliveredGestures); finishedGestures -= undeliveredGestures; } @@ -274,7 +281,7 @@ bool QGestureManager::filterEventThroughContexts(const QMap<QObject *, QSet<QGesture *> undeliveredGestures; deliverEvents(startedGestures+triggeredGestures+finishedGestures+canceledGestures, - receiver, &undeliveredGestures); + &undeliveredGestures); activeGestures -= undeliveredGestures; @@ -292,38 +299,47 @@ bool QGestureManager::filterEventThroughContexts(const QMap<QObject *, bool QGestureManager::filterEvent(QWidget *receiver, QEvent *event) { + QSet<Qt::GestureType> types; QMap<QObject *, Qt::GestureType> contexts; QWidget *w = receiver; + typedef QMap<Qt::GestureType, Qt::GestureContext>::const_iterator ContextIterator; if (!w->d_func()->gestureContext.isEmpty()) { - typedef QMap<Qt::GestureType, Qt::GestureContext>::const_iterator ContextIterator; for(ContextIterator it = w->d_func()->gestureContext.begin(), e = w->d_func()->gestureContext.end(); it != e; ++it) { + types.insert(it.key()); contexts.insertMulti(w, it.key()); } } // find all gesture contexts for the widget tree - w = w->parentWidget(); + w = w->isWindow() ? 0 : w->parentWidget(); while (w) { - typedef QMap<Qt::GestureType, Qt::GestureContext>::const_iterator ContextIterator; for (ContextIterator it = w->d_func()->gestureContext.begin(), e = w->d_func()->gestureContext.end(); it != e; ++it) { - if (it.value() == Qt::WidgetWithChildrenGesture) - contexts.insertMulti(w, it.key()); + if (it.value() == Qt::WidgetWithChildrenGesture) { + if (!types.contains(it.key())) { + types.insert(it.key()); + contexts.insertMulti(w, it.key()); + } + } } + if (w->isWindow()) + break; w = w->parentWidget(); } - return filterEventThroughContexts(contexts , receiver, event); + return filterEventThroughContexts(contexts, event); } -bool QGestureManager::filterEvent(QGraphicsObject *graphicsObject, QEvent *event) +bool QGestureManager::filterEvent(QGraphicsObject *receiver, QEvent *event) { + QSet<Qt::GestureType> types; QMap<QObject *, Qt::GestureType> contexts; - QGraphicsObject *item = graphicsObject; + QGraphicsObject *item = receiver; if (!item->QGraphicsItem::d_func()->gestureContext.isEmpty()) { typedef QMap<Qt::GestureType, Qt::GestureContext>::const_iterator ContextIterator; for(ContextIterator it = item->QGraphicsItem::d_func()->gestureContext.begin(), e = item->QGraphicsItem::d_func()->gestureContext.end(); it != e; ++it) { + types.insert(it.key()); contexts.insertMulti(item, it.key()); } } @@ -334,55 +350,110 @@ bool QGestureManager::filterEvent(QGraphicsObject *graphicsObject, QEvent *event typedef QMap<Qt::GestureType, Qt::GestureContext>::const_iterator ContextIterator; for (ContextIterator it = item->QGraphicsItem::d_func()->gestureContext.begin(), e = item->QGraphicsItem::d_func()->gestureContext.end(); it != e; ++it) { - if (it.value() == Qt::ItemWithChildrenGesture) - contexts.insertMulti(item, it.key()); + if (it.value() == Qt::ItemWithChildrenGesture) { + if (!types.contains(it.key())) + contexts.insertMulti(item, it.key()); + } } item = item->parentObject(); } - return filterEventThroughContexts(contexts, graphicsObject, event); + return filterEventThroughContexts(contexts, event); } bool QGestureManager::filterEvent(QGesture *state, QEvent *event) { QMap<QObject *, Qt::GestureType> contexts; contexts.insert(state, state->gestureType()); - return filterEventThroughContexts(contexts, 0, event); + return filterEventThroughContexts(contexts, event); +} + +void QGestureManager::getGestureTargets(const QSet<QGesture*> &gestures, + QMap<QWidget *, QList<QGesture *> > *conflicts, + QMap<QWidget *, QList<QGesture *> > *normal) +{ + typedef QHash<Qt::GestureType, QHash<QWidget *, QGesture *> > GestureByTypes; + GestureByTypes gestureByTypes; + + // sort gestures by types + foreach (QGesture *gesture, gestures) { + QWidget *receiver = gestureTargets.value(gesture, 0); + Q_ASSERT(receiver); + gestureByTypes[gesture->gestureType()].insert(receiver, gesture); + } + + // for each gesture type + foreach (Qt::GestureType type, gestureByTypes.keys()) { + QHash<QWidget *, QGesture *> gestures = gestureByTypes.value(type); + foreach (QWidget *widget, gestures.keys()) { + QWidget *w = widget->parentWidget(); + while (w) { + QMap<Qt::GestureType, Qt::GestureContext>::const_iterator it + = w->d_func()->gestureContext.find(type); + if (it != w->d_func()->gestureContext.end()) { + // i.e. 'w' listens to gesture 'type' + Qt::GestureContext context = it.value(); + if (context == Qt::WidgetWithChildrenGesture && w != widget) { + // conflicting gesture! + (*conflicts)[widget].append(gestures[widget]); + break; + } + } + if (w->isWindow()) { + w = 0; + break; + } + w = w->parentWidget(); + } + if (!w) + (*normal)[widget].append(gestures[widget]); + } + } } -void QGestureManager::deliverEvents(const QSet<QGesture*> &gestures, - QObject *lastReceiver, +void QGestureManager::deliverEvents(const QSet<QGesture *> &gestures, QSet<QGesture *> *undeliveredGestures) { if (gestures.isEmpty()) return; - // group gestures by widgets - typedef QMap<QObject *, QList<QGesture *> > GesturesPerReceiver; - GesturesPerReceiver groupedGestures; - // for conflicted gestures the key is always the innermost widget (i.e. the child) - GesturesPerReceiver conflictedGestures; - typedef QMultiHash<QWidget *, QGesture *> WidgetMultiGestures; - WidgetMultiGestures widgetMultiGestures; + typedef QMap<QWidget *, QList<QGesture *> > GesturesPerWidget; + GesturesPerWidget conflictedGestures; + GesturesPerWidget normalStartedGestures; - foreach (QGesture *gesture, gestures) { - QObject *target = gestureTargets.value(gesture, 0); + QSet<QGesture *> startedGestures; + // first figure out the initial receivers of gestures + for (QSet<QGesture *>::const_iterator it = gestures.begin(), + e = gestures.end(); it != e; ++it) { + QGesture *gesture = *it; + QWidget *target = gestureTargets.value(gesture, 0); if (!target) { + // the gesture has just started and doesn't have a target yet. Q_ASSERT(gesture->state() == Qt::GestureStarted); if (gesture->hasHotSpot()) { - // guess the target using the hotspot of the gesture + // guess the target widget using the hotspot of the gesture QPoint pt = gesture->hotSpot().toPoint(); if (QWidget *w = qApp->topLevelAt(pt)) { target = w->childAt(w->mapFromGlobal(pt)); } + } else { + // or use the context of the gesture + QObject *context = gestureOwners.value(gesture, 0); + if (context->isWidgetType()) + target = static_cast<QWidget *>(context); } - if (!target) - target = lastReceiver; + if (target) + gestureTargets.insert(gesture, target); } + + Qt::GestureType gestureType = gesture->gestureType(); + Q_ASSERT(gestureType != Qt::CustomGesture); + if (target) { - gestureTargets.insert(gesture, target); - if (target->isWidgetType()) - widgetMultiGestures.insert(static_cast<QWidget *>(target), gesture); - groupedGestures[target].append(gesture); + if (gesture->state() == Qt::GestureStarted) { + startedGestures.insert(gesture); + } else { + normalStartedGestures[target].append(gesture); + } } else { qWarning() << "QGestureManager::deliverEvent: could not find the target for gesture" << gesture->gestureType(); @@ -390,56 +461,44 @@ void QGestureManager::deliverEvents(const QSet<QGesture*> &gestures, } } - typedef WidgetMultiGestures::const_iterator WidgetMultiGesturesIterator; - for (WidgetMultiGesturesIterator it = widgetMultiGestures.begin(), - e = widgetMultiGestures.end(); it != e; ++it) { - QWidget *widget1 = it.key(); - QGesture *gesture1 = it.value(); - WidgetMultiGesturesIterator cit = it; - for (++cit; cit != e; ++cit) { - QWidget *widget2 = cit.key(); - QGesture *gesture2 = cit.value(); - if (gesture1->gestureType() != gesture2->gestureType()) - continue; - // TODO: ugly, rewrite this. - if ((widget1 && widget2 && widget2->isAncestorOf(widget1))) { - groupedGestures[widget2].removeOne(gesture2); - groupedGestures[widget1].removeOne(gesture1); - conflictedGestures[widget1].append(gesture1); - } else if ((widget1 && widget2 && widget1->isAncestorOf(widget2))) { - groupedGestures[widget2].removeOne(gesture2); - groupedGestures[widget1].removeOne(gesture1); - conflictedGestures[widget2].append(gesture2); - } - } - } - - DEBUG() << "deliverEvents: conflicted =" << conflictedGestures.values() - << " grouped =" << groupedGestures.values(); + getGestureTargets(startedGestures, &conflictedGestures, &normalStartedGestures); + DEBUG() << "QGestureManager::deliverEvents:" + << "\nstarted: " << startedGestures + << "\nconflicted: " << conflictedGestures + << "\nnormal: " << normalStartedGestures + << "\n"; // if there are conflicting gestures, send the GestureOverride event - for (GesturesPerReceiver::const_iterator it = conflictedGestures.begin(), + for (GesturesPerWidget::const_iterator it = conflictedGestures.begin(), e = conflictedGestures.end(); it != e; ++it) { + QWidget *receiver = it.key(); + QList<QGesture *> gestures = it.value(); DEBUG() << "QGestureManager::deliverEvents: sending GestureOverride to" - << it.key() - << " gestures:" << it.value(); - QGestureEvent event(it.value()); + << receiver + << "gestures:" << gestures; + QGestureEvent event(gestures); event.t = QEvent::GestureOverride; event.ignore(); - QApplication::sendEvent(it.key(), &event); + QApplication::sendEvent(receiver, &event); if (!event.isAccepted()) { - // nobody accepted the GestureOverride, put it back to deliver to - // the closest context (i.e. to the inner-most widget). - DEBUG() <<" override was not accepted"; - groupedGestures[it.key()].append(it.value()); + // nobody accepted the GestureOverride, put gestures that were not + // accepted back to deliver as usual + QList<QGesture *> &gestures = normalStartedGestures[receiver]; + foreach(QGesture *gesture, event.allGestures()) { + if (!event.isAccepted(gesture)) { + DEBUG() << "override event wasn't accepted. putting back:" << gesture; + gestures.append(gesture); + } + } } } - for (GesturesPerReceiver::const_iterator it = groupedGestures.begin(), - e = groupedGestures.end(); it != e; ++it) { + // delivering gestures that are not in conflicted state + for (GesturesPerWidget::const_iterator it = normalStartedGestures.begin(), + e = normalStartedGestures.end(); it != e; ++it) { if (!it.value().isEmpty()) { DEBUG() << "QGestureManager::deliverEvents: sending to" << it.key() - << " gestures:" << it.value(); + << "gestures:" << it.value(); QGestureEvent event(it.value()); QApplication::sendEvent(it.key(), &event); } @@ -457,7 +516,8 @@ void QGestureManager::timerEvent(QTimerEvent *event) timer.stop(); QGesture *gesture = it.key(); it = maybeGestures.erase(it); - DEBUG() << "QGestureManager::timerEvent: gesture stopped due to timeout:" << gesture; + DEBUG() << "QGestureManager::timerEvent: gesture stopped due to timeout:" + << gesture; QGestureRecognizer *recognizer = gestureToRecognizer.value(gesture, 0); if (recognizer) recognizer->reset(gesture); diff --git a/src/gui/kernel/qgesturemanager_p.h b/src/gui/kernel/qgesturemanager_p.h index 5fc02ab..f0e7225 100644 --- a/src/gui/kernel/qgesturemanager_p.h +++ b/src/gui/kernel/qgesturemanager_p.h @@ -82,7 +82,7 @@ public: protected: void timerEvent(QTimerEvent *event); bool filterEventThroughContexts(const QMap<QObject *, Qt::GestureType> &contexts, - QObject *receiver, QEvent *event); + QEvent *event); private: QMultiMap<Qt::GestureType, QGestureRecognizer *> recognizers; @@ -114,16 +114,20 @@ private: } }; - QMap<ObjectGesture, QWeakPointer<QGesture> > objectGestures; + QMap<ObjectGesture, QGesture *> objectGestures; QMap<QGesture *, QGestureRecognizer *> gestureToRecognizer; + QHash<QGesture *, QObject *> gestureOwners; - QHash<QGesture *, QObject *> gestureTargets; + QHash<QGesture *, QWidget *> gestureTargets; int lastCustomGestureId; QGesture *getState(QObject *widget, Qt::GestureType gesture); - void deliverEvents(const QSet<QGesture *> &gestures, QObject *lastReceiver, + void deliverEvents(const QSet<QGesture *> &gestures, QSet<QGesture *> *undeliveredGestures); + void getGestureTargets(const QSet<QGesture*> &gestures, + QMap<QWidget *, QList<QGesture *> > *conflicts, + QMap<QWidget *, QList<QGesture *> > *normal); }; QT_END_NAMESPACE |