--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ginebra2/Gestures/GestureRecognizer.cpp Wed Aug 18 09:37:05 2010 +0300
@@ -0,0 +1,398 @@
+/*
+* Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Lesser General Public License as published by
+* the Free Software Foundation, version 2.1 of the License.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License
+* along with this program. If not,
+* see "http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html/".
+*
+* Description:
+*
+*/
+
+/*
+ W A R N I N G
+ -------------
+ THIS IS A TEMPORARY GESTURE CODE. WOULD BE REPLACED WHEN BROWSER HAS ITS OWN GESTURE FRAMEWORK
+ */
+
+#include "GestureRecognizer.h"
+
+#include "GestureListener.h"
+#include "GestureRecognizer_p.h"
+
+#include <QtGui>
+
+static const int DoubleClickFilterDuration = 300;
+static const int LongClickFilterDuration = 1000;
+static const int MinTimeHoldForClick = 50;
+static const int ThresholdForMove = 30;
+
+namespace GVA {
+
+GestureRecognizer::GestureRecognizer(GestureListener* gestureListener)
+ : d_ptr(new GestureRecognizerPrivate(gestureListener))
+{
+ Q_D(GestureRecognizer);
+ d->q_ptr = this;
+}
+
+GestureRecognizer::~GestureRecognizer()
+{ }
+
+bool GestureRecognizer::mouseEventFilter(QGraphicsSceneMouseEvent* event)
+{
+ Q_D(GestureRecognizer);
+ bool handled = false;
+
+ switch(event->type()) {
+ case QEvent::GraphicsSceneMouseDoubleClick:
+ handled = d->mouseDoubleClickEvent(event);
+ break;
+ case QEvent::GraphicsSceneMouseMove:
+ handled = d->mouseMoveEvent(event);
+ break;
+ case QEvent::GraphicsSceneMousePress:
+ handled = d->mousePressEvent(event);
+ break;
+ case QEvent::GraphicsSceneMouseRelease:
+ handled = d->mouseReleaseEvent(event);
+ break;
+ case QEvent::GraphicsSceneContextMenu:
+ //Swallow context menu event.
+ //Since we have own way of handling it
+ handled = true;
+ default:
+ break;
+ }
+ return handled;
+}
+
+qreal GestureRecognizer::dragInertia() const
+{
+ Q_D(const GestureRecognizer);
+ return d->m_dragInertia;
+}
+
+void GestureRecognizer::setDragInertia(qreal inertia)
+{
+ Q_D(GestureRecognizer);
+ d->m_dragInertia = inertia;
+}
+
+int GestureRecognizer::directionErrorMargin() const
+{
+ Q_D(const GestureRecognizer);
+ return d->m_directionErrorMargin;
+}
+
+void GestureRecognizer::setDirectionErrorMargin(int errorMargin)
+{
+ Q_D(GestureRecognizer);
+ d->m_directionErrorMargin = errorMargin;
+}
+
+qreal GestureRecognizer::axisLockThreshold() const
+{
+ Q_D(const GestureRecognizer);
+ return d->m_axisLockThreshold;
+}
+
+void GestureRecognizer::setAxisLockThreshold(qreal threshold)
+{
+ Q_D(GestureRecognizer);
+ d->m_axisLockThreshold = threshold;
+}
+
+qreal GestureRecognizer::maximumVelocity() const
+{
+ Q_D(const GestureRecognizer);
+ return d->m_maxVelocity;
+}
+
+void GestureRecognizer::setMaximumVelocity(qreal v)
+{
+ Q_D(GestureRecognizer);
+ d->m_maxVelocity = v;
+}
+
+qreal GestureRecognizer::minimumVelocity() const
+{
+ Q_D(const GestureRecognizer);
+ return d->m_minVelocity;
+}
+
+void GestureRecognizer::setMinimumVelocity(qreal v)
+{
+ Q_D(GestureRecognizer);
+ d->m_minVelocity = v;
+}
+
+int GestureRecognizer::panningThreshold() const
+{
+ Q_D(const GestureRecognizer);
+ return d->m_panningThreshold;
+}
+
+void GestureRecognizer::setPanningThreshold(int threshold)
+{
+ Q_D(GestureRecognizer);
+ d->m_panningThreshold = threshold;
+}
+
+qreal GestureRecognizer::fastVelocityFactor() const
+{
+ Q_D(const GestureRecognizer);
+ return d->m_fastVelocityFactor;
+}
+
+void GestureRecognizer::setFastVelocityFactor(qreal v)
+{
+ Q_D(GestureRecognizer);
+ d->m_fastVelocityFactor = v;
+}
+
+int GestureRecognizer::scrollsPerSecond() const
+{
+ Q_D(const GestureRecognizer);
+ return d->m_fastVelocityFactor;
+}
+
+void GestureRecognizer::setScrollsPerSecond(int sps)
+{
+ Q_D(GestureRecognizer);
+ d->m_scrollsPerSecond = sps;
+}
+
+//
+//GestureRecognizerPrivate DIFINITION
+//
+GestureRecognizerPrivate::GestureRecognizerPrivate(GestureListener* gestureListener)
+ : m_gestureListener(gestureListener)
+ , m_state(GestureRecognizerPrivate::Inactive)
+ , m_dragInertia(0.85)
+ , m_directionErrorMargin(10)
+ , m_axisLockThreshold(0)
+ , m_maxVelocity(3500)
+ , m_minVelocity(10)
+ , m_panningThreshold(25)
+ , m_fastVelocityFactor(0.01)
+ , m_scrollsPerSecond(20)
+ , m_velocity(QPointF(0, 0))
+ , m_position(QPointF(-1, -1))
+ , m_initialPos(QPointF(-1, -1))
+
+{ }
+
+GestureRecognizerPrivate::~GestureRecognizerPrivate()
+{ }
+
+bool GestureRecognizerPrivate::mousePressEvent(QGraphicsSceneMouseEvent* event)
+{
+ if (event->button() != Qt::LeftButton)
+ return false;
+
+ if (m_state == GestureRecognizerPrivate::Inactive) {
+ //First mouse press.
+ m_position = event->scenePos();
+ m_initialPos = m_position;
+
+ changeState(GestureRecognizerPrivate::Press);
+
+ GestureEvent gesture = gestureEvent(m_initialPos, GestureEvent::Touch);
+ m_gestureListener->handleGesture(&gesture);
+
+ m_lastTime.start();
+ m_delayedPressMoment.start();
+ m_timer.start(LongClickFilterDuration, this);
+ } else if(m_state == GestureRecognizerPrivate::Release) {
+ //This press is for double tap.
+ changeState(GestureRecognizerPrivate::DoublePress);
+ }
+ event->setAccepted(true);
+ return true;
+}
+
+bool GestureRecognizerPrivate::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
+{
+ if (event->button() != Qt::LeftButton)
+ return true;
+
+ if (m_state == GestureRecognizerPrivate::Inactive) {
+ //Release event cannot be generated in gesture recognizer is in active!
+ event->setAccepted(true);
+ resetTouchPositions();
+ return true;
+ }
+
+ if (m_state == GestureRecognizerPrivate::Press) {
+ if (m_delayedPressMoment.elapsed() > MinTimeHoldForClick) {
+ //Waiting for MinTimeHoldForClick to make sure use has actually pressed.
+ //Removes accidentall press
+ changeState(GestureRecognizerPrivate::Release);
+ m_timer.start(DoubleClickFilterDuration, this);
+ } else
+ resetTouchPositions();
+ } else if (m_state == GestureRecognizerPrivate::Move) {
+ if (qAbs(m_velocity.x()) > m_minVelocity
+ || qAbs(m_velocity.y()) > m_minVelocity) {
+ GestureEvent gesture = gestureEvent(m_position, GestureEvent::Flick);
+ gesture.setVelocity(m_velocity);
+ m_gestureListener->handleGesture(&gesture);
+ }
+ m_velocity = QPointF(0,0);
+ resetTouchPositions();
+ } else if (m_state == GestureRecognizerPrivate::DoublePress) {
+ //Stop double tap timer
+ m_timer.stop();
+ GestureEvent gesture = gestureEvent(m_initialPos, GestureEvent::DoubleTap);
+ m_gestureListener->handleGesture(&gesture);
+ resetTouchPositions();
+ }
+ event->setAccepted(true);
+ return true;
+}
+
+bool GestureRecognizerPrivate::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
+{
+ QPoint delta;
+ if (m_state == GestureRecognizerPrivate::Press) {
+ delta = (event->scenePos() - m_initialPos).toPoint();
+ QPoint absDelta;
+ absDelta.setX(qAbs(delta.x()));
+ absDelta.setY(qAbs(delta.y()));
+
+ if ((absDelta.x() > ThresholdForMove) || (absDelta.y() > ThresholdForMove)) {
+ //Stop long tap timer
+ m_timer.stop();
+
+ changeState(GestureRecognizerPrivate::Move);
+ m_position = event->scenePos();
+ } else {
+ //Ignore until user has actually moved
+ return true;
+ }
+ } else if (m_state == GestureRecognizerPrivate::Move) {
+ delta = (event->scenePos() - m_position).toPoint();
+ m_position = event->scenePos();
+ } else {
+ resetTouchPositions();
+ return true;
+ }
+
+ //Aplly if axis stickiness is specified.
+ if (m_axisLockThreshold) {
+ int dx = qAbs(delta.x());
+ int dy = qAbs(delta.y());
+
+ if (dx || dy) {
+ bool vertical = (dy > dx);
+ qreal alpha = qreal(vertical ? dx : dy) / qreal(vertical ? dy : dx);
+ if (alpha <= m_axisLockThreshold) {
+ if (vertical)
+ delta.setX(0);
+ else
+ delta.setY(0);
+ }
+ }
+ }
+
+ m_velocity = calculateVelocity(delta, m_lastTime.elapsed());
+
+ //Send pan gesture
+ GestureEvent gesture = gestureEvent(m_position, GestureEvent::Pan);
+ gesture.setDelta(delta);
+ m_gestureListener->handleGesture(&gesture);
+ m_lastTime.restart();
+
+ event->setAccepted(true);
+ return true;
+}
+
+bool GestureRecognizerPrivate::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event)
+{
+ if (m_state == GestureRecognizerPrivate::Release) {
+ m_timer.stop();
+ GestureEvent gesture = gestureEvent(m_initialPos, GestureEvent::DoubleTap);
+ m_gestureListener->handleGesture(&gesture);
+ }
+ resetTouchPositions();
+ event->setAccepted(true);
+ return true;
+}
+
+void GestureRecognizerPrivate::changeState(GestureRecognizerPrivate::State state)
+{
+ m_state = state;
+}
+
+GestureEvent GestureRecognizerPrivate::gestureEvent(const QPointF& pos, GestureEvent::Type type)
+{
+ GestureEvent gestureEvent;
+ gestureEvent.setType(type);
+ gestureEvent.setPosition(pos);
+ return gestureEvent;
+}
+
+void GestureRecognizerPrivate::timerEvent(QTimerEvent* event)
+{
+ if (event->timerId() != m_timer.timerId())
+ return;
+
+ m_timer.stop();
+
+ QPointF position;
+ GestureEvent::Type gestureType;
+
+ if (m_state == GestureRecognizerPrivate::Press) {
+ //Long press
+ position = m_position;
+ gestureType = GestureEvent::LongTap;
+ } else if (m_state == GestureRecognizerPrivate::Release) {
+ //Actual release event
+ position = m_initialPos;
+ gestureType = GestureEvent::Release;
+ } else {
+ resetTouchPositions();
+ changeState(GestureRecognizerPrivate::Inactive);
+ return;
+ }
+
+ resetTouchPositions();
+ GestureEvent gesture = gestureEvent(position, gestureType);
+ m_gestureListener->handleGesture(&gesture);
+ return;
+}
+
+void GestureRecognizerPrivate::resetTouchPositions()
+{
+ m_position = QPointF(-1, -1);
+ m_initialPos = m_position;
+ changeState(GestureRecognizerPrivate::Inactive);
+}
+
+QPointF GestureRecognizerPrivate::calculateVelocity(const QPointF& delta, int time)
+{
+ QPointF newVelocity = m_velocity;
+
+ if ((delta / qreal(time)).manhattanLength() < 25) {
+ QPointF rawVelocity = delta / qreal(time) * qreal(1000) / qreal(m_scrollsPerSecond);
+ newVelocity = newVelocity * (qreal(1) - m_dragInertia) + rawVelocity * m_dragInertia;
+ }
+
+ newVelocity.setX(delta.x() ? qBound(-m_maxVelocity, newVelocity.x(), m_maxVelocity) : m_velocity.x());
+ newVelocity.setY(delta.y() ? qBound(-m_maxVelocity, newVelocity.y(), m_maxVelocity) : m_velocity.y());
+ return newVelocity;
+}
+
+} // namespace GVA
+