src/gui/kernel/qstandardgestures.cpp
changeset 3 41300fa6a67c
parent 0 1918ee327afb
child 4 3b1da2848fc7
--- a/src/gui/kernel/qstandardgestures.cpp	Tue Jan 26 12:42:25 2010 +0200
+++ b/src/gui/kernel/qstandardgestures.cpp	Tue Feb 02 00:43:10 2010 +0200
@@ -44,6 +44,8 @@
 #include "qgesture_p.h"
 #include "qevent.h"
 #include "qwidget.h"
+#include "qabstractscrollarea.h"
+#include "qdebug.h"
 
 QT_BEGIN_NAMESPACE
 
@@ -51,29 +53,35 @@
 {
 }
 
-QGesture *QPanGestureRecognizer::createGesture(QObject *target)
+QGesture *QPanGestureRecognizer::create(QObject *target)
 {
     if (target && target->isWidgetType()) {
+#if defined(Q_OS_WIN) && !defined(QT_NO_NATIVE_GESTURES)
+        // for scroll areas on Windows we want to use native gestures instead
+        if (!qobject_cast<QAbstractScrollArea *>(target->parent()))
+            static_cast<QWidget *>(target)->setAttribute(Qt::WA_AcceptTouchEvents);
+#else
         static_cast<QWidget *>(target)->setAttribute(Qt::WA_AcceptTouchEvents);
+#endif
     }
     return new QPanGesture;
 }
 
-QGestureRecognizer::Result QPanGestureRecognizer::filterEvent(QGesture *state, QObject *, QEvent *event)
+QGestureRecognizer::Result QPanGestureRecognizer::recognize(QGesture *state,
+                                                            QObject *,
+                                                            QEvent *event)
 {
-    QPanGesture *q = static_cast<QPanGesture*>(state);
+    QPanGesture *q = static_cast<QPanGesture *>(state);
     QPanGesturePrivate *d = q->d_func();
 
-    const QTouchEvent *ev = static_cast<const QTouchEvent*>(event);
+    const QTouchEvent *ev = static_cast<const QTouchEvent *>(event);
 
     QGestureRecognizer::Result result;
-
     switch (event->type()) {
     case QEvent::TouchBegin: {
-        result = QGestureRecognizer::MaybeGesture;
+        result = QGestureRecognizer::MayBeGesture;
         QTouchEvent::TouchPoint p = ev->touchPoints().at(0);
-        d->lastPosition = p.pos().toPoint();
-        d->lastOffset = d->totalOffset = d->offset = QPointF();
+        d->lastOffset = d->offset = QPointF();
         break;
     }
     case QEvent::TouchEnd: {
@@ -83,13 +91,12 @@
                 QTouchEvent::TouchPoint p2 = ev->touchPoints().at(1);
                 d->lastOffset = d->offset;
                 d->offset =
-                        QPointF(p1.pos().x() - p1.lastPos().x() + p2.pos().x() - p2.lastPos().x(),
-                              p1.pos().y() - p1.lastPos().y() + p2.pos().y() - p2.lastPos().y()) / 2;
-                d->totalOffset += d->offset;
+                        QPointF(p1.pos().x() - p1.startPos().x() + p2.pos().x() - p2.startPos().x(),
+                              p1.pos().y() - p1.startPos().y() + p2.pos().y() - p2.startPos().y()) / 2;
             }
-            result = QGestureRecognizer::GestureFinished;
+            result = QGestureRecognizer::FinishGesture;
         } else {
-            result = QGestureRecognizer::NotGesture;
+            result = QGestureRecognizer::CancelGesture;
         }
         break;
     }
@@ -99,14 +106,13 @@
             QTouchEvent::TouchPoint p2 = ev->touchPoints().at(1);
             d->lastOffset = d->offset;
             d->offset =
-                    QPointF(p1.pos().x() - p1.lastPos().x() + p2.pos().x() - p2.lastPos().x(),
-                          p1.pos().y() - p1.lastPos().y() + p2.pos().y() - p2.lastPos().y()) / 2;
-            d->totalOffset += d->offset;
-            if (d->totalOffset.x() > 10  || d->totalOffset.y() > 10 ||
-                d->totalOffset.x() < -10 || d->totalOffset.y() < -10) {
-                result = QGestureRecognizer::GestureTriggered;
+                    QPointF(p1.pos().x() - p1.startPos().x() + p2.pos().x() - p2.startPos().x(),
+                          p1.pos().y() - p1.startPos().y() + p2.pos().y() - p2.startPos().y()) / 2;
+            if (d->offset.x() > 10  || d->offset.y() > 10 ||
+                d->offset.x() < -10 || d->offset.y() < -10) {
+                result = QGestureRecognizer::TriggerGesture;
             } else {
-                result = QGestureRecognizer::MaybeGesture;
+                result = QGestureRecognizer::MayBeGesture;
             }
         }
         break;
@@ -128,109 +134,433 @@
     QPanGesture *pan = static_cast<QPanGesture*>(state);
     QPanGesturePrivate *d = pan->d_func();
 
-    d->totalOffset = d->lastOffset = d->offset = QPointF();
-    d->lastPosition = QPoint();
+    d->lastOffset = d->offset = QPointF();
     d->acceleration = 0;
 
-//#if defined(QT_MAC_USE_COCOA)
-//    d->singleTouchPanTimer.stop();
-//    d->prevMousePos = QPointF(0, 0);
-//#endif
+    QGestureRecognizer::reset(state);
+}
+
+
+//
+// QPinchGestureRecognizer
+//
+
+QPinchGestureRecognizer::QPinchGestureRecognizer()
+{
+}
+
+QGesture *QPinchGestureRecognizer::create(QObject *target)
+{
+    if (target && target->isWidgetType()) {
+        static_cast<QWidget *>(target)->setAttribute(Qt::WA_AcceptTouchEvents);
+    }
+    return new QPinchGesture;
+}
+
+QGestureRecognizer::Result QPinchGestureRecognizer::recognize(QGesture *state,
+                                                              QObject *,
+                                                              QEvent *event)
+{
+    QPinchGesture *q = static_cast<QPinchGesture *>(state);
+    QPinchGesturePrivate *d = q->d_func();
+
+    const QTouchEvent *ev = static_cast<const QTouchEvent *>(event);
+
+    QGestureRecognizer::Result result;
+
+    switch (event->type()) {
+    case QEvent::TouchBegin: {
+        result = QGestureRecognizer::MayBeGesture;
+        break;
+    }
+    case QEvent::TouchEnd: {
+        if (q->state() != Qt::NoGesture) {
+            result = QGestureRecognizer::FinishGesture;
+        } else {
+            result = QGestureRecognizer::CancelGesture;
+        }
+        break;
+    }
+    case QEvent::TouchUpdate: {
+        d->changeFlags = 0;
+        if (ev->touchPoints().size() == 2) {
+            QTouchEvent::TouchPoint p1 = ev->touchPoints().at(0);
+            QTouchEvent::TouchPoint p2 = ev->touchPoints().at(1);
+
+            d->hotSpot = p1.screenPos();
+            d->isHotSpotSet = true;
+
+            if (d->isNewSequence) {
+                d->startPosition[0] = p1.screenPos();
+                d->startPosition[1] = p2.screenPos();
+            }
+            QLineF line(p1.screenPos(), p2.screenPos());
+            QLineF tmp(line);
+            tmp.setLength(line.length() / 2.);
+            QPointF centerPoint = tmp.p2();
+
+            d->lastCenterPoint = d->centerPoint;
+            d->centerPoint = centerPoint;
+            d->changeFlags |= QPinchGesture::CenterPointChanged;
+
+            const qreal scaleFactor =
+                    QLineF(p1.screenPos(), p2.screenPos()).length()
+                    / QLineF(d->startPosition[0],  d->startPosition[1]).length();
+            if (d->isNewSequence) {
+                d->lastScaleFactor = scaleFactor;
+            } else {
+                d->lastScaleFactor = d->scaleFactor;
+            }
+            d->scaleFactor = scaleFactor;
+            d->totalScaleFactor += d->scaleFactor - d->lastScaleFactor;
+            d->changeFlags |= QPinchGesture::ScaleFactorChanged;
+
+            qreal angle = QLineF(p1.screenPos(), p2.screenPos()).angle();
+            if (angle > 180)
+                angle -= 360;
+            qreal startAngle = QLineF(p1.startScreenPos(), p2.startScreenPos()).angle();
+            if (startAngle > 180)
+                startAngle -= 360;
+            const qreal rotationAngle = startAngle - angle;
+            if (d->isNewSequence)
+                d->lastRotationAngle = rotationAngle;
+            else
+                d->lastRotationAngle = d->rotationAngle;
+            d->rotationAngle = rotationAngle;
+            d->totalRotationAngle += d->rotationAngle - d->lastRotationAngle;
+            d->changeFlags |= QPinchGesture::RotationAngleChanged;
+
+            d->totalChangeFlags |= d->changeFlags;
+            d->isNewSequence = false;
+            result = QGestureRecognizer::TriggerGesture;
+        } else {
+            d->isNewSequence = true;
+            if (q->state() == Qt::NoGesture)
+                result = QGestureRecognizer::Ignore;
+            else
+                result = QGestureRecognizer::FinishGesture;
+        }
+        break;
+    }
+    case QEvent::MouseButtonPress:
+    case QEvent::MouseMove:
+    case QEvent::MouseButtonRelease:
+        result = QGestureRecognizer::Ignore;
+        break;
+    default:
+        result = QGestureRecognizer::Ignore;
+        break;
+    }
+    return result;
+}
+
+void QPinchGestureRecognizer::reset(QGesture *state)
+{
+    QPinchGesture *pinch = static_cast<QPinchGesture *>(state);
+    QPinchGesturePrivate *d = pinch->d_func();
+
+    d->totalChangeFlags = d->changeFlags = 0;
+
+    d->startCenterPoint = d->lastCenterPoint = d->centerPoint = QPointF();
+    d->totalScaleFactor = d->lastScaleFactor = d->scaleFactor = 0;
+    d->totalRotationAngle = d->lastRotationAngle = d->rotationAngle = 0;
+
+    d->isNewSequence = true;
+    d->startPosition[0] = d->startPosition[1] = QPointF();
 
     QGestureRecognizer::reset(state);
 }
 
-/*! \internal */
-/*
-bool QPanGestureRecognizer::event(QEvent *event)
+//
+// QSwipeGestureRecognizer
+//
+
+QSwipeGestureRecognizer::QSwipeGestureRecognizer()
+{
+}
+
+QGesture *QSwipeGestureRecognizer::create(QObject *target)
 {
-#if defined(QT_MAC_USE_COCOA)
-    Q_D(QPanGesture);
-    if (event->type() == QEvent::Timer) {
-        const QTimerEvent *te = static_cast<QTimerEvent *>(event);
-        if (te->timerId() == d->singleTouchPanTimer.timerId()) {
-            d->singleTouchPanTimer.stop();
-            updateState(Qt::GestureStarted);
-        }
+    if (target && target->isWidgetType()) {
+        static_cast<QWidget *>(target)->setAttribute(Qt::WA_AcceptTouchEvents);
     }
-#endif
-
-    bool consume = false;
+    return new QSwipeGesture;
+}
 
-#if defined(Q_WS_WIN)
-#elif defined(QT_MAC_USE_COCOA)
-    // The following implements single touch
-    // panning on Mac:
-    const int panBeginDelay = 300;
-    const int panBeginRadius = 3;
-    const QTouchEvent *ev = static_cast<const QTouchEvent*>(event);
+QGestureRecognizer::Result QSwipeGestureRecognizer::recognize(QGesture *state,
+                                                              QObject *,
+                                                              QEvent *event)
+{
+    QSwipeGesture *q = static_cast<QSwipeGesture *>(state);
+    QSwipeGesturePrivate *d = q->d_func();
+
+    const QTouchEvent *ev = static_cast<const QTouchEvent *>(event);
+
+    QGestureRecognizer::Result result;
 
     switch (event->type()) {
     case QEvent::TouchBegin: {
-        if (ev->touchPoints().size() == 1) {
-            d->delayManager->setEnabled(true);
-            consume = d->delayManager->append(d->gestureTarget, *event);
-            d->lastPosition = QCursor::pos();
-            d->singleTouchPanTimer.start(panBeginDelay, this);
-        }
-        break;}
+        d->speed = 1;
+        d->time = QTime::currentTime();
+        d->started = true;
+        result = QGestureRecognizer::MayBeGesture;
+        break;
+    }
     case QEvent::TouchEnd: {
-        d->delayManager->setEnabled(false);
-        if (state() != Qt::NoGesture) {
-            updateState(Qt::GestureFinished);
-            consume = true;
-            d->delayManager->clear();
+        if (q->state() != Qt::NoGesture) {
+            result = QGestureRecognizer::FinishGesture;
         } else {
-            d->delayManager->replay();
+            result = QGestureRecognizer::CancelGesture;
         }
-        reset();
-        break;}
+        break;
+    }
     case QEvent::TouchUpdate: {
-        consume = d->delayManager->append(d->gestureTarget, *event);
-        if (ev->touchPoints().size() == 1) {
-            if (state() == Qt::NoGesture) {
-                // INVARIANT: The singleTouchTimer has still not fired.
-                // Lets check if the user moved his finger so much from
-                // the starting point that it makes sense to cancel:
-                const QPointF startPos = ev->touchPoints().at(0).startPos().toPoint();
-                const QPointF p = ev->touchPoints().at(0).pos().toPoint();
-                if ((startPos - p).manhattanLength() > panBeginRadius) {
-                    d->delayManager->replay();
-                    consume = false;
-                    reset();
-                } else {
-                    d->lastPosition = QCursor::pos();
+        if (!d->started)
+            result = QGestureRecognizer::CancelGesture;
+        else if (ev->touchPoints().size() == 3) {
+            QTouchEvent::TouchPoint p1 = ev->touchPoints().at(0);
+            QTouchEvent::TouchPoint p2 = ev->touchPoints().at(1);
+            QTouchEvent::TouchPoint p3 = ev->touchPoints().at(2);
+
+            if (d->lastPositions[0].isNull()) {
+                d->lastPositions[0] = p1.startScreenPos().toPoint();
+                d->lastPositions[1] = p2.startScreenPos().toPoint();
+                d->lastPositions[2] = p3.startScreenPos().toPoint();
+            }
+            d->hotSpot = p1.screenPos();
+            d->isHotSpotSet = true;
+
+            int xDistance = (p1.screenPos().x() - d->lastPositions[0].x() +
+                             p2.screenPos().x() - d->lastPositions[1].x() +
+                             p3.screenPos().x() - d->lastPositions[2].x()) / 3;
+            int yDistance = (p1.screenPos().y() - d->lastPositions[0].y() +
+                             p2.screenPos().y() - d->lastPositions[1].y() +
+                             p3.screenPos().y() - d->lastPositions[2].y()) / 3;
+
+            const int distance = xDistance >= yDistance ? xDistance : yDistance;
+            int elapsedTime = d->time.msecsTo(QTime::currentTime());
+            if (!elapsedTime)
+                elapsedTime = 1;
+            d->speed = 0.9 * d->speed + distance / elapsedTime;
+            d->time = QTime::currentTime();
+            d->swipeAngle = QLineF(p1.startScreenPos(), p1.screenPos()).angle();
+
+            static const int MoveThreshold = 50;
+            if (xDistance > MoveThreshold || yDistance > MoveThreshold) {
+                // measure the distance to check if the direction changed
+                d->lastPositions[0] = p1.screenPos().toPoint();
+                d->lastPositions[1] = p2.screenPos().toPoint();
+                d->lastPositions[2] = p3.screenPos().toPoint();
+                QSwipeGesture::SwipeDirection horizontal =
+                        xDistance > 0 ? QSwipeGesture::Right : QSwipeGesture::Left;
+                QSwipeGesture::SwipeDirection vertical =
+                        yDistance > 0 ? QSwipeGesture::Down : QSwipeGesture::Up;
+                if (d->verticalDirection == QSwipeGesture::NoDirection)
+                    d->verticalDirection = vertical;
+                if (d->horizontalDirection == QSwipeGesture::NoDirection)
+                    d->horizontalDirection = horizontal;
+                if (d->verticalDirection != vertical || d->horizontalDirection != horizontal) {
+                    // the user has changed the direction!
+                    result = QGestureRecognizer::CancelGesture;
                 }
+                result = QGestureRecognizer::TriggerGesture;
             } else {
-                d->delayManager->clear();
-                QPointF mousePos = QCursor::pos();
-                QPointF dist = mousePos - d->lastPosition;
-                d->lastPosition = mousePos;
-                d->lastOffset = d->offset;
-                d->offset = QSizeF(dist.x(), dist.y());
-                d->totalOffset += d->offset;
-                updateState(Qt::GestureUpdated);
+                if (q->state() != Qt::NoGesture)
+                    result = QGestureRecognizer::TriggerGesture;
+                else
+                    result = QGestureRecognizer::MayBeGesture;
             }
-        } else if (state() == Qt::NoGesture) {
-            d->delayManager->replay();
-            consume = false;
-            reset();
+        } else if (ev->touchPoints().size() > 3) {
+            result = QGestureRecognizer::CancelGesture;
+        } else { // less than 3 touch points
+            if (d->started && (ev->touchPointStates() & Qt::TouchPointPressed))
+                result = QGestureRecognizer::CancelGesture;
+            else if (d->started)
+                result = QGestureRecognizer::Ignore;
+            else
+                result = QGestureRecognizer::MayBeGesture;
         }
-        break;}
+        break;
+    }
     case QEvent::MouseButtonPress:
     case QEvent::MouseMove:
     case QEvent::MouseButtonRelease:
-        if (d->delayManager->isEnabled())
-            consume = d->delayManager->append(d->gestureTarget, *event);
+        result = QGestureRecognizer::Ignore;
+        break;
+    default:
+        result = QGestureRecognizer::Ignore;
+        break;
+    }
+    return result;
+}
+
+void QSwipeGestureRecognizer::reset(QGesture *state)
+{
+    QSwipeGesture *q = static_cast<QSwipeGesture *>(state);
+    QSwipeGesturePrivate *d = q->d_func();
+
+    d->verticalDirection = d->horizontalDirection = QSwipeGesture::NoDirection;
+    d->swipeAngle = 0;
+
+    d->lastPositions[0] = d->lastPositions[1] = d->lastPositions[2] = QPoint();
+    d->started = false;
+    d->speed = 0;
+    d->time = QTime();
+
+    QGestureRecognizer::reset(state);
+}
+
+//
+// QTapGestureRecognizer
+//
+
+QTapGestureRecognizer::QTapGestureRecognizer()
+{
+}
+
+QGesture *QTapGestureRecognizer::create(QObject *target)
+{
+    if (target && target->isWidgetType()) {
+        static_cast<QWidget *>(target)->setAttribute(Qt::WA_AcceptTouchEvents);
+    }
+    return new QTapGesture;
+}
+
+QGestureRecognizer::Result QTapGestureRecognizer::recognize(QGesture *state,
+                                                            QObject *,
+                                                            QEvent *event)
+{
+    QTapGesture *q = static_cast<QTapGesture *>(state);
+    QTapGesturePrivate *d = q->d_func();
+
+    const QTouchEvent *ev = static_cast<const QTouchEvent *>(event);
+
+    QGestureRecognizer::Result result = QGestureRecognizer::CancelGesture;
+
+    switch (event->type()) {
+    case QEvent::TouchBegin: {
+        d->position = ev->touchPoints().at(0).pos();
+        result = QGestureRecognizer::TriggerGesture;
+        break;
+    }
+    case QEvent::TouchUpdate:
+    case QEvent::TouchEnd: {
+        if (q->state() != Qt::NoGesture && ev->touchPoints().size() == 1) {
+            QTouchEvent::TouchPoint p = ev->touchPoints().at(0);
+            QPoint delta = p.pos().toPoint() - p.startPos().toPoint();
+            enum { TapRadius = 40 };
+            if (delta.manhattanLength() <= TapRadius) {
+                if (event->type() == QEvent::TouchEnd)
+                    result = QGestureRecognizer::FinishGesture;
+                else
+                    result = QGestureRecognizer::TriggerGesture;
+            }
+        }
+        break;
+    }
+    case QEvent::MouseButtonPress:
+    case QEvent::MouseMove:
+    case QEvent::MouseButtonRelease:
+        result = QGestureRecognizer::Ignore;
         break;
     default:
-        return false;
+        result = QGestureRecognizer::Ignore;
+        break;
+    }
+    return result;
+}
+
+void QTapGestureRecognizer::reset(QGesture *state)
+{
+    QTapGesture *q = static_cast<QTapGesture *>(state);
+    QTapGesturePrivate *d = q->d_func();
+
+    d->position = QPointF();
+
+    QGestureRecognizer::reset(state);
+}
+
+//
+// QTapAndHoldGestureRecognizer
+//
+
+QTapAndHoldGestureRecognizer::QTapAndHoldGestureRecognizer()
+{
+}
+
+QGesture *QTapAndHoldGestureRecognizer::create(QObject *target)
+{
+    if (target && target->isWidgetType()) {
+        static_cast<QWidget *>(target)->setAttribute(Qt::WA_AcceptTouchEvents);
+    }
+    return new QTapAndHoldGesture;
+}
+
+QGestureRecognizer::Result
+QTapAndHoldGestureRecognizer::recognize(QGesture *state, QObject *object,
+                                        QEvent *event)
+{
+    QTapAndHoldGesture *q = static_cast<QTapAndHoldGesture *>(state);
+    QTapAndHoldGesturePrivate *d = q->d_func();
+
+    if (object == state && event->type() == QEvent::Timer) {
+        q->killTimer(d->timerId);
+        d->timerId = 0;
+        return QGestureRecognizer::Ignore | QGestureRecognizer::ConsumeEventHint;
     }
-#else
-    Q_UNUSED(event);
-#endif
-    return QGestureRecognizer::Ignore;
+
+    const QTouchEvent *ev = static_cast<const QTouchEvent *>(event);
+
+    QGestureRecognizer::Result result = QGestureRecognizer::CancelGesture;
+
+    enum { TimerInterval = 2000 };
+    enum { TapRadius = 40 };
+
+    switch (event->type()) {
+    case QEvent::TouchBegin:
+        d->position = ev->touchPoints().at(0).pos();
+        if (d->timerId)
+            q->killTimer(d->timerId);
+        d->timerId = q->startTimer(TimerInterval);
+        result = QGestureRecognizer::TriggerGesture;
+        break;
+    case QEvent::TouchEnd:
+        if (d->timerId)
+            result = QGestureRecognizer::CancelGesture;
+        else
+            result = QGestureRecognizer::FinishGesture;
+        break;
+    case QEvent::TouchUpdate:
+        if (q->state() != Qt::NoGesture && ev->touchPoints().size() == 1) {
+            QTouchEvent::TouchPoint p = ev->touchPoints().at(0);
+            QPoint delta = p.pos().toPoint() - p.startPos().toPoint();
+            if (delta.manhattanLength() <= TapRadius)
+                result = QGestureRecognizer::TriggerGesture;
+        }
+        break;
+    case QEvent::MouseButtonPress:
+    case QEvent::MouseMove:
+    case QEvent::MouseButtonRelease:
+        result = QGestureRecognizer::Ignore;
+        break;
+    default:
+        result = QGestureRecognizer::Ignore;
+        break;
+    }
+    return result;
 }
-    */
+
+void QTapAndHoldGestureRecognizer::reset(QGesture *state)
+{
+    QTapAndHoldGesture *q = static_cast<QTapAndHoldGesture *>(state);
+    QTapAndHoldGesturePrivate *d = q->d_func();
+
+    d->position = QPointF();
+    if (d->timerId)
+        q->killTimer(d->timerId);
+    d->timerId = 0;
+
+    QGestureRecognizer::reset(state);
+}
 
 QT_END_NAMESPACE