From 4b81f1a34c6936b7b5a752ed4555f65847b13337 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Wed, 28 Feb 2018 14:31:53 +0100 Subject: Remove all C++ event handling, prep for pointer handlers The QML Map type is now non-interactive. The plan is to replace QQuickGeoMapGestureArea with a set of pointer handlers on top of Map, probably in a new QML type called MapView. [ChangeLog][Map] Map is now non-interactive, concerned only with rendering. Pick-to: 6.5 Task-number: QTBUG-68108 Change-Id: Ie64ffd13abc2b3e00d8e90043f3bb672299f4f15 Reviewed-by: Volker Hilsheimer --- src/location/CMakeLists.txt | 1 - src/location/quickmapitems/qdeclarativegeomap.cpp | 231 +-- src/location/quickmapitems/qdeclarativegeomap_p.h | 20 +- .../quickmapitems/qdeclarativegeomapitembase.cpp | 19 - .../quickmapitems/qdeclarativegeomapitembase_p.h | 1 - .../quickmapitems/qdeclarativegeomapquickitem.cpp | 35 +- .../quickmapitems/qdeclarativegeomapquickitem_p.h | 2 - .../quickmapitems/qquickgeomapgesturearea.cpp | 1876 -------------------- .../quickmapitems/qquickgeomapgesturearea_p.h | 394 ---- 9 files changed, 16 insertions(+), 2563 deletions(-) delete mode 100644 src/location/quickmapitems/qquickgeomapgesturearea.cpp delete mode 100644 src/location/quickmapitems/qquickgeomapgesturearea_p.h diff --git a/src/location/CMakeLists.txt b/src/location/CMakeLists.txt index ee5ba7e9..e4ab95a7 100644 --- a/src/location/CMakeLists.txt +++ b/src/location/CMakeLists.txt @@ -144,7 +144,6 @@ qt_internal_add_qml_module(Location quickmapitems/qdeclarativecirclemapitem.cpp quickmapitems/qdeclarativecirclemapitem_p.h quickmapitems/qdeclarativecirclemapitem_p_p.h quickmapitems/qdeclarativeroutemapitem.cpp quickmapitems/qdeclarativeroutemapitem_p.h - quickmapitems/qquickgeomapgesturearea_p.h quickmapitems/qquickgeomapgesturearea.cpp quickmapitems/qdeclarativegeomapcopyrightsnotice_p.h quickmapitems/qdeclarativegeomapcopyrightsnotice.cpp quickmapitems/qdeclarativegeomapitemtransitionmanager_p.h diff --git a/src/location/quickmapitems/qdeclarativegeomap.cpp b/src/location/quickmapitems/qdeclarativegeomap.cpp index 97d3261c..daeafdc3 100644 --- a/src/location/quickmapitems/qdeclarativegeomap.cpp +++ b/src/location/quickmapitems/qdeclarativegeomap.cpp @@ -125,14 +125,6 @@ static qreal sanitizeBearing(qreal bearing) does not involve any special interaction with Map itself -- changing these properties in a map object will automatically update the display. - \section2 Interaction - - The Map type includes support for pinch and flick gestures to control - zooming and panning. These are enabled by default, and available at any - time by using the \l gesture object. The actual GestureArea is constructed - specially at startup and cannot be replaced or destroyed. Its properties - can be altered, however, to control its behavior. - \section2 Performance Maps are rendered using OpenGL (ES) and the Qt Scene Graph stack, and as @@ -177,13 +169,7 @@ static qreal sanitizeBearing(qreal bearing) QDeclarativeGeoMap::QDeclarativeGeoMap(QQuickItem *parent) : QQuickItem(parent) { - m_gestureArea = new QQuickGeoMapGestureArea(this); - - setAcceptHoverEvents(false); - setAcceptTouchEvents(true); - setAcceptedMouseButtons(Qt::LeftButton); setFlags(QQuickItem::ItemHasContents | QQuickItem::ItemClipsChildrenToShape); - setFiltersChildMouseEvents(true); // needed for childMouseEventFilter to work. m_activeMapType = QGeoMapType(QGeoMapType::NoMap, tr("No Map"), @@ -390,19 +376,6 @@ void QDeclarativeGeoMap::componentComplete() QQuickItem::componentComplete(); } -/*! - \qmlproperty MapGestureArea QtLocation::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. -*/ - -QQuickGeoMapGestureArea *QDeclarativeGeoMap::gesture() -{ - return m_gestureArea; -} - /*! \internal @@ -492,32 +465,21 @@ void QDeclarativeGeoMap::onCameraCapabilitiesChanged(const QGeoCameraCapabilitie //strict zoom level limit before initialization nothing is done here. //minimum zoom level might be changed to limit gray bundaries //This code assumes that plugins' maximum zoom level will never exceed 30.0 - if (m_cameraCapabilities.maximumZoomLevelAt256() < m_gestureArea->maximumZoomLevel()) { + if (!qIsFinite(m_userMaximumZoomLevel)) { + // If the user didn't set anything setMaximumZoomLevel(m_cameraCapabilities.maximumZoomLevelAt256(), false); - } else if (m_cameraCapabilities.maximumZoomLevelAt256() > m_gestureArea->maximumZoomLevel()) { - if (!qIsFinite(m_userMaximumZoomLevel)) { - // If the user didn't set anything - setMaximumZoomLevel(m_cameraCapabilities.maximumZoomLevelAt256(), false); - } else { // Try to set what the user requested - // Else if the user set something larger, but that got clamped by the previous camera caps - setMaximumZoomLevel(qMin(m_cameraCapabilities.maximumZoomLevelAt256(), - m_userMaximumZoomLevel), false); - } + } else { // Try to set what the user requested + // Else if the user set something larger, but that got clamped by the previous camera caps + setMaximumZoomLevel(qMin(m_cameraCapabilities.maximumZoomLevelAt256(), + m_userMaximumZoomLevel), false); } - if (m_cameraCapabilities.minimumZoomLevelAt256() > m_gestureArea->minimumZoomLevel()) { + if (!qIsFinite(m_userMinimumZoomLevel)) { + // If the user didn't set anything, trying to set the new caps. setMinimumZoomLevel(m_cameraCapabilities.minimumZoomLevelAt256(), false); - } else if (m_cameraCapabilities.minimumZoomLevelAt256() < m_gestureArea->minimumZoomLevel()) { - if (!qIsFinite(m_userMinimumZoomLevel)) { - // If the user didn't set anything, trying to set the new caps. - setMinimumZoomLevel(m_cameraCapabilities.minimumZoomLevelAt256(), false); - } else { // Try to set what the user requested - // Else if the user set a minimum, m_gestureArea->minimumZoomLevel() might be larger - // because of different reasons. Resetting it, as if it ends to be the same, - // no signal will be emitted. - setMinimumZoomLevel(qMax(m_cameraCapabilities.minimumZoomLevelAt256(), - m_userMinimumZoomLevel), false); - } + } else { // Try to set what the user requested + setMinimumZoomLevel(qMax(m_cameraCapabilities.minimumZoomLevelAt256(), + m_userMinimumZoomLevel), false); } // Tilt @@ -585,8 +547,6 @@ void QDeclarativeGeoMap::mappingManagerInitialized() m_copyrights->setCopyrightsVisible(m_copyrightsVisible); m_copyrights->setMapSource(this); - m_gestureArea->setMap(m_map); - m_supportedMapTypes = m_mappingManager->supportedMapTypes(); if (m_activeMapType != QGeoMapType() && m_plugin->name().toLatin1() == m_activeMapType.pluginName()) { @@ -695,9 +655,8 @@ void QDeclarativeGeoMap::setMinimumZoomLevel(qreal minimumZoomLevel, bool userSe minimumZoomLevel = qMax(minimumZoomLevel, m_map->minimumZoom()); // minimumZoomLevel is, at this point, the implicit minimum zoom level - m_gestureArea->setMinimumZoomLevel(minimumZoomLevel); - if (zoomLevel() < minimumZoomLevel && (m_gestureArea->enabled() || !m_cameraCapabilities.overzoomEnabled())) + if (zoomLevel() < minimumZoomLevel) setZoomLevel(minimumZoomLevel); if (qIsNaN(m_userMinimumZoomLevel) && oldMinimumZoomLevel != minimumZoomLevel) @@ -743,9 +702,7 @@ void QDeclarativeGeoMap::setMaximumZoomLevel(qreal maximumZoomLevel, bool userSe maximumZoomLevel = qBound(minimumZoomLevel(), maximumZoomLevel, qreal(m_cameraCapabilities.maximumZoomLevelAt256())); - m_gestureArea->setMaximumZoomLevel(maximumZoomLevel); - - if (zoomLevel() > maximumZoomLevel && (m_gestureArea->enabled() || !m_cameraCapabilities.overzoomEnabled())) + if (zoomLevel() > maximumZoomLevel) setZoomLevel(maximumZoomLevel); if (oldMaximumZoomLevel != maximumZoomLevel) @@ -1659,11 +1616,6 @@ void QDeclarativeGeoMap::itemChange(ItemChange change, const ItemChangeData &val QQuickItem::itemChange(change, value); } -bool QDeclarativeGeoMap::isInteractive() const -{ - return (m_gestureArea->enabled() && m_gestureArea->acceptedGestures()) || m_gestureArea->isActive(); -} - void QDeclarativeGeoMap::attachCopyrightNotice(bool initialVisibility) { if (initialVisibility) { @@ -2042,7 +1994,6 @@ QGeoMapType QDeclarativeGeoMap::activeMapType() const */ void QDeclarativeGeoMap::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) { - m_gestureArea->setSize(newGeometry.size()); QQuickItem::geometryChange(newGeometry, oldGeometry); if (!m_map || newGeometry.size().isEmpty()) @@ -2246,160 +2197,4 @@ void QDeclarativeGeoMap::fitViewportToMapItemsRefine(const QListsource() == Qt::MouseEventNotSynthesized) - m_gestureArea->handleMousePressEvent(event); - else - QQuickItem::mousePressEvent(event); -} - -/*! - \internal -*/ -void QDeclarativeGeoMap::mouseMoveEvent(QMouseEvent *event) -{ - if (isInteractive() && event->source() == Qt::MouseEventNotSynthesized) - m_gestureArea->handleMouseMoveEvent(event); - else - QQuickItem::mouseMoveEvent(event); -} - -/*! - \internal -*/ -void QDeclarativeGeoMap::mouseReleaseEvent(QMouseEvent *event) -{ - if (isInteractive() && event->source() == Qt::MouseEventNotSynthesized) - m_gestureArea->handleMouseReleaseEvent(event); - else - QQuickItem::mouseReleaseEvent(event); -} - -void QDeclarativeGeoMap::touchUngrabEvent() -{ - if (isInteractive()) - m_gestureArea->handleTouchUngrabEvent(); - else - QQuickItem::touchUngrabEvent(); -} - -/*! - \internal -*/ -void QDeclarativeGeoMap::touchEvent(QTouchEvent *event) -{ - if (isInteractive()) { - m_gestureArea->handleTouchEvent(event); - } else { - //ignore event so sythesized event is generated; - QQuickItem::touchEvent(event); - } -} - -#if QT_CONFIG(wheelevent) -/*! - \internal -*/ -void QDeclarativeGeoMap::wheelEvent(QWheelEvent *event) -{ - if (isInteractive()) - m_gestureArea->handleWheelEvent(event); - else - QQuickItem::wheelEvent(event); - -} -#endif - -/*! - \internal -*/ -bool QDeclarativeGeoMap::childMouseEventFilter(QQuickItem *item, QEvent *event) -{ - Q_UNUSED(item); - if (!isVisible() || !isEnabled() || !isInteractive()) - return QQuickItem::childMouseEventFilter(item, event); - - switch (event->type()) { - case QEvent::TouchBegin: - case QEvent::TouchUpdate: - case QEvent::TouchEnd: - case QEvent::TouchCancel: - return sendTouchEvent(static_cast(event)); - case QEvent::MouseButtonPress: - case QEvent::MouseMove: - case QEvent::MouseButtonRelease: - { - auto mEvent = static_cast(event); - if (mEvent->source() == Qt::MouseEventNotSynthesized){ - return sendTouchEvent(mEvent); - } - } - break; - case QEvent::UngrabMouse: - Q_ASSERT(event->isSinglePointEvent()); - return sendTouchEvent(static_cast(event)); - default: - break; - } - return QQuickItem::childMouseEventFilter(item, event); -} - -bool QDeclarativeGeoMap::sendMouseEvent(QMouseEvent *event) -{ - bool stealEvent = m_gestureArea->isActive(); - - if ((stealEvent || contains(mapFromScene(event->scenePosition())))) { - switch (event->type()) { - case QEvent::MouseMove: - m_gestureArea->handleMouseMoveEvent(event); - break; - case QEvent::MouseButtonPress: - m_gestureArea->handleMousePressEvent(event); - break; - case QEvent::MouseButtonRelease: - m_gestureArea->handleMouseReleaseEvent(event); - break; - default: - break; - } - - stealEvent = m_gestureArea->isActive(); - - if (stealEvent) { - //do not deliver - event->setAccepted(true); - return true; - } else { - return false; - } - } - return false; -} - -bool QDeclarativeGeoMap::sendTouchEvent(QPointerEvent *event) -{ - const QTouchEvent::TouchPoint &point = event->points().first(); - - bool stealEvent = m_gestureArea->isActive(); - bool containsPoint = contains(mapFromScene(point.scenePosition())); - - if ((stealEvent || containsPoint)) { - - m_gestureArea->handleTouchEvent(event); - stealEvent = m_gestureArea->isActive(); - - if (stealEvent) { - //event->setAccepted(true); - //return true; - } else { - return false; - } - } - return false; -} - QT_END_NAMESPACE diff --git a/src/location/quickmapitems/qdeclarativegeomap_p.h b/src/location/quickmapitems/qdeclarativegeomap_p.h index 837e976b..cf8f10b8 100644 --- a/src/location/quickmapitems/qdeclarativegeomap_p.h +++ b/src/location/quickmapitems/qdeclarativegeomap_p.h @@ -53,7 +53,6 @@ #include #include -#include #include #include #include @@ -80,7 +79,6 @@ class Q_LOCATION_PRIVATE_EXPORT QDeclarativeGeoMap : public QQuickItem QML_NAMED_ELEMENT(Map) QML_ADDED_IN_VERSION(5, 0) Q_ENUMS(QGeoServiceProvider::Error) - Q_PROPERTY(QQuickGeoMapGestureArea *gesture READ gesture CONSTANT) Q_PROPERTY(QDeclarativeGeoServiceProvider *plugin READ plugin WRITE setPlugin NOTIFY pluginChanged) Q_PROPERTY(qreal minimumZoomLevel READ minimumZoomLevel WRITE setMinimumZoomLevel NOTIFY minimumZoomLevelChanged) Q_PROPERTY(qreal maximumZoomLevel READ maximumZoomLevel WRITE setMaximumZoomLevel NOTIFY maximumZoomLevelChanged) @@ -183,7 +181,7 @@ public: Q_INVOKABLE QGeoCoordinate toCoordinate(const QPointF &position, bool clipToViewPort = true) const; Q_INVOKABLE QPointF fromCoordinate(const QGeoCoordinate &coordinate, bool clipToViewPort = true) const; - QQuickGeoMapGestureArea *gesture(); +// QQuickGeoMapGestureArea *gesture(); Q_INVOKABLE void fitViewportToMapItems(const QVariantList &items = {}); Q_INVOKABLE void fitViewportToVisibleMapItems(); @@ -227,19 +225,6 @@ Q_SIGNALS: Q_REVISION(14) void visibleRegionChanged(); protected: - void mousePressEvent(QMouseEvent *event) override ; - void mouseMoveEvent(QMouseEvent *event) override ; - void mouseReleaseEvent(QMouseEvent *event) override ; - void touchUngrabEvent() override; - void touchEvent(QTouchEvent *event) override ; -#if QT_CONFIG(wheelevent) - void wheelEvent(QWheelEvent *event) override ; -#endif - - bool childMouseEventFilter(QQuickItem *item, QEvent *event) override; - bool sendMouseEvent(QMouseEvent *event); - bool sendTouchEvent(QPointerEvent *event); - void componentComplete() override; QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *) override; void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override; @@ -272,7 +257,6 @@ private: void setupMapView(QDeclarativeGeoMapItemView *view); void populateMap(); void fitViewportToMapItemsRefine(const QList > &mapItems, bool refine, bool onlyVisible); - bool isInteractive() const; void attachCopyrightNotice(bool initialVisibility); void detachCopyrightNotice(bool currentVisibility); QMargins mapMargins() const; @@ -284,7 +268,6 @@ private: QGeoMapType m_activeMapType; QList m_supportedMapTypes; QList m_mapViews; - QQuickGeoMapGestureArea *m_gestureArea = nullptr; QPointer m_map; QPointer m_copyrights; QList > m_mapItems; @@ -322,7 +305,6 @@ private: friend class QDeclarativeGeoMapItem; friend class QDeclarativeGeoMapItemView; - friend class QQuickGeoMapGestureArea; friend class QDeclarativeGeoMapCopyrightNotice; Q_DISABLE_COPY(QDeclarativeGeoMap) }; diff --git a/src/location/quickmapitems/qdeclarativegeomapitembase.cpp b/src/location/quickmapitems/qdeclarativegeomapitembase.cpp index 1d75cec4..2aabeff3 100644 --- a/src/location/quickmapitems/qdeclarativegeomapitembase.cpp +++ b/src/location/quickmapitems/qdeclarativegeomapitembase.cpp @@ -54,7 +54,6 @@ QT_BEGIN_NAMESPACE QDeclarativeGeoMapItemBase::QDeclarativeGeoMapItemBase(QQuickItem *parent) : QQuickItem(parent) { - setFiltersChildMouseEvents(true); connect(this, &QDeclarativeGeoMapItemBase::childrenChanged, this, &QDeclarativeGeoMapItemBase::afterChildrenChanged); // Changing opacity on a mapItemGroup should affect also the opacity on the children. @@ -236,24 +235,6 @@ float QDeclarativeGeoMapItemBase::zoomLevelOpacity() const return 0.0; } -bool QDeclarativeGeoMapItemBase::childMouseEventFilter(QQuickItem *item, QEvent *event) -{ - Q_UNUSED(item); - if (event->type() == QEvent::MouseButtonPress && !contains(static_cast(event)->pos())) { - // In case of items that are not rectangles, this filter is used to test if the event has landed - // inside the actual item shape. - // If so, the method returns true, meaning that it prevents the event delivery to child "*item" (for example, - // a mouse area that is on top of this map item). - // However, this method sets "accepted" to false, so that the event can still be passed further up, - // specifically to the parent Map, that is a sort of flickable. - // Otherwise, if the event is not contained within the map item, the method returns false, meaning the event - // is delivered to the child *item (like the mouse area associated). - event->setAccepted(false); - return true; - } - return false; -} - /*! \internal */ diff --git a/src/location/quickmapitems/qdeclarativegeomapitembase_p.h b/src/location/quickmapitems/qdeclarativegeomapitembase_p.h index be61e0c5..f115601a 100644 --- a/src/location/quickmapitems/qdeclarativegeomapitembase_p.h +++ b/src/location/quickmapitems/qdeclarativegeomapitembase_p.h @@ -143,7 +143,6 @@ protected Q_SLOTS: protected: float zoomLevelOpacity() const; - bool childMouseEventFilter(QQuickItem *item, QEvent *event) override; bool isPolishScheduled() const; virtual void setMaterialDirty(); diff --git a/src/location/quickmapitems/qdeclarativegeomapquickitem.cpp b/src/location/quickmapitems/qdeclarativegeomapquickitem.cpp index 59c770d5..60860066 100644 --- a/src/location/quickmapitems/qdeclarativegeomapquickitem.cpp +++ b/src/location/quickmapitems/qdeclarativegeomapquickitem.cpp @@ -153,7 +153,6 @@ QDeclarativeGeoMapQuickItem::QDeclarativeGeoMapQuickItem(QQuickItem *parent) opacityContainer_ = new QQuickItem(this); opacityContainer_->setParentItem(this); opacityContainer_->setFlag(ItemHasContents, true); - setFiltersChildMouseEvents(true); } QDeclarativeGeoMapQuickItem::~QDeclarativeGeoMapQuickItem() {} @@ -196,20 +195,6 @@ void QDeclarativeGeoMapQuickItem::setMap(QDeclarativeGeoMap *quickMap, QGeoMap * polishAndUpdate(); } } -// See QQuickMultiPointTouchArea::childMouseEventFilter for reference -bool QDeclarativeGeoMapQuickItem::childMouseEventFilter(QQuickItem *receiver, QEvent *event) -{ - if (isEnabled() && isVisible()) { - switch (event->type()) { - case QEvent::MouseButtonPress: - case QEvent::TouchBegin: - dragStartCoordinate_ = coordinate_; - default: - break; - } - } - return QQuickItem::childMouseEventFilter(receiver, event); -} /*! \internal @@ -222,24 +207,8 @@ void QDeclarativeGeoMapQuickItem::geometryChange(const QRectF &newGeometry, cons return; } - QGeoCoordinate newCoordinate; - // with zoomLevel set the anchorPoint has to be factored into the transformation to properly transform around it. - if (zoomLevel_ != 0.0 - && map()->geoProjection().projectionType() == QGeoProjection::ProjectionWebMercator) { - const QGeoProjectionWebMercator &p = static_cast(map()->geoProjection()); - - // When dragStartCoordinate_ can't be projected to screen, dragging must be disabled. - if (!p.isProjectable(p.geoToWrappedMapProjection(dragStartCoordinate_))) - return; - - QDoubleVector2D pos = map()->geoProjection().coordinateToItemPosition(dragStartCoordinate_, false); - // oldGeometry.topLeft() is always intended to be (0,0), even when for some reason it's not. - pos.setX(pos.x() + newGeometry.topLeft().x()); - pos.setY(pos.y() + newGeometry.topLeft().y()); - newCoordinate = map()->geoProjection().itemPositionToCoordinate(pos, false); - } else { - newCoordinate = map()->geoProjection().itemPositionToCoordinate(QDoubleVector2D(x(), y()) + QDoubleVector2D(anchorPoint_), false); - } + QGeoCoordinate newCoordinate = map()->geoProjection(). + itemPositionToCoordinate(QDoubleVector2D(x(), y()) + QDoubleVector2D(anchorPoint_), false); if (newCoordinate.isValid()) setCoordinate(newCoordinate); diff --git a/src/location/quickmapitems/qdeclarativegeomapquickitem_p.h b/src/location/quickmapitems/qdeclarativegeomapquickitem_p.h index e0d32d7d..7ed4dd23 100644 --- a/src/location/quickmapitems/qdeclarativegeomapquickitem_p.h +++ b/src/location/quickmapitems/qdeclarativegeomapquickitem_p.h @@ -113,7 +113,6 @@ Q_SIGNALS: protected: void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override; void updatePolish() override; - bool childMouseEventFilter(QQuickItem *item, QEvent *event) override; protected Q_SLOTS: void afterChildrenChanged() override; @@ -121,7 +120,6 @@ protected Q_SLOTS: private: qreal scaleFactor(); - QGeoCoordinate dragStartCoordinate_; QGeoCoordinate coordinate_; QGeoRectangle geoshape_; QPointer sourceItem_; 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 -#include -#include -#include -#if QT_CONFIG(wheelevent) -#include -#endif -#include -#include -#include -#include -#include "qgeomap_p.h" -#include -#include -#include - -#include "math.h" -#include - -#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(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(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(-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(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(-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 diff --git a/src/location/quickmapitems/qquickgeomapgesturearea_p.h b/src/location/quickmapitems/qquickgeomapgesturearea_p.h deleted file mode 100644 index 86bd3537..00000000 --- a/src/location/quickmapitems/qquickgeomapgesturearea_p.h +++ /dev/null @@ -1,394 +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$ -** -****************************************************************************/ - -#ifndef QQUICKGEOMAPGESTUREAREA_P_H -#define QQUICKGEOMAPGESTUREAREA_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include - -#include -#include -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -class QGraphicsSceneMouseEvent; -class QQuickGeoCoordinateAnimation; -class QDeclarativeGeoMap; -class QTouchEvent; -class QWheelEvent; -class QGeoMap; - -class Q_LOCATION_PRIVATE_EXPORT QGeoMapPinchEvent : public QObject -{ - Q_OBJECT - QML_NAMED_ELEMENT(MapPinchEvent) - QML_UNCREATABLE("(Map)PinchEvent is not intended instantiable by developer.") - QML_ADDED_IN_VERSION(5, 0) - - 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: - QGeoMapPinchEvent(const QPointF ¢er, qreal angle, - const QPointF &point1, const QPointF &point2, - int pointCount = 0, bool accepted = true) - : QObject(), m_center(center), m_angle(angle), - m_point1(point1), m_point2(point2), - m_pointCount(pointCount), m_accepted(accepted) - {} - QGeoMapPinchEvent() = default; - - QPointF center() const { return m_center; } - void setCenter(const QPointF ¢er) { m_center = center; } - qreal angle() const { return m_angle; } - void setAngle(qreal angle) { m_angle = angle; } - QPointF point1() const { return m_point1; } - void setPoint1(const QPointF &p) { m_point1 = p; } - QPointF point2() const { return m_point2; } - void setPoint2(const QPointF &p) { m_point2 = p; } - int pointCount() const { return m_pointCount; } - void setPointCount(int count) { m_pointCount = count; } - bool accepted() const { return m_accepted; } - void setAccepted(bool a) { m_accepted = a; } - -private: - QPointF m_center; - qreal m_angle = 0.0; - QPointF m_point1; - QPointF m_point2; - int m_pointCount = 0; - bool m_accepted = true; -}; - -class Q_LOCATION_PRIVATE_EXPORT QQuickGeoMapGestureArea: public QQuickItem -{ - Q_OBJECT - QML_NAMED_ELEMENT(MapGestureArea) - QML_UNCREATABLE("(Map)GestureArea is not intended instantiable by developer.") - QML_ADDED_IN_VERSION(5, 0) - Q_ENUMS(GeoMapGesture) - Q_FLAGS(AcceptedGestures) - - Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) - Q_PROPERTY(bool pinchActive READ isPinchActive NOTIFY pinchActiveChanged) - Q_PROPERTY(bool panActive READ isPanActive NOTIFY panActiveChanged) - Q_PROPERTY(bool rotationActive READ isRotationActive NOTIFY rotationActiveChanged) - Q_PROPERTY(bool tiltActive READ isTiltActive NOTIFY tiltActiveChanged) - Q_PROPERTY(AcceptedGestures acceptedGestures READ acceptedGestures WRITE setAcceptedGestures NOTIFY acceptedGesturesChanged) - Q_PROPERTY(qreal maximumZoomLevelChange READ maximumZoomLevelChange WRITE setMaximumZoomLevelChange NOTIFY maximumZoomLevelChangeChanged) - Q_PROPERTY(qreal flickDeceleration READ flickDeceleration WRITE setFlickDeceleration NOTIFY flickDecelerationChanged) - Q_PROPERTY(bool preventStealing READ preventStealing WRITE setPreventStealing NOTIFY preventStealingChanged REVISION(5, 1)) - -public: - QQuickGeoMapGestureArea(QDeclarativeGeoMap *map); - ~QQuickGeoMapGestureArea(); - - enum GeoMapGesture { - NoGesture = 0x0000, - PinchGesture = 0x0001, - PanGesture = 0x0002, - FlickGesture = 0x0004, - RotationGesture = 0x0008, - TiltGesture = 0x0010, - AllGestures = 0xffff - }; - - Q_DECLARE_FLAGS(AcceptedGestures, GeoMapGesture) - - AcceptedGestures acceptedGestures() const; - void setAcceptedGestures(AcceptedGestures acceptedGestures); - - bool isPinchActive() const; - bool isRotationActive() const; - bool isTiltActive() const; - bool isPanActive() const; - bool isActive() const; - - bool enabled() const; - void setEnabled(bool enabled); - - qreal maximumZoomLevelChange() const; - void setMaximumZoomLevelChange(qreal maxChange); - - qreal flickDeceleration() const; - void setFlickDeceleration(qreal deceleration); - - void handleTouchEvent(QPointerEvent *event); -#if QT_CONFIG(wheelevent) - void handleWheelEvent(QWheelEvent *event); -#endif - void handleMousePressEvent(QMouseEvent *event); - void handleMouseMoveEvent(QMouseEvent *event); - void handleMouseReleaseEvent(QMouseEvent *event); - void handleTouchUngrabEvent(); - - void setMinimumZoomLevel(qreal min); - qreal minimumZoomLevel() const; - - void setMaximumZoomLevel(qreal max); - qreal maximumZoomLevel() const; - - void setMap(QGeoMap* map); - - bool preventStealing() const; - void setPreventStealing(bool prevent); - -Q_SIGNALS: - void panActiveChanged(); - void pinchActiveChanged(); - void rotationActiveChanged(); - void tiltActiveChanged(); - void enabledChanged(); - void maximumZoomLevelChangeChanged(); - void acceptedGesturesChanged(); - void flickDecelerationChanged(); - void pinchStarted(QGeoMapPinchEvent *pinch); - void pinchUpdated(QGeoMapPinchEvent *pinch); - void pinchFinished(QGeoMapPinchEvent *pinch); - void panStarted(); - void panFinished(); - void flickStarted(); - void flickFinished(); - void rotationStarted(QGeoMapPinchEvent *pinch); - void rotationUpdated(QGeoMapPinchEvent *pinch); - void rotationFinished(QGeoMapPinchEvent *pinch); - void tiltStarted(QGeoMapPinchEvent *pinch); - void tiltUpdated(QGeoMapPinchEvent *pinch); - void tiltFinished(QGeoMapPinchEvent *pinch); - void preventStealingChanged(); -private: - void update(); - - // Create general data relating to the touch points - void touchPointStateMachine(); - void startOneTouchPoint(); - void updateOneTouchPoint(); - void startTwoTouchPoints(); - void updateTwoTouchPoints(); - - // All two fingers vertical parallel panning related code, which encompasses tilting - void tiltStateMachine(); - bool canStartTilt(); - void startTilt(); - void updateTilt(); - void endTilt(); - - // All two fingers rotation related code, which encompasses rotation - void rotationStateMachine(); - bool canStartRotation(); - void startRotation(); - void updateRotation(); - void endRotation(); - - // All pinch related code, which encompasses zoom - 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); - void stopFlick(); - - bool pinchEnabled() const; - void setPinchEnabled(bool enabled); - bool rotationEnabled() const; - void setRotationEnabled(bool enabled); - bool tiltEnabled() const; - void setTiltEnabled(bool enabled); - bool panEnabled() const; - void setPanEnabled(bool enabled); - bool flickEnabled() const; - void setFlickEnabled(bool enabled); - -private Q_SLOTS: - void handleFlickAnimationStopped(); - - -private: - void stopPan(); - void clearTouchData(); - void updateFlickParameters(const QPointF &pos); - -private: - QGeoMap* m_map = nullptr; - QDeclarativeGeoMap *m_declarativeMap = nullptr; - bool m_enabled = true; - - // This should be intended as a "two fingers gesture" struct - struct Pinch - { - QGeoMapPinchEvent m_event; - bool m_pinchEnabled = true; - bool m_rotationEnabled = true; - bool m_tiltEnabled = true; - struct Zoom - { - qreal m_minimum = 0.0; - qreal m_maximum = 30.0; - qreal m_start = 0.0; - qreal m_previous = 0.0; - qreal maximumChange = 4.0; - } m_zoom; - - struct Rotation - { - qreal m_startBearing = 0.0; - qreal m_previousTouchAngle = 0.0; // needed for detecting crossing +- 180 in a safer way - qreal m_totalAngle = 0.0; - } m_rotation; - - struct Tilt - { - QPointF m_startTouchCentroid; - qreal m_startTilt; - } m_tilt; - - QPointF m_lastPoint1; - QPointF m_lastPoint2; - qreal m_startDist = 0.0; - qreal m_lastAngle = 0.0; - } m_pinch; - - AcceptedGestures m_acceptedGestures = AllGestures; - - struct Pan - { - qreal m_maxVelocity = 2500; - qreal m_deceleration = 2500; - QQuickGeoCoordinateAnimation *m_animation = nullptr; - bool m_flickEnabled = true; - bool m_panEnabled = true; - } m_flick; - - - // these are calculated regardless of gesture or number of touch points - QVector2D m_flickVector; - QElapsedTimer m_lastPosTime; - QPointF m_lastPos; - QList m_allPoints; - QList m_touchPoints; - QPointF m_sceneStartPoint1; - - // only set when two points in contact - QPointF m_sceneStartPoint2; - QGeoCoordinate m_startCoord; - QGeoCoordinate m_touchCenterCoord; - qreal m_twoTouchAngle; - qreal m_twoTouchAngleStart; - qreal m_distanceBetweenTouchPoints; - qreal m_distanceBetweenTouchPointsStart; - QPointF m_twoTouchPointsCentroidStart; - QPointF m_touchPointsCentroid; - bool m_preventStealing = false; - -private: - // prototype state machine... - enum TouchPointState - { - touchPoints0, - touchPoints1, - touchPoints2 - } m_touchPointState; - - enum PinchState - { - pinchInactive, - pinchInactiveTwoPoints, - pinchActive - } m_pinchState; - - enum RotationState - { - rotationInactive, - rotationInactiveTwoPoints, - rotationActive - } m_rotationState; - - enum TiltState - { - tiltInactive, - tiltInactiveTwoPoints, - tiltActive - } m_tiltState; - - enum FlickState - { - flickInactive, - panActive, - flickActive - } m_flickState; - - inline void setTouchPointState(TouchPointState state); - inline void setFlickState(FlickState state); - inline void setTiltState(TiltState state); - inline void setRotationState(RotationState state); - inline void setPinchState(PinchState state); -}; - -QT_END_NAMESPACE -QML_DECLARE_TYPE(QQuickGeoMapGestureArea) - -#endif // QQUICKGEOMAPGESTUREAREA_P_H -- cgit v1.2.1