From 81a64345258f2f2ad5cf77355f7ecbd3ebad9734 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Fri, 8 May 2009 18:46:44 +0200 Subject: Improved gesture propagation. Each gesture can now be accepted separately and not accepted gestures will be propagated to parent widget that are subscribed to them. Added an internal flag to specify that gesture is a "singleshot" - aka if the gesture is not continious and will not have a GestureStarted state, but only GestureFinished. Asynchronous gestures still need to fixed, as well as QGraphicsView. --- src/gui/kernel/qapplication.cpp | 31 ---------- src/gui/kernel/qevent.cpp | 51 +++++++++++++++- src/gui/kernel/qevent.h | 11 +++- src/gui/kernel/qgesture.cpp | 2 +- src/gui/kernel/qgesture.h | 8 +++ src/gui/kernel/qgesture_p.h | 3 +- src/gui/kernel/qgesturemanager.cpp | 120 +++++++++++++++++++++++++++++-------- src/gui/kernel/qgesturemanager_p.h | 3 +- 8 files changed, 167 insertions(+), 62 deletions(-) diff --git a/src/gui/kernel/qapplication.cpp b/src/gui/kernel/qapplication.cpp index 9772e0f..ba105e0 100644 --- a/src/gui/kernel/qapplication.cpp +++ b/src/gui/kernel/qapplication.cpp @@ -4019,37 +4019,6 @@ bool QApplication::notify(QObject *receiver, QEvent *e) } break; #endif - - case QEvent::Gesture: { - QWidget *w = static_cast(receiver); - QGestureEvent *ge = static_cast(e); - QSet eventGestures; - foreach(const QString &gesture, ge->gestureTypes()) - eventGestures << gesture; - bool eventAccepted = ge->isAccepted(); - - QPoint offset; - while (w) { - QSet widgetGestures = w->d_func()->gestures; - foreach(int gestureId, widgetGestures) { - if (eventGestures.contains(QGestureManager::instance()->gestureNameFromId(gestureId))) { - foreach(QGesture *gesture, ge->gestures()) - gesture->translate(offset); - offset = QPoint(); - res = d->notify_helper(w, ge); - ge->spont = false; - eventAccepted = ge->isAccepted(); - if (res && eventAccepted) - break; - } - } - if (w->isWindow()) - break; - offset += w->pos(); - w = w->parentWidget(); - } - break; - } case QEvent::TouchBegin: // Note: TouchUpdate and TouchEnd events are sent to d->currentMultitouchWidget and never propagated { diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp index f24f186..fa85bf2 100644 --- a/src/gui/kernel/qevent.cpp +++ b/src/gui/kernel/qevent.cpp @@ -3532,10 +3532,11 @@ QMenubarUpdatedEvent::QMenubarUpdatedEvent(QMenuBar * const menuBar) are being executed and a list of gesture that were cancelled (\a cancelledGestures). */ -QGestureEvent::QGestureEvent(const QList &gestures, +QGestureEvent::QGestureEvent(const QSet &gestures, const QSet &cancelledGestures) : QEvent(QEvent::Gesture), m_cancelledGestures(cancelledGestures) { + setAccepted(false); foreach(QGesture *r, gestures) m_gestures.insert(r->type(), r); } @@ -3606,6 +3607,54 @@ QSet QGestureEvent::cancelledGestures() const return m_cancelledGestures; } +/*! + Sets the accept flag of the all gestures inside the event object, + the equivalent of calling \l{QEvent::accept()}{accept()} or + \l{QEvent::setAccepted()}{setAccepted(true)}. + + Setting the accept parameter indicates that the event receiver + wants the gesture. Unwanted gestures might be propagated to the parent + widget. +*/ +void QGestureEvent::acceptAll() +{ + QHash::iterator it = m_gestures.begin(), + e = m_gestures.end(); + for(; it != e; ++it) + it.value()->accept(); + setAccepted(true); +} + +/*! + Sets the accept flag of the specified gesture inside the event + object, the equivalent of calling + \l{QGestureEvent::gesture()}{gesture(type)}->\l{QGesture::accept()}{accept()} + + Setting the accept parameter indicates that the event receiver + wants the gesture. Unwanted gestures might be propagated to the parent + widget. +*/ +void QGestureEvent::accept(Qt::GestureType type) +{ + if (QGesture *g = m_gestures.value(qt_getStandardGestureTypeName(type), 0)) + g->accept(); +} + +/*! + Sets the accept flag of the specified gesture inside the event + object, the equivalent of calling + \l{QGestureEvent::gesture()}{gesture(type)}->\l{QGesture::accept()}{accept()} + + Setting the accept parameter indicates that the event receiver + wants the gesture. Unwanted gestures might be propagated to the parent + widget. +*/ +void QGestureEvent::accept(const QString &type) +{ + if (QGesture *g = m_gestures.value(type, 0)) + g->accept(); +} + /*! \class QTouchEvent \brief The QTouchEvent class contains parameters that describe a touch event . diff --git a/src/gui/kernel/qevent.h b/src/gui/kernel/qevent.h index 666cd3f..ea4f577 100644 --- a/src/gui/kernel/qevent.h +++ b/src/gui/kernel/qevent.h @@ -716,7 +716,7 @@ private: class Q_GUI_EXPORT QGestureEvent : public QEvent { public: - QGestureEvent(const QList &gestures, + QGestureEvent(const QSet &gestures, const QSet &cancelledGestures = QSet()); ~QGestureEvent(); @@ -731,6 +731,15 @@ public: QSet cancelledGestures() const; + void acceptAll(); +#ifndef Q_NO_USING_KEYWORD + using QEvent::accept; +#else + inline void accept() { QEvent::accept(); } +#endif + void accept(Qt::GestureType type); + void accept(const QString &type); + protected: QHash m_gestures; QSet m_cancelledGestures; diff --git a/src/gui/kernel/qgesture.cpp b/src/gui/kernel/qgesture.cpp index 4d492f8..7437ed3 100644 --- a/src/gui/kernel/qgesture.cpp +++ b/src/gui/kernel/qgesture.cpp @@ -79,7 +79,7 @@ QString qt_getStandardGestureTypeName(Qt::GestureType type); QGestureRecognizer classes. */ QGesture::QGesture(QObject *parent, const QString &type, Qt::GestureState state) - : QObject(*new QGesturePrivate, parent) + : QObject(*new QGesturePrivate, parent), m_accept(0) { Q_D(QGesture); d->type = type; diff --git a/src/gui/kernel/qgesture.h b/src/gui/kernel/qgesture.h index da1bc90..d220232 100644 --- a/src/gui/kernel/qgesture.h +++ b/src/gui/kernel/qgesture.h @@ -82,6 +82,12 @@ public: uint duration, Qt::GestureState state); virtual ~QGesture(); + inline void setAccepted(bool accepted) { m_accept = accepted; } + inline bool isAccepted() const { return m_accept; } + + inline void accept() { m_accept = true; } + inline void ignore() { m_accept = false; } + QString type() const; Qt::GestureState state() const; @@ -100,6 +106,8 @@ protected: virtual void translate(const QPoint &offset); private: + ushort m_accept : 1; + friend class QGestureManager; friend class QApplication; friend class QGestureRecognizerPan; diff --git a/src/gui/kernel/qgesture_p.h b/src/gui/kernel/qgesture_p.h index 22d6d7f..d2d77cc 100644 --- a/src/gui/kernel/qgesture_p.h +++ b/src/gui/kernel/qgesture_p.h @@ -66,7 +66,7 @@ class QGesturePrivate : public QObjectPrivate public: QGesturePrivate() - : duration(0) { } + : state(Qt::NoGesture), singleshot(0), duration(0) { } void init(const QPoint &startPos, const QPoint &lastPos, const QPoint &pos, const QRect &rect, @@ -86,6 +86,7 @@ public: Qt::GestureState state; QPointer widget; + uint singleshot:1; QRect rect; QPoint hotSpot; diff --git a/src/gui/kernel/qgesturemanager.cpp b/src/gui/kernel/qgesturemanager.cpp index a93248a..c7341f2 100644 --- a/src/gui/kernel/qgesturemanager.cpp +++ b/src/gui/kernel/qgesturemanager.cpp @@ -166,14 +166,18 @@ bool QGestureManager::filterEvent(QWidget *receiver, QEvent *event) DEBUG() << "QGestureManager: sending gesture event for: " << activeGestures << " and " << finishedGestures; - QList gestures; + QSet gestures; foreach(QGestureRecognizer *r, finishedGestures) { - if (QGesture *gesture = r->getGesture()) + if (QGesture *gesture = r->getGesture()) { gestures << gesture; + gesture->d_func()->singleshot = true; + } } foreach(QGestureRecognizer *r, activeGestures) { - if (QGesture *gesture = r->getGesture()) + if (QGesture *gesture = r->getGesture()) { gestures << gesture; + gesture->d_func()->singleshot = false; + } } Q_ASSERT(!gestures.isEmpty()); ret = sendGestureEvent(receiver, gestures); @@ -243,7 +247,6 @@ bool QGestureManager::filterEvent(QWidget *receiver, QEvent *event) activeGestures -= newMaybeGestures; activeGestures -= cancelledGestures; - activeGestures -= finishedGestures; activeGestures += startedGestures; foreach(QGestureRecognizer *r, startedGestures+finishedGestures+notGestures) { QMap::iterator it = maybeGestures.find(r); @@ -260,7 +263,7 @@ bool QGestureManager::filterEvent(QWidget *receiver, QEvent *event) maybeGestures.insert(r, timerId); } } - QList gestures; + QSet gestures; if (!finishedGestures.isEmpty() || !activeGestures.isEmpty()) { // another gesture found! ret = true; @@ -268,12 +271,17 @@ bool QGestureManager::filterEvent(QWidget *receiver, QEvent *event) << activeGestures << " and " << finishedGestures; foreach(QGestureRecognizer *r, finishedGestures) { - if (QGesture *gesture = r->getGesture()) + if (QGesture *gesture = r->getGesture()) { gestures << gesture; + gesture->d_func()->singleshot = activeGestures.contains(r); + } } + activeGestures -= finishedGestures; foreach(QGestureRecognizer *r, activeGestures) { - if (QGesture *gesture = r->getGesture()) + if (QGesture *gesture = r->getGesture()) { gestures << gesture; + gesture->d_func()->singleshot = false; + } } } QSet cancelledGestureNames; @@ -439,7 +447,7 @@ void QGestureManager::recognizerStateChanged(QGestureRecognizer::Result result) killTimer(maybeGestures.value(recognizer)); maybeGestures.remove(recognizer); } - QList gestures; + QSet gestures; if (QGesture *gesture = recognizer->getGesture()) gestures << gesture; if(!gestures.isEmpty()) { @@ -452,7 +460,7 @@ void QGestureManager::recognizerStateChanged(QGestureRecognizer::Result result) case QGestureRecognizer::MaybeGesture: { DEBUG() << "QGestureManager: maybe gesture: " << recognizer; if (activeGestures.contains(recognizer)) { - //FIXME: sendGestureEvent(targetWidget, QList(), QSet() << recognizer->gestureType()); + //FIXME: sendGestureEvent(targetWidget, QSet(), QSet() << recognizer->gestureType()); } if (!maybeGestures.contains(recognizer)) { int timerId = startTimer(MaximumGestureRecognitionTimeout); @@ -480,43 +488,63 @@ void QGestureManager::recognizerStateChanged(QGestureRecognizer::Result result) } } -bool QGestureManager::sendGestureEvent(QWidget *receiver, const QList &gestures, +bool QGestureManager::widgetHasGesture(QWidget *widget, QGesture *gesture) const +{ + const QString gestureName = gesture->type(); + QSet::iterator it = widget->d_func()->gestures.begin(), + e = widget->d_func()->gestures.end(); + for (; it != e; ++it) { + if (gestureNameFromId(*it) == gestureName) + return true; + } + return false; +} + +bool QGestureManager::sendGestureEvent(QWidget *receiver, const QSet &gestures, const QSet &cancelled) { - typedef QMap > WidgetGesturesMap; + if (gestures.isEmpty()) + return false; + DEBUG() << "QGestureManager::sendGestureEvent: sending to" << receiver + << "gestures:" << gestures << "; cancelled:" << cancelled; + QSet startedGestures; + // grouping gesture objects by receiver widgets. + typedef QMap > WidgetGesturesMap; WidgetGesturesMap widgetGestures; - for(QList::const_iterator it = gestures.begin(), e = gestures.end(); + for(QSet::const_iterator it = gestures.begin(), e = gestures.end(); it != e; ++it) { QGesture *g = *it; QGesturePrivate *gd = g->d_func(); - if (g->state() == Qt::GestureStarted) { + if (!gd->widget && (g->state() == Qt::GestureStarted || g->state() == Qt::GestureFinished)) { + startedGestures.insert(g); // find the target widget QWidget *w = receiver; + QPoint offset; while (w) { - QSet::iterator it = w->d_func()->gestures.begin(), - e = w->d_func()->gestures.end(); - for (; it != e; ++it) { - if (gestureNameFromId(*it) == g->type()) - break; - } - if (it != e) + if (widgetHasGesture(w, g)) break; + if (w->isWindow()) { + w = 0; + break; + } + offset += w->pos(); w = w->parentWidget(); } if (!w) // no widget in the tree that accepts this gesture. continue; gd->widget = w; + g->translate(offset); } if (!gd->widget) { DEBUG() << "QGestureManager: didn't find a widget to send gesture event (" << g->type() << ") for tree:" << receiver; + // TODO: maybe we should reset gesture recognizers when nobody interested in its gestures. continue; } - widgetGestures[gd->widget].append(g); + widgetGestures[gd->widget].insert(g); } - // we return true and stop original from being delivered if any of - // the gesture events were accepted by a receiver. + QSet ignoredGestures; bool ret = false; for(WidgetGesturesMap::const_iterator it = widgetGestures.begin(), e = widgetGestures.end(); it != e; ++it) { @@ -524,10 +552,50 @@ bool QGestureManager::sendGestureEvent(QWidget *receiver, const QList Q_ASSERT(receiver != 0 /*should be taken care above*/); // TODO: send cancelled gesture event to the widget that received the original gesture! QGestureEvent event(it.value(), cancelled); - if (qt_sendSpontaneousEvent(receiver, &event) && event.isAccepted()) - ret = true; + bool processed = qt_sendSpontaneousEvent(receiver, &event); + QSet started = startedGestures.intersect(it.value()); + if (!started.isEmpty() && !(processed && event.isAccepted())) { + // there are started gestures event that weren't + // accepted, so propagating each gesture independently. + QSet::const_iterator it = started.begin(), + e = started.end(); + for(; it != e; ++it) { + QGesture *g = *it; + if (processed && g->isAccepted()) { + ret = true; + continue; + } + // if it wasn't accepted, find the first parent widget + // that is subscribed to the gesture. + QGesturePrivate *gd = g->d_func(); + QWidget *w = gd->widget; + gd->widget = 0; + + if (!w->isWindow()) { + g->translate(w->pos()); + w = w->parentWidget(); + QPoint offset; + while (w) { + if (widgetHasGesture(w, g)) { + DEBUG() << "QGestureManager: sendGestureEvent:" << w << "didn't accept gesture" << g; + ignoredGestures.insert(g); + break; + } + if (w->isWindow()) { + w = 0; + break; + } + offset += w->pos(); + w = w->parentWidget(); + } + if (w) + g->translate(offset); + } + } + } } - return ret; + // try to send all gestures that were ignored to the next parent + return sendGestureEvent(0, ignoredGestures, cancelled) || ret; } int QGestureManager::eventDeliveryDelay() const diff --git a/src/gui/kernel/qgesturemanager_p.h b/src/gui/kernel/qgesturemanager_p.h index beed323..963edf9 100644 --- a/src/gui/kernel/qgesturemanager_p.h +++ b/src/gui/kernel/qgesturemanager_p.h @@ -93,7 +93,8 @@ private slots: void recognizerStateChanged(QGestureRecognizer::Result); private: - bool sendGestureEvent(QWidget *receiver, const QList &gestures, + bool widgetHasGesture(QWidget *widget, QGesture *gesture) const; + bool sendGestureEvent(QWidget *receiver, const QSet &gestures, const QSet &cancelled = QSet()); QSet activeGestures; -- cgit v0.12