summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/gui/kernel/qgesture.cpp33
-rw-r--r--src/gui/kernel/qgesture.h10
-rw-r--r--src/gui/kernel/qgesture_p.h10
-rw-r--r--src/gui/kernel/qgesturemanager.cpp85
-rw-r--r--src/gui/kernel/qgesturemanager_p.h2
-rw-r--r--tests/auto/gestures/tst_gestures.cpp62
6 files changed, 195 insertions, 7 deletions
diff --git a/src/gui/kernel/qgesture.cpp b/src/gui/kernel/qgesture.cpp
index a161876..c302c51 100644
--- a/src/gui/kernel/qgesture.cpp
+++ b/src/gui/kernel/qgesture.cpp
@@ -175,6 +175,29 @@ void QGesture::unsetHotSpot()
}
/*!
+ \enum QGesture::GestureCancelPolicy
+
+ This enum describes how accepting a gesture can cancel other gestures
+ automatically.
+
+ \value CancelNone On accepting this gesture no other gestures will be affected.
+ \value CancelAllInContext On accepting this gesture all gestures that are active
+ in the context (Qt::GestureContext) will be cancelled.
+*/
+
+void QGesture::setGestureCancelPolicy(GestureCancelPolicy policy)
+{
+ Q_D(QGesture);
+ d->gestureCancelPolicy = static_cast<uint>(policy);
+}
+
+QGesture::GestureCancelPolicy QGesture::gestureCancelPolicy() const
+{
+ Q_D(const QGesture);
+ return static_cast<GestureCancelPolicy>(d->gestureCancelPolicy);
+}
+
+/*!
\class QPanGesture
\since 4.6
\brief The QPanGesture class describes a panning gesture made by the user.
@@ -195,6 +218,16 @@ void QGesture::unsetHotSpot()
*/
/*!
+ \property QGesture::GestureCancelPolicy
+ \brief the policy for deciding what happens on accepting a gesture
+
+ On accepting one gesture Qt can automatically cancel other gestures
+ that belong to other targets. The policy is normally set to not cancel
+ any other gestures and can be set to cancel all active gestures in the
+ context. For example for all child widgets.
+*/
+
+/*!
\property QPanGesture::lastOffset
\brief the last offset recorded for this gesture
diff --git a/src/gui/kernel/qgesture.h b/src/gui/kernel/qgesture.h
index 6469959..524d26e 100644
--- a/src/gui/kernel/qgesture.h
+++ b/src/gui/kernel/qgesture.h
@@ -65,6 +65,7 @@ class Q_GUI_EXPORT QGesture : public QObject
Q_PROPERTY(Qt::GestureState state READ state)
Q_PROPERTY(Qt::GestureType gestureType READ gestureType)
+ Q_PROPERTY(QGesture::GestureCancelPolicy gestureCancelPolicy READ gestureCancelPolicy WRITE setGestureCancelPolicy)
Q_PROPERTY(QPointF hotSpot READ hotSpot WRITE setHotSpot RESET unsetHotSpot)
Q_PROPERTY(bool hasHotSpot READ hasHotSpot)
@@ -81,6 +82,14 @@ public:
bool hasHotSpot() const;
void unsetHotSpot();
+ enum GestureCancelPolicy {
+ CancelNone = 0,
+ CancelAllInContext
+ };
+
+ void setGestureCancelPolicy(GestureCancelPolicy policy);
+ GestureCancelPolicy gestureCancelPolicy() const;
+
protected:
QGesture(QGesturePrivate &dd, QObject *parent);
@@ -208,6 +217,7 @@ public:
QT_END_NAMESPACE
+Q_DECLARE_METATYPE(QGesture::GestureCancelPolicy)
QT_END_HEADER
#endif // QGESTURE_H
diff --git a/src/gui/kernel/qgesture_p.h b/src/gui/kernel/qgesture_p.h
index 975c0c9..d2ef8a7 100644
--- a/src/gui/kernel/qgesture_p.h
+++ b/src/gui/kernel/qgesture_p.h
@@ -67,16 +67,20 @@ class QGesturePrivate : public QObjectPrivate
public:
QGesturePrivate()
- : gestureType(Qt::CustomGesture), state(Qt::NoGesture), isHotSpotSet(false),
- targetObject(0)
+ : gestureType(Qt::CustomGesture), state(Qt::NoGesture),
+ targetObject(0),
+ isHotSpotSet(false),
+ gestureCancelPolicy(0)
+
{
}
Qt::GestureType gestureType;
Qt::GestureState state;
QPointF hotSpot;
- bool isHotSpotSet;
QObject *targetObject;
+ uint isHotSpotSet : 1;
+ uint gestureCancelPolicy : 2;
};
class QPanGesturePrivate : public QGesturePrivate
diff --git a/src/gui/kernel/qgesturemanager.cpp b/src/gui/kernel/qgesturemanager.cpp
index 52f8eef..fc7c8b2 100644
--- a/src/gui/kernel/qgesturemanager.cpp
+++ b/src/gui/kernel/qgesturemanager.cpp
@@ -322,21 +322,96 @@ bool QGestureManager::filterEventThroughContexts(const QMap<QObject *,
deliverEvents(startedGestures+triggeredGestures+finishedGestures+canceledGestures,
&undeliveredGestures);
+ foreach (QGesture *g, startedGestures) {
+ if (undeliveredGestures.contains(g))
+ continue;
+ if (g->gestureCancelPolicy() == QGesture::CancelAllInContext) {
+ DEBUG() << "lets try to cancel some";
+ // find gestures in context in Qt::GestureStarted or Qt::GestureUpdated state and cancel them
+ cancelGesturesForChildren(g);
+ }
+ }
+
activeGestures -= undeliveredGestures;
// reset gestures that ended
QSet<QGesture *> endedGestures =
finishedGestures + canceledGestures + undeliveredGestures;
foreach (QGesture *gesture, endedGestures) {
- if (QGestureRecognizer *recognizer = gestureToRecognizer.value(gesture, 0))
+ if (QGestureRecognizer *recognizer = gestureToRecognizer.value(gesture, 0)) {
+ gesture->setGestureCancelPolicy(QGesture::CancelNone);
recognizer->reset(gesture);
- else
+ } else {
cleanupGesturesForRemovedRecognizer(gesture);
+ }
gestureTargets.remove(gesture);
}
return ret;
}
+// Cancel all gestures of children of the widget that original is associated with
+void QGestureManager::cancelGesturesForChildren(QGesture *original)
+{
+ Q_ASSERT(original);
+ QWidget *originatingWidget = gestureTargets.value(original);
+ Q_ASSERT(originatingWidget);
+
+ // iterate over all active gestures and all maybe gestures
+ // for each find the owner
+ // if the owner is part of our sub-hierarchy, cancel it.
+
+ QSet<QGesture*> cancelledGestures;
+ QSet<QGesture*>::Iterator iter = activeGestures.begin();
+ while (iter != activeGestures.end()) {
+ QWidget *widget = gestureTargets.value(*iter);
+ // note that we don't touch the gestures for our originatingWidget
+ if (widget != originatingWidget && originatingWidget->isAncestorOf(widget)) {
+ DEBUG() << " found a gesture to cancel" << (*iter);
+ (*iter)->d_func()->state = Qt::GestureCanceled;
+ cancelledGestures << *iter;
+ iter = activeGestures.erase(iter);
+ } else {
+ ++iter;
+ }
+ }
+
+ // TODO handle 'maybe' gestures too
+
+ // sort them per target widget by cherry picking from almostCanceledGestures and delivering
+ QSet<QGesture *> almostCanceledGestures = cancelledGestures;
+ while (!almostCanceledGestures.isEmpty()) {
+ QWidget *target = 0;
+ QSet<QGesture*> gestures;
+ iter = almostCanceledGestures.begin();
+ // sort per target widget
+ while (iter != almostCanceledGestures.end()) {
+ QWidget *widget = gestureTargets.value(*iter);
+ if (target == 0)
+ target = widget;
+ if (target == widget) {
+ gestures << *iter;
+ iter = almostCanceledGestures.erase(iter);
+ } else {
+ ++iter;
+ }
+ }
+ Q_ASSERT(target);
+
+ QSet<QGesture*> undeliveredGestures;
+ deliverEvents(gestures, &undeliveredGestures);
+ }
+
+ for (iter = cancelledGestures.begin(); iter != cancelledGestures.end(); ++iter) {
+ QGestureRecognizer *recognizer = gestureToRecognizer.value(*iter, 0);
+ if (recognizer) {
+ (*iter)->setGestureCancelPolicy(QGesture::CancelNone);
+ recognizer->reset(*iter);
+ } else {
+ cleanupGesturesForRemovedRecognizer(*iter);
+ }
+ }
+}
+
void QGestureManager::cleanupGesturesForRemovedRecognizer(QGesture *gesture)
{
QGestureRecognizer *recognizer = m_deletedRecognizers.value(gesture);
@@ -585,10 +660,12 @@ void QGestureManager::timerEvent(QTimerEvent *event)
DEBUG() << "QGestureManager::timerEvent: gesture stopped due to timeout:"
<< gesture;
QGestureRecognizer *recognizer = gestureToRecognizer.value(gesture, 0);
- if (recognizer)
+ if (recognizer) {
+ gesture->setGestureCancelPolicy(QGesture::CancelNone);
recognizer->reset(gesture);
- else
+ } else {
cleanupGesturesForRemovedRecognizer(gesture);
+ }
} else {
++it;
}
diff --git a/src/gui/kernel/qgesturemanager_p.h b/src/gui/kernel/qgesturemanager_p.h
index 96c2fb7..e6a1d50 100644
--- a/src/gui/kernel/qgesturemanager_p.h
+++ b/src/gui/kernel/qgesturemanager_p.h
@@ -135,6 +135,8 @@ private:
void getGestureTargets(const QSet<QGesture*> &gestures,
QMap<QWidget *, QList<QGesture *> > *conflicts,
QMap<QWidget *, QList<QGesture *> > *normal);
+
+ void cancelGesturesForChildren(QGesture *originatingGesture);
};
QT_END_NAMESPACE
diff --git a/tests/auto/gestures/tst_gestures.cpp b/tests/auto/gestures/tst_gestures.cpp
index 6acfe70..39cdf63 100644
--- a/tests/auto/gestures/tst_gestures.cpp
+++ b/tests/auto/gestures/tst_gestures.cpp
@@ -331,6 +331,7 @@ private slots:
void ungrabGesture();
void consumeEventHint();
void unregisterRecognizer();
+ void autoCancelGestures();
};
tst_Gestures::tst_Gestures()
@@ -1292,5 +1293,66 @@ void tst_Gestures::unregisterRecognizer() // a method on QApplication
}
+void tst_Gestures::autoCancelGestures()
+{
+ class MockRecognizer : public QGestureRecognizer {
+ public:
+ QGestureRecognizer::Result filterEvent(QGesture *gesture, QObject *watched, QEvent *event)
+ {
+ Q_UNUSED(gesture);
+ Q_UNUSED(watched);
+ if (event->type() == QEvent::MouseButtonPress)
+ return QGestureRecognizer::GestureTriggered;
+ if (event->type() == QEvent::MouseButtonRelease)
+ return QGestureRecognizer::GestureFinished;
+ return QGestureRecognizer::Ignore;
+ }
+ };
+
+ class MockWidget : public GestureWidget {
+ public:
+ MockWidget(const char *name) : GestureWidget(name) { }
+
+ bool event(QEvent *event)
+ {
+ if (event->type() == QEvent::Gesture) {
+ QGestureEvent *ge = static_cast<QGestureEvent*>(event);
+ Q_ASSERT(ge->allGestures().count() == 1); // can't use QCOMPARE here...
+ ge->allGestures().first()->setGestureCancelPolicy(QGesture::CancelAllInContext);
+ }
+ return GestureWidget::event(event);
+ }
+ };
+
+ MockWidget parent("parent"); // this one sets the cancel policy to CancelAllInContext
+ parent.resize(300, 100);
+ GestureWidget *child = new GestureWidget("child", &parent);
+ child->setGeometry(10, 10, 100, 80);
+
+ Qt::GestureType type = qApp->registerGestureRecognizer(new MockRecognizer());
+ parent.grabGesture(type, Qt::WidgetWithChildrenGesture);
+ child->grabGesture(type, Qt::WidgetWithChildrenGesture);
+
+ /*
+ An event is send to both the child and the parent, when the child gets it a gesture is triggered
+ and send to the child.
+ When the parent gets the event a new gesture is triggered and delivered to the parent. When the
+ parent gets it he accepts it and that causes the cancel policy to activate.
+ The cause of that is the gesture for the child is cancelled and send to the child as such.
+ */
+ QMouseEvent event(QEvent::MouseButtonPress, QPoint(20,20), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
+ QApplication::sendEvent(child, &event);
+ QCOMPARE(child->events.started.count(), 1);
+ QCOMPARE(child->events.all.count(), 1);
+ QCOMPARE(parent.events.all.count(), 0);
+ child->reset();
+ QApplication::sendEvent(&parent, &event);
+ QCOMPARE(parent.events.all.count(), 1);
+ QCOMPARE(parent.events.started.count(), 1);
+ QCOMPARE(child->events.started.count(), 0);
+ QCOMPARE(child->events.all.count(), 1);
+ QCOMPARE(child->events.canceled.count(), 1);
+}
+
QTEST_MAIN(tst_Gestures)
#include "tst_gestures.moc"