summaryrefslogtreecommitdiffstats
path: root/src/location/declarativemaps/qquickgeomapgesturearea.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/location/declarativemaps/qquickgeomapgesturearea.cpp')
-rw-r--r--src/location/declarativemaps/qquickgeomapgesturearea.cpp594
1 files changed, 537 insertions, 57 deletions
diff --git a/src/location/declarativemaps/qquickgeomapgesturearea.cpp b/src/location/declarativemaps/qquickgeomapgesturearea.cpp
index a2b02261..e7242fd1 100644
--- a/src/location/declarativemaps/qquickgeomapgesturearea.cpp
+++ b/src/location/declarativemaps/qquickgeomapgesturearea.cpp
@@ -65,7 +65,13 @@
// before we perform a flick.
static const int FlickThreshold = 20;
// Really slow flicks can be annoying.
-const qreal MinimumFlickVelocity = 75.0;
+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 = 5.0; // in degrees
+// Tolerance for starting rotation
+static const qreal MinimumRotationStartingAngle = 15.0; // in degrees
// Returns the new map center after anchoring coordinate to anchorPoint on the screen
// Approach: find the displacement in (wrapped) mercator space, and apply that to the center
@@ -79,6 +85,61 @@ static QGeoCoordinate anchorCoordinateToPoint(QGeoMap &map, const QGeoCoordinate
return map.geoProjection().wrappedMapProjectionToGeo(centerProj + coordProj - anchorProj);
}
+// Keeps it in +- 180
+static qreal touchAngle(const QPointF &p1, const QPointF &p2)
+{
+ qreal angle = QLineF(p1, p2).angle();
+ 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());
+}
+
+static bool movingParallel(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 = touchAngle(p1new, p2new);
+ const qreal oldAngle = touchAngle(p1old, p2old);
+ const qreal angleDiff = angleDelta(newAngle, oldAngle);
+
+ if (qAbs(angleDiff) > MaximumParallelSlidingAngle)
+ return false;
+
+ return true;
+}
+
QT_BEGIN_NAMESPACE
@@ -166,7 +227,8 @@ QT_BEGIN_NAMESPACE
\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.
+ 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.
A MapGestureArea is automatically created with a new Map and available with
the \l{Map::gesture}{gesture} property. This is the only way
@@ -222,6 +284,22 @@ QT_BEGIN_NAMESPACE
*/
/*!
+ \qmlproperty bool QtLocation::MapGestureArea::rotationActive
+
+ This read-only property holds whether the two-finger rotation gesture is active.
+
+ \since Qt Location 5.9
+*/
+
+/*!
+ \qmlproperty bool QtLocation::MapGestureArea::tiltActive
+
+ This read-only property holds whether the two-finger tilt gesture is active.
+
+ \since Qt Location 5.9
+*/
+
+/*!
\qmlproperty real QtLocation::MapGestureArea::maximumZoomLevelChange
This property holds the maximum zoom level change per pinch, essentially
@@ -312,22 +390,93 @@ QT_BEGIN_NAMESPACE
The corresponding handler is \c onFlickFinished.
*/
+/*!
+ \qmlsignal QtLocation::MapGestureArea::rotationStarted(PinchEvent event)
+
+ This signal is emitted when a two-finger rotation gesture is started.
+
+ The corresponding handler is \c onRotationStarted.
+
+ \sa rotationUpdated, rotationFinished
+
+ \since Qt Location 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.
+
+ The corresponding handler is \c onRotationUpdated.
+
+ \sa rotationStarted, rotationFinished
+
+ \since Qt Location 5.9
+*/
+
+/*!
+ \qmlsignal QtLocation::MapGestureArea::rotationFinished(PinchEvent event)
+
+ This signal is emitted at the end of a two-finger rotation gesture.
+
+ The corresponding handler is \c onRotationFinished.
+
+ \sa rotationStarted, rotationUpdated
+
+ \since Qt Location 5.9
+*/
+
+/*!
+ \qmlsignal QtLocation::MapGestureArea::tiltStarted(PinchEvent event)
+
+ This signal is emitted when a two-finger tilt gesture is started.
+
+ The corresponding handler is \c onTiltStarted.
+
+ \sa tiltUpdated, tiltFinished
+
+ \since Qt Location 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.
+
+ The corresponding handler is \c onTiltUpdated.
+
+ \sa tiltStarted, tiltFinished
+
+ \since Qt Location 5.9
+*/
+
+/*!
+ \qmlsignal QtLocation::MapGestureArea::tiltFinished(PinchEvent event)
+
+ This signal is emitted at the end of a two-finger tilt gesture.
+
+ The corresponding handler is \c onTiltFinished.
+
+ \sa tiltStarted, tiltUpdated
+
+ \since Qt Location 5.9
+*/
+
QQuickGeoMapGestureArea::QQuickGeoMapGestureArea(QDeclarativeGeoMap *map)
: QQuickItem(map),
m_map(0),
m_declarativeMap(map),
m_enabled(true),
- m_acceptedGestures(PinchGesture | PanGesture | FlickGesture),
- m_preventStealing(false),
- m_panEnabled(true)
-{
- m_flick.m_enabled = true,
- m_flick.m_maxVelocity = QML_MAP_FLICK_DEFAULTMAXVELOCITY;
- m_flick.m_deceleration = QML_MAP_FLICK_DEFAULTDECELERATION;
- m_flick.m_animation = 0;
+ m_acceptedGestures(PinchGesture | PanGesture | FlickGesture | RotationGesture | TiltGesture),
+ m_preventStealing(false)
+{
m_touchPointState = touchPoints0;
m_pinchState = pinchInactive;
m_flickState = flickInactive;
+ m_rotationState = rotationInactive;
+ m_tiltState = tiltInactive;
}
/*!
@@ -393,6 +542,8 @@ QQuickGeoMapGestureArea::~QQuickGeoMapGestureArea()
\li MapGestureArea.PinchGesture - Support the map pinch gesture (value: 0x0001).
\li MapGestureArea.PanGesture - Support the map pan gesture (value: 0x0002).
\li MapGestureArea.FlickGesture - Support the map flick gesture (value: 0x0004).
+ \li MapGestureArea.RotationGesture - Support the map rotation gesture (value: 0x0008).
+ \li MapGestureArea.TiltGesture - Support the map tilt gesture (value: 0x0010).
\endlist
*/
@@ -408,9 +559,13 @@ void QQuickGeoMapGestureArea::setAcceptedGestures(AcceptedGestures acceptedGestu
return;
m_acceptedGestures = acceptedGestures;
- setPanEnabled(acceptedGestures & PanGesture);
- setFlickEnabled(acceptedGestures & FlickGesture);
- setPinchEnabled(acceptedGestures & PinchGesture);
+ if (enabled()) {
+ setPanEnabled(acceptedGestures & PanGesture);
+ setFlickEnabled(acceptedGestures & FlickGesture);
+ setPinchEnabled(acceptedGestures & PinchGesture);
+ setRotationEnabled(acceptedGestures & RotationGesture);
+ setTiltEnabled(acceptedGestures & TiltGesture);
+ }
emit acceptedGesturesChanged();
}
@@ -426,6 +581,22 @@ bool QQuickGeoMapGestureArea::isPinchActive() const
/*!
\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;
@@ -452,22 +623,25 @@ void QQuickGeoMapGestureArea::setEnabled(bool 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);
}
emit enabledChanged();
}
-
/*!
\internal
*/
bool QQuickGeoMapGestureArea::pinchEnabled() const
{
- return m_pinch.m_enabled;
+ return m_pinch.m_pinchEnabled;
}
/*!
@@ -475,7 +649,39 @@ bool QQuickGeoMapGestureArea::pinchEnabled() const
*/
void QQuickGeoMapGestureArea::setPinchEnabled(bool enabled)
{
- m_pinch.m_enabled = 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;
}
/*!
@@ -483,7 +689,7 @@ void QQuickGeoMapGestureArea::setPinchEnabled(bool enabled)
*/
bool QQuickGeoMapGestureArea::panEnabled() const
{
- return m_panEnabled;
+ return m_flick.m_panEnabled;
}
/*!
@@ -491,9 +697,9 @@ bool QQuickGeoMapGestureArea::panEnabled() const
*/
void QQuickGeoMapGestureArea::setPanEnabled(bool enabled)
{
- if (enabled == m_flick.m_enabled)
+ if (enabled == m_flick.m_panEnabled)
return;
- m_panEnabled = enabled;
+ m_flick.m_panEnabled = enabled;
// unlike the pinch, the pan existing functionality is to stop immediately
if (!enabled)
@@ -505,7 +711,7 @@ void QQuickGeoMapGestureArea::setPanEnabled(bool enabled)
*/
bool QQuickGeoMapGestureArea::flickEnabled() const
{
- return m_flick.m_enabled;
+ return m_flick.m_flickEnabled;
}
/*!
@@ -513,9 +719,9 @@ bool QQuickGeoMapGestureArea::flickEnabled() const
*/
void QQuickGeoMapGestureArea::setFlickEnabled(bool enabled)
{
- if (enabled == m_flick.m_enabled)
+ if (enabled == m_flick.m_flickEnabled)
return;
- m_flick.m_enabled = enabled;
+ m_flick.m_flickEnabled = enabled;
// unlike the pinch, the flick existing functionality is to stop immediately
if (!enabled) {
stopFlick();
@@ -726,8 +932,8 @@ void QQuickGeoMapGestureArea::clearTouchData()
{
m_velocityX = 0;
m_velocityY = 0;
- m_sceneCenter.setX(0);
- m_sceneCenter.setY(0);
+ m_touchPointsCentroid.setX(0);
+ m_touchPointsCentroid.setY(0);
m_touchCenterCoord.setLongitude(0);
m_touchCenterCoord.setLatitude(0);
m_startCoord.setLongitude(0);
@@ -763,7 +969,7 @@ void QQuickGeoMapGestureArea::updateVelocityList(const QPointF &pos)
bool QQuickGeoMapGestureArea::isActive() const
{
- return isPanActive() || isPinchActive();
+ return isPanActive() || isPinchActive() || isRotationActive() || isTiltActive();
}
/*!
@@ -774,7 +980,6 @@ void QQuickGeoMapGestureArea::update()
{
if (!m_map)
return;
-
// First state machine is for the number of touch points
//combine touch with mouse event
@@ -785,16 +990,27 @@ void QQuickGeoMapGestureArea::update()
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_enabled && m_pinch.m_enabled && (m_acceptedGestures & (PinchGesture))))
+ if (isPinchActive() || m_pinch.m_pinchEnabled)
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() || (m_enabled && m_flick.m_enabled && (m_acceptedGestures & (PanGesture | FlickGesture))))
+ // but the isPanActive() below allows pan continue its current gesture if you disable
+ // the whole gesture.
+ if (isPanActive() || m_flick.m_flickEnabled || m_flick.m_panEnabled)
panStateMachine();
+
+ // Parallel state machine for rotation.
+ // Rotation goes last because when panning and rotating, first the new center has to be set,
+ // then the rotation has to be applied
+ if (isRotationActive() || m_pinch.m_rotationEnabled)
+ rotationStateMachine();
}
/*!
@@ -819,7 +1035,7 @@ void QQuickGeoMapGestureArea::touchPointStateMachine()
if (m_allPoints.count() == 0) {
m_touchPointState = touchPoints0;
} else if (m_allPoints.count() == 2) {
- m_touchCenterCoord = m_map->geoProjection().itemPositionToCoordinate(QDoubleVector2D(m_sceneCenter), false);
+ m_touchCenterCoord = m_map->geoProjection().itemPositionToCoordinate(QDoubleVector2D(m_touchPointsCentroid), false);
startTwoTouchPoints();
m_touchPointState = touchPoints2;
}
@@ -828,7 +1044,7 @@ void QQuickGeoMapGestureArea::touchPointStateMachine()
if (m_allPoints.count() == 0) {
m_touchPointState = touchPoints0;
} else if (m_allPoints.count() == 1) {
- m_touchCenterCoord = m_map->geoProjection().itemPositionToCoordinate(QDoubleVector2D(m_sceneCenter), false);
+ m_touchCenterCoord = m_map->geoProjection().itemPositionToCoordinate(QDoubleVector2D(m_touchPointsCentroid), false);
startOneTouchPoint();
m_touchPointState = touchPoints1;
}
@@ -869,11 +1085,10 @@ void QQuickGeoMapGestureArea::startOneTouchPoint()
*/
void QQuickGeoMapGestureArea::updateOneTouchPoint()
{
- m_sceneCenter = mapFromScene(m_allPoints.at(0).scenePos());
- updateVelocityList(m_sceneCenter);
+ m_touchPointsCentroid = mapFromScene(m_allPoints.at(0).scenePos());
+ updateVelocityList(m_touchPointsCentroid);
}
-
/*!
\internal
*/
@@ -889,6 +1104,7 @@ void QQuickGeoMapGestureArea::startTwoTouchPoints()
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
}
/*!
@@ -901,12 +1117,280 @@ void QQuickGeoMapGestureArea::updateTwoTouchPoints()
qreal dx = p1.x() - p2.x();
qreal dy = p1.y() - p2.y();
m_distanceBetweenTouchPoints = sqrt(dx * dx + dy * dy);
- m_sceneCenter = (p1 + p2) / 2;
- updateVelocityList(m_sceneCenter);
+ m_touchPointsCentroid = (p1 + p2) / 2;
+ updateVelocityList(m_touchPointsCentroid);
- m_twoTouchAngle = QLineF(p1, p2).angle();
- if (m_twoTouchAngle > 180)
- m_twoTouchAngle -= 360;
+ 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() && !isPanActive() && !isPinchActive() && canStartTilt()) {
+ m_declarativeMap->setKeepMouseGrab(true);
+ m_declarativeMap->setKeepTouchGrab(true);
+ startTilt();
+ m_tiltState = tiltActive;
+ } else {
+ m_tiltState = tiltInactiveTwoPoints;
+ }
+ }
+ break;
+ case tiltInactiveTwoPoints:
+ if (m_allPoints.count() <= 1) {
+ m_tiltState = tiltInactive;
+ } else {
+ if (!isRotationActive() && !isPanActive() && !isPinchActive() && canStartTilt()) {
+ m_declarativeMap->setKeepMouseGrab(true);
+ m_declarativeMap->setKeepTouchGrab(true);
+ startTilt();
+ m_tiltState = tiltActive;
+ }
+ }
+ break;
+ case tiltActive:
+ if (m_allPoints.count() <= 1) {
+ m_tiltState = tiltInactive;
+ m_declarativeMap->setKeepMouseGrab(m_preventStealing);
+ 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;
+ }
+}
+
+/*!
+ \internal
+*/
+bool QQuickGeoMapGestureArea::canStartTilt()
+{
+ if (m_allPoints.count() >= 2) {
+ QPointF p1 = mapFromScene(m_allPoints.at(0).scenePos());
+ QPointF p2 = mapFromScene(m_allPoints.at(1).scenePos());
+ if (qAbs(m_twoTouchAngle) < MaximumParallelPosition
+ && movingParallel(m_sceneStartPoint1, p1, m_sceneStartPoint2, p2)) {
+ 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 m_pinch.m_event.accepted();
+ }
+ }
+ return false;
+}
+
+/*!
+ \internal
+*/
+void QQuickGeoMapGestureArea::startTilt()
+{
+ 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).scenePos());
+ m_pinch.m_lastPoint2 = mapFromScene(m_allPoints.at(1).scenePos());
+ 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->setKeepMouseGrab(true);
+ m_declarativeMap->setKeepTouchGrab(true);
+ startRotation();
+ m_rotationState = rotationActive;
+ } else {
+ m_rotationState = rotationInactiveTwoPoints;
+ }
+ }
+ break;
+ case rotationInactiveTwoPoints:
+ if (m_allPoints.count() <= 1) {
+ m_rotationState = rotationInactive;
+ } else {
+ if (!isTiltActive() && canStartRotation()) {
+ m_declarativeMap->setKeepMouseGrab(true);
+ m_declarativeMap->setKeepTouchGrab(true);
+ startRotation();
+ m_rotationState = rotationActive;
+ }
+ }
+ break;
+ case rotationActive:
+ if (m_allPoints.count() <= 1) {
+ m_rotationState = rotationInactive;
+ m_declarativeMap->setKeepMouseGrab(m_preventStealing);
+ 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).scenePos());
+ QPointF p2 = mapFromScene(m_allPoints.at(1).scenePos());
+ 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_twoTouchAngleStart;
+ 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).scenePos());
+ m_pinch.m_lastPoint2 = mapFromScene(m_allPoints.at(1).scenePos());
+ 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);
}
/*!
@@ -919,7 +1403,7 @@ void QQuickGeoMapGestureArea::pinchStateMachine()
switch (m_pinchState) {
case pinchInactive:
if (m_allPoints.count() >= 2) {
- if (canStartPinch()) {
+ if (!isTiltActive() && canStartPinch()) {
m_declarativeMap->setKeepMouseGrab(true);
m_declarativeMap->setKeepTouchGrab(true);
startPinch();
@@ -933,7 +1417,7 @@ void QQuickGeoMapGestureArea::pinchStateMachine()
if (m_allPoints.count() <= 1) {
m_pinchState = pinchInactive;
} else {
- if (canStartPinch()) {
+ if (!isTiltActive() && canStartPinch()) {
m_declarativeMap->setKeepMouseGrab(true);
m_declarativeMap->setKeepTouchGrab(true);
startPinch();
@@ -942,7 +1426,7 @@ void QQuickGeoMapGestureArea::pinchStateMachine()
}
break;
case pinchActive:
- if (m_allPoints.count() <= 1) {
+ if (m_allPoints.count() <= 1) { // Once started, pinch goes off only when finger(s) are release
m_pinchState = pinchInactive;
m_declarativeMap->setKeepMouseGrab(m_preventStealing);
m_declarativeMap->setKeepTouchGrab(m_preventStealing);
@@ -973,16 +1457,12 @@ void QQuickGeoMapGestureArea::pinchStateMachine()
*/
bool QQuickGeoMapGestureArea::canStartPinch()
{
- const int startDragDistance = qApp->styleHints()->startDragDistance();
-
if (m_allPoints.count() >= 2) {
QPointF p1 = mapFromScene(m_allPoints.at(0).scenePos());
QPointF p2 = mapFromScene(m_allPoints.at(1).scenePos());
- if (qAbs(p1.x()-m_sceneStartPoint1.x()) > startDragDistance
- || qAbs(p1.y()-m_sceneStartPoint1.y()) > startDragDistance
- || qAbs(p2.x()-m_sceneStartPoint2.x()) > startDragDistance
- || qAbs(p2.y()-m_sceneStartPoint2.y()) > startDragDistance) {
- m_pinch.m_event.setCenter(mapFromScene(m_sceneCenter));
+ if (pointDragged(m_sceneStartPoint1, p1)
+ || pointDragged(m_sceneStartPoint2, p2)) {
+ 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);
@@ -1027,7 +1507,7 @@ void QQuickGeoMapGestureArea::updatePinch()
m_pinch.m_zoom.m_start;
}
- m_pinch.m_event.setCenter(mapFromScene(m_sceneCenter));
+ m_pinch.m_event.setCenter(mapFromScene(m_touchPointsCentroid));
m_pinch.m_event.setAngle(m_twoTouchAngle);
m_pinch.m_lastPoint1 = mapFromScene(m_allPoints.at(0).scenePos());
@@ -1077,9 +1557,9 @@ void QQuickGeoMapGestureArea::panStateMachine()
// Transitions
switch (m_flickState) {
case flickInactive:
- if (canStartPan()) {
+ if (!isTiltActive() && canStartPan()) {
// Update startCoord_ to ensure smooth start for panning when going over startDragDistance
- QGeoCoordinate newStartCoord = m_map->geoProjection().itemPositionToCoordinate(QDoubleVector2D(m_sceneCenter), false);
+ QGeoCoordinate newStartCoord = m_map->geoProjection().itemPositionToCoordinate(QDoubleVector2D(m_touchPointsCentroid), false);
m_startCoord.setLongitude(newStartCoord.longitude());
m_startCoord.setLatitude(newStartCoord.latitude());
m_declarativeMap->setKeepMouseGrab(true);
@@ -1092,7 +1572,7 @@ void QQuickGeoMapGestureArea::panStateMachine()
{
m_flickState = flickInactive;
// mark as inactive for use by camera
- if (m_pinchState == pinchInactive) {
+ if (m_pinchState == pinchInactive && m_rotationState == rotationInactive && m_tiltState == tiltInactive) {
m_declarativeMap->setKeepMouseGrab(m_preventStealing);
m_map->prefetchData();
}
@@ -1154,7 +1634,7 @@ bool QQuickGeoMapGestureArea::canStartPan()
*/
void QQuickGeoMapGestureArea::updatePan()
{
- QGeoCoordinate animationStartCoordinate = anchorCoordinateToPoint(*m_map, m_startCoord, m_sceneCenter);
+ QGeoCoordinate animationStartCoordinate = anchorCoordinateToPoint(*m_map, m_startCoord, m_touchPointsCentroid);
m_declarativeMap->setCenter(animationStartCoordinate);
}
@@ -1176,7 +1656,7 @@ bool QQuickGeoMapGestureArea::tryStartFlick()
int flickTimeX = 0;
int flickPixelsX = 0;
int flickPixelsY = 0;
- if (qAbs(velocityY) > MinimumFlickVelocity && qAbs(m_sceneCenter.y() - m_sceneStartPoint1.y()) > FlickThreshold) {
+ if (qAbs(velocityY) > MinimumFlickVelocity && qAbs(m_touchPointsCentroid.y() - m_sceneStartPoint1.y()) > FlickThreshold) {
// calculate Y flick animation values
qreal acceleration = m_flick.m_deceleration;
if ((velocityY > 0.0f) == (m_flick.m_deceleration > 0.0f))
@@ -1184,7 +1664,7 @@ bool QQuickGeoMapGestureArea::tryStartFlick()
flickTimeY = static_cast<int>(-1000 * velocityY / acceleration);
flickPixelsY = (flickTimeY * velocityY) / (1000.0 * 2);
}
- if (qAbs(velocityX) > MinimumFlickVelocity && qAbs(m_sceneCenter.x() - m_sceneStartPoint1.x()) > FlickThreshold) {
+ if (qAbs(velocityX) > MinimumFlickVelocity && qAbs(m_touchPointsCentroid.x() - m_sceneStartPoint1.x()) > FlickThreshold) {
// calculate X flick animation values
qreal acceleration = m_flick.m_deceleration;
if ((velocityX > 0.0f) == (m_flick.m_deceleration > 0.0f))