diff options
-rw-r--r-- | src/imports/location/qdeclarativegeomap.cpp | 76 | ||||
-rw-r--r-- | src/imports/location/qdeclarativegeomap_p.h | 4 | ||||
-rw-r--r-- | src/imports/location/qdeclarativegeomapgesturearea.cpp | 174 | ||||
-rw-r--r-- | src/imports/location/qdeclarativegeomapgesturearea_p.h | 21 | ||||
-rw-r--r-- | tests/auto/declarative_ui/tst_map_mouse.qml | 26 |
5 files changed, 212 insertions, 89 deletions
diff --git a/src/imports/location/qdeclarativegeomap.cpp b/src/imports/location/qdeclarativegeomap.cpp index 4c4b8888..a2d139e9 100644 --- a/src/imports/location/qdeclarativegeomap.cpp +++ b/src/imports/location/qdeclarativegeomap.cpp @@ -58,7 +58,6 @@ #include <QtQml/QQmlContext> #include <QtQml/qqmlinfo.h> #include <QModelIndex> -#include <QtQuick/QQuickWindow> #include <QtQuick/QSGSimpleRectNode> #include <QtGui/QGuiApplication> #include <QCoreApplication> @@ -196,7 +195,7 @@ QDeclarativeGeoMap::QDeclarativeGeoMap(QQuickItem *parent) { QLOC_TRACE0; setAcceptHoverEvents(false); - setAcceptedMouseButtons(Qt::LeftButton | Qt::MidButton | Qt::RightButton); + setAcceptedMouseButtons(Qt::LeftButton); setFlags(QQuickItem::ItemHasContents | QQuickItem::ItemClipsChildrenToShape); setFiltersChildMouseEvents(true); @@ -322,8 +321,7 @@ void QDeclarativeGeoMap::componentComplete() */ void QDeclarativeGeoMap::mousePressEvent(QMouseEvent *event) { - if (!mouseEvent(event)) - event->ignore(); + event->setAccepted(gestureArea_->mousePressEvent(event)); } /*! @@ -331,8 +329,7 @@ void QDeclarativeGeoMap::mousePressEvent(QMouseEvent *event) */ void QDeclarativeGeoMap::mouseMoveEvent(QMouseEvent *event) { - if (!mouseEvent(event)) - event->ignore(); + event->setAccepted(gestureArea_->mouseMoveEvent(event)); } /*! @@ -340,31 +337,17 @@ void QDeclarativeGeoMap::mouseMoveEvent(QMouseEvent *event) */ void QDeclarativeGeoMap::mouseReleaseEvent(QMouseEvent *event) { - if (!mouseEvent(event)) - event->ignore(); + event->setAccepted(gestureArea_->mouseReleaseEvent(event)); } /*! \internal - returns whether flickable used the event */ -bool QDeclarativeGeoMap::mouseEvent(QMouseEvent *event) +void QDeclarativeGeoMap::mouseUngrabEvent() { - if (!mappingManagerInitialized_) - return false; - switch (event->type()) { - case QEvent::MouseButtonPress: - return gestureArea_->mousePressEvent(event); - case QEvent::MouseButtonRelease: - return gestureArea_->mouseReleaseEvent(event); - case QEvent::MouseMove: - return gestureArea_->mouseMoveEvent(event); - default: - return false; - } + gestureArea_->mouseUngrabEvent(); } - /*! \qmlproperty MapGestureArea QtLocation::Map::gesture @@ -839,8 +822,7 @@ void QDeclarativeGeoMap::touchEvent(QTouchEvent *event) return; } QLOC_TRACE0; - event->accept(); - gestureArea_->touchEvent(event); + event->setAccepted(gestureArea_->touchEvent(event)); } /*! @@ -859,29 +841,51 @@ void QDeclarativeGeoMap::wheelEvent(QWheelEvent *event) */ bool QDeclarativeGeoMap::childMouseEventFilter(QQuickItem *item, QEvent *event) { - Q_UNUSED(item) + if (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseMove || + event->type() == QEvent::MouseButtonRelease || + event->type() == QEvent::MouseButtonDblClick) { + // Check if we have already grabbed input. This can happen if the previous touch event + // immediately triggers a gesture, the synthesized mouse event is still generated. Filter + // these events so that child items do no receive them. + if (gestureArea_->hasGrabbedInput()) + return true; + + // Ignore synthesized mouse events, but don't filter them, as child items may not handle + // touch events directly. After a gesture is recognized and an input grab obtained these + // events should cease to be generated, except for the case above. + QMouseEvent *me = static_cast<QMouseEvent *>(event); + if (me->source() != Qt::MouseEventNotSynthesized) + return false; + } else if (event->type() == QEvent::TouchBegin || event->type() == QEvent::TouchUpdate || + event->type() == QEvent::TouchEnd) { + // Check if already grabbed mouse, if so filter out touch events. + // FIXME: Returning true here gives priority to touch input, which is not what is intended. + // In response to returning true ungrabMouse() is called and touch input will be handled + // thereafter. + if (gestureArea_->hasGrabbedInput()) + return true; + } + QLOC_TRACE0; switch (event->type()) { case QEvent::MouseButtonPress: - case QEvent::MouseButtonRelease: case QEvent::MouseMove: if (item->keepMouseGrab()) return false; - if (!gestureArea_->filterMapChildMouseEvent(static_cast<QMouseEvent *>(event))) - return false; - grabMouse(); - return true; - case QEvent::UngrabMouse: return gestureArea_->filterMapChildMouseEvent(static_cast<QMouseEvent *>(event)); + case QEvent::MouseButtonRelease: + return gestureArea_->filterMapChildMouseEvent(static_cast<QMouseEvent *>(event)); + case QEvent::UngrabMouse: + // Never filter ungrab mouse events. This event notifies 'item' that the mouse has been + // grabbed and it should cancel any outstanding input event processing. For example, press + // and hold timers. + return false; case QEvent::TouchBegin: case QEvent::TouchUpdate: case QEvent::TouchEnd: if (item->keepMouseGrab()) return false; - if (!gestureArea_->filterMapChildTouchEvent(static_cast<QTouchEvent *>(event))) - return false; - grabMouse(); - return true; + return gestureArea_->filterMapChildTouchEvent(static_cast<QTouchEvent *>(event)); case QEvent::Wheel: return gestureArea_->wheelEvent(static_cast<QWheelEvent *>(event)); default: diff --git a/src/imports/location/qdeclarativegeomap_p.h b/src/imports/location/qdeclarativegeomap_p.h index 256ddcfb..ce66acaa 100644 --- a/src/imports/location/qdeclarativegeomap_p.h +++ b/src/imports/location/qdeclarativegeomap_p.h @@ -146,9 +146,6 @@ public: QT_DEPRECATED Q_INVOKABLE QPointF toScreenPosition(const QGeoCoordinate &coordinate) const; #endif - // callback for map mouse areas - bool mouseEvent(QMouseEvent *event); - QDeclarativeGeoMapGestureArea *gesture(); Q_INVOKABLE void fitViewportToGeoShape(const QVariant &shape); @@ -163,6 +160,7 @@ protected: void mousePressEvent(QMouseEvent *event); void mouseMoveEvent(QMouseEvent *event); void mouseReleaseEvent(QMouseEvent *event); + void mouseUngrabEvent(); void touchEvent(QTouchEvent *event); void wheelEvent(QWheelEvent *event); diff --git a/src/imports/location/qdeclarativegeomapgesturearea.cpp b/src/imports/location/qdeclarativegeomapgesturearea.cpp index 0fbf5557..bdba9110 100644 --- a/src/imports/location/qdeclarativegeomapgesturearea.cpp +++ b/src/imports/location/qdeclarativegeomapgesturearea.cpp @@ -40,6 +40,7 @@ #include <QtGui/QWheelEvent> #include <QtGui/QStyleHints> #include <QtQml/qqmlinfo.h> +#include <QtQuick/QQuickWindow> #include <QPropertyAnimation> #include <QDebug> #include "math.h" @@ -329,6 +330,8 @@ QDeclarativeGeoMapGestureArea::QDeclarativeGeoMapGestureArea(QDeclarativeGeoMap : QObject(parent), declarativeMap_(map), enabled_(true), + hasGrab_(false), + activeInput_(NoInput), activeGestures_(ZoomGesture | PanGesture | FlickGesture) { map_ = 0; @@ -339,8 +342,8 @@ QDeclarativeGeoMapGestureArea::QDeclarativeGeoMapGestureArea(QDeclarativeGeoMap touchPointState_ = touchPoints0; pinchState_ = pinchInactive; panState_ = panInactive; - } + /*! \internal */ @@ -356,6 +359,11 @@ void QDeclarativeGeoMapGestureArea::setMap(QGeoMap *map) map_, SLOT(cameraStopped())); } +bool QDeclarativeGeoMapGestureArea::hasGrabbedInput() const +{ + return hasGrab_; +} + QDeclarativeGeoMapGestureArea::~QDeclarativeGeoMapGestureArea() { } @@ -563,6 +571,11 @@ bool QDeclarativeGeoMapGestureArea::mousePressEvent(QMouseEvent *event) if (!(enabled_ && activeGestures_)) return false; + if (activeInput_ == TouchInput) + return false; + + activeInput_ = MouseInput; + touchPoints_.clear(); touchPoints_ << makeTouchPointFromMouseEvent(event, Qt::TouchPointPressed); @@ -578,6 +591,9 @@ bool QDeclarativeGeoMapGestureArea::mouseMoveEvent(QMouseEvent *event) if (!(enabled_ && activeGestures_)) return false; + if (activeInput_ != MouseInput) + return false; + touchPoints_.clear(); touchPoints_ << makeTouchPointFromMouseEvent(event, Qt::TouchPointMoved); @@ -593,23 +609,63 @@ bool QDeclarativeGeoMapGestureArea::mouseReleaseEvent(QMouseEvent *) if (!(enabled_ && activeGestures_)) return false; + if (activeInput_ == TouchInput) + return false; + touchPoints_.clear(); update(); + + activeInput_ = NoInput; + return true; } /*! \internal */ -void QDeclarativeGeoMapGestureArea::touchEvent(QTouchEvent *event) +void QDeclarativeGeoMapGestureArea::mouseUngrabEvent() { + touchPoints_.clear(); + hasGrab_ = false; + if (activeInput_ == MouseInput) + activeInput_ = NoInput; + update(); +} + +/*! + \internal +*/ +bool QDeclarativeGeoMapGestureArea::touchEvent(QTouchEvent *event) +{ + if (!(enabled_ && activeGestures_)) + return false; + + if (activeInput_ == MouseInput) + return false; + switch (event->type()) { case QEvent::TouchBegin: case QEvent::TouchUpdate: - touchPoints_.clear(); - for (int i = 0; i < event->touchPoints().count(); ++i) { - if (!(event->touchPoints().at(i).state() & Qt::TouchPointReleased)) { - touchPoints_ << event->touchPoints().at(i); + activeInput_ = TouchInput; + foreach (const QTouchEvent::TouchPoint &p, event->touchPoints()) { + QList<QTouchEvent::TouchPoint>::iterator i; + for (i = touchPoints_.begin(); i != touchPoints_.end(); ++i) { + if (i->id() == p.id()) { + i = touchPoints_.erase(i); + break; + } + } + switch (p.state()) { + case Qt::TouchPointPressed: + case Qt::TouchPointMoved: + case Qt::TouchPointStationary: + touchPoints_.insert(i, p); + break; + case Qt::TouchPointReleased: + // already removed + break; + default: + break; } } update(); @@ -617,11 +673,14 @@ void QDeclarativeGeoMapGestureArea::touchEvent(QTouchEvent *event) case QEvent::TouchEnd: touchPoints_.clear(); update(); + activeInput_ = NoInput; break; default: // no-op break; } + + return true; } bool QDeclarativeGeoMapGestureArea::wheelEvent(QWheelEvent *event) @@ -635,26 +694,25 @@ bool QDeclarativeGeoMapGestureArea::wheelEvent(QWheelEvent *event) */ bool QDeclarativeGeoMapGestureArea::filterMapChildMouseEvent(QMouseEvent *event) { - bool used = false; + if (!(enabled_ && activeGestures_)) + return false; + + // We have not grabbed the mouse yet. Process it but don't filter it so the child can use it + // (until we grab it). switch (event->type()) { case QEvent::MouseButtonPress: - used = mousePressEvent(event); + mousePressEvent(event); break; case QEvent::MouseButtonRelease: - used = mouseReleaseEvent(event); + mouseReleaseEvent(event); break; case QEvent::MouseMove: - used = mouseMoveEvent(event); - break; - case QEvent::UngrabMouse: - touchPoints_.clear(); - update(); + mouseMoveEvent(event); break; default: - used = false; break; } - return used && (isPanActive() || isPinchActive()); + return false; } /*! @@ -662,8 +720,13 @@ bool QDeclarativeGeoMapGestureArea::filterMapChildMouseEvent(QMouseEvent *event) */ bool QDeclarativeGeoMapGestureArea::filterMapChildTouchEvent(QTouchEvent *event) { + if (!(enabled_ && activeGestures_)) + return false; + + // We have not grabbed the touch id associated with this touch event yet. Process it but don't + // filter it so the child can use it (until we grab it). touchEvent(event); - return isPanActive() || isPinchActive(); + return false; } /*! @@ -724,6 +787,31 @@ void QDeclarativeGeoMapGestureArea::update() // the whole gesture (enabled_ flag), this keeps the enabled_ consistent with the pinch if (isPanActive() || (enabled_ && pan_.enabled_ && (activeGestures_ & (PanGesture | FlickGesture)))) panStateMachine(); + + if (pinchState_ != pinchInactive || panState_ == panActive) { + if (!hasGrab_) { + if (activeInput_ == MouseInput) { + hasGrab_ = true; + declarativeMap_->grabMouse(); + } else if (activeInput_ == TouchInput) { + hasGrab_ = true; + QVector<int> ids; + foreach (const QTouchEvent::TouchPoint &tp, touchPoints_) + ids.append(tp.id()); + declarativeMap_->grabTouchPoints(ids); + } + } + } else { + if (hasGrab_) { + if (activeInput_ == MouseInput) { + hasGrab_ = false; + declarativeMap_->ungrabMouse(); + } else if (activeInput_ == TouchInput) { + hasGrab_ = false; + declarativeMap_->ungrabTouchPoints(); + } + } + } } /*! @@ -738,7 +826,7 @@ void QDeclarativeGeoMapGestureArea::touchPointStateMachine() clearTouchData(); startOneTouchPoint(); touchPointState_ = touchPoints1; - } else if (touchPoints_.count() == 2) { + } else if (touchPoints_.count() >= 2) { clearTouchData(); startTwoTouchPoints(); touchPointState_ = touchPoints2; @@ -842,43 +930,49 @@ void QDeclarativeGeoMapGestureArea::updateTwoTouchPoints() /*! \internal */ -void QDeclarativeGeoMapGestureArea::setPinchActive(bool active) -{ - if ((active && pinchState_ == pinchActive) || (!active && pinchState_ != pinchActive)) - return; - pinchState_ = active ? pinchActive : pinchInactive; - emit pinchActiveChanged(); -} - - -/*! - \internal -*/ void QDeclarativeGeoMapGestureArea::pinchStateMachine() { PinchState lastState = pinchState_; // Transitions: switch (pinchState_) { case pinchInactive: - if (canStartPinch()) { - startPinch(); - setPinchActive(true); + if (touchPoints_.count() >= 2) { + if (canStartPinch()) { + startPinch(); + pinchState_ = pinchActive; + } else { + pinchState_ = pinchInactiveTwoPoints; + } + } + break; + case pinchInactiveTwoPoints: + if (touchPoints_.count() <= 1) { + pinchState_ = pinchInactive; + } else { + if (canStartPinch()) { + startPinch(); + pinchState_ = pinchActive; + } } break; case pinchActive: if (touchPoints_.count() <= 1) { endPinch(); - setPinchActive(false); + pinchState_ = pinchInactive; } break; } // This line implements an exclusive state machine, where the transitions and updates don't // happen on the same frame - if (pinchState_ != lastState) - return; + if (pinchState_ != lastState) { + emit pinchActiveChanged(); + return; + } + // Update switch (pinchState_) { case pinchInactive: + case pinchInactiveTwoPoints: break; // do nothing case pinchActive: updatePinch(); @@ -1026,6 +1120,10 @@ void QDeclarativeGeoMapGestureArea::panStateMachine() } break; } + + if (panState_ != lastState) + emit panActiveChanged(); + // Update switch (panState_) { case panInactive: // do nothing @@ -1147,10 +1245,11 @@ void QDeclarativeGeoMapGestureArea::stopPan() if (panState_ == panFlick) { endFlick(); } else if (panState_ == panActive) { + panState_ = panInactive; emit panFinished(); + emit panActiveChanged(); emit movementStopped(); } - panState_ = panInactive; } /*! @@ -1163,6 +1262,7 @@ void QDeclarativeGeoMapGestureArea::endFlick() pan_.animation_->stop(); emit flickFinished(); panState_ = panInactive; + emit panActiveChanged(); emit movementStopped(); } diff --git a/src/imports/location/qdeclarativegeomapgesturearea_p.h b/src/imports/location/qdeclarativegeomapgesturearea_p.h index 24b209d3..863d4bdd 100644 --- a/src/imports/location/qdeclarativegeomapgesturearea_p.h +++ b/src/imports/location/qdeclarativegeomapgesturearea_p.h @@ -109,7 +109,7 @@ class QDeclarativeGeoMapGestureArea: public QObject Q_PROPERTY(bool pinchEnabled READ pinchEnabled WRITE setPinchEnabled NOTIFY pinchEnabledChanged) Q_PROPERTY(bool panEnabled READ panEnabled WRITE setPanEnabled NOTIFY panEnabledChanged) Q_PROPERTY(bool isPinchActive READ isPinchActive NOTIFY pinchActiveChanged) - Q_PROPERTY(bool isPanActive READ isPanActive) + Q_PROPERTY(bool isPanActive READ isPanActive NOTIFY panActiveChanged) Q_PROPERTY(ActiveGestures activeGestures READ activeGestures WRITE setActiveGestures NOTIFY activeGesturesChanged) Q_PROPERTY(qreal maximumZoomLevelChange READ maximumZoomLevelChange WRITE setMaximumZoomLevelChange NOTIFY maximumZoomLevelChangeChanged) Q_PROPERTY(qreal flickDeceleration READ flickDeceleration WRITE setFlickDeceleration NOTIFY flickDecelerationChanged) @@ -129,7 +129,6 @@ public: void setActiveGestures(ActiveGestures activeGestures); bool isPinchActive() const; - void setPinchActive(bool active); bool isPanActive() const; bool enabled() const; @@ -147,13 +146,14 @@ public: qreal flickDeceleration() const; void setFlickDeceleration(qreal deceleration); - void touchEvent(QTouchEvent *event); + bool touchEvent(QTouchEvent *event); bool wheelEvent(QWheelEvent *event); bool mousePressEvent(QMouseEvent *event); bool mouseMoveEvent(QMouseEvent *event); bool mouseReleaseEvent(QMouseEvent *event); + void mouseUngrabEvent(); bool filterMapChildMouseEvent(QMouseEvent *event); bool filterMapChildTouchEvent(QTouchEvent *event); @@ -166,7 +166,10 @@ public: void setMap(QGeoMap *map); + bool hasGrabbedInput() const; + Q_SIGNALS: + void panActiveChanged(); void pinchActiveChanged(); void enabledChanged(); void maximumZoomLevelChangeChanged(); @@ -222,6 +225,15 @@ private: QGeoMap *map_; QDeclarativeGeoMap *declarativeMap_; bool enabled_; + bool hasGrab_; + + enum InputType + { + NoInput, + MouseInput, + TouchInput + }; + InputType activeInput_; struct Pinch { @@ -283,6 +295,7 @@ private: enum PinchState { pinchInactive, + pinchInactiveTwoPoints, pinchActive } pinchState_; @@ -295,6 +308,6 @@ private: }; QT_END_NAMESPACE -QML_DECLARE_TYPE(QDeclarativeGeoMapGestureArea); +QML_DECLARE_TYPE(QDeclarativeGeoMapGestureArea) #endif // QDECLARATIVEGEOMAPGESTUREAREA_P_H diff --git a/tests/auto/declarative_ui/tst_map_mouse.qml b/tests/auto/declarative_ui/tst_map_mouse.qml index 0e2472c8..8841c45a 100644 --- a/tests/auto/declarative_ui/tst_map_mouse.qml +++ b/tests/auto/declarative_ui/tst_map_mouse.qml @@ -494,14 +494,21 @@ Item { compare(mouseUpper.lastWasHeld, false) compare(mouseUpper.lastX, 5) compare(mouseUpper.lastY, 5) // remember 20 offset of the mouse area - mousePress(map, 5, 26) + + mouseRelease(map, 5, 25) compare(mouseUpperPressedSpy.count, 1) + compare(mouseUpperReleasedSpy.count, 1) + compare(mouseLowerPressedSpy.count, 0) + compare(mouseLowerReleasedSpy.count, 0) + + mousePress(map, 5, 26) + compare(mouseUpperPressedSpy.count, 2) compare(mouseLowerPressedSpy.count, 0) compare(mouseOverlapperPressedSpy.count, 0) mouseRelease(map, 5, 26) - compare(mouseUpperPressedSpy.count, 1) - compare(mouseUpperReleasedSpy.count, 1) + compare(mouseUpperPressedSpy.count, 2) + compare(mouseUpperReleasedSpy.count, 2) compare(mouseLowerPressedSpy.count, 0) compare(mouseLowerReleasedSpy.count, 0) compare(mouseUpper.lastAccepted, true) @@ -512,7 +519,7 @@ Item { compare(mouseUpper.lastY, 6) // remember 20 offset of the mouse area mousePress(map, 5, 75) - compare(mouseUpperPressedSpy.count, 1) + compare(mouseUpperPressedSpy.count, 2) compare(mouseLowerPressedSpy.count, 1) compare(mouseOverlapperPressedSpy.count, 0) compare(mouseLower.lastAccepted, true) @@ -523,18 +530,19 @@ Item { compare(mouseLower.lastY, 25) // remember 50 offset of the mouse area mouseRelease(map, 5, 75) - compare(mouseUpperPressedSpy.count, 1) - compare(mouseUpperReleasedSpy.count, 1) + compare(mouseUpperPressedSpy.count, 2) + compare(mouseUpperReleasedSpy.count, 2) compare(mouseLowerPressedSpy.count, 1) compare(mouseLowerReleasedSpy.count, 1) + mousePress(map, 55, 75) - compare(mouseUpperPressedSpy.count, 1) + compare(mouseUpperPressedSpy.count, 2) compare(mouseLowerPressedSpy.count, 1) compare(mouseOverlapperPressedSpy.count, 1) compare(mouseOverlapperReleasedSpy.count, 0) mouseRelease(map, 55, 25) - compare(mouseUpperPressedSpy.count, 1) - compare(mouseUpperReleasedSpy.count, 1) + compare(mouseUpperPressedSpy.count, 2) + compare(mouseUpperReleasedSpy.count, 2) compare(mouseLowerPressedSpy.count, 1) compare(mouseLowerReleasedSpy.count, 1) compare(mouseOverlapperReleasedSpy.count, 1) |