diff options
author | Shawn Rutledge <shawn.rutledge@digia.com> | 2014-11-13 17:54:26 +0100 |
---|---|---|
committer | Shawn Rutledge <shawn.rutledge@digia.com> | 2015-01-25 21:59:35 +0100 |
commit | d896d76b0f2ff0387cd09a04107ea5df087268e1 (patch) | |
tree | be6d7385188baa3a83c8e59b400baf4d7cae5984 | |
parent | 817cd03a5f437c9e8862646cca5ea016b1223bc7 (diff) |
PinchArea: handle native gestures when available
PinchArea is the first Item where we handle a QNativeGestureEvent,
so QQuickWindowPrivate::deliverGestureEvent is added to deliver these
events in the same way as the other deliverXEvent methods.
For now there is not a separate virtual event handler in QQuickItem,
because QNativeGestureEvent is not universally available, so it's
necessary to handle this event type in QQuickPinchArea::event().
updatePinchTarget() contains code factored out from updatePinch()
to do the necessary tranformations on the Item which was set as
pinch.target in QML.
So far the QNativeGestureEvents are generated only on OSX, so
the behavior of the PinchArea on other platforms is unmodified.
On OSX the intention is that we do not need to enable multiple-touch
events in order to use any of the common 2-finger gestures, and the
"feel" will be similar to that of gestures in native applications.
[ChangeLog][QtQuick][PinchArea] Pinch gestures are recognized by
the operating system on OSX
Change-Id: I693526ea120a9144beb7666afeab6256caa73e51
Reviewed-by: Morten Johan Sørvig <morten.sorvig@digia.com>
-rw-r--r-- | src/quick/items/qquickitem.cpp | 3 | ||||
-rw-r--r-- | src/quick/items/qquickpincharea.cpp | 176 | ||||
-rw-r--r-- | src/quick/items/qquickpincharea_p.h | 7 | ||||
-rw-r--r-- | src/quick/items/qquickwindow.cpp | 35 | ||||
-rw-r--r-- | src/quick/items/qquickwindow_p.h | 1 |
5 files changed, 167 insertions, 55 deletions
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 38736e44b2..2d81714ee1 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -7316,6 +7316,9 @@ bool QQuickItem::event(QEvent *ev) dropEvent(static_cast<QDropEvent*>(ev)); break; #endif // QT_NO_DRAGANDDROP + case QEvent::NativeGesture: + ev->ignore(); + break; default: return QObject::event(ev); } diff --git a/src/quick/items/qquickpincharea.cpp b/src/quick/items/qquickpincharea.cpp index 7e934316d8..c2ea91ed1e 100644 --- a/src/quick/items/qquickpincharea.cpp +++ b/src/quick/items/qquickpincharea.cpp @@ -37,7 +37,10 @@ #include <QtGui/qevent.h> #include <QtGui/qguiapplication.h> #include <QtGui/qstylehints.h> +#include <qpa/qplatformintegration.h> #include <qpa/qplatformnativeinterface.h> +#include <private/qguiapplication_p.h> +#include <QVariant> #include <float.h> #include <math.h> @@ -178,7 +181,10 @@ QQuickPinchAreaPrivate::~QQuickPinchAreaPrivate() \li using the onPinchStarted, onPinchUpdated and onPinchFinished handlers \endlist - \sa PinchEvent + Since Qt 5.5, PinchArea can react to native pinch gesture events from the + operating system if available; otherwise it reacts only to touch events. + + \sa PinchEvent, QNativeGestureEvent, QTouchEvent */ /*! @@ -526,34 +532,40 @@ void QQuickPinchArea::updatePinch() d->lastPoint1 = touchPoint1.scenePos(); d->lastPoint2 = touchPoint2.scenePos(); emit pinchUpdated(&pe); - if (d->pinch && d->pinch->target()) { - qreal s = d->pinchStartScale * scale; - s = qMin(qMax(pinch()->minimumScale(),s), pinch()->maximumScale()); - pinch()->target()->setScale(s); - QPointF pos = sceneCenter - d->sceneStartCenter + d->pinchStartPos; - if (pinch()->axis() & QQuickPinch::XAxis) { - qreal x = pos.x(); - if (x < pinch()->xmin()) - x = pinch()->xmin(); - else if (x > pinch()->xmax()) - x = pinch()->xmax(); - pinch()->target()->setX(x); - } - if (pinch()->axis() & QQuickPinch::YAxis) { - qreal y = pos.y(); - if (y < pinch()->ymin()) - y = pinch()->ymin(); - else if (y > pinch()->ymax()) - y = pinch()->ymax(); - pinch()->target()->setY(y); - } - if (d->pinchStartRotation >= pinch()->minimumRotation() - && d->pinchStartRotation <= pinch()->maximumRotation()) { - qreal r = d->pinchRotation + d->pinchStartRotation; - r = qMin(qMax(pinch()->minimumRotation(),r), pinch()->maximumRotation()); - pinch()->target()->setRotation(r); - } - } + updatePinchTarget(); + } + } +} + +void QQuickPinchArea::updatePinchTarget() +{ + Q_D(QQuickPinchArea); + if (d->pinch && d->pinch->target()) { + qreal s = d->pinchStartScale * d->pinchLastScale; + s = qMin(qMax(pinch()->minimumScale(),s), pinch()->maximumScale()); + pinch()->target()->setScale(s); + QPointF pos = d->sceneLastCenter - d->sceneStartCenter + d->pinchStartPos; + if (pinch()->axis() & QQuickPinch::XAxis) { + qreal x = pos.x(); + if (x < pinch()->xmin()) + x = pinch()->xmin(); + else if (x > pinch()->xmax()) + x = pinch()->xmax(); + pinch()->target()->setX(x); + } + if (pinch()->axis() & QQuickPinch::YAxis) { + qreal y = pos.y(); + if (y < pinch()->ymin()) + y = pinch()->ymin(); + else if (y > pinch()->ymax()) + y = pinch()->ymax(); + pinch()->target()->setY(y); + } + if (d->pinchStartRotation >= pinch()->minimumRotation() + && d->pinchStartRotation <= pinch()->maximumRotation()) { + qreal r = d->pinchRotation + d->pinchStartRotation; + r = qMin(qMax(pinch()->minimumRotation(),r), pinch()->maximumRotation()); + pinch()->target()->setRotation(r); } } } @@ -597,31 +609,95 @@ void QQuickPinchArea::itemChange(ItemChange change, const ItemChangeData &value) QQuickItem::itemChange(change, value); } -#ifdef Q_OS_OSX -void QQuickPinchArea::hoverEnterEvent(QHoverEvent *event) +bool QQuickPinchArea::event(QEvent *event) { - Q_UNUSED(event); - setTouchEventsEnabled(true); -} + Q_D(QQuickPinchArea); + if (!d->enabled || !isVisible()) + return QQuickItem::event(event); -void QQuickPinchArea::hoverLeaveEvent(QHoverEvent *event) -{ - Q_UNUSED(event); - setTouchEventsEnabled(false); -} + switch (event->type()) { +#ifndef QT_NO_GESTURES + case QEvent::NativeGesture: { + QNativeGestureEvent *gesture = static_cast<QNativeGestureEvent *>(event); + switch (gesture->gestureType()) { + case Qt::BeginNativeGesture: + clearPinch(); // probably not necessary; JIC + d->pinchStartCenter = gesture->localPos(); + d->pinchStartAngle = 0.0; + d->pinchStartRotation = 0.0; + d->pinchRotation = 0.0; + d->pinchStartScale = 1.0; + d->pinchLastAngle = 0.0; + d->pinchLastScale = 1.0; + d->sceneStartPoint1 = gesture->windowPos(); + d->sceneStartPoint2 = gesture->windowPos(); // TODO we never really know + d->lastPoint1 = gesture->windowPos(); + d->lastPoint2 = gesture->windowPos(); // TODO we never really know + if (d->pinch && d->pinch->target()) { + d->pinchStartPos = d->pinch->target()->position(); + d->pinchStartScale = d->pinch->target()->scale(); + d->pinchStartRotation = d->pinch->target()->rotation(); + d->pinch->setActive(true); + } + break; + case Qt::EndNativeGesture: + clearPinch(); + break; + case Qt::ZoomNativeGesture: { + qreal scale = d->pinchLastScale * (1.0 + gesture->value()); + QQuickPinchEvent pe(d->pinchStartCenter, scale, d->pinchLastAngle, 0.0); + pe.setStartCenter(d->pinchStartCenter); + pe.setPreviousCenter(d->pinchStartCenter); + pe.setPreviousAngle(d->pinchLastAngle); + pe.setPreviousScale(d->pinchLastScale); + pe.setStartPoint1(mapFromScene(d->sceneStartPoint1)); + pe.setStartPoint2(mapFromScene(d->sceneStartPoint2)); + pe.setPoint1(mapFromScene(d->lastPoint1)); + pe.setPoint2(mapFromScene(d->lastPoint2)); + pe.setPointCount(2); + d->pinchLastScale = scale; + if (d->inPinch) + emit pinchUpdated(&pe); + else + emit pinchStarted(&pe); + d->inPinch = true; + updatePinchTarget(); + } break; + case Qt::RotateNativeGesture: { + qreal angle = d->pinchLastAngle + gesture->value(); + QQuickPinchEvent pe(d->pinchStartCenter, d->pinchLastScale, angle, 0.0); + pe.setStartCenter(d->pinchStartCenter); + pe.setPreviousCenter(d->pinchStartCenter); + pe.setPreviousAngle(d->pinchLastAngle); + pe.setPreviousScale(d->pinchLastScale); + pe.setStartPoint1(mapFromScene(d->sceneStartPoint1)); + pe.setStartPoint2(mapFromScene(d->sceneStartPoint2)); + pe.setPoint1(mapFromScene(d->lastPoint1)); + pe.setPoint2(mapFromScene(d->lastPoint2)); + pe.setPointCount(2); + d->pinchLastAngle = angle; + if (d->inPinch) + emit pinchUpdated(&pe); + else + emit pinchStarted(&pe); + d->inPinch = true; + d->pinchRotation = angle; + updatePinchTarget(); + } break; +#endif // QT_NO_GESTURES + default: + return QQuickItem::event(event); + } + } break; + case QEvent::Wheel: + event->ignore(); + return false; + default: + return QQuickItem::event(event); + } -void QQuickPinchArea::setTouchEventsEnabled(bool enable) -{ - // Resolve function for enabling touch events from the (cocoa) platform plugin. - typedef void (*RegisterTouchWindowFunction)(QWindow *, bool); - RegisterTouchWindowFunction registerTouchWindow = reinterpret_cast<RegisterTouchWindowFunction>( - QGuiApplication::platformNativeInterface()->nativeResourceFunctionForIntegration("registertouchwindow")); - if (!registerTouchWindow) - return; // Not necessarily an error, Qt might be using a different platform plugin. - - registerTouchWindow(window(), enable); + return true; } -#endif // Q_OS_OSX QQuickPinch *QQuickPinchArea::pinch() { diff --git a/src/quick/items/qquickpincharea_p.h b/src/quick/items/qquickpincharea_p.h index 28db59fb55..21282237b7 100644 --- a/src/quick/items/qquickpincharea_p.h +++ b/src/quick/items/qquickpincharea_p.h @@ -274,15 +274,12 @@ protected: void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE; void itemChange(ItemChange change, const ItemChangeData& value) Q_DECL_OVERRIDE; -#ifdef Q_OS_OSX - void hoverEnterEvent(QHoverEvent *event) Q_DECL_OVERRIDE; - void hoverLeaveEvent(QHoverEvent *event) Q_DECL_OVERRIDE; - void setTouchEventsEnabled(bool enable); -#endif + bool event(QEvent *) Q_DECL_OVERRIDE; private: void clearPinch(); void updatePinch(); + void updatePinchTarget(); void handlePress(); void handleRelease(); diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index f58bd489fe..a387046d46 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1394,6 +1394,9 @@ bool QQuickWindow::event(QEvent *e) d->windowManager->handleUpdateRequest(this); break; } + case QEvent::NativeGesture: + d->deliverGestureEvent(d->contentItem, static_cast<QNativeGestureEvent*>(e)); + break; default: break; } @@ -1751,6 +1754,38 @@ bool QQuickWindowPrivate::deliverWheelEvent(QQuickItem *item, QWheelEvent *event return false; } +bool QQuickWindowPrivate::deliverGestureEvent(QQuickItem *item, QNativeGestureEvent *event) +{ + QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); + + if ((itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) && !item->contains(event->localPos())) + return false; + + QList<QQuickItem *> children = itemPrivate->paintOrderChildItems(); + for (int ii = children.count() - 1; ii >= 0; --ii) { + QQuickItem *child = children.at(ii); + if (!child->isVisible() || !child->isEnabled() || QQuickItemPrivate::get(child)->culled) + continue; + if (deliverGestureEvent(child, event)) + return true; + } + + QPointF p = item->mapFromScene(event->localPos()); + + if (item->contains(p)) { + QNativeGestureEvent copy(event->gestureType(), p, event->windowPos(), event->screenPos(), + event->value(), 0L, 0L); // TODO can't copy things I can't access + event->accept(); + item->event(©); + if (copy.isAccepted()) { + event->accept(); + return true; + } + } + + return false; +} + /*! \reimp */ void QQuickWindow::wheelEvent(QWheelEvent *event) { diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 4f6149117a..2bfc928e0d 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -141,6 +141,7 @@ public: #ifndef QT_NO_WHEELEVENT bool deliverWheelEvent(QQuickItem *, QWheelEvent *); #endif + bool deliverGestureEvent(QQuickItem *, QNativeGestureEvent *); bool deliverTouchPoints(QQuickItem *, QTouchEvent *, const QList<QTouchEvent::TouchPoint> &, QSet<int> *, QHash<QQuickItem *, QList<QTouchEvent::TouchPoint> > *, QSet<QQuickItem*> *filtered); void deliverTouchEvent(QTouchEvent *); |