tests/auto/gestures/tst_gestures.cpp
changeset 33 3e2da88830cd
parent 30 5dc02b23752f
child 37 758a864f9613
--- a/tests/auto/gestures/tst_gestures.cpp	Tue Jul 06 15:10:48 2010 +0300
+++ b/tests/auto/gestures/tst_gestures.cpp	Wed Aug 18 10:37:55 2010 +0300
@@ -49,6 +49,7 @@
 #include <qgesture.h>
 #include <qgesturerecognizer.h>
 #include <qgraphicsitem.h>
+#include <qgraphicswidget.h>
 #include <qgraphicsview.h>
 #include <qmainwindow.h>
 
@@ -333,6 +334,7 @@
     void gestureOverChild();
     void multipleWidgetOnlyGestureInTree();
     void conflictingGestures();
+    void conflictingGesturesInGraphicsView();
     void finishedWithoutStarted();
     void unknownGesture();
     void graphicsItemGesture();
@@ -358,6 +360,7 @@
     void viewportCoordinates();
     void partialGesturePropagation();
     void testQGestureRecognizerCleanup();
+    void testReuseCanceledGestures();
 };
 
 tst_Gestures::tst_Gestures()
@@ -2069,5 +2072,234 @@
     delete w;
 }
 
+class ReuseCanceledGesturesRecognizer : public QGestureRecognizer
+{
+public:
+    enum Type {
+        RmbAndCancelAllType,
+        LmbType
+    };
+
+    ReuseCanceledGesturesRecognizer(Type type) : m_type(type) {}
+
+    QGesture *create(QObject *) {
+        QGesture *g = new QGesture;
+        return g;
+    }
+
+    Result recognize(QGesture *gesture, QObject *, QEvent *event) {
+        QMouseEvent *me = static_cast<QMouseEvent *>(event);
+        Qt::MouseButton mouseButton(m_type == LmbType ? Qt::LeftButton : Qt::RightButton);
+
+        switch(event->type()) {
+        case QEvent::MouseButtonPress:
+            if (me->button() == mouseButton && gesture->state() == Qt::NoGesture) {
+                gesture->setHotSpot(QPointF(me->globalPos()));
+                if (m_type == RmbAndCancelAllType)
+                    gesture->setGestureCancelPolicy(QGesture::CancelAllInContext);
+                return QGestureRecognizer::TriggerGesture;
+            }
+            break;
+        case QEvent::MouseButtonRelease:
+            if (me->button() == mouseButton && gesture->state() > Qt::NoGesture)
+                return QGestureRecognizer::FinishGesture;
+        default:
+            break;
+        }
+        return QGestureRecognizer::Ignore;
+    }
+private:
+    Type m_type;
+};
+
+class ReuseCanceledGesturesWidget : public QGraphicsWidget
+{
+  public:
+    ReuseCanceledGesturesWidget(Qt::GestureType gestureType = Qt::TapGesture, QGraphicsItem *parent = 0)
+        : QGraphicsWidget(parent),
+        m_gestureType(gestureType),
+        m_started(0), m_updated(0), m_canceled(0), m_finished(0)
+    {
+    }
+
+    bool event(QEvent *event) {
+        if (event->type() == QEvent::Gesture) {
+            QGesture *gesture = static_cast<QGestureEvent*>(event)->gesture(m_gestureType);
+            if (gesture) {
+                switch(gesture->state()) {
+                case Qt::GestureStarted: m_started++; break;
+                case Qt::GestureUpdated: m_updated++; break;
+                case Qt::GestureFinished: m_finished++; break;
+                case Qt::GestureCanceled: m_canceled++; break;
+                default: break;
+                }
+            }
+            return true;
+        }
+        if (event->type() == QEvent::GraphicsSceneMousePress) {
+            return true;
+        }
+        return QGraphicsWidget::event(event);
+    }
+
+    int started() { return m_started; }
+    int updated() { return m_updated; }
+    int finished() { return m_finished; }
+    int canceled() { return m_canceled; }
+
+  private:
+    Qt::GestureType m_gestureType;
+    int m_started;
+    int m_updated;
+    int m_canceled;
+    int m_finished;
+};
+
+void tst_Gestures::testReuseCanceledGestures()
+{
+    Qt::GestureType cancellingGestureTypeId = QGestureRecognizer::registerRecognizer(
+            new ReuseCanceledGesturesRecognizer(ReuseCanceledGesturesRecognizer::RmbAndCancelAllType));
+    Qt::GestureType tapGestureTypeId = QGestureRecognizer::registerRecognizer(
+            new ReuseCanceledGesturesRecognizer(ReuseCanceledGesturesRecognizer::LmbType));
+
+    QMainWindow mw;
+    mw.setWindowFlags(Qt::X11BypassWindowManagerHint);
+    QGraphicsView *gv = new QGraphicsView(&mw);
+    QGraphicsScene *scene = new QGraphicsScene;
+
+    gv->setScene(scene);
+    scene->setSceneRect(0,0,100,100);
+
+    // Create container and add to the scene
+    ReuseCanceledGesturesWidget *container = new ReuseCanceledGesturesWidget;
+    container->grabGesture(cancellingGestureTypeId); // << container grabs canceling gesture
+
+    // Create widget and add to the scene
+    ReuseCanceledGesturesWidget *target = new ReuseCanceledGesturesWidget(tapGestureTypeId, container);
+    target->grabGesture(tapGestureTypeId);
+
+    container->setGeometry(scene->sceneRect());
+
+    scene->addItem(container);
+
+    mw.setCentralWidget(gv);
+
+    // Viewport needs to grab all gestures that widgets in scene grab
+    gv->viewport()->grabGesture(cancellingGestureTypeId);
+    gv->viewport()->grabGesture(tapGestureTypeId);
+
+    mw.show();
+    QTest::qWaitForWindowShown(&mw);
+
+    QPoint targetPos(gv->mapFromScene(target->mapToScene(target->rect().center())));
+    targetPos = gv->viewport()->mapFromParent(targetPos);
+
+    // "Tap" starts on child widget
+    QTest::mousePress(gv->viewport(), Qt::LeftButton, 0, targetPos);
+    QCOMPARE(target->started(),  1);
+    QCOMPARE(target->updated(),  0);
+    QCOMPARE(target->finished(), 0);
+    QCOMPARE(target->canceled(), 0);
+
+    // Canceling gesture starts on parent
+    QTest::mousePress(gv->viewport(), Qt::RightButton, 0, targetPos);
+    QCOMPARE(target->started(),  1);
+    QCOMPARE(target->updated(),  0);
+    QCOMPARE(target->finished(), 0);
+    QCOMPARE(target->canceled(), 1); // <- child canceled
+
+    // Canceling gesture ends
+    QTest::mouseRelease(gv->viewport(), Qt::RightButton, 0, targetPos);
+    QCOMPARE(target->started(),  1);
+    QCOMPARE(target->updated(),  0);
+    QCOMPARE(target->finished(), 0);
+    QCOMPARE(target->canceled(), 1);
+
+    // Tap would end if not canceled
+    QTest::mouseRelease(gv->viewport(), Qt::LeftButton, 0, targetPos);
+    QCOMPARE(target->started(),  1);
+    QCOMPARE(target->updated(),  0);
+    QCOMPARE(target->finished(), 0);
+    QCOMPARE(target->canceled(), 1);
+
+    // New "Tap" starts
+    QTest::mousePress(gv->viewport(), Qt::LeftButton, 0, targetPos);
+    QCOMPARE(target->started(),  2);
+    QCOMPARE(target->updated(),  0);
+    QCOMPARE(target->finished(), 0);
+    QCOMPARE(target->canceled(), 1);
+
+    QTest::mouseRelease(gv->viewport(), Qt::LeftButton, 0, targetPos);
+    QCOMPARE(target->started(),  2);
+    QCOMPARE(target->updated(),  0);
+    QCOMPARE(target->finished(), 1);
+    QCOMPARE(target->canceled(), 1);
+}
+
+void tst_Gestures::conflictingGesturesInGraphicsView()
+{
+    QGraphicsScene scene;
+    GraphicsView view(&scene);
+    view.setWindowFlags(Qt::X11BypassWindowManagerHint);
+
+    GestureItem *item1 = new GestureItem("item1");
+    item1->grabGesture(CustomGesture::GestureType);
+    item1->size = QRectF(0, 0, 100, 100);
+    item1->setZValue(2);
+    scene.addItem(item1);
+
+    GestureItem *item2 = new GestureItem("item2");
+    item2->grabGesture(CustomGesture::GestureType);
+    item2->size = QRectF(0, 0, 100, 100);
+    item2->setZValue(5);
+    scene.addItem(item2);
+
+    view.show();
+    QTest::qWaitForWindowShown(&view);
+    view.ensureVisible(scene.sceneRect());
+
+    static const int TotalGestureEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialStartedThreshold + 1;
+
+    CustomEvent event;
+
+    // nobody accepts override
+    item1->acceptGestureOverride = false;
+    item2->acceptGestureOverride = false;
+    event.hotSpot = mapToGlobal(item2->boundingRect().center(), item2, &view);
+    event.hasHotSpot = true;
+    sendCustomGesture(&event, item2, &scene);
+    QCOMPARE(item2->gestureOverrideEventsReceived, 1);
+    QCOMPARE(item2->gestureEventsReceived, TotalGestureEventsCount);
+    QCOMPARE(item1->gestureOverrideEventsReceived, 1);
+    QCOMPARE(item1->gestureEventsReceived, 0);
+
+    item1->reset(); item2->reset();
+
+    // the original target accepts override
+    item1->acceptGestureOverride = false;
+    item2->acceptGestureOverride = true;
+    event.hotSpot = mapToGlobal(item2->boundingRect().center(), item2, &view);
+    event.hasHotSpot = true;
+    sendCustomGesture(&event, item2, &scene);
+    QCOMPARE(item2->gestureOverrideEventsReceived, 1);
+    QCOMPARE(item2->gestureEventsReceived, TotalGestureEventsCount);
+    QCOMPARE(item1->gestureOverrideEventsReceived, 0);
+    QCOMPARE(item1->gestureEventsReceived, 0);
+
+    item1->reset(); item2->reset();
+
+    // the item behind accepts override
+    item1->acceptGestureOverride = true;
+    item2->acceptGestureOverride = false;
+    event.hotSpot = mapToGlobal(item2->boundingRect().center(), item2, &view);
+    event.hasHotSpot = true;
+    sendCustomGesture(&event, item2, &scene);
+
+    QCOMPARE(item2->gestureOverrideEventsReceived, 1);
+    QCOMPARE(item2->gestureEventsReceived, 0);
+    QCOMPARE(item1->gestureOverrideEventsReceived, 1);
+    QCOMPARE(item1->gestureEventsReceived, TotalGestureEventsCount);
+}
+
 QTEST_MAIN(tst_Gestures)
 #include "tst_gestures.moc"