diff options
Diffstat (limited to 'src/location/quickmapitems/qquickgeomapgesturearea.cpp')
-rw-r--r-- | src/location/quickmapitems/qquickgeomapgesturearea.cpp | 1876 |
1 files changed, 0 insertions, 1876 deletions
diff --git a/src/location/quickmapitems/qquickgeomapgesturearea.cpp b/src/location/quickmapitems/qquickgeomapgesturearea.cpp deleted file mode 100644 index c5547278..00000000 --- a/src/location/quickmapitems/qquickgeomapgesturearea.cpp +++ /dev/null @@ -1,1876 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtLocation module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qquickgeomapgesturearea_p.h" -#include "qdeclarativegeomap_p.h" -#include "error_messages_p.h" - -#include <QDebug> -#include <QPropertyAnimation> -#include <QtGui/QGuiApplication> -#include <QtGui/qevent.h> -#if QT_CONFIG(wheelevent) -#include <QtGui/QWheelEvent> -#endif -#include <QtGui/QMatrix4x4> -#include <QtGui/QStyleHints> -#include <QtQml/qqmlinfo.h> -#include <QtQuick/QQuickWindow> -#include "qgeomap_p.h" -#include <QtPositioning/private/qdoublevector2d_p.h> -#include <QtPositioning/private/qlocationutils_p.h> -#include <QtPositioningQuick/private/qquickgeocoordinateanimation_p.h> - -#include "math.h" -#include <cmath> - -#define QML_MAP_FLICK_DEFAULTMAXVELOCITY 2500 -#define QML_MAP_FLICK_MINIMUMDECELERATION 500 -#define QML_MAP_FLICK_DEFAULTDECELERATION 2500 -#define QML_MAP_FLICK_MAXIMUMDECELERATION 10000 - -#define QML_MAP_FLICK_VELOCITY_SAMPLE_PERIOD 38 -// 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. -static const qreal MinimumFlickVelocity = 75.0; -// Tolerance for detecting two finger sliding start -static const qreal MaximumParallelPosition = 40.0; // in degrees -// Tolerance for detecting parallel sliding -static const qreal MaximumParallelSlidingAngle = 4.0; // in degrees -// Tolerance for starting rotation -static const qreal MinimumRotationStartingAngle = 15.0; // in degrees -// Tolerance for starting pinch -static const qreal MinimumPinchDelta = 40; // in pixels -// Tolerance for starting tilt when sliding vertical -static const qreal MinimumPanToTiltDelta = 80; // in pixels; - -static qreal distanceBetweenTouchPoints(const QPointF &p1, const QPointF &p2) -{ - return QLineF(p1, p2).length(); -} - -static qreal angleFromPoints(const QPointF &p1, const QPointF &p2) -{ - return QLineF(p1, p2).angle(); -} - -// Keeps it in +- 180 -static qreal touchAngle(const QPointF &p1, const QPointF &p2) -{ - qreal angle = angleFromPoints(p1, p2); - if (angle > 180) - angle -= 360; - return angle; -} - -// Deals with angles crossing the +-180 edge, assumes that the delta can't be > 180 -static qreal angleDelta(const qreal angle1, const qreal angle2) -{ - qreal delta = angle1 - angle2; - if (delta > 180.0) // detect crossing angle1 positive, angle2 negative, rotation counterclockwise, difference negative - delta = angle1 - angle2 - 360.0; - else if (delta < -180.0) // detect crossing angle1 negative, angle2 positive, rotation clockwise, difference positive - delta = angle1 - angle2 + 360.0; - - return delta; -} - -static bool pointDragged(const QPointF &pOld, const QPointF &pNew) -{ - static const int startDragDistance = qApp->styleHints()->startDragDistance(); - return ( qAbs(pNew.x() - pOld.x()) > startDragDistance - || qAbs(pNew.y() - pOld.y()) > startDragDistance); -} - -static qreal vectorSize(const QPointF &vector) -{ - return std::sqrt(vector.x() * vector.x() + vector.y() * vector.y()); -} - -// This linearizes the angles around 0, and keep it linear around 180, allowing to differentiate -// touch angles that are supposed to be parallel (0 or 180 depending on what finger goes first) -static qreal touchAngleTilting(const QPointF &p1, const QPointF &p2) -{ - qreal angle = angleFromPoints(p1, p2); - if (angle > 270) - angle -= 360; - return angle; -} - -static bool movingParallelVertical(const QPointF &p1old, const QPointF &p1new, const QPointF &p2old, const QPointF &p2new) -{ - if (!pointDragged(p1old, p1new) || !pointDragged(p2old, p2new)) - return false; - - QPointF v1 = p1new - p1old; - QPointF v2 = p2new - p2old; - qreal v1v2size = vectorSize(v1 + v2); - - if (v1v2size < vectorSize(v1) || v1v2size < vectorSize(v2)) // going in opposite directions - return false; - - const qreal newAngle = touchAngleTilting(p1new, p2new); - const qreal oldAngle = touchAngleTilting(p1old, p2old); - const qreal angleDiff = angleDelta(newAngle, oldAngle); - - if (qAbs(angleDiff) > MaximumParallelSlidingAngle) - return false; - - return true; -} - -QT_BEGIN_NAMESPACE - - -/*! - \qmltype MapPinchEvent - \instantiates QGeoMapPinchEvent - \inqmlmodule QtLocation - - \brief MapPinchEvent type provides basic information about pinch event. - - MapPinchEvent type 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 QtLocation 5.0 -*/ - -/*! - \qmlproperty QPoint QtLocation::MapPinchEvent::center - - This read-only property holds the current center point. -*/ - -/*! - \qmlproperty real QtLocation::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 QtLocation::MapPinchEvent::point1 - \qmlproperty QPoint QtLocation::MapPinchEvent::point2 - - These read-only properties hold the actual touch points generating the pinch. - The points are not in any particular order. -*/ - -/*! - \qmlproperty int QtLocation::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 QtLocation::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. -*/ - -/*! - \qmltype MapGestureArea - \instantiates QQuickGeoMapGestureArea - - \inqmlmodule QtLocation - - \brief The MapGestureArea type provides Map gesture interaction. - - MapGestureArea objects are used as part of a Map, to provide for panning, - flicking and pinch-to-zoom gesture used on touch displays, as well as two finger rotation - and two finger parallel vertical sliding to tilt the map. - On platforms supporting \l QWheelEvent, using the scroll wheel alone, or in combination with - key modifiers Shift or Control will also zoom, rotate or tilt the map, respectively. - - A MapGestureArea is automatically created with a new Map and available with - the \l{Map::gesture}{gesture} 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 acceptedGestures 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 pinch 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.acceptedGestures: MapGestureArea.PinchGesture | MapGestureArea.PanGesture - } - \endcode - - \ingroup qml-QtLocation5-maps - \since QtLocation 5.0 -*/ - -/*! - \qmlproperty bool QtLocation::MapGestureArea::enabled - - This property holds whether the gestures are enabled. -*/ - -/*! - \qmlproperty bool QtLocation::MapGestureArea::pinchActive - - This read-only property holds whether the pinch gesture is active. -*/ - -/*! - \qmlproperty bool QtLocation::MapGestureArea::panActive - - This read-only property holds whether the pan gesture is active. - - \note Change notifications for this property were introduced in Qt 5.5. -*/ - -/*! - \qmlproperty bool QtLocation::MapGestureArea::rotationActive - - This read-only property holds whether the two-finger rotation gesture is active. - - \since QtLocation 5.9 -*/ - -/*! - \qmlproperty bool QtLocation::MapGestureArea::tiltActive - - This read-only property holds whether the two-finger tilt gesture is active. - - \since QtLocation 5.9 -*/ - -/*! - \qmlproperty real QtLocation::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 4.0, maximum value is 10.0 -*/ - -/*! - \qmlproperty real MapGestureArea::flickDeceleration - - This property holds the rate at which a flick will decelerate. - - The default value is 2500. -*/ - -/*! - \qmlsignal QtLocation::MapGestureArea::pinchStarted(PinchEvent event) - - This signal is emitted when a pinch gesture is started. - - Information about the pinch event is provided in \a event. - - The corresponding handler is \c onPinchStarted. - - \sa pinchUpdated, pinchFinished -*/ - -/*! - \qmlsignal QtLocation::MapGestureArea::pinchUpdated(PinchEvent event) - - This signal is emitted as the user's fingers move across the map, - after the \l pinchStarted signal is emitted. - - Information about the pinch event is provided in \a event. - - The corresponding handler is \c onPinchUpdated. - - \sa pinchStarted, pinchFinished -*/ - -/*! - \qmlsignal QtLocation::MapGestureArea::pinchFinished(PinchEvent event) - - This signal is emitted at the end of a pinch gesture. - - Information about the pinch event is provided in \a event. - - The corresponding handler is \c onPinchFinished. - - \sa pinchStarted, pinchUpdated -*/ - -/*! - \qmlsignal QtLocation::MapGestureArea::panStarted() - - This signal is emitted when the map begins to move 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. - - The corresponding handler is \c onPanStarted. -*/ - -/*! - \qmlsignal QtLocation::MapGestureArea::panFinished() - - This signal is emitted when the map stops moving due to user - interaction. If a flick was generated, this signal is - emitted before flick starts. If a flick was not - generated, this signal is emitted when the - user stops dragging - that is a mouse or touch release. - - The corresponding handler is \c onPanFinished. - -*/ - -/*! - \qmlsignal QtLocation::MapGestureArea::flickStarted() - - This signal is emitted when the map is flicked. A flick - starts from the point where the mouse or touch was released, - while still in motion. - - The corresponding handler is \c onFlickStarted. -*/ - -/*! - \qmlsignal QtLocation::MapGestureArea::flickFinished() - - This signal is emitted when the map stops moving due to a flick. - - The corresponding handler is \c onFlickFinished. -*/ - -/*! - \qmlsignal QtLocation::MapGestureArea::rotationStarted(PinchEvent event) - - This signal is emitted when a two-finger rotation gesture is started. - - Information about the pinch event is provided in \a event. - - The corresponding handler is \c onRotationStarted. - - \sa rotationUpdated(), rotationFinished() - - \since QtLocation 5.9 -*/ - -/*! - \qmlsignal QtLocation::MapGestureArea::rotationUpdated(PinchEvent event) - - This signal is emitted as the user's fingers move across the map, - after the \l rotationStarted() signal is emitted. - - Information about the pinch event is provided in \a event. - - The corresponding handler is \c onRotationUpdated. - - \sa rotationStarted(), rotationFinished() - - \since QtLocation 5.9 -*/ - -/*! - \qmlsignal QtLocation::MapGestureArea::rotationFinished(PinchEvent event) - - This signal is emitted at the end of a two-finger rotation gesture. - - Information about the pinch event is provided in \a event. - - The corresponding handler is \c onRotationFinished. - - \sa rotationStarted(), rotationUpdated() - - \since QtLocation 5.9 -*/ - -/*! - \qmlsignal QtLocation::MapGestureArea::tiltStarted(PinchEvent event) - - This signal is emitted when a two-finger tilt gesture is started. - - Information about the pinch event is provided in \a event. - - The corresponding handler is \c onTiltStarted. - - \sa tiltUpdated(), tiltFinished() - - \since QtLocation 5.9 -*/ - -/*! - \qmlsignal QtLocation::MapGestureArea::tiltUpdated(PinchEvent event) - - This signal is emitted as the user's fingers move across the map, - after the \l tiltStarted signal is emitted. - - Information about the pinch event is provided in \a event. - - The corresponding handler is \c onTiltUpdated. - - \sa tiltStarted(), tiltFinished() - - \since QtLocation 5.9 -*/ - -/*! - \qmlsignal QtLocation::MapGestureArea::tiltFinished(PinchEvent event) - - This signal is emitted at the end of a two-finger tilt gesture. - - Information about the pinch event is provided in \a event. - - The corresponding handler is \c onTiltFinished. - - \sa tiltStarted(), tiltUpdated() - - \since QtLocation 5.9 -*/ - -QQuickGeoMapGestureArea::QQuickGeoMapGestureArea(QDeclarativeGeoMap *map) - : QQuickItem(map), m_declarativeMap(map) -{ - m_touchPointState = touchPoints0; - m_pinchState = pinchInactive; - m_flickState = flickInactive; - m_rotationState = rotationInactive; - m_tiltState = tiltInactive; -} - -/*! - \internal -*/ -void QQuickGeoMapGestureArea::setMap(QGeoMap *map) -{ - if (m_map || !map) - return; - - m_map = map; - m_flick.m_animation = new QQuickGeoCoordinateAnimation(this); - m_flick.m_animation->setTargetObject(m_declarativeMap); - m_flick.m_animation->setProperty(QStringLiteral("center")); - m_flick.m_animation->setEasing(QEasingCurve(QEasingCurve::OutQuad)); - connect(m_flick.m_animation, &QQuickAbstractAnimation::stopped, this, &QQuickGeoMapGestureArea::handleFlickAnimationStopped); - m_map->setAcceptedGestures(panEnabled(), flickEnabled(), pinchEnabled(), rotationEnabled(), tiltEnabled()); -} - -/*! - \qmlproperty bool QtQuick::MapGestureArea::preventStealing - This property holds whether the mouse events may be stolen from this - MapGestureArea. - - If a Map is placed within an item that filters child mouse - and touch events, such as Flickable, the mouse and touch events - may be stolen from the MapGestureArea if a gesture is recognized - by the parent item, e.g. a flick gesture. If preventStealing is - set to \c true, no item will steal the mouse and touch events. - - Note that setting preventStealing to \c true once an item has started - stealing events has no effect until the next press event. - - By default this property is set to \c false. -*/ - -bool QQuickGeoMapGestureArea::preventStealing() const -{ - return m_preventStealing; -} - -void QQuickGeoMapGestureArea::setPreventStealing(bool prevent) -{ - if (prevent != m_preventStealing) { - m_preventStealing = prevent; - m_declarativeMap->setKeepMouseGrab(m_preventStealing && m_enabled); - m_declarativeMap->setKeepTouchGrab(m_preventStealing && m_enabled); - emit preventStealingChanged(); - } -} - -QQuickGeoMapGestureArea::~QQuickGeoMapGestureArea() -{ -} - -/*! - \qmlproperty enumeration QtLocation::MapGestureArea::acceptedGestures - - This property holds a bit field of gestures that are accepted. By default, - all gestures are enabled. - - \value MapGestureArea.NoGesture - Don't support any additional gestures (value: 0x0000). - - \value MapGestureArea.PinchGesture - Support the map pinch gesture (value: 0x0001). - - \value MapGestureArea.PanGesture - Support the map pan gesture (value: 0x0002). - - \value MapGestureArea.FlickGesture - Support the map flick gesture (value: 0x0004). - - \value MapGestureArea.RotationGesture - Support the map rotation gesture (value: 0x0008). - - \value MapGestureArea.TiltGesture - Support the map tilt gesture (value: 0x0010). -*/ - -QQuickGeoMapGestureArea::AcceptedGestures QQuickGeoMapGestureArea::acceptedGestures() const -{ - return m_acceptedGestures; -} - - -void QQuickGeoMapGestureArea::setAcceptedGestures(AcceptedGestures acceptedGestures) -{ - if (acceptedGestures == m_acceptedGestures) - return; - m_acceptedGestures = acceptedGestures; - - if (enabled()) { - setPanEnabled(acceptedGestures & PanGesture); - setFlickEnabled(acceptedGestures & FlickGesture); - setPinchEnabled(acceptedGestures & PinchGesture); - setRotationEnabled(acceptedGestures & RotationGesture); - setTiltEnabled(acceptedGestures & TiltGesture); - } - - if (m_map) - m_map->setAcceptedGestures(panEnabled(), flickEnabled(), pinchEnabled(), rotationEnabled(), tiltEnabled()); - - emit acceptedGesturesChanged(); -} - -/*! - \internal -*/ -bool QQuickGeoMapGestureArea::isPinchActive() const -{ - return m_pinchState == pinchActive; -} - -/*! - \internal -*/ -bool QQuickGeoMapGestureArea::isRotationActive() const -{ - return m_rotationState == rotationActive; -} - -/*! - \internal -*/ -bool QQuickGeoMapGestureArea::isTiltActive() const -{ - return m_tiltState == tiltActive; -} - -/*! - \internal -*/ -bool QQuickGeoMapGestureArea::isPanActive() const -{ - return m_flickState == panActive || m_flickState == flickActive; -} - -/*! - \internal -*/ -bool QQuickGeoMapGestureArea::enabled() const -{ - return m_enabled; -} - -/*! - \internal -*/ -void QQuickGeoMapGestureArea::setEnabled(bool enabled) -{ - if (enabled == m_enabled) - return; - m_enabled = enabled; - - if (enabled) { - setPanEnabled(m_acceptedGestures & PanGesture); - setFlickEnabled(m_acceptedGestures & FlickGesture); - setPinchEnabled(m_acceptedGestures & PinchGesture); - setRotationEnabled(m_acceptedGestures & RotationGesture); - setTiltEnabled(m_acceptedGestures & TiltGesture); - } else { - setPanEnabled(false); - setFlickEnabled(false); - setPinchEnabled(false); - setRotationEnabled(false); - setTiltEnabled(false); - } - if (m_map) - m_map->setAcceptedGestures(panEnabled(), flickEnabled(), pinchEnabled(), rotationEnabled(), tiltEnabled()); - - emit enabledChanged(); -} - -/*! - \internal -*/ -bool QQuickGeoMapGestureArea::pinchEnabled() const -{ - return m_pinch.m_pinchEnabled; -} - -/*! - \internal -*/ -void QQuickGeoMapGestureArea::setPinchEnabled(bool enabled) -{ - m_pinch.m_pinchEnabled = enabled; -} - -/*! - \internal -*/ -bool QQuickGeoMapGestureArea::rotationEnabled() const -{ - return m_pinch.m_rotationEnabled; -} - -/*! - \internal -*/ -void QQuickGeoMapGestureArea::setRotationEnabled(bool enabled) -{ - m_pinch.m_rotationEnabled = enabled; -} - -/*! - \internal -*/ -bool QQuickGeoMapGestureArea::tiltEnabled() const -{ - return m_pinch.m_tiltEnabled; -} - -/*! - \internal -*/ -void QQuickGeoMapGestureArea::setTiltEnabled(bool enabled) -{ - m_pinch.m_tiltEnabled = enabled; -} - -/*! - \internal -*/ -bool QQuickGeoMapGestureArea::panEnabled() const -{ - return m_flick.m_panEnabled; -} - -/*! - \internal -*/ -void QQuickGeoMapGestureArea::setPanEnabled(bool enabled) -{ - if (enabled == m_flick.m_panEnabled) - return; - m_flick.m_panEnabled = enabled; - - // unlike the pinch, the pan existing functionality is to stop immediately - if (!enabled) { - stopPan(); - m_flickState = flickInactive; - } -} - -/*! - \internal -*/ -bool QQuickGeoMapGestureArea::flickEnabled() const -{ - return m_flick.m_flickEnabled; -} - -/*! - \internal -*/ -void QQuickGeoMapGestureArea::setFlickEnabled(bool enabled) -{ - if (enabled == m_flick.m_flickEnabled) - return; - m_flick.m_flickEnabled = enabled; - // unlike the pinch, the flick existing functionality is to stop immediately - if (!enabled) { - bool stateActive = (m_flickState != flickInactive); - stopFlick(); - if (stateActive) { - if (m_flick.m_panEnabled) - m_flickState = panActive; - else - m_flickState = flickInactive; - } - } -} - -/*! - \internal - Used internally to set the minimum zoom level of the gesture area. - The caller is responsible to only send values that are valid - for the map plugin. Negative values are ignored. - */ -void QQuickGeoMapGestureArea::setMinimumZoomLevel(qreal min) -{ - // TODO: remove m_zoom.m_minimum and m_maximum and use m_declarativeMap directly instead. - if (min >= 0) - m_pinch.m_zoom.m_minimum = min; -} - -/*! - \internal - */ -qreal QQuickGeoMapGestureArea::minimumZoomLevel() const -{ - return m_pinch.m_zoom.m_minimum; -} - -/*! - \internal - Used internally to set the maximum zoom level of the gesture area. - The caller is responsible to only send values that are valid - for the map plugin. Negative values are ignored. - */ -void QQuickGeoMapGestureArea::setMaximumZoomLevel(qreal max) -{ - if (max >= 0) - m_pinch.m_zoom.m_maximum = max; -} - -/*! - \internal - */ -qreal QQuickGeoMapGestureArea::maximumZoomLevel() const -{ - return m_pinch.m_zoom.m_maximum; -} - -/*! - \internal -*/ -qreal QQuickGeoMapGestureArea::maximumZoomLevelChange() const -{ - return m_pinch.m_zoom.maximumChange; -} - -/*! - \internal -*/ -void QQuickGeoMapGestureArea::setMaximumZoomLevelChange(qreal maxChange) -{ - if (maxChange == m_pinch.m_zoom.maximumChange || maxChange < 0.1 || maxChange > 10.0) - return; - m_pinch.m_zoom.maximumChange = maxChange; - emit maximumZoomLevelChangeChanged(); -} - -/*! - \internal -*/ -qreal QQuickGeoMapGestureArea::flickDeceleration() const -{ - return m_flick.m_deceleration; -} - -/*! - \internal -*/ -void QQuickGeoMapGestureArea::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 == m_flick.m_deceleration) - return; - m_flick.m_deceleration = deceleration; - emit flickDecelerationChanged(); -} - - -/*! - \internal -*/ -void QQuickGeoMapGestureArea::handleMousePressEvent(QMouseEvent *event) -{ - if (m_map && m_map->handleEvent(event)) { - event->accept(); - return; - } - - handleTouchEvent(event); -} - -/*! - \internal -*/ -void QQuickGeoMapGestureArea::handleMouseMoveEvent(QMouseEvent *event) -{ - if (m_map && m_map->handleEvent(event)) { - event->accept(); - return; - } - - handleTouchEvent(event); -} - -/*! - \internal -*/ -void QQuickGeoMapGestureArea::handleMouseReleaseEvent(QMouseEvent *event) -{ - if (m_map && m_map->handleEvent(event)) { - event->accept(); - return; - } - - handleTouchEvent(event); -} - -/*! - \internal -*/ -void QQuickGeoMapGestureArea::handleTouchUngrabEvent() -{ - m_touchPoints.clear(); - update(); -} - -/*! - \internal -*/ -void QQuickGeoMapGestureArea::handleTouchEvent(QPointerEvent *event) -{ - if (m_map && m_map->handleEvent(event)) { - event->accept(); - return; - } - - // m_touchPoints.clear(); - - // Update the points we are going to use ourselves. - for (const auto &point: event->points()){ - auto grabber = qobject_cast<QQuickItem*>(event->exclusiveGrabber(point)); - // qDebug() << event->type() << point.state() << point << grabber; - - bool canBeGrabbed = grabber == nullptr || grabber == m_declarativeMap || (!grabber->keepTouchGrab() && !grabber->keepMouseGrab()); - // if (canBeGrabbed) m_touchPoints << point; - - //TODO: Testing shows that events are not always propagated with full set of points. - // Therefore, it can happen that our first point is a different point for a moment - // and that will trigger pan or pinch right away. So we keep track of points by their states - // an IDs but then testing shows that sometimes not all points are finished with Release. - // They just dissapear. Child MouseArea will 'eat up' second touch point if first point - // is grabbed by child ListView, for example. Maybe it's a bug in 6.2 RC? - if (point.state() == QEventPoint::Released || !canBeGrabbed){ - for (qsizetype i = 0; i < m_touchPoints.count(); ++i) { - if (m_touchPoints.at(i).id() == point.id()){ - m_touchPoints.removeAt(i); - } - } - }else{ - bool replaced = false; - for (qsizetype i = 0; i < m_touchPoints.count(); ++i) { - if (m_touchPoints.at(i).id() == point.id()){ - m_touchPoints.replace(i, point); - replaced = true; - } - } - if (!replaced){ - m_touchPoints << point; - } - } - - } - // qDebug() << "m_touchPoints.count=" << m_touchPoints.count(); - update(); - - if (isPanActive()){ - // In case we are pannin, we are using only the first point - // We let others to grab the second, if needed. - if (m_touchPoints.count() > 0){ - event->setExclusiveGrabber(m_touchPoints.at(0), m_declarativeMap); - } - }else if (isActive()){ - for (const auto &point: m_touchPoints) { - event->setExclusiveGrabber(point, m_declarativeMap); - } - } -} - -#if QT_CONFIG(wheelevent) -void QQuickGeoMapGestureArea::handleWheelEvent(QWheelEvent *event) -{ - if (!m_map) - return; - - if (m_map->handleEvent(event)) { - event->accept(); - return; - } - - const QGeoCoordinate &wheelGeoPos = m_declarativeMap->toCoordinate(event->position(), false); - const QPointF &preZoomPoint = event->position(); - - // Not using AltModifier as, for some reason, it causes angleDelta to be 0 - if (event->modifiers() & Qt::ShiftModifier && rotationEnabled()) { - emit rotationStarted(&m_pinch.m_event); - // First set bearing - const double bearingDelta = event->angleDelta().y() * qreal(0.05); - m_declarativeMap->setBearing(m_declarativeMap->bearing() + bearingDelta, wheelGeoPos); - emit rotationUpdated(&m_pinch.m_event); - emit rotationFinished(&m_pinch.m_event); - } else if (event->modifiers() & Qt::ControlModifier && tiltEnabled()) { - emit tiltStarted(&m_pinch.m_event); - const double tiltDelta = event->angleDelta().y() * qreal(0.05); - m_declarativeMap->setTilt(m_declarativeMap->tilt() + tiltDelta); - emit tiltUpdated(&m_pinch.m_event); - emit tiltFinished(&m_pinch.m_event); - } else if (pinchEnabled()) { - const double zoomLevelDelta = event->angleDelta().y() * qreal(0.001); - // Gesture area should always honor maxZL, but Map might not. - m_declarativeMap->setZoomLevel(qMin<qreal>(m_declarativeMap->zoomLevel() + zoomLevelDelta, maximumZoomLevel()), - false); - const QPointF &postZoomPoint = m_declarativeMap->fromCoordinate(wheelGeoPos, false); - - if (preZoomPoint != postZoomPoint) // need to re-anchor the wheel geoPos to the event position - m_declarativeMap->alignCoordinateToPoint(wheelGeoPos, preZoomPoint); - } - // event->accept(); -} -#endif - -/*! - \internal -*/ -void QQuickGeoMapGestureArea::clearTouchData() -{ - m_flickVector = QVector2D(); - m_touchPointsCentroid.setX(0); - m_touchPointsCentroid.setY(0); - m_touchCenterCoord.setLongitude(0); - m_touchCenterCoord.setLatitude(0); - m_startCoord.setLongitude(0); - m_startCoord.setLatitude(0); -} - - -/*! - \internal -*/ -void QQuickGeoMapGestureArea::updateFlickParameters(const QPointF &pos) -{ - // Take velocity samples every sufficient period of time, used later to determine the flick - // duration and speed (when mouse is released). - qreal elapsed = qreal(m_lastPosTime.elapsed()); - - if (elapsed >= QML_MAP_FLICK_VELOCITY_SAMPLE_PERIOD) { - elapsed /= 1000.; - qreal vel = distanceBetweenTouchPoints(pos, m_lastPos) / elapsed; - m_flickVector = (QVector2D(pos) - QVector2D(m_lastPos)).normalized(); - m_flickVector *= qBound<qreal>(-m_flick.m_maxVelocity, vel, m_flick.m_maxVelocity); - - m_lastPos = pos; - m_lastPosTime.restart(); - } -} - -void QQuickGeoMapGestureArea::setTouchPointState(const QQuickGeoMapGestureArea::TouchPointState state) -{ - m_touchPointState = state; -} - -void QQuickGeoMapGestureArea::setFlickState(const QQuickGeoMapGestureArea::FlickState state) -{ - m_flickState = state; -} - -void QQuickGeoMapGestureArea::setTiltState(const QQuickGeoMapGestureArea::TiltState state) -{ - m_tiltState = state; -} - -void QQuickGeoMapGestureArea::setRotationState(const QQuickGeoMapGestureArea::RotationState state) -{ - m_rotationState = state; -} - -void QQuickGeoMapGestureArea::setPinchState(const QQuickGeoMapGestureArea::PinchState state) -{ - m_pinchState = state; -} - -/*! - \internal -*/ - -bool QQuickGeoMapGestureArea::isActive() const -{ - return isPanActive() || isPinchActive() || isRotationActive() || isTiltActive(); -} - -/*! - \internal -*/ -// simplify the gestures by using a state-machine format (easy to move to a future state machine) -void QQuickGeoMapGestureArea::update() -{ - if (!m_map) - return; - // First state machine is for the number of touch points - - //combine touch with mouse event - m_allPoints.clear(); - m_allPoints << m_touchPoints; - std::sort(m_allPoints.begin(), m_allPoints.end(), [](const QTouchEvent::TouchPoint &tp1, const QTouchEvent::TouchPoint &tp2) { return tp1.id() < tp2.id(); }); - - touchPointStateMachine(); - - // Parallel state machine for tilt. Tilt goes first as it blocks anything else, when started. - // But tilting can also only start if nothing else is active. - if (isTiltActive() || m_pinch.m_tiltEnabled) - tiltStateMachine(); - - // Parallel state machine for pinch - if (isPinchActive() || m_pinch.m_pinchEnabled) - pinchStateMachine(); - - // Parallel state machine for rotation. - if (isRotationActive() || m_pinch.m_rotationEnabled) - rotationStateMachine(); - - // 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 isPanActive() below allows pan continue its current gesture if you disable - // the whole gesture. - // Pan goes last because it does reanchoring in updatePan() which makes the map - // properly rotate around the touch point centroid. - if (isPanActive() || m_flick.m_flickEnabled || m_flick.m_panEnabled) - panStateMachine(); -} - -/*! - \internal -*/ -void QQuickGeoMapGestureArea::touchPointStateMachine() -{ - // Transitions: - switch (m_touchPointState) { - case touchPoints0: - if (m_allPoints.count() == 1) { - clearTouchData(); - startOneTouchPoint(); - setTouchPointState(touchPoints1); - } else if (m_allPoints.count() >= 2) { - clearTouchData(); - startTwoTouchPoints(); - setTouchPointState(touchPoints2); - } - break; - case touchPoints1: - if (m_allPoints.count() == 0) { - setTouchPointState(touchPoints0); - } else if (m_allPoints.count() == 2) { - m_touchCenterCoord = m_declarativeMap->toCoordinate(m_touchPointsCentroid, false); - startTwoTouchPoints(); - setTouchPointState(touchPoints2); - } - break; - case touchPoints2: - if (m_allPoints.count() == 0) { - setTouchPointState(touchPoints0); - } else if (m_allPoints.count() == 1) { - m_touchCenterCoord = m_declarativeMap->toCoordinate(m_touchPointsCentroid, false); - startOneTouchPoint(); - setTouchPointState(touchPoints1); - } - break; - }; - - // Update - switch (m_touchPointState) { - case touchPoints0: - break; // do nothing if no touch points down - case touchPoints1: - updateOneTouchPoint(); - break; - case touchPoints2: - updateTwoTouchPoints(); - break; - } -} - -/*! - \internal -*/ -void QQuickGeoMapGestureArea::startOneTouchPoint() -{ - m_sceneStartPoint1 = mapFromScene(m_allPoints.at(0).scenePosition()); - m_lastPos = m_sceneStartPoint1; - m_lastPosTime.start(); - QGeoCoordinate startCoord = m_declarativeMap->toCoordinate(m_sceneStartPoint1, false); - // ensures a smooth transition for panning - m_startCoord.setLongitude(m_startCoord.longitude() + startCoord.longitude() - - m_touchCenterCoord.longitude()); - m_startCoord.setLatitude(m_startCoord.latitude() + startCoord.latitude() - - m_touchCenterCoord.latitude()); -} - -/*! - \internal -*/ -void QQuickGeoMapGestureArea::updateOneTouchPoint() -{ - m_sceneStartPoint1 = mapFromScene(m_allPoints.at(0).scenePressPosition()); - m_touchPointsCentroid = mapFromScene(m_allPoints.at(0).scenePosition()); - updateFlickParameters(m_touchPointsCentroid); -} - -/*! - \internal -*/ -void QQuickGeoMapGestureArea::startTwoTouchPoints() -{ - m_sceneStartPoint1 = mapFromScene(m_allPoints.at(0).scenePosition()); - m_sceneStartPoint2 = mapFromScene(m_allPoints.at(1).scenePosition()); - QPointF startPos = (m_sceneStartPoint1 + m_sceneStartPoint2) * 0.5; - m_lastPos = startPos; - m_lastPosTime.start(); - QGeoCoordinate startCoord = m_declarativeMap->toCoordinate(startPos, false); - m_startCoord.setLongitude(m_startCoord.longitude() + startCoord.longitude() - - m_touchCenterCoord.longitude()); - m_startCoord.setLatitude(m_startCoord.latitude() + startCoord.latitude() - - m_touchCenterCoord.latitude()); - m_twoTouchAngleStart = touchAngle(m_sceneStartPoint1, m_sceneStartPoint2); // Initial angle used for calculating rotation - m_distanceBetweenTouchPointsStart = distanceBetweenTouchPoints(m_sceneStartPoint1, m_sceneStartPoint2); - m_twoTouchPointsCentroidStart = (m_sceneStartPoint1 + m_sceneStartPoint2) / 2; -} - -/*! - \internal -*/ -void QQuickGeoMapGestureArea::updateTwoTouchPoints() -{ - m_sceneStartPoint1 = mapFromScene(m_allPoints.at(0).scenePressPosition()); - QPointF p1 = mapFromScene(m_allPoints.at(0).scenePosition()); - m_sceneStartPoint2 = mapFromScene(m_allPoints.at(1).scenePressPosition()); - QPointF p2 = mapFromScene(m_allPoints.at(1).scenePosition()); - m_distanceBetweenTouchPoints = distanceBetweenTouchPoints(p1, p2); - m_touchPointsCentroid = (p1 + p2) / 2; - updateFlickParameters(m_touchPointsCentroid); - - m_twoTouchAngle = touchAngle(p1, p2); -} - -/*! - \internal -*/ -void QQuickGeoMapGestureArea::tiltStateMachine() -{ - TiltState lastState = m_tiltState; - // Transitions: - switch (m_tiltState) { - case tiltInactive: - if (m_allPoints.count() >= 2) { - if (!isRotationActive() && !isPinchActive() && canStartTilt()) { // only gesture that can be overridden: pan/flick - m_declarativeMap->setKeepTouchGrab(true); - startTilt(); - setTiltState(tiltActive); - } else { - setTiltState(tiltInactiveTwoPoints); - } - } - break; - case tiltInactiveTwoPoints: - if (m_allPoints.count() <= 1) { - setTiltState(tiltInactive); - } else { - if (!isRotationActive() && !isPinchActive() && canStartTilt()) { // only gesture that can be overridden: pan/flick - m_declarativeMap->setKeepTouchGrab(true); - startTilt(); - setTiltState(tiltActive); - } - } - break; - case tiltActive: - if (m_allPoints.count() <= 1) { - setTiltState(tiltInactive); - m_declarativeMap->setKeepTouchGrab(m_preventStealing); - endTilt(); - } - break; - } - // This line implements an exclusive state machine, where the transitions and updates don't - // happen on the same frame - if (m_tiltState != lastState) { - emit tiltActiveChanged(); - return; - } - - // Update - switch (m_tiltState) { - case tiltInactive: - case tiltInactiveTwoPoints: - break; // do nothing - case tiltActive: - updateTilt(); - break; - } -} - -bool validateTouchAngleForTilting(const qreal angle) -{ - return ((qAbs(angle) - 180.0) < MaximumParallelPosition) || (qAbs(angle) < MaximumParallelPosition); -} - -/*! - \internal -*/ -bool QQuickGeoMapGestureArea::canStartTilt() -{ - if (m_allPoints.count() >= 2) { - QPointF p1 = mapFromScene(m_allPoints.at(0).scenePosition()); - QPointF p2 = mapFromScene(m_allPoints.at(1).scenePosition()); - if (validateTouchAngleForTilting(m_twoTouchAngle) - && movingParallelVertical(m_sceneStartPoint1, p1, m_sceneStartPoint2, p2) - && qAbs(m_twoTouchPointsCentroidStart.y() - m_touchPointsCentroid.y()) > MinimumPanToTiltDelta) { - m_pinch.m_event.setCenter(mapFromScene(m_touchPointsCentroid)); - m_pinch.m_event.setAngle(m_twoTouchAngle); - m_pinch.m_event.setPoint1(p1); - m_pinch.m_event.setPoint2(p2); - m_pinch.m_event.setPointCount(m_allPoints.count()); - m_pinch.m_event.setAccepted(true); - emit tiltStarted(&m_pinch.m_event); - return true; - } - } - return false; -} - -/*! - \internal -*/ -void QQuickGeoMapGestureArea::startTilt() -{ - if (isPanActive()) { - stopPan(); - setFlickState(flickInactive); - } - - m_pinch.m_tilt.m_startTouchCentroid = m_touchPointsCentroid; - m_pinch.m_tilt.m_startTilt = m_declarativeMap->tilt(); -} - -/*! - \internal -*/ -void QQuickGeoMapGestureArea::updateTilt() -{ - // Calculate the new tilt - qreal verticalDisplacement = (m_touchPointsCentroid - m_pinch.m_tilt.m_startTouchCentroid).y(); - - // Approach: 10pixel = 1 degree. - qreal tilt = verticalDisplacement / 10.0; - qreal newTilt = m_pinch.m_tilt.m_startTilt - tilt; - m_declarativeMap->setTilt(newTilt); - - m_pinch.m_event.setCenter(mapFromScene(m_touchPointsCentroid)); - m_pinch.m_event.setAngle(m_twoTouchAngle); - m_pinch.m_lastPoint1 = mapFromScene(m_allPoints.at(0).scenePosition()); - m_pinch.m_lastPoint2 = mapFromScene(m_allPoints.at(1).scenePosition()); - m_pinch.m_event.setPoint1(m_pinch.m_lastPoint1); - m_pinch.m_event.setPoint2(m_pinch.m_lastPoint2); - m_pinch.m_event.setPointCount(m_allPoints.count()); - m_pinch.m_event.setAccepted(true); - - emit tiltUpdated(&m_pinch.m_event); -} - -/*! - \internal -*/ -void QQuickGeoMapGestureArea::endTilt() -{ - QPointF p1 = mapFromScene(m_pinch.m_lastPoint1); - QPointF p2 = mapFromScene(m_pinch.m_lastPoint2); - m_pinch.m_event.setCenter((p1 + p2) / 2); - m_pinch.m_event.setAngle(m_pinch.m_lastAngle); - m_pinch.m_event.setPoint1(p1); - m_pinch.m_event.setPoint2(p2); - m_pinch.m_event.setAccepted(true); - m_pinch.m_event.setPointCount(0); - emit tiltFinished(&m_pinch.m_event); -} - -/*! - \internal -*/ -void QQuickGeoMapGestureArea::rotationStateMachine() -{ - RotationState lastState = m_rotationState; - // Transitions: - switch (m_rotationState) { - case rotationInactive: - if (m_allPoints.count() >= 2) { - if (!isTiltActive() && canStartRotation()) { - m_declarativeMap->setKeepTouchGrab(true); - startRotation(); - setRotationState(rotationActive); - } else { - setRotationState(rotationInactiveTwoPoints); - } - } - break; - case rotationInactiveTwoPoints: - if (m_allPoints.count() <= 1) { - setRotationState(rotationInactive); - } else { - if (!isTiltActive() && canStartRotation()) { - m_declarativeMap->setKeepTouchGrab(true); - startRotation(); - setRotationState(rotationActive); - } - } - break; - case rotationActive: - if (m_allPoints.count() <= 1) { - setRotationState(rotationInactive); - m_declarativeMap->setKeepTouchGrab(m_preventStealing); - endRotation(); - } - break; - } - // This line implements an exclusive state machine, where the transitions and updates don't - // happen on the same frame - if (m_rotationState != lastState) { - emit rotationActiveChanged(); - return; - } - - // Update - switch (m_rotationState) { - case rotationInactive: - case rotationInactiveTwoPoints: - break; // do nothing - case rotationActive: - updateRotation(); - break; - } -} - -/*! - \internal -*/ -bool QQuickGeoMapGestureArea::canStartRotation() -{ - if (m_allPoints.count() >= 2) { - QPointF p1 = mapFromScene(m_allPoints.at(0).scenePosition()); - QPointF p2 = mapFromScene(m_allPoints.at(1).scenePosition()); - if (pointDragged(m_sceneStartPoint1, p1) || pointDragged(m_sceneStartPoint2, p2)) { - qreal delta = angleDelta(m_twoTouchAngleStart, m_twoTouchAngle); - if (qAbs(delta) < MinimumRotationStartingAngle) { - return false; - } - m_pinch.m_event.setCenter(mapFromScene(m_touchPointsCentroid)); - m_pinch.m_event.setAngle(m_twoTouchAngle); - m_pinch.m_event.setPoint1(p1); - m_pinch.m_event.setPoint2(p2); - m_pinch.m_event.setPointCount(m_allPoints.count()); - m_pinch.m_event.setAccepted(true); - emit rotationStarted(&m_pinch.m_event); - return m_pinch.m_event.accepted(); - } - } - return false; -} - -/*! - \internal -*/ -void QQuickGeoMapGestureArea::startRotation() -{ - m_pinch.m_rotation.m_startBearing = m_declarativeMap->bearing(); - m_pinch.m_rotation.m_previousTouchAngle = m_twoTouchAngle; - m_pinch.m_rotation.m_totalAngle = 0.0; -} - -/*! - \internal -*/ -void QQuickGeoMapGestureArea::updateRotation() -{ - // Calculate the new bearing - qreal angle = angleDelta(m_pinch.m_rotation.m_previousTouchAngle, m_twoTouchAngle); - if (qAbs(angle) < 0.2) // avoiding too many updates - return; - - m_pinch.m_rotation.m_previousTouchAngle = m_twoTouchAngle; - m_pinch.m_rotation.m_totalAngle += angle; - qreal newBearing = m_pinch.m_rotation.m_startBearing - m_pinch.m_rotation.m_totalAngle; - m_declarativeMap->setBearing(newBearing); - - m_pinch.m_event.setCenter(mapFromScene(m_touchPointsCentroid)); - m_pinch.m_event.setAngle(m_twoTouchAngle); - m_pinch.m_lastPoint1 = mapFromScene(m_allPoints.at(0).scenePosition()); - m_pinch.m_lastPoint2 = mapFromScene(m_allPoints.at(1).scenePosition()); - m_pinch.m_event.setPoint1(m_pinch.m_lastPoint1); - m_pinch.m_event.setPoint2(m_pinch.m_lastPoint2); - m_pinch.m_event.setPointCount(m_allPoints.count()); - m_pinch.m_event.setAccepted(true); - - emit rotationUpdated(&m_pinch.m_event); -} - -/*! - \internal -*/ -void QQuickGeoMapGestureArea::endRotation() -{ - QPointF p1 = mapFromScene(m_pinch.m_lastPoint1); - QPointF p2 = mapFromScene(m_pinch.m_lastPoint2); - m_pinch.m_event.setCenter((p1 + p2) / 2); - m_pinch.m_event.setAngle(m_pinch.m_lastAngle); - m_pinch.m_event.setPoint1(p1); - m_pinch.m_event.setPoint2(p2); - m_pinch.m_event.setAccepted(true); - m_pinch.m_event.setPointCount(0); - emit rotationFinished(&m_pinch.m_event); -} - -/*! - \internal -*/ -void QQuickGeoMapGestureArea::pinchStateMachine() -{ - PinchState lastState = m_pinchState; - // Transitions: - switch (m_pinchState) { - case pinchInactive: - if (m_allPoints.count() >= 2) { - if (!isTiltActive() && canStartPinch()) { - m_declarativeMap->setKeepTouchGrab(true); - startPinch(); - setPinchState(pinchActive); - } else { - setPinchState(pinchInactiveTwoPoints); - } - } - break; - case pinchInactiveTwoPoints: - if (m_allPoints.count() <= 1) { - setPinchState(pinchInactive); - } else { - if (!isTiltActive() && canStartPinch()) { - m_declarativeMap->setKeepTouchGrab(true); - startPinch(); - setPinchState(pinchActive); - } - } - break; - case pinchActive: - if (m_allPoints.count() <= 1) { // Once started, pinch goes off only when finger(s) are release - setPinchState(pinchInactive); - m_declarativeMap->setKeepTouchGrab(m_preventStealing); - endPinch(); - } - break; - } - // This line implements an exclusive state machine, where the transitions and updates don't - // happen on the same frame - if (m_pinchState != lastState) { - emit pinchActiveChanged(); - return; - } - - // Update - switch (m_pinchState) { - case pinchInactive: - case pinchInactiveTwoPoints: - break; // do nothing - case pinchActive: - updatePinch(); - break; - } -} - -/*! - \internal -*/ -bool QQuickGeoMapGestureArea::canStartPinch() -{ - if (m_allPoints.count() >= 2) { - QPointF p1 = mapFromScene(m_allPoints.at(0).scenePosition()); - QPointF p2 = mapFromScene(m_allPoints.at(1).scenePosition()); - if (qAbs(m_distanceBetweenTouchPoints - m_distanceBetweenTouchPointsStart) > MinimumPinchDelta) { - m_pinch.m_event.setCenter(mapFromScene(m_touchPointsCentroid)); - m_pinch.m_event.setAngle(m_twoTouchAngle); - m_pinch.m_event.setPoint1(p1); - m_pinch.m_event.setPoint2(p2); - m_pinch.m_event.setPointCount(m_allPoints.count()); - m_pinch.m_event.setAccepted(true); - emit pinchStarted(&m_pinch.m_event); - return m_pinch.m_event.accepted(); - } - } - return false; -} - -/*! - \internal -*/ -void QQuickGeoMapGestureArea::startPinch() -{ - m_pinch.m_startDist = m_distanceBetweenTouchPoints; - m_pinch.m_zoom.m_previous = m_declarativeMap->zoomLevel(); - m_pinch.m_lastAngle = m_twoTouchAngle; - - m_pinch.m_lastPoint1 = mapFromScene(m_allPoints.at(0).scenePosition()); - m_pinch.m_lastPoint2 = mapFromScene(m_allPoints.at(1).scenePosition()); - - m_pinch.m_zoom.m_start = m_declarativeMap->zoomLevel(); -} - -/*! - \internal -*/ -void QQuickGeoMapGestureArea::updatePinch() -{ - // Calculate the new zoom level if we have distance ( >= 2 touchpoints), otherwise stick with old. - qreal newZoomLevel = m_pinch.m_zoom.m_previous; - if (m_distanceBetweenTouchPoints) { - newZoomLevel = - // How much further/closer the current touchpoints are (in pixels) compared to pinch start - ((m_distanceBetweenTouchPoints - m_pinch.m_startDist) * - // How much one pixel corresponds in units of zoomlevel (and multiply by above delta) - (m_pinch.m_zoom.maximumChange / ((width() + height()) / 2))) + - // Add to starting zoom level. Sign of (dist-pinchstartdist) takes care of zoom in / out - m_pinch.m_zoom.m_start; - } - - m_pinch.m_event.setCenter(mapFromScene(m_touchPointsCentroid)); - m_pinch.m_event.setAngle(m_twoTouchAngle); - - m_pinch.m_lastPoint1 = mapFromScene(m_allPoints.at(0).scenePosition()); - m_pinch.m_lastPoint2 = mapFromScene(m_allPoints.at(1).scenePosition()); - m_pinch.m_event.setPoint1(m_pinch.m_lastPoint1); - m_pinch.m_event.setPoint2(m_pinch.m_lastPoint2); - m_pinch.m_event.setPointCount(m_allPoints.count()); - m_pinch.m_event.setAccepted(true); - - m_pinch.m_lastAngle = m_twoTouchAngle; - emit pinchUpdated(&m_pinch.m_event); - - if (m_acceptedGestures & PinchGesture) { - // Take maximum and minimumzoomlevel into account - qreal perPinchMinimumZoomLevel = qMax(m_pinch.m_zoom.m_start - m_pinch.m_zoom.maximumChange, m_pinch.m_zoom.m_minimum); - qreal perPinchMaximumZoomLevel = qMin(m_pinch.m_zoom.m_start + m_pinch.m_zoom.maximumChange, m_pinch.m_zoom.m_maximum); - newZoomLevel = qMin(qMax(perPinchMinimumZoomLevel, newZoomLevel), perPinchMaximumZoomLevel); - m_declarativeMap->setZoomLevel(qMin<qreal>(newZoomLevel, maximumZoomLevel()), false); - m_pinch.m_zoom.m_previous = newZoomLevel; - } -} - -/*! - \internal -*/ -void QQuickGeoMapGestureArea::endPinch() -{ - QPointF p1 = mapFromScene(m_pinch.m_lastPoint1); - QPointF p2 = mapFromScene(m_pinch.m_lastPoint2); - m_pinch.m_event.setCenter((p1 + p2) / 2); - m_pinch.m_event.setAngle(m_pinch.m_lastAngle); - m_pinch.m_event.setPoint1(p1); - m_pinch.m_event.setPoint2(p2); - m_pinch.m_event.setAccepted(true); - m_pinch.m_event.setPointCount(0); - emit pinchFinished(&m_pinch.m_event); - m_pinch.m_startDist = 0; -} - -/*! - \internal -*/ -void QQuickGeoMapGestureArea::panStateMachine() -{ - FlickState lastState = m_flickState; - - // Transitions - switch (m_flickState) { - case flickInactive: - if (!isTiltActive() && canStartPan()) { - // Update startCoord_ to ensure smooth start for panning when going over startDragDistance - QGeoCoordinate newStartCoord = m_declarativeMap->toCoordinate(m_touchPointsCentroid, false); - m_startCoord.setLongitude(newStartCoord.longitude()); - m_startCoord.setLatitude(newStartCoord.latitude()); - m_declarativeMap->setKeepMouseGrab(true); - m_declarativeMap->setKeepTouchGrab(true); - setFlickState(panActive); - } - break; - case panActive: - if (m_allPoints.count() == 0) { - if (!tryStartFlick()) - { - setFlickState(flickInactive); - // mark as inactive for use by camera - if (m_pinchState == pinchInactive && m_rotationState == rotationInactive && m_tiltState == tiltInactive) { - m_declarativeMap->setKeepMouseGrab(m_preventStealing); - m_declarativeMap->setKeepTouchGrab(m_preventStealing); - m_map->prefetchData(); - } - emit panFinished(); - } else { - setFlickState(flickActive); - emit panFinished(); - emit flickStarted(); - } - } - break; - case flickActive: - if (m_allPoints.count() > 0) { // re touched before movement ended - stopFlick(); - m_declarativeMap->setKeepMouseGrab(true); - m_declarativeMap->setKeepTouchGrab(true); - setFlickState(panActive); - } - break; - } - - if (m_flickState != lastState) - emit panActiveChanged(); - - // Update - switch (m_flickState) { - case flickInactive: // do nothing - break; - case panActive: - updatePan(); - // this ensures 'panStarted' occurs after the pan has actually started - if (lastState != panActive) - emit panStarted(); - break; - case flickActive: - break; - } -} -/*! - \internal -*/ -bool QQuickGeoMapGestureArea::canStartPan() -{ - if (m_allPoints.count() == 0 || (m_acceptedGestures & 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() * 2; - QPointF p1 = mapFromScene(m_allPoints.at(0).scenePosition()); - int dyFromPress = int(p1.y() - m_sceneStartPoint1.y()); - int dxFromPress = int(p1.x() - m_sceneStartPoint1.x()); - if ((qAbs(dyFromPress) >= startDragDistance || qAbs(dxFromPress) >= startDragDistance)) - return true; - - return false; -} - -/*! - \internal -*/ -void QQuickGeoMapGestureArea::updatePan() -{ - m_declarativeMap->alignCoordinateToPoint(m_startCoord, m_touchPointsCentroid); -} - -/*! - \internal -*/ -bool QQuickGeoMapGestureArea::tryStartFlick() -{ - if ((m_acceptedGestures & FlickGesture) == 0) - return false; - // if we drag then pause before release we should not cause a flick. - qreal flickSpeed = 0.0; - if (m_lastPosTime.elapsed() < QML_MAP_FLICK_VELOCITY_SAMPLE_PERIOD) - flickSpeed = m_flickVector.length(); - - int flickTime = 0; - int flickPixels = 0; - QVector2D flickVector; - - if (qAbs(flickSpeed) > MinimumFlickVelocity - && distanceBetweenTouchPoints(m_touchPointsCentroid, m_sceneStartPoint1) > FlickThreshold) { - qreal acceleration = m_flick.m_deceleration; - if ((flickSpeed > 0.0f) == (m_flick.m_deceleration > 0.0f)) - acceleration = acceleration * -1.0f; - flickTime = static_cast<int>(-1000 * flickSpeed / acceleration); - flickPixels = (flickTime * flickSpeed) / 2000.0; - flickVector = m_flickVector.normalized() * flickPixels; - } - - if (flickTime > 0) { - startFlick(flickVector.x(), flickVector.y(), flickTime); - return true; - } - return false; -} - -/*! - \internal -*/ -void QQuickGeoMapGestureArea::startFlick(int dx, int dy, int timeMs) -{ - if (!m_flick.m_animation) - return; - if (timeMs < 0) - return; - - QGeoCoordinate animationStartCoordinate = m_declarativeMap->center(); - - if (m_flick.m_animation->isRunning()) - m_flick.m_animation->stop(); - QGeoCoordinate animationEndCoordinate = m_declarativeMap->center(); - m_flick.m_animation->setDuration(timeMs); - - QPointF delta(dx, dy); - QMatrix4x4 matBearing; - matBearing.rotate(m_map->cameraData().bearing(), 0, 0, 1); - delta = matBearing.map(delta); - - double zoom = pow(2.0, m_declarativeMap->zoomLevel()); - double longitude = animationStartCoordinate.longitude() - (delta.x() / zoom); - double latitude = animationStartCoordinate.latitude() + (delta.y() / zoom); - - if (delta.x() > 0) - m_flick.m_animation->setDirection(QQuickGeoCoordinateAnimation::West); - else - m_flick.m_animation->setDirection(QQuickGeoCoordinateAnimation::East); - - //keep animation in correct bounds - animationEndCoordinate.setLongitude(QLocationUtils::wrapLong(longitude)); - animationEndCoordinate.setLatitude(QLocationUtils::clipLat(latitude, QLocationUtils::mercatorMaxLatitude())); - - m_flick.m_animation->setFrom(animationStartCoordinate); - m_flick.m_animation->setTo(animationEndCoordinate); - m_flick.m_animation->start(); -} - -void QQuickGeoMapGestureArea::stopPan() -{ - if (m_flickState == flickActive) { - stopFlick(); - } else if (m_flickState == panActive) { - m_flickVector = QVector2D(); - setFlickState(flickInactive); - m_declarativeMap->setKeepMouseGrab(m_preventStealing); - emit panFinished(); - emit panActiveChanged(); - m_map->prefetchData(); - } -} - -/*! - \internal -*/ -void QQuickGeoMapGestureArea::stopFlick() -{ - if (!m_flick.m_animation) - return; - m_flickVector = QVector2D(); - if (m_flick.m_animation->isRunning()) - m_flick.m_animation->stop(); - else - handleFlickAnimationStopped(); -} - -void QQuickGeoMapGestureArea::handleFlickAnimationStopped() -{ - m_declarativeMap->setKeepMouseGrab(m_preventStealing); - if (m_flickState == flickActive) { - setFlickState(flickInactive); - emit flickFinished(); - emit panActiveChanged(); - m_map->prefetchData(); - } -} - -QT_END_NAMESPACE |