diff options
-rw-r--r-- | src/imports/location/location.cpp | 1 | ||||
-rw-r--r-- | src/imports/location/location.pro | 7 | ||||
-rw-r--r-- | src/imports/location/qdeclarativegeomap.cpp | 30 | ||||
-rw-r--r-- | src/imports/location/qdeclarativegeomap_p.h | 5 | ||||
-rw-r--r-- | src/imports/location/qdeclarativegeomapflickable.cpp | 352 | ||||
-rw-r--r-- | src/imports/location/qdeclarativegeomapflickable_p.h | 49 | ||||
-rw-r--r-- | src/imports/location/qdeclarativegeomapgesturearea.cpp | 1407 | ||||
-rw-r--r-- | src/imports/location/qdeclarativegeomapgesturearea_p.h | 365 | ||||
-rw-r--r-- | src/imports/location/qdeclarativegeomappincharea.cpp | 499 | ||||
-rw-r--r-- | src/imports/location/qdeclarativegeomappincharea_p.h | 209 | ||||
-rw-r--r-- | src/src.pro | 1 |
11 files changed, 1927 insertions, 998 deletions
diff --git a/src/imports/location/location.cpp b/src/imports/location/location.cpp index acf84429..015ec3dc 100644 --- a/src/imports/location/location.cpp +++ b/src/imports/location/location.cpp @@ -136,6 +136,7 @@ public: qmlRegisterUncreatableType<QDeclarativeGeoMapFlickable>(uri, 5, 0, "MapFlickable", QDeclarativeGeoMapFlickable::tr("(Map)Flickable is not intended instantiable by developer.")); qmlRegisterUncreatableType<QDeclarativeGeoMapPinchArea>(uri, 5, 0, "MapPinchArea", QDeclarativeGeoMapPinchArea::tr("(Map)PinchArea is not intended instantiable by developer.")); qmlRegisterUncreatableType<QDeclarativeGeoMapPinchEvent>(uri, 5, 0, "MapPinchEvent", QDeclarativeGeoMapPinchEvent::tr("(Map)PinchEvent is not intended instantiable by developer.")); + qmlRegisterUncreatableType<QDeclarativeGeoMapGestureArea>(uri, 5, 0, "MapGestureArea", QDeclarativeGeoMapGestureArea::tr("(Map)GestureArea is not intended instantiable by developer.")); qmlRegisterUncreatableType<QDeclarativeGeoMapType>(uri, 5, 0, "MapType",QDeclarativeGeoMapType::tr("MapType is not intended instantiable by developer.")); qmlRegisterType<QDeclarativeCategory>(uri, 5, 0, "Category"); qmlRegisterType<QDeclarativePlaceEditorialModel>(uri, 5, 0, "EditorialModel"); diff --git a/src/imports/location/location.pro b/src/imports/location/location.pro index 189058bc..f17ce858 100644 --- a/src/imports/location/location.pro +++ b/src/imports/location/location.pro @@ -10,6 +10,7 @@ target.path = $$[QT_INSTALL_IMPORTS]/$$TARGETPATH INCLUDEPATH += ../../location/maps INCLUDEPATH *= $$PWD +DEFINES += TOUCH_EVENT_WORKAROUND # On some platforms, build both versions because debug and release # versions are incompatible @@ -45,7 +46,8 @@ HEADERS += qdeclarativeposition_p.h \ qdeclarativepolylinemapitem_p.h \ qdeclarativeroutemapitem_p.h \ qgeomapitemgeometry_p.h \ - qdeclarativegeomapcopyrightsnotice_p.h + qdeclarativegeomapcopyrightsnotice_p.h \ + qdeclarativegeomapgesturearea_p.h SOURCES += qdeclarativeposition.cpp \ location.cpp \ @@ -78,7 +80,8 @@ SOURCES += qdeclarativeposition.cpp \ qdeclarativepolylinemapitem.cpp \ qdeclarativeroutemapitem.cpp \ qgeomapitemgeometry.cpp \ - qdeclarativegeomapcopyrightsnotice.cpp + qdeclarativegeomapcopyrightsnotice.cpp \ + qdeclarativegeomapgesturearea.cpp include(declarativeplaces/declarativeplaces.pri) diff --git a/src/imports/location/qdeclarativegeomap.cpp b/src/imports/location/qdeclarativegeomap.cpp index 9b8473d4..e5d38555 100644 --- a/src/imports/location/qdeclarativegeomap.cpp +++ b/src/imports/location/qdeclarativegeomap.cpp @@ -207,8 +207,9 @@ QDeclarativeGeoMap::QDeclarativeGeoMap(QQuickItem *parent) connect(this, SIGNAL(childrenChanged()), this, SLOT(onMapChildrenChanged()), Qt::QueuedConnection); // Create internal flickable and pinch area. - flickable_ = new QDeclarativeGeoMapFlickable(this); - pinchArea_ = new QDeclarativeGeoMapPinchArea(this, this); + gestureArea_ = new QDeclarativeGeoMapGestureArea(this, this); + flickable_ = new QDeclarativeGeoMapFlickable(this, gestureArea_); + pinchArea_ = new QDeclarativeGeoMapPinchArea(this, gestureArea_); } QDeclarativeGeoMap::~QDeclarativeGeoMap() @@ -344,21 +345,36 @@ bool QDeclarativeGeoMap::mouseEvent(QMouseEvent* event) return false; switch (event->type()) { case QEvent::MouseButtonPress: - return flickable_->mousePressEvent(event); + return gestureArea_->mousePressEvent(event); case QEvent::MouseButtonRelease: - return flickable_->mouseReleaseEvent(event); + return gestureArea_->mouseReleaseEvent(event); case QEvent::MouseMove: - return flickable_->mouseMoveEvent(event); + return gestureArea_->mouseMoveEvent(event); default: return false; } } + +/*! + \qmlproperty MapPinchArea QtLocation5::Map::gesture + + Contains the MapGestureArea created with the Map. This covers pan, flick and pinch gestures. + Use \c{gesture.enabled: true} to enable basic gestures, or see \l{MapGestureArea} for + further details. +*/ + +QDeclarativeGeoMapGestureArea* QDeclarativeGeoMap::gesture() +{ + return gestureArea_; +} + /*! \qmlproperty MapPinchArea QtLocation5::Map::pinch Contains the MapPinchArea created with the Map. Use \c{pinch.enabled: true} to enable basic pinch gestures, or see \l{MapPinchArea} for further details. + This object will be deprecated, use the gesture object instead. */ QDeclarativeGeoMapPinchArea* QDeclarativeGeoMap::pinch() @@ -371,6 +387,7 @@ QDeclarativeGeoMapPinchArea* QDeclarativeGeoMap::pinch() Contains the MapFlickable created with the Map. Use \c{flick.enabled: true} to enable basic flick gestures, or see \l{MapFlickable} for further details. + This object will be deprecated, use the gesture object instead. */ QDeclarativeGeoMapFlickable* QDeclarativeGeoMap::flick() @@ -486,6 +503,7 @@ void QDeclarativeGeoMap::mappingManagerInitialized() map_->setActiveMapType(QGeoMapType()); flickable_->setMap(map_); + pinchArea_->setMap(map_); copyrightsWPtr_ = new QDeclarativeGeoMapCopyrightNotice(this); connect(map_, @@ -967,7 +985,7 @@ void QDeclarativeGeoMap::touchEvent(QTouchEvent *event) } QLOC_TRACE0; event->accept(); - pinchArea_->touchEvent(event); + gestureArea_->touchEvent(event); } /*! diff --git a/src/imports/location/qdeclarativegeomap_p.h b/src/imports/location/qdeclarativegeomap_p.h index cf44ee95..b95b3dc3 100644 --- a/src/imports/location/qdeclarativegeomap_p.h +++ b/src/imports/location/qdeclarativegeomap_p.h @@ -53,6 +53,7 @@ #include <QtQuick/QQuickPaintedItem> #include <QtQml/QQmlParserStatus> #include "qdeclarativegeomapitemview_p.h" +#include "qdeclarativegeomapgesturearea_p.h" #include "qdeclarativegeomapflickable_p.h" #include "qdeclarativegeomappincharea_p.h" #include "qgeomapcontroller_p.h" @@ -100,6 +101,7 @@ class QDeclarativeGeoMap : public QQuickItem { Q_OBJECT + Q_PROPERTY(QDeclarativeGeoMapGestureArea* gesture READ gesture CONSTANT) Q_PROPERTY(QDeclarativeGeoMapPinchArea* pinch READ pinch CONSTANT) Q_PROPERTY(QDeclarativeGeoMapFlickable* flick READ flick CONSTANT) Q_PROPERTY(QDeclarativeGeoServiceProvider *plugin READ plugin WRITE setPlugin NOTIFY pluginChanged) @@ -166,6 +168,7 @@ public: bool mouseEvent(QMouseEvent* event); QDeclarativeGeoMapPinchArea* pinch(); + QDeclarativeGeoMapGestureArea* gesture(); Q_INVOKABLE void fitViewportToMapItems(); @@ -227,6 +230,7 @@ private: bool mappingManagerInitialized_; QList<QDeclarativeGeoMapItemView*> mapViews_; + QDeclarativeGeoMapGestureArea* gestureArea_; QDeclarativeGeoMapFlickable* flickable_; QDeclarativeGeoMapPinchArea* pinchArea_; @@ -246,6 +250,7 @@ private: QMutex updateMutex_; friend class QDeclarativeGeoMapItem; friend class QDeclarativeGeoMapItemView; + friend class QDeclarativeGeoMapGestureArea; friend class QDeclarativeGeoMapPinchArea; friend class QDeclarativeGeoMapFlickable; Q_DISABLE_COPY(QDeclarativeGeoMap) diff --git a/src/imports/location/qdeclarativegeomapflickable.cpp b/src/imports/location/qdeclarativegeomapflickable.cpp index 78e6a3b0..5a5650ab 100644 --- a/src/imports/location/qdeclarativegeomapflickable.cpp +++ b/src/imports/location/qdeclarativegeomapflickable.cpp @@ -40,33 +40,9 @@ ****************************************************************************/ #include <QtGui/QGuiApplication> -#include <QtGui/QStyleHints> -#include <QPropertyAnimation> -#include <QEasingCurve> #include "qdeclarativegeomapflickable_p.h" -#include "qgeomapcontroller_p.h" -#include <QTimer> -#include "qgeomap_p.h" -#include "math.h" - -#define QML_MAP_FLICK_DEFAULTMAXVELOCITY 2500 -#define QML_MAP_FLICK_MINIMUMDECELERATION 500 -#define QML_MAP_FLICK_DEFAULTDECELERATION 2500 -#define QML_MAP_FLICK_MAXIMUMDECELERATION 10000 -// The number of samples to use in calculating the velocity of a flick -#define QML_MAP_FLICK_SAMPLEBUFFER 3 -// The number of samples to discard when calculating the flick velocity. -// Touch panels often produce inaccurate results as the finger is lifted. -#define QML_MAP_FLICK_DISCARDSAMPLES 1 - -// FlickThreshold determines how far the "mouse" must have moved -// before we perform a flick. -static const int FlickThreshold = 20; -// RetainGrabVelocity is the maxmimum instantaneous velocity that -// will ensure the Flickable retains the grab on consecutive flicks. -static const int RetainGrabVelocity = 15; -// Really slow flicks can be annoying. -const qreal MinimumFlickVelocity = 75.0; +#include "qdeclarativegeomapgesturearea_p.h" + QT_BEGIN_NAMESPACE @@ -171,334 +147,18 @@ QT_BEGIN_NAMESPACE The order of onMovementEnded() and onFlickEnded() is not specified. */ -QDeclarativeGeoMapFlickable::QDeclarativeGeoMapFlickable(QObject *parent) +QDeclarativeGeoMapFlickable::QDeclarativeGeoMapFlickable(QObject *parent, + QDeclarativeGeoMapGestureArea *gestureArea) : QObject(parent), - pressed_(false), - maxVelocity_(QML_MAP_FLICK_DEFAULTMAXVELOCITY), - deceleration_(QML_MAP_FLICK_DEFAULTDECELERATION), - velocityX_(0.0), - velocityY_(0.0), - flicking_(false), - map_(0), - animation_(0), - enabled_(true), - moving_(false) + gestureArea_(gestureArea) { - pressTime_.invalidate(); - lastPosTime_.invalidate(); - velocityTime_.invalidate(); + gestureArea_->registerFlickDeprecated(this); } QDeclarativeGeoMapFlickable::~QDeclarativeGeoMapFlickable() { } -/*! - \internal -*/ -void QDeclarativeGeoMapFlickable::setMap(QGeoMap* map) -{ - if (map_ || !map) - return; - map_ = map; - animation_ = new QPropertyAnimation(map_->mapController(), "center", this); - animation_->setEasingCurve(QEasingCurve(QEasingCurve::OutQuad)); - connect(animation_, SIGNAL(finished()), this, SLOT(flickAnimationFinished())); -} - -/*! - \internal -*/ -qreal QDeclarativeGeoMapFlickable::deceleration() const -{ - return deceleration_; -} - -/*! - \internal -*/ -void QDeclarativeGeoMapFlickable::setDeceleration(qreal deceleration) -{ - if (deceleration < QML_MAP_FLICK_MINIMUMDECELERATION) - deceleration = QML_MAP_FLICK_MINIMUMDECELERATION; - else if (deceleration > QML_MAP_FLICK_MAXIMUMDECELERATION) - deceleration = QML_MAP_FLICK_MAXIMUMDECELERATION; - if (deceleration == deceleration_) - return; - deceleration_ = deceleration; - emit decelerationChanged(); -} - -/*! - \internal -*/ -bool QDeclarativeGeoMapFlickable::mousePressEvent(QMouseEvent *event) -{ - if (!enabled_) - return false; - stop(); - pressed_ = true; - lastPos_ = QPointF(); - pressPos_ = event->pos(); - lastPosTime_.start(); - pressTime_.start(); - velocityTime_.start(); - return true; -} - -/*! - \internal -*/ -void QDeclarativeGeoMapFlickable::stop() -{ - velocityBufferX_.clear(); - velocityBufferY_.clear(); - velocityX_ = 0.0; - velocityY_ = 0.0; - if (moving_) { - moving_ = false; - emit movementEnded(); - } - if (flicking_) { - flicking_ = false; - if (animation_->state() == QPropertyAnimation::Running) - animation_->stop(); - emit flickEnded(); - } - lastPosTime_.invalidate(); - pressTime_.invalidate(); - velocityTime_.invalidate(); -} - -/*! - \internal -*/ -bool QDeclarativeGeoMapFlickable::mouseMoveEvent(QMouseEvent *event) -{ - if (!enabled_ || !pressed_ || !lastPosTime_.isValid()) - return false; - // Check if thresholds for normal panning are met. - // (normal panning vs flicking: flicking will start from mouse release event). - int dyFromPress = int(event->pos().y() - pressPos_.y()); - int dxFromPress = int(event->pos().x() - pressPos_.x()); - int dyFromLastPos; - int dxFromLastPos; - if (!lastPos_.isNull()) { - dyFromLastPos = event->pos().y() - lastPos_.y(); - dxFromLastPos = event->pos().x() - lastPos_.x(); - } - - // Simple pan (drag) while being pressed - const int startDragDistance = qApp->styleHints()->startDragDistance(); - if ((qAbs(dyFromPress) > startDragDistance - || qAbs(dxFromPress) > startDragDistance - || pressTime_.elapsed() > 200) && !lastPos_.isNull()) { - - if (!moving_) { - moving_ = true; - emit movementStarted(); - } - updateCamera(dxFromLastPos, dyFromLastPos, 0); - } - // Take velocity samples, used later to determine the flick - // duration and speed (when mouse is released). - if (!lastPos_.isNull()) { - qreal elapsed = qreal(lastPosTime_.elapsed()) / 1000.; - if (elapsed <= 0) { - lastPos_ = event->pos(); - return false; - } - lastPosTime_.restart(); - addVelocitySample(velocityBufferY_, double(dyFromLastPos)/elapsed); - addVelocitySample(velocityBufferX_, double(dxFromLastPos)/elapsed); - } - lastPos_ = event->pos(); - return true; -} - -/*! - \internal -*/ -// FIXME: -// - not left right / up down flicking, so if map is rotated, will act unintuitively -void QDeclarativeGeoMapFlickable::updateCamera(int dx, int dy, int timeMs) -{ - if (timeMs < 0) - return; - AnimatableCoordinate animationStartCoordinate = map_->mapController()->center(); - QGeoCoordinate coordinate = animationStartCoordinate.coordinate(); - if (timeMs == 0) { - // No animation, just set new values. - QPointF p = map_->coordinateToScreenPosition(coordinate); - p.setY(p.y() - dy); - p.setX(p.x() - dx); - animationStartCoordinate.setCoordinate(map_->screenPositionToCoordinate(p)); - map_->mapController()->setCenter(animationStartCoordinate); - } else { - //qDebug() << "Will do flick animation dx (pix), dy (pix), time (ms): " << dx << dy << timeMs; - if (animation_->state() == QPropertyAnimation::Running) - animation_->stop(); - AnimatableCoordinate animationEndCoordinate = map_->mapController()->center(); - animation_->setDuration(timeMs); - coordinate.setLongitude(coordinate.longitude() - (dx / pow(2.0, map_->mapController()->zoom()))); - coordinate.setLatitude(coordinate.latitude() + (dy / pow(2.0, map_->mapController()->zoom()))); - animationEndCoordinate.setCoordinate(coordinate); - animation_->setStartValue(QVariant::fromValue(animationStartCoordinate)); - animation_->setEndValue(QVariant::fromValue(animationEndCoordinate)); - //qDebug() << "The latitude will go from:" << animationStartCoordinate.coordinate().latitude() << "to:" << animationEndCoordinate.coordinate().latitude(); - //qDebug() << "The longitude will go from:" << animationStartCoordinate.coordinate().longitude() << "to:" << animationEndCoordinate.coordinate().longitude(); - // start animation straight away, user may disable the flick in the flickStarted() handler - animation_->start(); - flicking_ = true; - emit flickStarted(); - } -} - -/*! - \internal - Adds velocity sample to sample buffer. Data is later used to calculate - flick speed. By default 3 latest samples are considered. -*/ -void QDeclarativeGeoMapFlickable::addVelocitySample(QVector<qreal>& buffer, qreal sample) -{ - if (sample > maxVelocity_) - sample = maxVelocity_; - else if (sample < -maxVelocity_) - sample = -maxVelocity_; - buffer.append(sample); - if (buffer.count() > QML_MAP_FLICK_SAMPLEBUFFER) - buffer.remove(0); -} - -/*! - \internal -*/ -void QDeclarativeGeoMapFlickable::updateVelocity(QVector<qreal>& buffer, qreal& velocity) -{ - if (buffer.count() > QML_MAP_FLICK_DISCARDSAMPLES) { - velocity = 0; - int count = buffer.count() - QML_MAP_FLICK_DISCARDSAMPLES; - for (int i = 0; i < count; ++i) { - qreal v = buffer.at(i); - velocity += v; - } - velocity /= count; - } -} - -/*! - \internal -*/ -void QDeclarativeGeoMapFlickable::setEnabled(bool enabled) -{ - if (enabled_ == enabled) - return; - enabled_ = enabled; - if (!enabled_) - stop(); - emit enabledChanged(); -} - -/*! - \internal -*/ -bool QDeclarativeGeoMapFlickable::enabled() const -{ - return enabled_; -} - -/*! - \internal -*/ -bool QDeclarativeGeoMapFlickable::mouseReleaseEvent(QMouseEvent *event) -{ - if (!pressed_ || !enabled_) - return false; - pressed_ = false; - - // if we drag then pause before release we should not cause a flick. - if (lastPosTime_.elapsed() < 100) { - updateVelocity(velocityBufferY_, velocityY_); - updateVelocity(velocityBufferX_, velocityX_); - } else { - velocityX_ = 0.0; - velocityY_ = 0.0; - } - int flickTimeY = 0; - int flickTimeX = 0; - int flickPixelsX = 0; - int flickPixelsY = 0; - if (qAbs(velocityY_) > MinimumFlickVelocity && qAbs(event->pos().y() - pressPos_.y()) > FlickThreshold) { - // calculate Y flick animation values - qreal acceleration = deceleration_; - if ((velocityY_ > 0.0f) == (deceleration_ > 0.0f)) - acceleration = acceleration * -1.0f; - flickTimeY = static_cast<int>(-1000 * velocityY_ / acceleration); - flickPixelsY = (flickTimeY * velocityY_) / (1000.0 * 2); - //qDebug() << "---=== would flick Y, velocity (pix/sec), flick duration (msec): ===---" << velocityY_ << flickTimeY; - } else { - // reset - //qDebug() << "---=== would NOT flick Y, velocity (pix/sec): ===---" << velocityY_; - } - if (qAbs(velocityX_) > MinimumFlickVelocity && qAbs(event->pos().x() - pressPos_.x()) > FlickThreshold) { - // calculate X flick animation values - qreal acceleration = deceleration_; - if ((velocityX_ > 0.0f) == (deceleration_ > 0.0f)) - acceleration = acceleration * -1.0f; - flickTimeX = static_cast<int>(-1000 * velocityX_ / acceleration); - flickPixelsX = (flickTimeX * velocityX_) / (1000.0 * 2); - //qDebug() << "---=== would flick X, velocity (pix/sec), flick duration (msec), pixels: ===---" << velocityX_ << flickTimeX << "pixels: " << flickPixelsX; - } else { - // reset - //qDebug() << "---=== would NOT flick X, velocity (pix/sec) ===---" << velocityX_; - } - int flickTime = qMax(flickTimeY, flickTimeX); - updateCamera(flickPixelsX, flickPixelsY, flickTime); - - if (flickTime == 0 && moving_) { - emit movementEnded(); - moving_ = false; - } - velocityBufferX_.clear(); - velocityBufferY_.clear(); - velocityX_ = 0.0; - velocityY_ = 0.0; - lastPosTime_.invalidate(); - pressTime_.invalidate(); - velocityTime_.invalidate(); - return true; -} - -/*! - \internal -*/ -void QDeclarativeGeoMapFlickable::flickAnimationFinished() -{ - //qDebug() << metaObject()->className() << __FUNCTION__; - //Q_ASSERT(flicking_); - //Q_ASSERT(moving_); - flicking_ = false; - moving_ = false; - emit flickEnded(); - emit movementEnded(); -} - -/* -void QDeclarativeGeoMapFlickable::flickAnimationValueChanged(const QVariant& value) -{ - qDebug() << metaObject()->className() << __FUNCTION__; -} -*/ - -/*! - \internal -*/ -void QDeclarativeGeoMapFlickable::timerEvent(QTimerEvent *event) -{ - Q_UNUSED(event); // TODO press delay handling - //qDebug() << metaObject()->className() << __FUNCTION__ ; -} - #include "moc_qdeclarativegeomapflickable_p.cpp" QT_END_NAMESPACE diff --git a/src/imports/location/qdeclarativegeomapflickable_p.h b/src/imports/location/qdeclarativegeomapflickable_p.h index 1e6ba57f..f5e581e6 100644 --- a/src/imports/location/qdeclarativegeomapflickable_p.h +++ b/src/imports/location/qdeclarativegeomapflickable_p.h @@ -49,6 +49,7 @@ #include <QVector> #include <QObject> #include <QDebug> +#include "qdeclarativegeomapgesturearea_p.h" QT_BEGIN_NAMESPACE @@ -57,6 +58,8 @@ class QPropertyAnimation; class QGeoCameraData; class QGeoMap; +// Note: this class will be deprecated in future versions, it remains as a wrapper +// Please use the gesture object instead. class QDeclarativeGeoMapFlickable: public QObject { Q_OBJECT @@ -64,59 +67,27 @@ class QDeclarativeGeoMapFlickable: public QObject Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged()) public: - QDeclarativeGeoMapFlickable(QObject *parent = 0); + QDeclarativeGeoMapFlickable(QObject *parent, QDeclarativeGeoMapGestureArea *gestureArea_); ~QDeclarativeGeoMapFlickable(); - qreal deceleration() const; - void setDeceleration(qreal deceleration); + qreal deceleration() const { return gestureArea_->flickDeceleration(); } + void setDeceleration(qreal deceleration){ gestureArea_->setFlickDeceleration(deceleration); } - bool enabled() const; - void setEnabled(bool enabled); + bool enabled() const { return gestureArea_->panEnabled(); } + void setEnabled(bool enabled){ gestureArea_->setPanEnabled(enabled); } - void setMap(QGeoMap* map); - - bool mousePressEvent(QMouseEvent *event); - bool mouseMoveEvent(QMouseEvent *event); - bool mouseReleaseEvent(QMouseEvent *event); - virtual void timerEvent(QTimerEvent *event); + void setMap(QGeoMap* map){ gestureArea_->setMap(map); } signals: void decelerationChanged(); void enabledChanged(); - // public (documented) signals: void movementStarted(); void movementEnded(); void flickStarted(); void flickEnded(); private: - void addVelocitySample(QVector<qreal>& buffer, qreal sample); - void updateVelocity(QVector<qreal>& buffer, qreal& velocity); - void updateCamera(int dx, int dy, int timeMs = 0); - void stop(); - -private slots: - void flickAnimationFinished(); - //void flickAnimationValueChanged(const QVariant&); - -private: - bool pressed_; - qreal maxVelocity_; - qreal deceleration_; - QElapsedTimer lastPosTime_; - QElapsedTimer pressTime_; - QElapsedTimer velocityTime_; - QVector<qreal> velocityBufferX_; - qreal velocityX_; - QVector<qreal> velocityBufferY_; - qreal velocityY_; - QPointF lastPos_; - QPointF pressPos_; - bool flicking_; - QGeoMap* map_; - QPropertyAnimation* animation_; - bool enabled_; - bool moving_; + QDeclarativeGeoMapGestureArea *gestureArea_; // the destination for this wrapper class }; QT_END_NAMESPACE diff --git a/src/imports/location/qdeclarativegeomapgesturearea.cpp b/src/imports/location/qdeclarativegeomapgesturearea.cpp new file mode 100644 index 00000000..43b392ec --- /dev/null +++ b/src/imports/location/qdeclarativegeomapgesturearea.cpp @@ -0,0 +1,1407 @@ + +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtGui/QGuiApplication> +#include "qdeclarativegeomapgesturearea_p.h" +#include "qdeclarativegeomap_p.h" +#include "qdeclarativecoordinate_p.h" +#include <QtGui/qevent.h> +#include <QtGui/QStyleHints> +#include <QtQml/qqmlinfo.h> +#include <QPropertyAnimation> +#include <QDebug> +#include "math.h" +#include "qgeomap_p.h" + +#define QML_MAP_FLICK_DEFAULTMAXVELOCITY 2500 +#define QML_MAP_FLICK_MINIMUMDECELERATION 500 +#define QML_MAP_FLICK_DEFAULTDECELERATION 2500 +#define QML_MAP_FLICK_MAXIMUMDECELERATION 10000 +// The number of samples to use in calculating the velocity of a flick +#define QML_MAP_FLICK_SAMPLEBUFFER 3 +// The number of samples to discard when calculating the flick velocity. +// Touch panels often produce inaccurate results as the finger is lifted. +#define QML_MAP_FLICK_DISCARDSAMPLES 1 + +// FlickThreshold determines how far the "mouse" must have moved +// before we perform a flick. +static const int FlickThreshold = 20; +// Really slow flicks can be annoying. +const qreal MinimumFlickVelocity = 75.0; + +QT_BEGIN_NAMESPACE + + +/*! + \qmlclass MapPinchEvent + \inqmlmodule QtLocation 5 + + \brief MapPinchEvent element provides basic information about pinch event. + + MapPinchEvent element provides basic information about pinch event. They are + present in handlers of MapPinch (for example pinchStarted/pinchUpdated). Events are only + guaranteed to be valid for the duration of the handler. + + Except for the \l accepted property, all properties are read-only. + + \section2 Example Usage + + The following example enables the pinch gesture on a map and reacts to the + finished event. + + \code + Map { + id: map + gesture.enabled: true + gesture.onPinchFinished:{ + var coordinate1 = map.toCoordinate(gesture.point1) + var coordinate2 = map.toCoordinate(gesture.point2) + console.log("Pinch started at:") + console.log(" Points (" + gesture.point1.x + ", " + gesture.point1.y + ") - (" + gesture.point2.x + ", " + gesture.point2.y + ")") + console.log(" Coordinates (" + coordinate1.latitude + ", " + coordinate1.longitude + ") - (" + coordinate2.latitude + ", " + coordinate2.longitude + ")") + } + } + \endcode + + \ingroup qml-QtLocation5-maps + \since Qt Location 5.0 +*/ + +/*! + \qmlproperty QPoint QtLocation5::MapPinchEvent::center + + This read-only property holds the current center point. +*/ + +/*! + \qmlproperty real QtLocation5::MapPinchEvent::angle + + This read-only property holds the current angle between the two points in + the range -180 to 180. Positive values for the angles mean counter-clockwise + while negative values mean the clockwise direction. Zero degrees is at the + 3 o'clock position. +*/ + +/*! + \qmlproperty QPoint QtLocation5::MapPinchEvent::point1 + \qmlproperty QPoint QtLocation5::MapPinchEvent::point2 + + These read-only properties hold the actual touch points generating the pinch. + The points are not in any particular order. +*/ + +/*! + \qmlproperty int QtLocation5::MapPinchEvent::pointCount + + This read-only property holds the number of points currently touched. + The MapPinch will not react until two touch points have initiated a gesture, + but will remain active until all touch points have been released. +*/ + +/*! + \qmlproperty bool QtLocation5::MapPinchEvent::accepted + + Setting this property to false in the \c MapPinch::onPinchStarted handler + will result in no further pinch events being generated, and the gesture + ignored. +*/ + +/*! + \qmlclass MapGestureArea QDeclarativeGeoMapGestureArea + + \inqmlmodule QtLocation 5 + + \brief The MapGestureArea element provides Map gesture interaction. + + MapGestureArea elements are used as part of a Map, to provide for panning, + flicking and pinch-to-zoom gesture used on touch displays. + + A MapGestureArea is automatically created with a new Map and available with + the \l{QtLocation5::Map::pinch}{pinch} property. This is the only way + to create a MapGestureArea, and once created this way cannot be destroyed + without its parent Map. + + The two most commonly used properties of the MapGestureArea are the \l enabled + and \l activeGestures properties. Both of these must be set before a + MapGestureArea will have any effect upon interaction with the Map. + The \l flickDeceleration property controls how quickly the map pan slows after contact + is released while panning the map. + + \section2 Performance + + The MapGestureArea, when enabled, must process all incoming touch events in + order to track the shape and size of the "pinch". The overhead added on + touch events can be considered constant time. + + \section2 Example Usage + + The following example enables the zoom and pan gestures on the map, but not flicking. So the + map scrolling will halt immediately on releasing the mouse button / touch. + + \code + Map { + gesture.enabled: true + gesture.activeGestures: MapGestureArea.ZoomGesture | MapGestureArea.PanGesture + } + \endcode + + \ingroup qml-QtLocation5-maps + \since Qt Location 5.0 +*/ + +/*! + \qmlproperty bool QtLocation5::MapGestureArea::enabled + + This property holds whether the gestures are enabled. + Note: disabling gestures during an active gesture does not have effect on + the potentially active current gesture. +*/ + + +/*! + \qmlproperty bool QtLocation5::MapGestureArea::panEnabled + + This property holds whether the pan gestures are enabled. + Note: disabling gestures during an active gesture does not have effect on + the potentially active current gesture. +*/ + +/*! + \qmlproperty bool QtLocation5::MapGestureArea::pinchEnabled + + This property holds whether the pinch gestures are enabled. + Note: disabling gestures during an active gesture does not have effect on + the potentially active current gesture. +*/ + +/*! + \qmlproperty bool QtLocation5::MapGestureArea::isPinchActive + + This read-only property holds whether any pinch gesture is active. +*/ + +/*! + \qmlproperty bool QtLocation5::MapGestureArea::isPanActive + + This read-only property holds whether any pan gesture (panning or flicking) is active. +*/ + +/*! + \qmlproperty enumeration QtLocation5::MapGestureArea::activeGestures + + This property holds the gestures that will be active. By default + the zoom, pan and flick gestures are enabled. + + \list + \li GestureArea.NoGesture - Don't support any additional gestures (value: 0x0000). + \li GestureArea.ZoomGesture - Support the map zoom gesture (value: 0x0001). + \li GestureArea.RotationGesture - Support the map rotation gesture (value: 0x0002). + \li GestureArea.TiltGesture - Support the map tilt gesture (value: 0x0004). + \li GestureArea.PanGesture - Support the map pan gesture (value: 0x0008). + \li GestureArea.FlickGesture - Support the map flick gesture (value: 0x0010). + \endlist + + For the extremist, one may OR flag the RotationGesture or TiltGesture + but these come with absolutely no warranty or guarantees at the moment + (may be removed, changed, moved around) + + \note For the time being, only \l GestureArea.ZoomGesture is supported. +*/ + +/*! + \qmlproperty real QtLocation5::MapGestureArea::maximumZoomLevelChange + + This property holds the maximum zoom level change per pinch, essentially + meant to be used for setting the zoom sensitivity. + + It is an indicative measure calculated from the dimensions of the + map area, roughly corresponding how much zoom level could change with + maximum pinch zoom. Default value is 2.0, maximum value is 10.0 +*/ + +/*! + \qmlproperty real MapGestureArea::rotationFactor + + This property holds the rotation factor for zoom, essentially meant to be used for setting + the rotation sensitivity. + + It is an indicative measure; the default value 1.0 means the map roughly follows the fingers, + whereas 2.0 means rotating twice as fast. Maximum value is 5.0. +*/ + +/*! + \qmlsignal void QtLocation5::MapGestureArea::pinchStarted(PinchEvent event) + + Raised when a pinch gesture is started. + + \sa pinchUpdated pinchFinished +*/ + +/*! + \qmlsignal void QtLocation5::MapGestureArea::pinchUpdated(PinchEvent event) + + Once a pinch has begun this event gets raised as the user moves her fingers + across the map. + + \sa pinchStarted pinchFinished +*/ + +/*! + \qmlsignal void QtLocation5::MapGestureArea::pinchUpdated(PinchEvent event) + + The end of a pinch gesture is signaled by this event. + + \sa pinchUpdated pinchFinished +*/ + +/*! + \qmlsignal QtLocation5::MapFlickable::panStarted() + + This handler is called when the view begins moving due to user + interaction. Typically this means that the user is dragging a finger - + or a mouse with one of more mouse buttons pressed - on the map. +*/ + +/*! + \qmlsignal QtLocation5::MapFlickable::panFinished() + + This handler is called when the view stops moving due to user + interaction. If a flick was generated, this handler will + be triggered once the flick stops. If a flick was not + generated, the handler will be triggered when the + user stops dragging - that is a mouse or touch release. + +*/ + +/*! + \qmlsignal QtLocation5::MapFlickable::flickStarted() + + This handler is called when the view is flicked. A flick + starts from the point that the mouse or touch is released, + while still in motion. +*/ + +/*! + \qmlsignal QtLocation5::MapFlickable::flickFinished() + + This handler is called when the view stops moving due to a flick. + The order of panFinished() and flickFinished() is not specified. +*/ + + +QDeclarativeGeoMapGestureArea::QDeclarativeGeoMapGestureArea(QDeclarativeGeoMap* map, QObject *parent) + : QObject(parent), + declarativeMap_(map), + enabled_(true), + pinchEnabled_(true), + minimumZoomLevel_(-1.0), + maximumZoomLevel_(-1.0), + minimumRotation_(0.0), + maximumRotation_(0.0), + pinchStartDist_(0), + pinchStartZoomLevel_(0.0), + pinchLastZoomLevel_(0.0), + pinchStartRotation_(0.0), + pinchLastAngle_(0.0), + pinchRotation_(0.0), + maximumZoomLevelChange_(2.0), + rotationFactor_(1.0), + minimumTilt_(0.0), + maximumTilt_(90.0), + maximumTiltChange_(20.0), + pinchLastTilt_(0.0), + pinchStartTilt_(0.0), + activeGestures_(ZoomGesture | PanGesture | FlickGesture) +{ + map_ = 0; + pan_.enabled_ = true, + pan_.maxVelocity_ = QML_MAP_FLICK_DEFAULTMAXVELOCITY; + pan_.deceleration_ = QML_MAP_FLICK_DEFAULTDECELERATION; + pan_.animation_ = 0; +#if defined(TOUCH_EVENT_WORKAROUND) + mouseBeingUsed_ = true; +#endif + touchPointState_ = touchPoints0; + pinchState_ = pinchInactive; + panState_ = panInactive; +} +/*! + \internal +*/ +void QDeclarativeGeoMapGestureArea::setMap(QGeoMap* map) +{ + if (map_ || !map) + return; + map_ = map; + pan_.animation_ = new QPropertyAnimation(map_->mapController(), "center", this); + pan_.animation_->setEasingCurve(QEasingCurve(QEasingCurve::OutQuad)); + connect(pan_.animation_, SIGNAL(finished()), this, SLOT(endFlick())); +} + +QDeclarativeGeoMapGestureArea::~QDeclarativeGeoMapGestureArea() +{ +} + +/*! + \internal +*/ +QDeclarativeGeoMapGestureArea::ActiveGestures QDeclarativeGeoMapGestureArea::activeGestures() const +{ + return activeGestures_; +} + +/*! + \internal +*/ +void QDeclarativeGeoMapGestureArea::setActiveGestures(ActiveGestures activeGestures) +{ + if (activeGestures == activeGestures_) + return; + activeGestures_ = activeGestures; + if (activeGestures_ & RotationGesture) + qmlInfo(this) << tr("Pinchrotation gesture activated. Note that it is experimental feature."); + if (activeGestures_ & TiltGesture) + qmlInfo(this) << tr("Pinchtilt gesture activated. Note that it is experimental feature."); + emit activeGesturesChanged(); + emit pinchDep_->activeGesturesChanged(); +} + +/*! + \internal +*/ +bool QDeclarativeGeoMapGestureArea::isPinchActive() const +{ + return pinchState_ == pinchActive; +} + +/*! + \internal +*/ +bool QDeclarativeGeoMapGestureArea::isPanActive() const +{ + return panState_ == panActive || panState_ == panFlick; +} + +/*! + \internal +*/ +bool QDeclarativeGeoMapGestureArea::enabled() const +{ + return enabled_; +} + +/*! + \internal +*/ +void QDeclarativeGeoMapGestureArea::setEnabled(bool enabled) +{ + if (enabled == enabled_) + return; + enabled_ = enabled; + emit enabledChanged(); +} + + +/*! + \internal +*/ +bool QDeclarativeGeoMapGestureArea::pinchEnabled() const +{ + return pinchEnabled_; +} + +/*! + \internal +*/ +void QDeclarativeGeoMapGestureArea::setPinchEnabled(bool enabled) +{ + if (enabled == pinchEnabled_) + return; + pinchEnabled_ = enabled; + emit pinchEnabledChanged(); + emit pinchDep_->enabledChanged(); +} + +/*! + \internal +*/ +bool QDeclarativeGeoMapGestureArea::panEnabled() const +{ + return pan_.enabled_; +} + +/*! + \internal +*/ +void QDeclarativeGeoMapGestureArea::setPanEnabled(bool enabled) +{ + if (enabled == pan_.enabled_) + return; + pan_.enabled_ = enabled; + emit panEnabledChanged(); + emit flickableDep_->enabledChanged(); + + // unlike the pinch, the pan existing functionality is to stop immediately + if (!enabled) + stopPan(); +} + +/*! + \internal +*/ +qreal QDeclarativeGeoMapGestureArea::minimumZoomLevel() const +{ + return minimumZoomLevel_; +} + +/*! + \internal +*/ +void QDeclarativeGeoMapGestureArea::setMinimumZoomLevel(qreal zoomLevel) +{ + if (zoomLevel == minimumZoomLevel_ || + zoomLevel < declarativeMap_->minimumZoomLevel() || + (maximumZoomLevel_ != -1.0 && zoomLevel > maximumZoomLevel_) ) + return; + minimumZoomLevel_ = zoomLevel; + emit minimumZoomLevelChanged(); + emit pinchDep_->minimumZoomLevelChanged(); +} + +/*! + \internal +*/ +qreal QDeclarativeGeoMapGestureArea::maximumZoomLevel() const +{ + return maximumZoomLevel_; +} + +/*! + \internal +*/ +void QDeclarativeGeoMapGestureArea::setMaximumZoomLevel(qreal zoomLevel) +{ + if (zoomLevel == maximumZoomLevel_ || + zoomLevel > declarativeMap_->maximumZoomLevel() || + (minimumZoomLevel_ != - 1.0 && zoomLevel < minimumZoomLevel_)) + return; + maximumZoomLevel_ = zoomLevel; + emit maximumZoomLevelChanged(); + emit pinchDep_->maximumZoomLevelChanged(); +} + +/*! + \internal + called internally when plugin's limits change. somewhat dodgy but + initialization order complicates the zoom limit settings a bit (for example when is + it possible to check against mapping plugins' limits) +*/ +void QDeclarativeGeoMapGestureArea::zoomLevelLimits(qreal min, qreal max) +{ + if (minimumZoomLevel_ == -1.0 || min > minimumZoomLevel_) + setMinimumZoomLevel(min); + if (maximumZoomLevel_ == -1.0 || max < maximumZoomLevel_) + setMaximumZoomLevel(max); +} + +/*! + \internal +*/ +qreal QDeclarativeGeoMapGestureArea::maximumZoomLevelChange() const +{ + return maximumZoomLevelChange_; +} + +/*! + \internal +*/ +void QDeclarativeGeoMapGestureArea::setMaximumZoomLevelChange(qreal maxChange) +{ + if (maxChange == maximumZoomLevelChange_ || maxChange < 0.1 || maxChange > 10.0) + return; + maximumZoomLevelChange_ = maxChange; + emit maximumZoomLevelChangeChanged(); + emit pinchDep_->maximumZoomLevelChangeChanged(); +} + +/*! + \internal +*/ +qreal QDeclarativeGeoMapGestureArea::minimumRotation() const +{ + return minimumRotation_; +} + +/*! + \internal +*/ +void QDeclarativeGeoMapGestureArea::setMinimumRotation(qreal rotation) +{ + if (rotation == minimumRotation_ || + rotation < 0 || + rotation > maximumRotation_) + return; + minimumRotation_ = rotation; + emit minimumRotationChanged(); + emit pinchDep_->minimumRotationChanged(); +} + +/*! + \internal +*/ +qreal QDeclarativeGeoMapGestureArea::maximumRotation() const +{ + return maximumRotation_; +} + +/*! + \internal +*/ +void QDeclarativeGeoMapGestureArea::setMaximumRotation(qreal rotation) +{ + if (rotation == maximumRotation_ || + rotation > 360 || + rotation < minimumRotation_) + return; + maximumRotation_ = rotation; + emit maximumRotationChanged(); + emit pinchDep_->maximumRotationChanged(); +} + +/*! + \internal +*/ +qreal QDeclarativeGeoMapGestureArea::rotationFactor() const +{ + return rotationFactor_; +} + +/*! + \internal +*/ +void QDeclarativeGeoMapGestureArea::setRotationFactor(qreal factor) +{ + if (rotationFactor_ == factor || + factor < 0 || + factor > 5) + return; + rotationFactor_ = factor; + emit rotationFactorChanged(); + emit pinchDep_->rotationFactorChanged(); +} + +/*! + \internal +*/ +qreal QDeclarativeGeoMapGestureArea::maximumTilt() const +{ + return maximumTilt_; +} + +/*! + \internal +*/ +void QDeclarativeGeoMapGestureArea::setMaximumTilt(qreal tilt) +{ + if (maximumTilt_ == tilt) + return; + maximumTilt_ = tilt; + emit maximumTiltChanged(); + emit pinchDep_->maximumTiltChanged(); +} + +/*! + \internal +*/ +qreal QDeclarativeGeoMapGestureArea::minimumTilt() const +{ + return minimumTilt_; +} + +/*! + \internal +*/ +void QDeclarativeGeoMapGestureArea::setMinimumTilt(qreal tilt) +{ + if (minimumTilt_ == tilt || tilt < 0.1) + return; + minimumTilt_ = tilt; + emit minimumTiltChanged(); + emit pinchDep_->minimumTiltChanged(); +} + +/*! + \internal +*/ +qreal QDeclarativeGeoMapGestureArea::maximumTiltChange() const +{ + return maximumTiltChange_; +} + +/*! + \internal +*/ +void QDeclarativeGeoMapGestureArea::setMaximumTiltChange(qreal tilt) +{ + if (maximumTiltChange_ == tilt || tilt < 0.1) + return; + maximumTiltChange_ = tilt; + emit maximumTiltChangeChanged(); + emit pinchDep_->maximumTiltChangeChanged(); +} + +/*! + \internal +*/ +qreal QDeclarativeGeoMapGestureArea::flickDeceleration() const +{ + return pan_.deceleration_; +} + +/*! + \internal +*/ +void QDeclarativeGeoMapGestureArea::setFlickDeceleration(qreal deceleration) +{ + if (deceleration < QML_MAP_FLICK_MINIMUMDECELERATION) + deceleration = QML_MAP_FLICK_MINIMUMDECELERATION; + else if (deceleration > QML_MAP_FLICK_MAXIMUMDECELERATION) + deceleration = QML_MAP_FLICK_MAXIMUMDECELERATION; + if (deceleration == pan_.deceleration_) + return; + pan_.deceleration_ = deceleration; + emit flickDecelerationChanged(); + emit flickableDep_->decelerationChanged(); +} + +/*! + \internal +*/ +QTouchEvent::TouchPoint makeTouchPointFromMouseEvent(QMouseEvent *event, Qt::TouchPointState state) +{ + // this is only partially filled. But since it is only partially used it works + // more robust would be to store a list of QPointFs rather than TouchPoints + QTouchEvent::TouchPoint newPoint; + newPoint.setPos(event->localPos()); + newPoint.setScenePos(event->windowPos()); + newPoint.setScreenPos(event->screenPos()); + newPoint.setState(state); + newPoint.setId(0); + return newPoint; +} + +/*! + \internal +*/ +bool QDeclarativeGeoMapGestureArea::mousePressEvent(QMouseEvent *event) +{ +#if defined(TOUCH_EVENT_WORKAROUND) + if (!mouseBeingUsed_) + return true; +#endif + touchPoints_.clear(); + touchPoints_ << makeTouchPointFromMouseEvent(event, Qt::TouchPointPressed); + + update(); + return true; +} + +/*! + \internal +*/ +bool QDeclarativeGeoMapGestureArea::mouseMoveEvent(QMouseEvent *event) +{ +#if defined(TOUCH_EVENT_WORKAROUND) + if (!mouseBeingUsed_) + return true; +#endif + touchPoints_.clear(); + + touchPoints_ << makeTouchPointFromMouseEvent(event, Qt::TouchPointMoved); + update(); + return true; +} + + +/*! + \internal +*/ +bool QDeclarativeGeoMapGestureArea::mouseReleaseEvent(QMouseEvent */*event*/) +{ +#if defined(TOUCH_EVENT_WORKAROUND) + if (!mouseBeingUsed_) + return true; +#endif + touchPoints_.clear(); + update(); + return true; +} + +/*! + \internal +*/ +void QDeclarativeGeoMapGestureArea::touchEvent(QTouchEvent *event) +{ +#if defined(TOUCH_EVENT_WORKAROUND) + mouseBeingUsed_ = false; +#endif + switch (event->type()) { + case QEvent::TouchBegin: + case QEvent::TouchUpdate: + touchPoints_.clear(); + for (int i = 0; i < event->touchPoints().count(); ++i) { + if (!(event->touchPoints().at(i).state() & Qt::TouchPointReleased)) { + touchPoints_ << event->touchPoints().at(i); + } + } + update(); + break; + case QEvent::TouchEnd: + touchPoints_.clear(); + update(); + break; + default: + // no-op + break; + } +} + +/*! + \internal +*/ +void QDeclarativeGeoMapGestureArea::clearTouchData() +{ + velocityBufferX_.clear(); + velocityBufferY_.clear(); + pressTime_.start(); + touchCenterCoord_.setLongitude(0); + touchCenterCoord_.setLatitude(0); + startCoord_.setLongitude(0); + startCoord_.setLatitude(0); +} + + +/*! + \internal +*/ +void QDeclarativeGeoMapGestureArea::updateVelocityList(const QPointF &pos) +{ + // Take velocity samples, used later to determine the flick + // duration and speed (when mouse is released). + if (!lastPos_.isNull()) { + int dyFromLastPos = pos.y() - lastPos_.y(); + int dxFromLastPos = pos.x() - lastPos_.x(); + + qreal elapsed = qreal(lastPosTime_.elapsed()) / 1000.; + if (elapsed <= 0) { + return; + } + lastPosTime_.restart(); + addVelocitySample(velocityBufferY_, double(dyFromLastPos)/elapsed); + addVelocitySample(velocityBufferX_, double(dxFromLastPos)/elapsed); + } +} + + +/*! + \internal + Adds velocity sample to sample buffer. Data is later used to calculate + flick speed. By default 3 latest samples are considered. +*/ +void QDeclarativeGeoMapGestureArea::addVelocitySample(QVector<qreal>& buffer, qreal sample) +{ + if (sample > pan_.maxVelocity_) + sample = pan_.maxVelocity_; + else if (sample < -pan_.maxVelocity_) + sample = -pan_.maxVelocity_; + buffer.append(sample); + if (buffer.count() > QML_MAP_FLICK_SAMPLEBUFFER) + buffer.remove(0); +} + +/*! + \internal +*/ +void QDeclarativeGeoMapGestureArea::updateVelocity(QVector<qreal>& buffer, qreal& velocity) +{ + if (buffer.count() > QML_MAP_FLICK_DISCARDSAMPLES) { + velocity = 0; + int count = buffer.count() - QML_MAP_FLICK_DISCARDSAMPLES; + for (int i = 0; i < count; ++i) { + qreal v = buffer.at(i); + velocity += v; + } + velocity /= count; + } +} + +/*! + \internal +*/ +// simplify the gestures by using a state-machine format (easy to move to a future state machine) +void QDeclarativeGeoMapGestureArea::update() +{ + // First state machine is for the number of touch points + touchPointStateMachine(); + + // Parallel state machine for pinch + if (isPinchActive() || (enabled_ && pinchEnabled_ && (activeGestures_ & (ZoomGesture | TiltGesture | RotationGesture)))) + pinchStateMachine(); + + // Parallel state machine for pan (since you can pan at the same time as pinching) + // The stopPan function ensures that pan stops immediately when disabled, + // but the line below allows pan continue its current gesture if you disable + // the whole gesture (enabled_ flag), this keeps the enabled_ consistent with the pinch + if (isPanActive() || (enabled_ && pan_.enabled_ && (activeGestures_ & (PanGesture | FlickGesture)))) + panStateMachine(); +} + +/*! + \internal +*/ +void QDeclarativeGeoMapGestureArea::touchPointStateMachine() +{ + // Transitions: + switch (touchPointState_) + { + case touchPoints0:{ + if (touchPoints_.count()==1){ + clearTouchData(); + startOneTouchPoint(); + touchPointState_ = touchPoints1; + } + else if (touchPoints_.count()==2){ + clearTouchData(); + startTwoTouchPoints(); + touchPointState_ = touchPoints2; + } + break; + } + case touchPoints1:{ + if (touchPoints_.count()==0){ + touchPointState_ = touchPoints0; + } + else if (touchPoints_.count()==2){ + startTwoTouchPoints(); + touchPointState_ = touchPoints2; + } + break; + } + case touchPoints2:{ + if (touchPoints_.count()==0){ + touchPointState_ = touchPoints0; + } + else if (touchPoints_.count()==1){ + startOneTouchPoint(); + touchPointState_ = touchPoints1; + } + break; + } + }; + + // Update + switch (touchPointState_) + { + case touchPoints0:{ + break; // do nothing if no touch points down + } + case touchPoints1:{ + updateOneTouchPoint(); + break; + } + case touchPoints2:{ + updateTwoTouchPoints(); + break; + } + } +} + +/*! + \internal +*/ +void QDeclarativeGeoMapGestureArea::startOneTouchPoint() +{ + sceneCenter_ = QPointF(); + lastPosTime_.start(); + + sceneStartPoint1_ = touchPoints_.at(0).scenePos(); + QGeoCoordinate startCoord = map_->screenPositionToCoordinate(sceneStartPoint1_, false); + // ensures a smooth transition for panning + startCoord_.setLongitude(startCoord_.longitude() + startCoord.longitude() - + touchCenterCoord_.longitude()); + startCoord_.setLatitude(startCoord_.latitude() + startCoord.latitude() - + touchCenterCoord_.latitude()); +} + +/*! + \internal +*/ +void QDeclarativeGeoMapGestureArea::updateOneTouchPoint() +{ + lastPos_ = sceneCenter_; + sceneCenter_ = touchPoints_.at(0).scenePos(); + touchCenterCoord_ = map_->screenPositionToCoordinate(sceneCenter_, false); + + updateVelocityList(sceneCenter_); +} + + +/*! + \internal +*/ +void QDeclarativeGeoMapGestureArea::startTwoTouchPoints() +{ + sceneCenter_ = QPointF(); + lastPosTime_.start(); + + sceneStartPoint1_ = touchPoints_.at(0).scenePos(); + sceneStartPoint2_ = touchPoints_.at(1).scenePos(); + QPointF startPos = (sceneStartPoint1_ + sceneStartPoint2_) * 0.5; + QGeoCoordinate startCoord = map_->screenPositionToCoordinate(startPos, false); + startCoord_.setLongitude(startCoord_.longitude() + startCoord.longitude() - + touchCenterCoord_.longitude()); + startCoord_.setLatitude(startCoord_.latitude() + startCoord.latitude() - + touchCenterCoord_.latitude()); +} + +/*! + \internal +*/ +void QDeclarativeGeoMapGestureArea::updateTwoTouchPoints() +{ + QPointF p1 = touchPoints_.at(0).scenePos(); + QPointF p2 = touchPoints_.at(1).scenePos(); + qreal dx = p1.x() - p2.x(); + qreal dy = p1.y() - p2.y(); + distanceBetweenTouchPoints_ = sqrt(dx*dx + dy*dy); + lastPos_ = sceneCenter_; + sceneCenter_ = (p1 + p2)/2; + touchCenterCoord_ = map_->screenPositionToCoordinate(sceneCenter_, false); + + updateVelocityList(sceneCenter_); + + twoTouchAngle_ = QLineF(p1, p2).angle(); + if (twoTouchAngle_ > 180) + twoTouchAngle_ -= 360; +} + +/*! + \internal +*/ +void QDeclarativeGeoMapGestureArea::setPinchActive(bool active) +{ + if ((active && pinchState_==pinchActive) || (!active && pinchState_!=pinchActive)) + return; + pinchState_ = active ? pinchActive : pinchInactive; + emit pinchDep_->activeChanged(); + emit pinchActiveChanged(); +} + + +/*! + \internal +*/ +void QDeclarativeGeoMapGestureArea::pinchStateMachine() +{ + PinchState lastState = pinchState_; + // Transitions: + switch (pinchState_) + { + case pinchInactive:{ + if (canStartPinch()){ + startPinch(); + setPinchActive(true); + } + break; + } + case pinchActive:{ + if (touchPoints_.count() <= 1){ + endPinch(); + setPinchActive(false); + } + break; + } + } + // This line implements an exclusive state machine, where the transitions and updates don't + // happen on the same frame + if (pinchState_ != lastState) + return; + // Update + switch (pinchState_) + { + case pinchInactive:{ + break; // do nothing + } + case pinchActive:{ + updatePinch(); + break; + } + } +} + +/*! + \internal +*/ +bool QDeclarativeGeoMapGestureArea::canStartPinch() +{ + const int startDragDistance = qApp->styleHints()->startDragDistance(); + + if (touchPoints_.count() >= 2){ + QPointF p1 = touchPoints_.at(0).scenePos(); + QPointF p2 = touchPoints_.at(1).scenePos(); + if (qAbs(p1.x()-sceneStartPoint1_.x()) > startDragDistance + || qAbs(p1.y()-sceneStartPoint1_.y()) > startDragDistance + || qAbs(p2.x()-sceneStartPoint2_.x()) > startDragDistance + || qAbs(p2.y()-sceneStartPoint2_.y()) > startDragDistance) { + pinchEvent_.setCenter(declarativeMap_->mapFromScene(sceneCenter_)); + pinchEvent_.setAngle(twoTouchAngle_); + pinchEvent_.setPoint1(p1); + pinchEvent_.setPoint2(p2); + pinchEvent_.setPointCount(touchPoints_.count()); + pinchEvent_.setAccepted(true); + emit pinchStarted(&pinchEvent_); + emit pinchDep_->pinchStarted(&pinchEvent_); + return pinchEvent_.accepted(); + } + } + return false; +} + +/*! + \internal +*/ +void QDeclarativeGeoMapGestureArea::startPinch() +{ + pinchStartDist_ = distanceBetweenTouchPoints_; + pinchLastZoomLevel_ = 1.0; + pinchLastTilt_ = 0.0; + pinchLastAngle_ = twoTouchAngle_; + pinchRotation_ = 0.0; + + lastPoint1_ = touchPoints_.at(0).scenePos(); + lastPoint2_ = touchPoints_.at(1).scenePos(); + + pinchStartZoomLevel_ = declarativeMap_->zoomLevel(); + pinchStartRotation_ = declarativeMap_->bearing(); + pinchStartTilt_ = declarativeMap_->tilt(); +} + +/*! + \internal +*/ +void QDeclarativeGeoMapGestureArea::updatePinch() +{ + // Calculate the new zoom level if we have distance ( >= 2 touchpoints), otherwise stick with old. + qreal newZoomLevel = pinchLastZoomLevel_; + if (distanceBetweenTouchPoints_) { + newZoomLevel = + // How much further/closer the current touchpoints are (in pixels) compared to pinch start + ((distanceBetweenTouchPoints_ - pinchStartDist_) * + // How much one pixel corresponds in units of zoomlevel (and multiply by above delta) + (maximumZoomLevelChange_ / ((declarativeMap_->width() + declarativeMap_->height()) / 2))) + + // Add to starting zoom level. Sign of (dist-pinchstartdist) takes care of zoom in / out + pinchStartZoomLevel_; + } + qreal da = pinchLastAngle_ - twoTouchAngle_; + if (da > 180) + da -= 360; + else if (da < -180) + da += 360; + pinchRotation_ -= da; + pinchEvent_.setCenter(declarativeMap_->mapFromScene(sceneCenter_)); + pinchEvent_.setAngle(twoTouchAngle_); + + lastPoint1_ = touchPoints_.at(0).scenePos(); + lastPoint2_ = touchPoints_.at(1).scenePos(); + pinchEvent_.setPoint1(lastPoint1_); + pinchEvent_.setPoint2(lastPoint2_); + pinchEvent_.setPointCount(touchPoints_.count()); + pinchEvent_.setAccepted(true); + + pinchLastAngle_ = twoTouchAngle_; + emit pinchUpdated(&pinchEvent_); + emit pinchDep_->pinchUpdated(&pinchEvent_); + + if (activeGestures_ & ZoomGesture) { + // Take maximum and minimumzoomlevel into account + qreal perPinchMinimumZoomLevel = qMax(pinchStartZoomLevel_ - maximumZoomLevelChange_, minimumZoomLevel_); + qreal perPinchMaximumZoomLevel = qMin(pinchStartZoomLevel_ + maximumZoomLevelChange_, maximumZoomLevel_); + newZoomLevel = qMin(qMax(perPinchMinimumZoomLevel, newZoomLevel), perPinchMaximumZoomLevel); + declarativeMap_->setZoomLevel(newZoomLevel); + } + if (activeGestures_ & TiltGesture && minimumZoomLevel_ >= 0 && maximumZoomLevel_ >= 0) { + // Note: tilt is not yet supported. + qreal newTilt = pinchLastTilt_; + if (distanceBetweenTouchPoints_) { + newTilt = + // How much further/closer the current touchpoints are (in pixels) compared to pinch start + ((distanceBetweenTouchPoints_ - pinchStartDist_) * + // How much one pixel corresponds in units of tilt degrees (and multiply by above delta) + (maximumTiltChange_ / ((declarativeMap_->width() + declarativeMap_->height()) / 2))) + + // Add to starting tilt. + pinchStartTilt_; + } + qreal perPinchMinimumTilt = qMax(pinchStartTilt_ - maximumTiltChange_, minimumTilt_); + qreal perPinchMaximumTilt = qMin(pinchStartTilt_ + maximumTiltChange_, maximumTilt_); + newTilt = qMin(qMax(perPinchMinimumTilt, newTilt), perPinchMaximumTilt); + pinchLastTilt_ = newTilt; + declarativeMap_->setTilt(newTilt); + } + if (activeGestures_ & RotationGesture) { + bool unlimitedRotation = (minimumRotation_ == 0.0 && maximumRotation_ == 0.0); + if ((pinchStartRotation_ >= minimumRotation_ && pinchStartRotation_ <= maximumRotation_) || unlimitedRotation) { + qreal r = pinchRotation_ * rotationFactor_ + pinchStartRotation_; + if (!unlimitedRotation) + r = qMin(qMax(minimumRotation_,r), maximumRotation_); + if (r > 360.0) + r -= 360; + if (r < -360.0) + r += 360.0; + declarativeMap_->setBearing(r); + } + } +} + +/*! + \internal +*/ +void QDeclarativeGeoMapGestureArea::endPinch() +{ + QPointF pinchCenter = declarativeMap_->mapFromScene(sceneCenter_); + pinchEvent_.setCenter(pinchCenter); + pinchEvent_.setAngle(pinchLastAngle_); + pinchEvent_.setPoint1(declarativeMap_->mapFromScene(lastPoint1_)); + pinchEvent_.setPoint2(declarativeMap_->mapFromScene(lastPoint2_)); + pinchEvent_.setAccepted(true); + pinchEvent_.setPointCount(0); + emit pinchFinished(&pinchEvent_); + emit pinchDep_->pinchFinished(&pinchEvent_); + pinchStartDist_ = 0; +} + +/*! + \internal +*/ +void QDeclarativeGeoMapGestureArea::panStateMachine() +{ + PanState lastState = panState_; + + // Transitions + switch (panState_) + { + case panInactive:{ + if (canStartPan()) + panState_ = panActive; + break; + } + case panActive:{ + if (touchPoints_.count()==0){ + panState_ = panFlick; + if (!tryStartFlick()) + panState_ = panInactive; + } + break; + } + case panFlick:{ + if (touchPoints_.count()>0){ // re touched before movement ended + endFlick(); + panState_ = panActive; + } + break; + } + } + // Update + switch (panState_) + { + case panInactive:{ // do nothing + break; + } + case panActive:{ + updatePan(); + // this ensures 'panStarted' occurs after the pan has actually started + if (lastState != panActive){ + emit panStarted(); + emit flickableDep_->movementStarted(); + } + break; + } + case panFlick:{ + break; + } + } +} +/*! + \internal +*/ +bool QDeclarativeGeoMapGestureArea::canStartPan() +{ + if (touchPoints_.count() == 0 || (activeGestures_ & PanGesture)==0) + return false; + + // Check if thresholds for normal panning are met. + // (normal panning vs flicking: flicking will start from mouse release event). + const int startDragDistance = qApp->styleHints()->startDragDistance(); + QPointF p1 = touchPoints_.at(0).scenePos(); + int dyFromPress = int(p1.y() - sceneStartPoint1_.y()); + int dxFromPress = int(p1.x() - sceneStartPoint1_.x()); + if ((qAbs(dyFromPress) > startDragDistance + || qAbs(dxFromPress) > startDragDistance + || pressTime_.elapsed() > 200) && !lastPos_.isNull()) + return true; + return false; +} + +/*! + \internal +*/ +void QDeclarativeGeoMapGestureArea::updatePan() +{ + QDeclarativeCoordinate *center = declarativeMap_->center(); + qreal newLat = center->latitude() + startCoord_.latitude() - touchCenterCoord_.latitude(); + qreal newLong = center->longitude() + startCoord_.longitude() - touchCenterCoord_.longitude(); + center->setLatitude(newLat); + center->setLongitude(newLong); +} + +/*! + \internal +*/ +bool QDeclarativeGeoMapGestureArea::tryStartFlick() +{ + if ((activeGestures_ & FlickGesture)==0) + return false; + // if we drag then pause before release we should not cause a flick. + qreal velocityX = 0.0; + qreal velocityY = 0.0; + if (lastPosTime_.elapsed() < 100) { + updateVelocity(velocityBufferY_, velocityY); + updateVelocity(velocityBufferX_, velocityX); + } + int flickTimeY = 0; + int flickTimeX = 0; + int flickPixelsX = 0; + int flickPixelsY = 0; + if (qAbs(velocityY) > MinimumFlickVelocity && qAbs(sceneCenter_.y() - sceneStartPoint1_.y()) > FlickThreshold) { + // calculate Y flick animation values + qreal acceleration = pan_.deceleration_; + if ((velocityY > 0.0f) == (pan_.deceleration_ > 0.0f)) + acceleration = acceleration * -1.0f; + flickTimeY = static_cast<int>(-1000 * velocityY / acceleration); + flickPixelsY = (flickTimeY * velocityY) / (1000.0 * 2); + } + if (qAbs(velocityX) > MinimumFlickVelocity && qAbs(sceneCenter_.x() - sceneStartPoint1_.x()) > FlickThreshold) { + // calculate X flick animation values + qreal acceleration = pan_.deceleration_; + if ((velocityX > 0.0f) == (pan_.deceleration_ > 0.0f)) + acceleration = acceleration * -1.0f; + flickTimeX = static_cast<int>(-1000 * velocityX / acceleration); + flickPixelsX = (flickTimeX * velocityX) / (1000.0 * 2); + } + int flickTime = qMax(flickTimeY, flickTimeX); + if (flickTime > 0){ + startFlick(flickPixelsX, flickPixelsY, flickTime); + return true; + } + return false; +} + +/*! + \internal +*/ +// FIXME: +// - not left right / up down flicking, so if map is rotated, will act unintuitively +void QDeclarativeGeoMapGestureArea::startFlick(int dx, int dy, int timeMs) +{ + if (timeMs < 0) + return; + AnimatableCoordinate animationStartCoordinate = map_->mapController()->center(); + QGeoCoordinate coordinate = animationStartCoordinate.coordinate(); + + if (pan_.animation_->state() == QPropertyAnimation::Running) + pan_.animation_->stop(); + AnimatableCoordinate animationEndCoordinate = map_->mapController()->center(); + pan_.animation_->setDuration(timeMs); + coordinate.setLongitude(coordinate.longitude() - (dx / pow(2.0, map_->mapController()->zoom()))); + coordinate.setLatitude(coordinate.latitude() + (dy / pow(2.0, map_->mapController()->zoom()))); + animationEndCoordinate.setCoordinate(coordinate); + pan_.animation_->setStartValue(QVariant::fromValue(animationStartCoordinate)); + pan_.animation_->setEndValue(QVariant::fromValue(animationEndCoordinate)); + pan_.animation_->start(); + emit flickStarted(); + emit flickableDep_->flickStarted(); +} + +void QDeclarativeGeoMapGestureArea::stopPan() +{ + velocityBufferX_.clear(); + velocityBufferY_.clear(); + if (panState_ == panFlick) + endFlick(); + else if (panState_ == panActive){ + emit panFinished(); + flickableDep_->movementEnded(); + } + panState_ = panInactive; +} + +/*! + \internal +*/ +void QDeclarativeGeoMapGestureArea::endFlick() +{ + emit panFinished(); + emit flickableDep_->movementEnded(); + if (pan_.animation_->state() == QPropertyAnimation::Running) + pan_.animation_->stop(); + emit flickFinished(); + emit flickableDep_->flickEnded(); + panState_ = panInactive; +} + + + + + + +#include "moc_qdeclarativegeomapgesturearea_p.cpp" + +QT_END_NAMESPACE diff --git a/src/imports/location/qdeclarativegeomapgesturearea_p.h b/src/imports/location/qdeclarativegeomapgesturearea_p.h new file mode 100644 index 00000000..a64b1962 --- /dev/null +++ b/src/imports/location/qdeclarativegeomapgesturearea_p.h @@ -0,0 +1,365 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEGEOMAPGESTUREAREA_P_H +#define QDECLARATIVEGEOMAPGESTUREAREA_P_H + +#include <QtQml/qqml.h> +#include <QTouchEvent> +#include <QObject> +#include <QDebug> +#include <QElapsedTimer> +#include "qgeocoordinate.h" + +QT_BEGIN_NAMESPACE + +class QGraphicsSceneMouseEvent; +class QDeclarativeGeoMap; +class QTouchEvent; +class QGeoMap; +class QPropertyAnimation; +class QDeclarativeGeoMapFlickable; +class QDeclarativeGeoMapPinchArea; + +class QDeclarativeGeoMapPinchEvent : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QPointF center READ center) + Q_PROPERTY(qreal angle READ angle) + Q_PROPERTY(QPointF point1 READ point1) + Q_PROPERTY(QPointF point2 READ point2) + Q_PROPERTY(int pointCount READ pointCount) + Q_PROPERTY(bool accepted READ accepted WRITE setAccepted) + +public: + QDeclarativeGeoMapPinchEvent(QPointF center, qreal angle, + QPointF point1, QPointF point2, + int pointCount = 0, bool accepted = true) + : QObject(), center_(center), angle_(angle), + point1_(point1), point2_(point2), + pointCount_(pointCount), accepted_(accepted) {} + QDeclarativeGeoMapPinchEvent() + : QObject(), + angle_(0.0), + pointCount_(0), + accepted_(true) {} + + QPointF center() const { return center_; } + void setCenter(QPointF center) { center_ = center; } + qreal angle() const { return angle_; } + void setAngle(qreal angle) { angle_ = angle; } + QPointF point1() const { return point1_; } + void setPoint1(QPointF p) { point1_ = p; } + QPointF point2() const { return point2_; } + void setPoint2(QPointF p) { point2_ = p; } + int pointCount() const { return pointCount_; } + void setPointCount(int count) { pointCount_ = count; } + bool accepted() const { return accepted_; } + void setAccepted(bool a) { accepted_ = a; } + +private: + QPointF center_; + qreal angle_; + QPointF point1_; + QPointF point2_; + int pointCount_; + bool accepted_; +}; + +// tbd: should we have a 'active' / 'moving' boolean attribute when pinch is active? + +// class QDeclarativeGeoMapGestureArea: public QObject // supporting pinching, panning, flicking, tilting +class QDeclarativeGeoMapGestureArea: public QObject +{ + Q_OBJECT + Q_ENUMS(ActiveGesture) + Q_FLAGS(ActiveGestures) + + Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) + Q_PROPERTY(bool pinchEnabled READ pinchEnabled WRITE setPinchEnabled NOTIFY pinchEnabledChanged) + Q_PROPERTY(bool panEnabled READ panEnabled WRITE setPanEnabled NOTIFY panEnabledChanged) + Q_PROPERTY(bool isPinchActive READ isPinchActive WRITE setPinchActive NOTIFY pinchActiveChanged) + Q_PROPERTY(bool isPanActive READ isPanActive) + Q_PROPERTY(ActiveGestures activeGestures READ activeGestures WRITE setActiveGestures NOTIFY activeGesturesChanged) + Q_PROPERTY(qreal maximumZoomLevelChange READ maximumZoomLevelChange WRITE setMaximumZoomLevelChange NOTIFY maximumZoomLevelChangeChanged) + Q_PROPERTY(qreal rotationFactor READ rotationFactor WRITE setRotationFactor NOTIFY rotationFactorChanged) + Q_PROPERTY(qreal flickDeceleration READ flickDeceleration WRITE setFlickDeceleration NOTIFY flickDecelerationChanged) + + // need for these is not clear, use-case(s) not yet identified: + //Q_PROPERTY(qreal minimumRotation READ minimumRotation WRITE setMinimumRotation NOTIFY minimumRotationChanged) + //Q_PROPERTY(qreal maximumRotation READ maximumRotation WRITE setMaximumRotation NOTIFY maximumRotationChanged) + //Q_PROPERTY(qreal minimumZoomLevel READ minimumZoomLevel WRITE setMinimumZoomLevel NOTIFY minimumZoomLevelChanged) + //Q_PROPERTY(qreal maximumZoomLevel READ maximumZoomLevel WRITE setMaximumZoomLevel NOTIFY maximumZoomLevelChanged) + // when tilt is supported, these are needed: + //Q_PROPERTY(qreal maximumTilt READ maximumTilt WRITE setMaximumTilt NOTIFY maximumTiltChanged) + //Q_PROPERTY(qreal minimumTilt READ minimumTilt WRITE setMinimumTilt NOTIFY minimumTiltChanged) + //Q_PROPERTY(qreal maximumTiltChange READ maximumTiltChange WRITE setMaximumTiltChange NOTIFY maximumTiltChangeChanged) + +public: + QDeclarativeGeoMapGestureArea(QDeclarativeGeoMap* map, QObject *parent = 0); + ~QDeclarativeGeoMapGestureArea(); + + enum ActiveGesture { + NoGesture = 0x0000, + ZoomGesture = 0x0001, + RotationGesture = 0x0002, + TiltGesture = 0x0004, + PanGesture = 0x0008, + FlickGesture = 0x0010 + }; + Q_DECLARE_FLAGS(ActiveGestures, ActiveGesture); + + ActiveGestures activeGestures() const; + void setActiveGestures(ActiveGestures activeGestures); + + bool isPinchActive() const; + void setPinchActive(bool active); + bool isPanActive() const; + + bool enabled() const; + void setEnabled(bool enabled); + + // backwards compatibility + bool pinchEnabled() const; + void setPinchEnabled(bool enabled); + bool panEnabled() const; + void setPanEnabled(bool enabled); + + qreal minimumZoomLevel() const; + void setMinimumZoomLevel(qreal zoomLevel); + + qreal maximumZoomLevel() const; + void setMaximumZoomLevel(qreal zoomLevel); + + qreal maximumZoomLevelChange() const; + void setMaximumZoomLevelChange(qreal maxChange); + + qreal minimumRotation() const; + void setMinimumRotation(qreal zoomLevel); + + qreal maximumRotation() const; + void setMaximumRotation(qreal zoomLevel); + + qreal rotationFactor() const; + void setRotationFactor(qreal factor); + + qreal maximumTilt() const; + void setMaximumTilt(qreal tilt); + + qreal minimumTilt() const; + void setMinimumTilt(qreal tilt); + + qreal maximumTiltChange() const; + void setMaximumTiltChange(qreal tilt); + + qreal flickDeceleration() const; + void setFlickDeceleration(qreal deceleration); + + void touchEvent(QTouchEvent *event); + + bool mousePressEvent(QMouseEvent *event); + bool mouseMoveEvent(QMouseEvent *event); + bool mouseReleaseEvent(QMouseEvent *event); + + void zoomLevelLimits(qreal min, qreal max); + void setMap(QGeoMap* map); + + // will be removed + void registerFlickDeprecated(QDeclarativeGeoMapFlickable *flickable){ + flickableDep_ = flickable; } + void registerPinchDeprecated(QDeclarativeGeoMapPinchArea *pinchArea){ + pinchDep_ = pinchArea; } + +signals: + void pinchActiveChanged(); + void enabledChanged(); + void minimumZoomLevelChanged(); + void maximumZoomLevelChanged(); + void maximumZoomLevelChangeChanged(); + void minimumRotationChanged(); + void maximumRotationChanged(); + void rotationFactorChanged(); + void activeGesturesChanged(); + void minimumTiltChanged(); + void maximumTiltChanged(); + void maximumTiltChangeChanged(); + void flickDecelerationChanged(); + + // backwards compatibility + void pinchEnabledChanged(); + void panEnabledChanged(); + + void pinchStarted(QDeclarativeGeoMapPinchEvent* pinch); + void pinchUpdated(QDeclarativeGeoMapPinchEvent* pinch); + void pinchFinished(QDeclarativeGeoMapPinchEvent* pinch); + void panStarted(); + void panFinished(); + void flickStarted(); + void flickFinished(); + +private: + class QDeclarativeGeoMapFlickable *flickableDep_; + class QDeclarativeGeoMapPinchArea *pinchDep_; + + void update(); + + // Create general data relating to the touch points + void touchPointStateMachine(); + void startOneTouchPoint(); + void updateOneTouchPoint(); + void startTwoTouchPoints(); + void updateTwoTouchPoints(); + + // All pinch related code, which encompasses zoom, rotation and tilt + void pinchStateMachine(); + bool canStartPinch(); + void startPinch(); + void updatePinch(); + void endPinch(); + + // Pan related code (regardles of number of touch points), + // includes the flick based panning after letting go + void panStateMachine(); + bool canStartPan(); + void updatePan(); + bool tryStartFlick(); + void startFlick(int dx, int dy, int timeMs = 0); +private slots: + void endFlick(); + +private: + void stopPan(); + void clearTouchData(); + void updateVelocityList(const QPointF &pos); + void addVelocitySample(QVector<qreal>& buffer, qreal sample); + void updateVelocity(QVector<qreal>& buffer, qreal& velocity); + +private: + QGeoMap *map_; + QDeclarativeGeoMap *declarativeMap_; + bool enabled_; + // TODO: put these into a structure for clarity + // struct Pinch + // { + QDeclarativeGeoMapPinchEvent pinchEvent_; + bool pinchEnabled_; + qreal minimumZoomLevel_; + qreal maximumZoomLevel_; + qreal minimumRotation_; + qreal maximumRotation_; + QPointF lastPoint1_; + QPointF lastPoint2_; + qreal pinchStartDist_; + qreal pinchStartZoomLevel_; + qreal pinchLastZoomLevel_; + qreal pinchStartRotation_; + qreal pinchLastAngle_; + qreal pinchRotation_; + qreal maximumZoomLevelChange_; + qreal rotationFactor_; + qreal minimumTilt_; + qreal maximumTilt_; + qreal maximumTiltChange_; + + qreal pinchLastTilt_; + qreal pinchStartTilt_; + // } pinch_; + + ActiveGestures activeGestures_; + + struct Pan + { + qreal maxVelocity_; + qreal deceleration_; + QPropertyAnimation *animation_; + bool enabled_; + } pan_; + + // these are calculated regardless of gesture or number of touch points + QVector<qreal> velocityBufferX_; + QVector<qreal> velocityBufferY_; + QElapsedTimer lastPosTime_; + QPointF lastPos_; + QElapsedTimer pressTime_; + QList<QTouchEvent::TouchPoint> touchPoints_; + QPointF sceneStartPoint1_; + + // only set when two points in contact + QPointF sceneStartPoint2_; + QGeoCoordinate startCoord_; + QGeoCoordinate touchCenterCoord_; + qreal twoTouchAngle_; + qreal distanceBetweenTouchPoints_; + QPointF sceneCenter_; + +#if defined(TOUCH_EVENT_WORKAROUND) // will be removed when review change 21896 goes into QML core + bool mouseBeingUsed_; +#endif + // prototype state machine... + enum TouchPointState + { + touchPoints0, + touchPoints1, + touchPoints2 + } touchPointState_; + + enum PinchState + { + pinchInactive, + pinchActive + } pinchState_; + + enum PanState + { + panInactive, + panActive, + panFlick + } panState_; + + friend class QDeclarativeGeoMapPinchArea; + friend class QDeclarativeGeoMapFlickable; +}; + +QT_END_NAMESPACE +QML_DECLARE_TYPE(QDeclarativeGeoMapGestureArea); + +#endif // QDECLARATIVEGEOMAPGESTUREAREA_P_H diff --git a/src/imports/location/qdeclarativegeomappincharea.cpp b/src/imports/location/qdeclarativegeomappincharea.cpp index fa5aa25a..7d591aeb 100644 --- a/src/imports/location/qdeclarativegeomappincharea.cpp +++ b/src/imports/location/qdeclarativegeomappincharea.cpp @@ -42,15 +42,37 @@ #include <QtGui/QGuiApplication> #include "qdeclarativegeomappincharea_p.h" #include "qdeclarativegeomap_p.h" +#include "qdeclarativecoordinate_p.h" #include <QtGui/qevent.h> #include <QtGui/QStyleHints> #include <QtQml/qqmlinfo.h> +#include <QPropertyAnimation> #include <QDebug> #include "math.h" #include "qgeomap_p.h" +#define QML_MAP_FLICK_DEFAULTMAXVELOCITY 2500 +#define QML_MAP_FLICK_MINIMUMDECELERATION 500 +#define QML_MAP_FLICK_DEFAULTDECELERATION 2500 +#define QML_MAP_FLICK_MAXIMUMDECELERATION 10000 +// The number of samples to use in calculating the velocity of a flick +#define QML_MAP_FLICK_SAMPLEBUFFER 3 +// The number of samples to discard when calculating the flick velocity. +// Touch panels often produce inaccurate results as the finger is lifted. +#define QML_MAP_FLICK_DISCARDSAMPLES 1 + +// FlickThreshold determines how far the "mouse" must have moved +// before we perform a flick. +static const int FlickThreshold = 20; +// RetainGrabVelocity is the maxmimum instantaneous velocity that +// will ensure the Flickable retains the grab on consecutive flicks. +static const int RetainGrabVelocity = 15; +// Really slow flicks can be annoying. +const qreal MinimumFlickVelocity = 75.0; + QT_BEGIN_NAMESPACE + /*! \qmlclass MapPinchEvent \inqmlmodule QtLocation 5 @@ -191,6 +213,7 @@ QT_BEGIN_NAMESPACE \li PinchArea.ZoomGesture - Support the map zoom gesture (value: 0x0001). \li PinchArea.RotationGesture - Support the map rotation gesture (value: 0x0002). \li PinchArea.TiltGesture - Support the map tilt gesture (value: 0x0004). + \li PinchArea.PanGesture - Support the map pan gesture while pinching (value: 0x0008). \endlist For the extremist, one may OR flag the RotationGesture or TiltGesture @@ -255,486 +278,18 @@ QT_BEGIN_NAMESPACE */ -QDeclarativeGeoMapPinchArea::QDeclarativeGeoMapPinchArea(QDeclarativeGeoMap* map, QObject *parent) +QDeclarativeGeoMapPinchArea::QDeclarativeGeoMapPinchArea(QObject *parent, + QDeclarativeGeoMapGestureArea *gestureArea) : QObject(parent), - map_(map), - enabled_(true), - active_(false), - minimumZoomLevel_(-1.0), - maximumZoomLevel_(-1.0), - minimumRotation_(0.0), - maximumRotation_(0.0), - inPinch_(false), - pinchRejected_(false), - pinchActivated_(false), - pinchStartDist_(0), - pinchStartZoomLevel_(0.0), - pinchLastZoomLevel_(0.0), - pinchStartRotation_(0.0), - pinchLastAngle_(0.0), - pinchRotation_(0.0), - id1_(-1), - maximumZoomLevelChange_(2.0), - rotationFactor_(1.0), - activeGestures_(ZoomGesture), - minimumTilt_(0.0), - maximumTilt_(90.0), - maximumTiltChange_(20.0), - pinchLastTilt_(0.0), - pinchStartTilt_(0.0) + gestureArea_(gestureArea) { + gestureArea_->registerPinchDeprecated(this); } QDeclarativeGeoMapPinchArea::~QDeclarativeGeoMapPinchArea() { } -/*! - \internal -*/ -QDeclarativeGeoMapPinchArea::ActiveGestures QDeclarativeGeoMapPinchArea::activeGestures() const -{ - return activeGestures_; -} - -/*! - \internal -*/ -void QDeclarativeGeoMapPinchArea::setActiveGestures(ActiveGestures activeGestures) -{ - if (activeGestures == activeGestures_) - return; - activeGestures_ = activeGestures; - if (activeGestures_ & RotationGesture) - qmlInfo(this) << tr("Pinchrotation gesture activated. Note that it is experimental feature."); - if (activeGestures_ & TiltGesture) - qmlInfo(this) << tr("Pinchtilt gesture activated. Note that it is experimental feature."); - emit activeGesturesChanged(); -} - -/*! - \internal -*/ -bool QDeclarativeGeoMapPinchArea::active() const -{ - return active_; -} - -/*! - \internal -*/ -void QDeclarativeGeoMapPinchArea::setActive(bool active) -{ - if (active == active_) - return; - active_ = active; - emit activeChanged(); -} - -/*! - \internal -*/ -bool QDeclarativeGeoMapPinchArea::enabled() const -{ - return enabled_; -} - -/*! - \internal -*/ -void QDeclarativeGeoMapPinchArea::setEnabled(bool enabled) -{ - if (enabled == enabled_) - return; - enabled_ = enabled; - emit enabledChanged(); -} - -/*! - \internal -*/ -qreal QDeclarativeGeoMapPinchArea::minimumZoomLevel() const -{ - return minimumZoomLevel_; -} - -/*! - \internal -*/ -void QDeclarativeGeoMapPinchArea::setMinimumZoomLevel(qreal zoomLevel) -{ - if (zoomLevel == minimumZoomLevel_ || - zoomLevel < map_->minimumZoomLevel() || - (maximumZoomLevel_ != -1.0 && zoomLevel > maximumZoomLevel_) ) - return; - minimumZoomLevel_ = zoomLevel; - emit minimumZoomLevelChanged(); -} - -/*! - \internal -*/ -qreal QDeclarativeGeoMapPinchArea::maximumZoomLevel() const -{ - return maximumZoomLevel_; -} - -/*! - \internal -*/ -void QDeclarativeGeoMapPinchArea::setMaximumZoomLevel(qreal zoomLevel) -{ - if (zoomLevel == maximumZoomLevel_ || - zoomLevel > map_->maximumZoomLevel() || - (minimumZoomLevel_ != - 1.0 && zoomLevel < minimumZoomLevel_)) - return; - maximumZoomLevel_ = zoomLevel; - emit maximumZoomLevelChanged(); -} - -/*! - \internal - called internally when plugin's limits change. somewhat dodgy but - initialization order complicates the zoom limit settings a bit (for example when is - it possible to check against mapping plugins' limits) -*/ -void QDeclarativeGeoMapPinchArea::zoomLevelLimits(qreal min, qreal max) -{ - if (minimumZoomLevel_ == -1.0 || min > minimumZoomLevel_) - setMinimumZoomLevel(min); - if (maximumZoomLevel_ == -1.0 || max < maximumZoomLevel_) - setMaximumZoomLevel(max); -} - -/*! - \internal -*/ -qreal QDeclarativeGeoMapPinchArea::maximumZoomLevelChange() const -{ - return maximumZoomLevelChange_; -} - -/*! - \internal -*/ -void QDeclarativeGeoMapPinchArea::setMaximumZoomLevelChange(qreal maxChange) -{ - if (maxChange == maximumZoomLevelChange_ || maxChange < 0.1 || maxChange > 10.0) - return; - maximumZoomLevelChange_ = maxChange; - emit maximumZoomLevelChangeChanged(); -} - -/*! - \internal -*/ -qreal QDeclarativeGeoMapPinchArea::minimumRotation() const -{ - return minimumRotation_; -} - -/*! - \internal -*/ -void QDeclarativeGeoMapPinchArea::setMinimumRotation(qreal rotation) -{ - if (rotation == minimumRotation_ || - rotation < 0 || - rotation > maximumRotation_) - return; - minimumRotation_ = rotation; - emit minimumRotationChanged(); -} - -/*! - \internal -*/ -qreal QDeclarativeGeoMapPinchArea::maximumRotation() const -{ - return maximumRotation_; -} - -/*! - \internal -*/ -void QDeclarativeGeoMapPinchArea::setMaximumRotation(qreal rotation) -{ - if (rotation == maximumRotation_ || - rotation > 360 || - rotation < minimumRotation_) - return; - maximumRotation_ = rotation; - emit maximumRotationChanged(); -} - -/*! - \internal -*/ -qreal QDeclarativeGeoMapPinchArea::rotationFactor() const -{ - return rotationFactor_; -} - -/*! - \internal -*/ -void QDeclarativeGeoMapPinchArea::setRotationFactor(qreal factor) -{ - if (rotationFactor_ == factor || - factor < 0 || - factor > 5) - return; - rotationFactor_ = factor; - emit rotationFactorChanged(); -} - -/*! - \internal -*/ -qreal QDeclarativeGeoMapPinchArea::maximumTilt() const -{ - return maximumTilt_; -} - -/*! - \internal -*/ -void QDeclarativeGeoMapPinchArea::setMaximumTilt(qreal tilt) -{ - if (maximumTilt_ == tilt) - return; - maximumTilt_ = tilt; - emit maximumTiltChanged(); -} - -/*! - \internal -*/ -qreal QDeclarativeGeoMapPinchArea::minimumTilt() const -{ - return minimumTilt_; -} - -/*! - \internal -*/ -void QDeclarativeGeoMapPinchArea::setMinimumTilt(qreal tilt) -{ - if (minimumTilt_ == tilt || tilt < 0.1) - return; - minimumTilt_ = tilt; - emit minimumTiltChanged(); -} - -/*! - \internal -*/ -qreal QDeclarativeGeoMapPinchArea::maximumTiltChange() const -{ - return maximumTiltChange_; -} - -/*! - \internal -*/ -void QDeclarativeGeoMapPinchArea::setMaximumTiltChange(qreal tilt) -{ - if (maximumTiltChange_ == tilt || tilt < 0.1) - return; - maximumTiltChange_ = tilt; - emit maximumTiltChangeChanged(); -} - -/*! - \internal -*/ -void QDeclarativeGeoMapPinchArea::touchEvent(QTouchEvent *event) -{ - // Keep processing if gesture(s) are in progress. Otherwise we might - // end up in lock. - if ((!enabled_ || (activeGestures_ == NoGesture)) && !active()) - return; - switch (event->type()) { - case QEvent::TouchBegin: - case QEvent::TouchUpdate: - touchPoints_.clear(); - for (int i = 0; i < event->touchPoints().count(); ++i) { - if (!(event->touchPoints().at(i).state() & Qt::TouchPointReleased)) { - touchPoints_ << event->touchPoints().at(i); - } - } - updatePinch(); - break; - case QEvent::TouchEnd: - touchPoints_.clear(); - updatePinch(); - break; - default: - // no-op - break; - } -} - -/*! - \internal -*/ -void QDeclarativeGeoMapPinchArea::updatePinch() -{ - if (touchPoints_.count() == 0) { - if (inPinch_) { - inPinch_ = false; - QPointF pinchCenter = map_->mapFromScene(sceneLastCenter_); - pinchEvent_.setCenter(pinchCenter); - pinchEvent_.setAngle(pinchLastAngle_); - pinchEvent_.setPoint1(map_->mapFromScene(lastPoint1_)); - pinchEvent_.setPoint2(map_->mapFromScene(lastPoint2_)); - pinchEvent_.setAccepted(true); - pinchEvent_.setPointCount(0); - emit pinchFinished(&pinchEvent_); - pinchStartDist_ = 0; - pinchActivated_ = false; - setActive(false); - } - return; - } - QTouchEvent::TouchPoint touchPoint1 = touchPoints_.at(0); - QTouchEvent::TouchPoint touchPoint2 = touchPoints_.at(touchPoints_.count() >= 2 ? 1 : 0); - if (touchPoints_.count() == 2 - && (touchPoint1.state() & Qt::TouchPointPressed || touchPoint2.state() & Qt::TouchPointPressed)) { - id1_ = touchPoint1.id(); - sceneStartPoint1_ = touchPoint1.scenePos(); - sceneStartPoint2_ = touchPoint2.scenePos(); - inPinch_ = false; - pinchRejected_ = false; - pinchActivated_ = true; - } else if (pinchActivated_ && !pinchRejected_){ - const int dragThreshold = qApp->styleHints()->startDragDistance(); - QPointF p1 = touchPoint1.scenePos(); - QPointF p2 = touchPoint2.scenePos(); - qreal dx = p1.x() - p2.x(); - qreal dy = p1.y() - p2.y(); - qreal dist = sqrt(dx*dx + dy*dy); - QPointF sceneCenter = (p1 + p2)/2; - qreal angle = QLineF(p1, p2).angle(); - if (touchPoints_.count() == 1) { - // If we only have one point then just move the center - if (id1_ == touchPoint1.id()) - sceneCenter = sceneLastCenter_ + touchPoint1.scenePos() - lastPoint1_; - else - sceneCenter = sceneLastCenter_ + touchPoint2.scenePos() - lastPoint2_; - angle = pinchLastAngle_; - } - id1_ = touchPoint1.id(); - if (angle > 180) - angle -= 360; - if (!inPinch_) { - if (touchPoints_.count() >= 2 - && (qAbs(p1.x()-sceneStartPoint1_.x()) > dragThreshold - || qAbs(p1.y()-sceneStartPoint1_.y()) > dragThreshold - || qAbs(p2.x()-sceneStartPoint2_.x()) > dragThreshold - || qAbs(p2.y()-sceneStartPoint2_.y()) > dragThreshold)) { - sceneLastCenter_ = sceneCenter; - pinchStartDist_ = dist; - pinchLastZoomLevel_ = 1.0; - pinchLastTilt_ = 0.0; - pinchLastAngle_ = angle; - pinchRotation_ = 0.0; - lastPoint1_ = p1; - lastPoint2_ = p2; - - pinchEvent_.setCenter(map_->mapFromScene(sceneCenter)); - pinchEvent_.setAngle(angle); - pinchEvent_.setPoint1(map_->mapFromScene(lastPoint1_)); - pinchEvent_.setPoint2(map_->mapFromScene(lastPoint2_)); - pinchEvent_.setPointCount(touchPoints_.count()); - pinchEvent_.setAccepted(true); - emit pinchStarted(&pinchEvent_); - - if (pinchEvent_.accepted()) { - inPinch_ = true; - pinchStartZoomLevel_ = map_->zoomLevel(); - pinchStartRotation_ = map_->bearing(); - pinchStartTilt_ = map_->tilt(); - setActive(true); - } else { - pinchRejected_ = true; - } - } - } else if (pinchStartDist_ > 0) { - // Calculate the new zoom level if we have distance ( >= 2 touchpoints), otherwise stick with old. - qreal newZoomLevel = pinchLastZoomLevel_; - if (dist) { - newZoomLevel = - // How much further/closer the current touchpoints are (in pixels) compared to pinch start - ((dist - pinchStartDist_) * - // How much one pixel corresponds in units of zoomlevel (and multiply by above delta) - (maximumZoomLevelChange_ / ((map_->width() + map_->height()) / 2))) + - // Add to starting zoom level. Sign of (dist-pinchstartdist) takes care of zoom in / out - pinchStartZoomLevel_; - } - qreal da = pinchLastAngle_ - angle; - if (da > 180) - da -= 360; - else if (da < -180) - da += 360; - pinchRotation_ -= da; - pinchEvent_.setCenter(map_->mapFromScene(sceneCenter)); - pinchEvent_.setAngle(angle); - pinchEvent_.setPoint1(touchPoint1.pos()); - pinchEvent_.setPoint2(touchPoint2.pos()); - pinchEvent_.setPointCount(touchPoints_.count()); - pinchEvent_.setAccepted(true); - - sceneLastCenter_ = sceneCenter; - pinchLastAngle_ = angle; - lastPoint1_ = touchPoint1.scenePos(); - lastPoint2_ = touchPoint2.scenePos(); - emit pinchUpdated(&pinchEvent_); - - if (activeGestures_ & ZoomGesture) { - // Take maximum and minimumzoomlevel into account - qreal perPinchMinimumZoomLevel = qMax(pinchStartZoomLevel_ - maximumZoomLevelChange_, minimumZoomLevel_); - qreal perPinchMaximumZoomLevel = qMin(pinchStartZoomLevel_ + maximumZoomLevelChange_, maximumZoomLevel_); - newZoomLevel = qMin(qMax(perPinchMinimumZoomLevel, newZoomLevel), perPinchMaximumZoomLevel); - pinchLastZoomLevel_ = newZoomLevel; - map_->setZoomLevel(newZoomLevel); - } - if (activeGestures_ & TiltGesture && minimumZoomLevel_ >= 0 && maximumZoomLevel_ >= 0) { - // Note: tilt is not yet supported. - qreal newTilt = pinchLastTilt_; - if (dist) { - newTilt = - // How much further/closer the current touchpoints are (in pixels) compared to pinch start - ((dist - pinchStartDist_) * - // How much one pixel corresponds in units of tilt degrees (and multiply by above delta) - (maximumTiltChange_ / ((map_->width() + map_->height()) / 2))) + - // Add to starting tilt. - pinchStartTilt_; - } - qreal perPinchMinimumTilt = qMax(pinchStartTilt_ - maximumTiltChange_, minimumTilt_); - qreal perPinchMaximumTilt = qMin(pinchStartTilt_ + maximumTiltChange_, maximumTilt_); - newTilt = qMin(qMax(perPinchMinimumTilt, newTilt), perPinchMaximumTilt); - pinchLastTilt_ = newTilt; - map_->setTilt(newTilt); - } - if (activeGestures_ & RotationGesture) { - bool unlimitedRotation = (minimumRotation_ == 0.0 && maximumRotation_ == 0.0); - if ((pinchStartRotation_ >= minimumRotation_ && pinchStartRotation_ <= maximumRotation_) || unlimitedRotation) { - qreal r = pinchRotation_ * rotationFactor_ + pinchStartRotation_; - if (!unlimitedRotation) - r = qMin(qMax(minimumRotation_,r), maximumRotation_); - if (r > 360.0) - r -= 360; - if (r < -360.0) - r += 360.0; - map_->setBearing(r); - } - } - } - } -} - - #include "moc_qdeclarativegeomappincharea_p.cpp" QT_END_NAMESPACE diff --git a/src/imports/location/qdeclarativegeomappincharea_p.h b/src/imports/location/qdeclarativegeomappincharea_p.h index 34619aeb..309e735c 100644 --- a/src/imports/location/qdeclarativegeomappincharea_p.h +++ b/src/imports/location/qdeclarativegeomappincharea_p.h @@ -46,6 +46,9 @@ #include <QTouchEvent> #include <QObject> #include <QDebug> +#include <QElapsedTimer> +#include "qgeocoordinate.h" +#include "qdeclarativegeomapgesturearea_p.h" QT_BEGIN_NAMESPACE @@ -53,55 +56,9 @@ class QGraphicsSceneMouseEvent; class QDeclarativeGeoMap; class QTouchEvent; class QGeoMap; +class QPropertyAnimation; -class QDeclarativeGeoMapPinchEvent : public QObject -{ - Q_OBJECT - - Q_PROPERTY(QPointF center READ center) - Q_PROPERTY(qreal angle READ angle) - Q_PROPERTY(QPointF point1 READ point1) - Q_PROPERTY(QPointF point2 READ point2) - Q_PROPERTY(int pointCount READ pointCount) - Q_PROPERTY(bool accepted READ accepted WRITE setAccepted) - -public: - QDeclarativeGeoMapPinchEvent(QPointF center, qreal angle, - QPointF point1, QPointF point2, - int pointCount = 0, bool accepted = true) - : QObject(), center_(center), angle_(angle), - point1_(point1), point2_(point2), - pointCount_(pointCount), accepted_(accepted) {} - QDeclarativeGeoMapPinchEvent() - : QObject(), - angle_(0.0), - pointCount_(0), - accepted_(true) {} - - QPointF center() const { return center_; } - void setCenter(QPointF center) { center_ = center; } - qreal angle() const { return angle_; } - void setAngle(qreal angle) { angle_ = angle; } - QPointF point1() const { return point1_; } - void setPoint1(QPointF p) { point1_ = p; } - QPointF point2() const { return point2_; } - void setPoint2(QPointF p) { point2_ = p; } - int pointCount() const { return pointCount_; } - void setPointCount(int count) { pointCount_ = count; } - bool accepted() const { return accepted_; } - void setAccepted(bool a) { accepted_ = a; } - -private: - QPointF center_; - qreal angle_; - QPointF point1_; - QPointF point2_; - int pointCount_; - bool accepted_; -}; - -// tbd: should we have a 'active' / 'moving' boolean attribute when pinch is active? - +// Note: this class id being deprecated, please use the gestureArea instead class QDeclarativeGeoMapPinchArea: public QObject { Q_OBJECT @@ -113,18 +70,10 @@ class QDeclarativeGeoMapPinchArea: public QObject Q_PROPERTY(ActiveGestures activeGestures READ activeGestures WRITE setActiveGestures NOTIFY activeGesturesChanged) Q_PROPERTY(qreal maximumZoomLevelChange READ maximumZoomLevelChange WRITE setMaximumZoomLevelChange NOTIFY maximumZoomLevelChangeChanged) Q_PROPERTY(qreal rotationFactor READ rotationFactor WRITE setRotationFactor NOTIFY rotationFactorChanged) - // need for these is not clear, use-case(s) not yet identified: - //Q_PROPERTY(qreal minimumRotation READ minimumRotation WRITE setMinimumRotation NOTIFY minimumRotationChanged) - //Q_PROPERTY(qreal maximumRotation READ maximumRotation WRITE setMaximumRotation NOTIFY maximumRotationChanged) - //Q_PROPERTY(qreal minimumZoomLevel READ minimumZoomLevel WRITE setMinimumZoomLevel NOTIFY minimumZoomLevelChanged) - //Q_PROPERTY(qreal maximumZoomLevel READ maximumZoomLevel WRITE setMaximumZoomLevel NOTIFY maximumZoomLevelChanged) - // when tilt is supported, these are needed: - //Q_PROPERTY(qreal maximumTilt READ maximumTilt WRITE setMaximumTilt NOTIFY maximumTiltChanged) - //Q_PROPERTY(qreal minimumTilt READ minimumTilt WRITE setMinimumTilt NOTIFY minimumTiltChanged) - //Q_PROPERTY(qreal maximumTiltChange READ maximumTiltChange WRITE setMaximumTiltChange NOTIFY maximumTiltChangeChanged) + Q_PROPERTY(qreal flickDeceleration READ flickDeceleration WRITE setFlickDeceleration NOTIFY flickDecelerationChanged) public: - QDeclarativeGeoMapPinchArea(QDeclarativeGeoMap* map, QObject *parent = 0); + QDeclarativeGeoMapPinchArea(QObject *parent, QDeclarativeGeoMapGestureArea *gestureArea); ~QDeclarativeGeoMapPinchArea(); enum ActiveGesture { @@ -134,46 +83,73 @@ public: TiltGesture = 0x0004 }; Q_DECLARE_FLAGS(ActiveGestures, ActiveGesture); - - ActiveGestures activeGestures() const; - void setActiveGestures(ActiveGestures activeGestures); - - bool active() const; - void setActive(bool active); - - bool enabled() const; - void setEnabled(bool enabled); - - qreal minimumZoomLevel() const; - void setMinimumZoomLevel(qreal zoomLevel); - - qreal maximumZoomLevel() const; - void setMaximumZoomLevel(qreal zoomLevel); - - qreal maximumZoomLevelChange() const; - void setMaximumZoomLevelChange(qreal maxChange); - - qreal minimumRotation() const; - void setMinimumRotation(qreal zoomLevel); - - qreal maximumRotation() const; - void setMaximumRotation(qreal zoomLevel); - - qreal rotationFactor() const; - void setRotationFactor(qreal factor); - - qreal maximumTilt() const; - void setMaximumTilt(qreal tilt); - - qreal minimumTilt() const; - void setMinimumTilt(qreal tilt); - - qreal maximumTiltChange() const; - void setMaximumTiltChange(qreal tilt); - - void touchEvent(QTouchEvent *event); - - void zoomLevelLimits(qreal min, qreal max); + ActiveGestures activeGestures() + { + QDeclarativeGeoMapGestureArea::ActiveGestures gestures = gestureArea_->activeGestures(); + activeGestures_ &= 0; // reset; + if (gestures & QDeclarativeGeoMapGestureArea::ZoomGesture) + activeGestures_ |= ZoomGesture; + if (gestures & QDeclarativeGeoMapGestureArea::RotationGesture) + activeGestures_ |= RotationGesture; + if (gestures & QDeclarativeGeoMapGestureArea::TiltGesture) + activeGestures_ |= TiltGesture; + return activeGestures_; + } + void setActiveGestures(ActiveGestures activeGestures) + { + if (activeGestures == activeGestures_) + return; + activeGestures_ = activeGestures; + QDeclarativeGeoMapGestureArea::ActiveGestures &gestures = gestureArea_->activeGestures_; + gestures &= ~7; // reset the pinch component; + if (activeGestures & ZoomGesture) + gestures |= QDeclarativeGeoMapGestureArea::ZoomGesture; + if (activeGestures & RotationGesture) + gestures |= QDeclarativeGeoMapGestureArea::RotationGesture; + if (activeGestures & TiltGesture) + gestures |= QDeclarativeGeoMapGestureArea::TiltGesture; + emit gestureArea_->activeGesturesChanged(); + emit activeGesturesChanged(); + } + + bool active() const { return gestureArea_->isPinchActive(); } + void setActive(bool active) { gestureArea_->setPinchActive(active); } + + bool enabled() const { return gestureArea_->pinchEnabled(); } + void setEnabled(bool enabled){ gestureArea_->setPinchEnabled(enabled); } + + qreal minimumZoomLevel() const { return gestureArea_->minimumZoomLevel(); } + void setMinimumZoomLevel(qreal zoomLevel){ gestureArea_->setMinimumZoomLevel(zoomLevel); } + + qreal maximumZoomLevel() const { return gestureArea_->maximumZoomLevel(); } + void setMaximumZoomLevel(qreal zoomLevel){ gestureArea_->setMaximumZoomLevel(zoomLevel); } + + qreal maximumZoomLevelChange() const { return gestureArea_->maximumZoomLevelChange(); } + void setMaximumZoomLevelChange(qreal maxChange){ gestureArea_->setMaximumZoomLevelChange(maxChange); } + + qreal minimumRotation() const { return gestureArea_->minimumRotation(); } + void setMinimumRotation(qreal zoomLevel){ gestureArea_->setMinimumRotation(zoomLevel); } + + qreal maximumRotation() const { return gestureArea_->maximumRotation(); } + void setMaximumRotation(qreal zoomLevel){ gestureArea_->setMaximumRotation(zoomLevel); } + + qreal rotationFactor() const { return gestureArea_->rotationFactor(); } + void setRotationFactor(qreal factor){ gestureArea_->setRotationFactor(factor); } + + qreal maximumTilt() const { return gestureArea_->maximumTilt(); } + void setMaximumTilt(qreal tilt){ gestureArea_->setMaximumTilt(tilt); } + + qreal minimumTilt() const { return gestureArea_->minimumTilt(); } + void setMinimumTilt(qreal tilt){ gestureArea_->setMinimumTilt(tilt); } + + qreal maximumTiltChange() const { return gestureArea_->maximumTiltChange(); } + void setMaximumTiltChange(qreal tilt){ gestureArea_->setMaximumTiltChange(tilt); } + + qreal flickDeceleration() const { return gestureArea_->flickDeceleration(); } + void setFlickDeceleration(qreal deceleration){ gestureArea_->setFlickDeceleration(deceleration); } + + void zoomLevelLimits(qreal min, qreal max){ gestureArea_->zoomLevelLimits(min, max); } + void setMap(QGeoMap* map){ gestureArea_->setMap(map); } signals: void activeChanged(); @@ -188,48 +164,15 @@ signals: void minimumTiltChanged(); void maximumTiltChanged(); void maximumTiltChangeChanged(); + void flickDecelerationChanged(); void pinchStarted(QDeclarativeGeoMapPinchEvent* pinch); void pinchUpdated(QDeclarativeGeoMapPinchEvent* pinch); void pinchFinished(QDeclarativeGeoMapPinchEvent* pinch); private: - void updatePinch(); - -private: - QDeclarativeGeoMap* map_; - QDeclarativeGeoMapPinchEvent pinchEvent_; - bool enabled_; - bool active_; - qreal minimumZoomLevel_; - qreal maximumZoomLevel_; - qreal minimumRotation_; - qreal maximumRotation_; - QList<QTouchEvent::TouchPoint> touchPoints_; - bool inPinch_; - bool pinchRejected_; - bool pinchActivated_; - QPointF sceneStartPoint1_; - QPointF sceneStartPoint2_; - QPointF lastPoint1_; - QPointF lastPoint2_; - qreal pinchStartDist_; - qreal pinchStartZoomLevel_; - qreal pinchLastZoomLevel_; - qreal pinchStartRotation_; - qreal pinchLastAngle_; - qreal pinchRotation_; - QPointF sceneLastCenter_; - int id1_; - qreal maximumZoomLevelChange_; - qreal rotationFactor_; + QDeclarativeGeoMapGestureArea *gestureArea_; // the destination for this wrapper class ActiveGestures activeGestures_; - qreal minimumTilt_; - qreal maximumTilt_; - qreal maximumTiltChange_; - - qreal pinchLastTilt_; - qreal pinchStartTilt_; }; QT_END_NAMESPACE diff --git a/src/src.pro b/src/src.pro index b321da3d..3b54b67b 100644 --- a/src/src.pro +++ b/src/src.pro @@ -1,3 +1,4 @@ TEMPLATE = subdirs CONFIG+=ordered SUBDIRS += location plugins imports + |