From 1d445eb5b1a14da342382a088f4e1cc5492ea32e Mon Sep 17 00:00:00 2001 From: Michael Brasser Date: Tue, 23 Aug 2011 13:30:38 +1000 Subject: Add MultiPointTouchArea element. Change-Id: I3a4f774cd96ab7f5d08e85c965f59e1416f02e0e Reviewed-by: Martin Jones --- src/declarative/items/items.pri | 2 + src/declarative/items/qquickcanvas.cpp | 17 +- src/declarative/items/qquickcanvas_p.h | 2 +- src/declarative/items/qquickitem.cpp | 95 ++- src/declarative/items/qquickitem.h | 6 + src/declarative/items/qquickitem_p.h | 2 +- src/declarative/items/qquickitemsmodule.cpp | 5 + .../items/qquickmultipointtoucharea.cpp | 729 +++++++++++++++++++++ .../items/qquickmultipointtoucharea_p.h | 267 ++++++++ 9 files changed, 1114 insertions(+), 11 deletions(-) create mode 100644 src/declarative/items/qquickmultipointtoucharea.cpp create mode 100644 src/declarative/items/qquickmultipointtoucharea_p.h (limited to 'src') diff --git a/src/declarative/items/items.pri b/src/declarative/items/items.pri index c3d6a2ab73..f83c65c203 100644 --- a/src/declarative/items/items.pri +++ b/src/declarative/items/items.pri @@ -64,6 +64,7 @@ HEADERS += \ $$PWD/qquickspriteimage_p.h \ $$PWD/qquickdrag_p.h \ $$PWD/qquickdroparea_p.h \ + $$PWD/qquickmultipointtoucharea_p.h \ $$PWD/qquickitemview_p.h \ $$PWD/qquickitemview_p_p.h @@ -110,6 +111,7 @@ SOURCES += \ $$PWD/qquickspriteimage.cpp \ $$PWD/qquickdrag.cpp \ $$PWD/qquickdroparea.cpp \ + $$PWD/qquickmultipointtoucharea.cpp \ $$PWD/qquickitemview.cpp SOURCES += \ diff --git a/src/declarative/items/qquickcanvas.cpp b/src/declarative/items/qquickcanvas.cpp index 52ef421213..6df0c65c94 100644 --- a/src/declarative/items/qquickcanvas.cpp +++ b/src/declarative/items/qquickcanvas.cpp @@ -1496,7 +1496,7 @@ bool QQuickCanvasPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQuickIte return accepted; } -bool QQuickCanvasPrivate::sendFilteredMouseEvent(QQuickItem *target, QQuickItem *item, QMouseEvent *event) +bool QQuickCanvasPrivate::sendFilteredMouseEvent(QQuickItem *target, QQuickItem *item, QEvent *event) { if (!target) return false; @@ -1550,12 +1550,9 @@ bool QQuickCanvas::sendEvent(QQuickItem *item, QEvent *e) case QEvent::MouseButtonDblClick: case QEvent::MouseMove: // XXX todo - should sendEvent be doing this? how does it relate to forwarded events? - { - QMouseEvent *se = static_cast(e); - if (!d->sendFilteredMouseEvent(item->parentItem(), item, se)) { - se->accept(); - QQuickItemPrivate::get(item)->deliverMouseEvent(se); - } + if (!d->sendFilteredMouseEvent(item->parentItem(), item, e)) { + e->accept(); + QQuickItemPrivate::get(item)->deliverMouseEvent(static_cast(e)); } break; case QEvent::Wheel: @@ -1569,7 +1566,11 @@ bool QQuickCanvas::sendEvent(QQuickItem *item, QEvent *e) case QEvent::TouchBegin: case QEvent::TouchUpdate: case QEvent::TouchEnd: - QQuickItemPrivate::get(item)->deliverTouchEvent(static_cast(e)); + // XXX todo - should sendEvent be doing this? how does it relate to forwarded events? + if (!d->sendFilteredMouseEvent(item->parentItem(), item, e)) { + e->accept(); + QQuickItemPrivate::get(item)->deliverTouchEvent(static_cast(e)); + } break; case QEvent::DragEnter: case QEvent::DragMove: diff --git a/src/declarative/items/qquickcanvas_p.h b/src/declarative/items/qquickcanvas_p.h index 5c68442f29..610ebe7912 100644 --- a/src/declarative/items/qquickcanvas_p.h +++ b/src/declarative/items/qquickcanvas_p.h @@ -110,7 +110,7 @@ public: static void transformTouchPoints(QList &touchPoints, const QTransform &transform); bool deliverInitialMousePressEvent(QQuickItem *, QMouseEvent *); bool deliverMouseEvent(QMouseEvent *); - bool sendFilteredMouseEvent(QQuickItem *, QQuickItem *, QMouseEvent *); + bool sendFilteredMouseEvent(QQuickItem *, QQuickItem *, QEvent *); bool deliverWheelEvent(QQuickItem *, QWheelEvent *); bool deliverTouchPoints(QQuickItem *, QTouchEvent *, const QList &, QSet *, QHash > *); diff --git a/src/declarative/items/qquickitem.cpp b/src/declarative/items/qquickitem.cpp index b6951d9a77..f50a4f1a91 100644 --- a/src/declarative/items/qquickitem.cpp +++ b/src/declarative/items/qquickitem.cpp @@ -2185,7 +2185,7 @@ QQuickItemPrivate::QQuickItemPrivate() : _anchors(0), _contents(0), baselineOffset(0), _anchorLines(0), _stateGroup(0), origin(QQuickItem::Center), flags(0), widthValid(false), heightValid(false), componentComplete(true), - keepMouse(false), hoverEnabled(false), smooth(false), focus(false), activeFocus(false), notifiedFocus(false), + keepMouse(false), keepTouch(false), hoverEnabled(false), smooth(false), focus(false), activeFocus(false), notifiedFocus(false), notifiedActiveFocus(false), filtersChildMouseEvents(false), explicitVisible(true), effectiveVisible(true), explicitEnable(true), effectiveEnable(true), polishScheduled(false), inheritedLayoutMirror(false), effectiveLayoutMirror(false), isMirrorImplicit(true), @@ -2856,6 +2856,11 @@ void QQuickItem::mouseUngrabEvent() // XXX todo } +void QQuickItem::touchUngrabEvent() +{ + // XXX todo +} + void QQuickItem::wheelEvent(QWheelEvent *event) { event->ignore(); @@ -4528,6 +4533,94 @@ void QQuickItem::setKeepMouseGrab(bool keep) d->keepMouse = keep; } +/*! + Grabs the touch points specified by \a ids. + + These touch points will be owned by the item until + they are released. Alternatively, the grab can be stolen + by a filtering item like Flickable. Use setKeepTouchGrab() + to prevent the grab from being stolen. + + \sa ungrabTouchPoints(), setKeepTouchGrab() +*/ +void QQuickItem::grabTouchPoints(const QList &ids) +{ + Q_D(QQuickItem); + if (!d->canvas) + return; + QQuickCanvasPrivate *canvasPriv = QQuickCanvasPrivate::get(d->canvas); + + QSet ungrab; + for (int i = 0; i < ids.count(); ++i) { + QQuickItem *oldGrabber = canvasPriv->itemForTouchPointId.value(ids.at(i)); + if (oldGrabber == this) + return; + + canvasPriv->itemForTouchPointId[ids.at(i)] = this; + if (oldGrabber) + ungrab.insert(oldGrabber); + } + foreach (QQuickItem *oldGrabber, ungrab) + oldGrabber->touchUngrabEvent(); +} + +/*! + Ungrabs the touch points owned by this item. + + \sa grabTouchPoints() +*/ +void QQuickItem::ungrabTouchPoints() +{ + Q_D(QQuickItem); + if (!d->canvas) + return; + QQuickCanvasPrivate *canvasPriv = QQuickCanvasPrivate::get(d->canvas); + + QMutableHashIterator i(canvasPriv->itemForTouchPointId); + while (i.hasNext()) { + i.next(); + if (i.value() == this) + i.remove(); + } + touchUngrabEvent(); +} + +/*! + Returns a value indicating whether the touch points grabbed by this item + should remain with this item exclusively. + + \sa setKeepTouchGrab(), keepMouseGrab() +*/ +bool QQuickItem::keepTouchGrab() const +{ + Q_D(const QQuickItem); + return d->keepTouch; +} + +/*! + The flag indicating whether the touch points grabbed + by this item should remain with this item is set to \a keep. + + This is useful for items that wish to grab and keep specific touch + points following a predefined gesture. For example, + an item that is interested in horizontal touch point movement + may set setKeepTouchGrab to true once a threshold has been + exceeded. Once setKeepTouchGrab has been set to true, filtering + items will not react to the relevant touch points. + + If the item does not indicate that it wishes to retain touch point grab, + a filtering item may steal the grab. For example, Flickable may attempt + to steal a touch point grab if it detects that the user has begun to + move the viewport. + + \sa keepTouchGrab(), setKeepMouseGrab() + */ +void QQuickItem::setKeepTouchGrab(bool keep) +{ + Q_D(QQuickItem); + d->keepTouch = keep; +} + /*! \qmlmethod object QtQuick2::Item::mapFromItem(Item item, real x, real y) diff --git a/src/declarative/items/qquickitem.h b/src/declarative/items/qquickitem.h index be50677bf1..0cbcfb890d 100644 --- a/src/declarative/items/qquickitem.h +++ b/src/declarative/items/qquickitem.h @@ -282,6 +282,11 @@ public: bool filtersChildMouseEvents() const; void setFiltersChildMouseEvents(bool filter); + void grabTouchPoints(const QList &ids); + void ungrabTouchPoints(); + bool keepTouchGrab() const; + void setKeepTouchGrab(bool); + QTransform itemTransform(QQuickItem *, bool *) const; QPointF mapToItem(const QQuickItem *item, const QPointF &point) const; QPointF mapToScene(const QPointF &point) const; @@ -368,6 +373,7 @@ protected: virtual void mouseReleaseEvent(QMouseEvent *event); virtual void mouseDoubleClickEvent(QMouseEvent *event); virtual void mouseUngrabEvent(); // XXX todo - params? + virtual void touchUngrabEvent(); virtual void wheelEvent(QWheelEvent *event); virtual void touchEvent(QTouchEvent *event); virtual void hoverEnterEvent(QHoverEvent *event); diff --git a/src/declarative/items/qquickitem_p.h b/src/declarative/items/qquickitem_p.h index 94b195f2d8..47f86c48aa 100644 --- a/src/declarative/items/qquickitem_p.h +++ b/src/declarative/items/qquickitem_p.h @@ -237,6 +237,7 @@ public: bool heightValid:1; bool componentComplete:1; bool keepMouse:1; + bool keepTouch:1; bool hoverEnabled:1; bool smooth:1; bool focus:1; @@ -255,7 +256,6 @@ public: bool inheritMirrorFromParent:1; bool inheritMirrorFromItem:1; bool childrenDoNotOverlap:1; - quint32 dummy:1; QQuickCanvas *canvas; QSGContext *sceneGraphContext() const { Q_ASSERT(canvas); return static_cast(QObjectPrivate::get(canvas))->context; } diff --git a/src/declarative/items/qquickitemsmodule.cpp b/src/declarative/items/qquickitemsmodule.cpp index 38b5a91e56..0a04e884ae 100644 --- a/src/declarative/items/qquickitemsmodule.cpp +++ b/src/declarative/items/qquickitemsmodule.cpp @@ -80,6 +80,7 @@ #include "qquickspriteimage_p.h" #include "qquickdrag_p.h" #include "qquickdroparea_p.h" +#include "qquickmultipointtoucharea_p.h" static QDeclarativePrivate::AutoParentResult qquickitem_autoParent(QObject *obj, QObject *parent) { @@ -201,6 +202,10 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor) qmlRegisterType(); qmlRegisterType(); qmlRegisterUncreatableType("QtQuick", 2, 0, "Drag", QQuickDragAttached::tr("Drag is only available via attached properties")); + + qmlRegisterType("QtQuick", 2, 0, "MultiPointTouchArea"); + qmlRegisterType("QtQuick", 2, 0, "TouchPoint"); + qmlRegisterType(); } void QQuickItemsModule::defineModule() diff --git a/src/declarative/items/qquickmultipointtoucharea.cpp b/src/declarative/items/qquickmultipointtoucharea.cpp new file mode 100644 index 0000000000..f7fdf97fb7 --- /dev/null +++ b/src/declarative/items/qquickmultipointtoucharea.cpp @@ -0,0 +1,729 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickmultipointtoucharea_p.h" +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmlclass TouchPoint QQuickTouchPoint + \inqmlmodule QtQuick 2 + \ingroup qml-event-elements + \brief The TouchPoint element describes a touch point in a MultiPointTouchArea. + + The TouchPoint element contains information about a touch point, such as the current + position, pressure, and area. +*/ + +/*! + \qmlproperty int QtQuick2::TouchPoint::pointId + + This property holds the point id of the touch point. + + Each touch point within a MultiPointTouchArea will have a unique id. +*/ +void QQuickTouchPoint::setPointId(int id) +{ + if (_id == id) + return; + _id = id; + emit pointIdChanged(); +} + +/*! + \qmlproperty real QtQuick2::TouchPoint::x + \qmlproperty real QtQuick2::TouchPoint::y + + These properties hold the current position of the touch point. +*/ + +void QQuickTouchPoint::setX(qreal x) +{ + if (_x == x) + return; + _x = x; + emit xChanged(); +} + +void QQuickTouchPoint::setY(qreal y) +{ + if (_y == y) + return; + _y = y; + emit yChanged(); +} + +/*! + \qmlproperty real QtQuick2::TouchPoint::pressure + \qmlproperty rectangle QtQuick2::TouchPoint::area + + These properties hold additional information about the current state of the touch point. + + \list + \i \c pressure is a value in the range of 0.0 to 1.0. + \i \c area is a rectangle covering the area of the touch point, centered on the current position of the touch point. + \endlist +*/ +void QQuickTouchPoint::setPressure(qreal pressure) +{ + if (_pressure == pressure) + return; + _pressure = pressure; + emit pressureChanged(); +} + +void QQuickTouchPoint::setArea(const QRectF &area) +{ + if (_area == area) + return; + _area = area; + emit areaChanged(); +} + +/*! + \qmlproperty bool QtQuick2::TouchPoint::valid + + This property holds whether the touch point is valid. + + An invalid touch point is one that has not yet been pressed, + or has already been released. +*/ +void QQuickTouchPoint::setValid(bool valid) +{ + if (_valid == valid) + return; + _valid = valid; + emit validityChanged(); +} + +/*! + \qmlproperty real QtQuick2::TouchPoint::startX + \qmlproperty real QtQuick2::TouchPoint::startY + + These properties hold the starting position of the touch point. +*/ + +void QQuickTouchPoint::setStartX(qreal startX) +{ + if (_startX == startX) + return; + _startX = startX; + emit startXChanged(); +} + +void QQuickTouchPoint::setStartY(qreal startY) +{ + if (_startY == startY) + return; + _startY = startY; + emit startYChanged(); +} + +/*! + \qmlproperty real QtQuick2::TouchPoint::previousX + \qmlproperty real QtQuick2::TouchPoint::previousY + + These properties hold the previous position of the touch point. +*/ +void QQuickTouchPoint::setPreviousX(qreal previousX) +{ + if (_previousX == previousX) + return; + _previousX = previousX; + emit previousXChanged(); +} + +void QQuickTouchPoint::setPreviousY(qreal previousY) +{ + if (_previousY == previousY) + return; + _previousY = previousY; + emit previousYChanged(); +} + +/*! + \qmlproperty real QtQuick2::TouchPoint::sceneX + \qmlproperty real QtQuick2::TouchPoint::sceneY + + These properties hold the current position of the touch point in scene coordinates. +*/ + +void QQuickTouchPoint::setSceneX(qreal sceneX) +{ + if (_sceneX == sceneX) + return; + _sceneX = sceneX; + emit sceneXChanged(); +} + +void QQuickTouchPoint::setSceneY(qreal sceneY) +{ + if (_sceneY == sceneY) + return; + _sceneY = sceneY; + emit sceneYChanged(); +} + +/*! + \qmlclass MultiPointTouchArea QQuickMultiPointTouchArea + \inqmlmodule QtQuick 2 + \brief The MultiPointTouchArea item enables handling of multiple touch points. + \inherits Item + + A MultiPointTouchArea is an invisible item that is used to track multiple touch points. + + The \l enabled property is used to enable and disable touch handling. When disabled, + the touch area becomes transparent to mouse/touch events. + + MultiPointTouchArea can be used in two ways: + + \list + \o setting \c touchPoints to provide touch point objects with properties that can be bound to + \o using the onTouchUpdated or onTouchPointsPressed, onTouchPointsUpdated and onTouchPointsReleased handlers + \endlist + + While a MultiPointTouchArea \i can take exclusive ownership of certain touch points, it is also possible to have + multiple MultiPointTouchAreas active at the same time, each operating on a different set of touch points. + + \sa TouchPoint +*/ + +/*! + \qmlsignal QtQuick2::MultiPointTouchArea::touchPointsPressed(list touchPoints) + + This handler is called when new touch points are added. \a touchPoints is a list of these new points. + + If minimumTouchPoints is set to a value greater than one, this handler will not be called until the minimum number + of required touch points has been reached. At that point, touchPointsPressed will be called with all the current touch points. +*/ + +/*! + \qmlsignal QtQuick2::MultiPointTouchArea::touchPointsUpdated(list touchPoints) + + This handler is called when existing touch points are updated. \a touchPoints is a list of these updated points. +*/ + +/*! + \qmlsignal QtQuick2::MultiPointTouchArea::touchPointsReleased(list touchPoints) + + This handler is called when existing touch points are removed. \a touchPoints is a list of these removed points. +*/ + +/*! + \qmlsignal QtQuick2::MultiPointTouchArea::touchPointsCanceled(list touchPoints) + + This handler is called when new touch events have been canceled because another element stole the touch event handling. + + This signal is for advanced use: it is useful when there is more than one MultiPointTouchArea + that is handling input, or when there is a MultiPointTouchArea inside a \l Flickable. In the latter + case, if you execute some logic on the touchPointsPressed signal and then start dragging, the + \l Flickable may steal the touch handling from the MultiPointTouchArea. In these cases, to reset + the logic when the MultiPointTouchArea has lost the touch handling to the \l Flickable, + \c onTouchPointsCanceled should be used in addition to onTouchPointsReleased. + + \a touchPoints is the list of canceled points. +*/ + +/*! + \qmlsignal QtQuick2::MultiPointTouchArea::gestureStarted(GestureEvent gesture) + + This handler is called when the global drag threshold has been reached. + + This function is typically used when a MultiPointTouchAreas has been nested in a Flickable or another MultiPointTouchArea. + Wnen the threshold has been reached, and the handler called, you can determine whether or not the touch + area should grab the current touch points. By default they will not be grabbed; to grab them call \c gesture.grab(). If the + gesture is not grabbed, the nesting Flickable, for example, would also have an opportunity to grab. + + The gesture object also includes information on the current set of \c touchPoints and the \c dragThreshold. +*/ + +/*! + \qmlsignal QtQuick2::MultiPointTouchArea::touchUpdated(list touchPoints) + + This handler is called when the touch points handled by the MultiPointTouchArea change. This includes adding new touch points, + removing previous touch points, as well as updating current touch point data. \a touchPoints is the list of all current touch + points. +*/ + +/*! + \qmlproperty list QtQuick2::MultiPointTouchArea::touchPoints + + This property holds a set of user-defined touch point objects that can be bound to. + + In the following example, we have two small rectangles that follow our touch points. + + \snippet doc/src/snippets/declarative/multipointtoucharea/multipointtoucharea.qml 0 + + By default this property holds an empty list. + + \sa TouchPoint +*/ + +QQuickMultiPointTouchArea::QQuickMultiPointTouchArea(QQuickItem *parent) + : QQuickItem(parent), + _minimumTouchPoints(0), + _maximumTouchPoints(INT_MAX), + _stealMouse(false) +{ + setAcceptedMouseButtons(Qt::LeftButton); + setFiltersChildMouseEvents(true); +} + +QQuickMultiPointTouchArea::~QQuickMultiPointTouchArea() +{ + clearTouchLists(); + foreach (QObject *obj, _touchPoints) { + QQuickTouchPoint *dtp = static_cast(obj); + if (!dtp->isQmlDefined()) + delete dtp; + } +} + +/*! + \qmlproperty int QtQuick2::MultiPointTouchArea::minimumTouchPoints + \qmlproperty int QtQuick2::MultiPointTouchArea::maximumTouchPoints + + These properties hold the range of touch points to be handled by the touch area. + + These are convenience that allow you to, for example, have nested MultiPointTouchAreas, + one handling two finger touches, and another handling three finger touches. + + By default, all touch points within the touch area are handled. +*/ + +int QQuickMultiPointTouchArea::minimumTouchPoints() const +{ + return _minimumTouchPoints; +} + +void QQuickMultiPointTouchArea::setMinimumTouchPoints(int num) +{ + if (_minimumTouchPoints == num) + return; + _minimumTouchPoints = num; + emit minimumTouchPointsChanged(); +} + +int QQuickMultiPointTouchArea::maximumTouchPoints() const +{ + return _maximumTouchPoints; +} + +void QQuickMultiPointTouchArea::setMaximumTouchPoints(int num) +{ + if (_maximumTouchPoints == num) + return; + _maximumTouchPoints = num; + emit maximumTouchPointsChanged(); +} + +void QQuickMultiPointTouchArea::touchEvent(QTouchEvent *event) +{ + switch (event->type()) { + case QEvent::TouchBegin: + case QEvent::TouchUpdate: + case QEvent::TouchEnd: { + //if e.g. a parent Flickable has the mouse grab, don't process the touch events + QQuickCanvas *c = canvas(); + QQuickItem *grabber = c ? c->mouseGrabberItem() : 0; + if (grabber && grabber != this && grabber->keepMouseGrab() && grabber->isEnabled()) { + QQuickItem *item = this; + while ((item = item->parentItem())) { + if (item == grabber) + return; + } + } + updateTouchData(event); + if (event->type() == QEvent::TouchEnd) { + //TODO: move to canvas + _stealMouse = false; + setKeepMouseGrab(false); + QQuickCanvas *c = canvas(); + if (c && c->mouseGrabberItem() == this) + ungrabMouse(); + setKeepTouchGrab(false); + ungrabTouchPoints(); + } + break; + } + default: + QQuickItem::touchEvent(event); + break; + } +} + +void QQuickMultiPointTouchArea::grabGesture() +{ + _stealMouse = true; + + grabMouse(); + setKeepMouseGrab(true); + + grabTouchPoints(_touchPoints.keys()); + setKeepTouchGrab(true); +} + +void QQuickMultiPointTouchArea::updateTouchData(QEvent *event) +{ + bool ended = false; + bool moved = false; + bool started = false; + + clearTouchLists(); + QTouchEvent *e = static_cast(event); + QList touchPoints = e->touchPoints(); + int numTouchPoints = touchPoints.count(); + //always remove released touches, and make sure we handle all releases before adds. + foreach (QTouchEvent::TouchPoint p, touchPoints) { + Qt::TouchPointState touchPointState = p.state(); + int id = p.id(); + if (touchPointState & Qt::TouchPointReleased) { + QQuickTouchPoint* dtp = static_cast(_touchPoints.value(id)); + if (!dtp) + continue; + _releasedTouchPoints.append(dtp); + _touchPoints.remove(id); + ended = true; + } + } + if (numTouchPoints >= _minimumTouchPoints && numTouchPoints <= _maximumTouchPoints) { + foreach (QTouchEvent::TouchPoint p, touchPoints) { + Qt::TouchPointState touchPointState = p.state(); + int id = p.id(); + if (touchPointState & Qt::TouchPointReleased) { + //handled above + } else if (!_touchPoints.contains(id)) { //could be pressed, moved, or stationary + addTouchPoint(&p); + started = true; + } else if (touchPointState & Qt::TouchPointMoved) { + QQuickTouchPoint* dtp = static_cast(_touchPoints[id]); + Q_ASSERT(dtp); + _movedTouchPoints.append(dtp); + updateTouchPoint(dtp,&p); + moved = true; + } else { + QQuickTouchPoint* dtp = static_cast(_touchPoints[id]); + Q_ASSERT(dtp); + updateTouchPoint(dtp,&p); + } + } + + //see if we should be grabbing the gesture + if (!_stealMouse /* !ignoring gesture*/) { + bool offerGrab = false; + const int dragThreshold = qApp->styleHints()->startDragDistance(); + foreach (const QTouchEvent::TouchPoint &p, touchPoints) { + if (p.state() == Qt::TouchPointReleased) + continue; + const QPointF ¤tPos = p.scenePos(); + const QPointF &startPos = p.startScenePos(); + if (qAbs(currentPos.x() - startPos.x()) > dragThreshold) + offerGrab = true; + else if (qAbs(currentPos.y() - startPos.y()) > dragThreshold) + offerGrab = true; + if (offerGrab) + break; + } + + if (offerGrab) { + QQuickGrabGestureEvent event; + event._touchPoints = _touchPoints.values(); + emit gestureStarted(&event); + if (event.wantsGrab()) + grabGesture(); + } + } + + if (ended) emit(touchPointsReleased(_releasedTouchPoints)); + if (moved) emit(touchPointsUpdated(_movedTouchPoints)); + if (started) emit(touchPointsPressed(_pressedTouchPoints)); + if (!_touchPoints.isEmpty()) emit touchUpdated(_touchPoints.values()); + } +} + +void QQuickMultiPointTouchArea::clearTouchLists() +{ + foreach (QObject *obj, _releasedTouchPoints) { + QQuickTouchPoint *dtp = static_cast(obj); + if (!dtp->isQmlDefined()) + delete dtp; + else + dtp->setValid(false); + } + _releasedTouchPoints.clear(); + _pressedTouchPoints.clear(); + _movedTouchPoints.clear(); +} + +void QQuickMultiPointTouchArea::addTouchPoint(const QTouchEvent::TouchPoint *p) +{ + QQuickTouchPoint *dtp = 0; + foreach (QQuickTouchPoint* tp, _touchPrototypes) { + if (!tp->isValid()) { + tp->setValid(true); + dtp = tp; + break; + } + } + + if (dtp == 0) + dtp = new QQuickTouchPoint(false); + dtp->setPointId(p->id()); + updateTouchPoint(dtp,p); + _touchPoints.insert(p->id(),dtp); + //we may have just obtained enough points to start tracking them -- in that case moved or stationary count as newly pressed + if (p->state() & Qt::TouchPointPressed || p->state() & Qt::TouchPointMoved || p->state() & Qt::TouchPointStationary) + _pressedTouchPoints.append(dtp); +} + +void QQuickMultiPointTouchArea::addTouchPrototype(QQuickTouchPoint *prototype) +{ + int id = _touchPrototypes.count(); + prototype->setPointId(id); + _touchPrototypes.insert(id, prototype); +} + +void QQuickMultiPointTouchArea::updateTouchPoint(QQuickTouchPoint *dtp, const QTouchEvent::TouchPoint *p) +{ + //TODO: if !qmlDefined, could bypass setters. + // also, should only emit signals after all values have been set + dtp->setX(p->pos().x()); + dtp->setY(p->pos().y()); + dtp->setPressure(p->pressure()); + dtp->setArea(p->rect()); + dtp->setStartX(p->startPos().x()); + dtp->setStartY(p->startPos().y()); + dtp->setPreviousX(p->lastPos().x()); + dtp->setPreviousY(p->lastPos().y()); + dtp->setSceneX(p->scenePos().x()); + dtp->setSceneY(p->scenePos().y()); +} + +void QQuickMultiPointTouchArea::mousePressEvent(QMouseEvent *event) +{ + if (!isEnabled()) { + QQuickItem::mousePressEvent(event); + return; + } + + _stealMouse = false; + setKeepMouseGrab(false); + event->setAccepted(true); +} + +void QQuickMultiPointTouchArea::mouseMoveEvent(QMouseEvent *event) +{ + if (!isEnabled()) { + QQuickItem::mouseMoveEvent(event); + return; + } + + //do nothing +} + +void QQuickMultiPointTouchArea::mouseReleaseEvent(QMouseEvent *event) +{ + _stealMouse = false; + if (!isEnabled()) { + QQuickItem::mouseReleaseEvent(event); + return; + } + QQuickCanvas *c = canvas(); + if (c && c->mouseGrabberItem() == this) + ungrabMouse(); + setKeepMouseGrab(false); +} + +void QQuickMultiPointTouchArea::ungrab() +{ + if (_touchPoints.count()) { + QQuickCanvas *c = canvas(); + if (c && c->mouseGrabberItem() == this) { + _stealMouse = false; + setKeepMouseGrab(false); + } + setKeepTouchGrab(false); + emit touchPointsCanceled(_touchPoints.values()); + clearTouchLists(); + foreach (QObject *obj, _touchPoints) { + QQuickTouchPoint *dtp = static_cast(obj); + if (!dtp->isQmlDefined()) + delete dtp; + else + dtp->setValid(false); + } + _touchPoints.clear(); + } +} + +void QQuickMultiPointTouchArea::mouseUngrabEvent() +{ + ungrab(); +} + +void QQuickMultiPointTouchArea::touchUngrabEvent() +{ + ungrab(); +} + +bool QQuickMultiPointTouchArea::sendMouseEvent(QMouseEvent *event) +{ + QRectF myRect = mapRectToScene(QRectF(0, 0, width(), height())); + + QQuickCanvas *c = canvas(); + QQuickItem *grabber = c ? c->mouseGrabberItem() : 0; + bool stealThisEvent = _stealMouse; + if ((stealThisEvent || myRect.contains(event->windowPos())) && (!grabber || !grabber->keepMouseGrab())) { + QMouseEvent mouseEvent(event->type(), mapFromScene(event->windowPos()), event->windowPos(), event->screenPos(), + event->button(), event->buttons(), event->modifiers()); + mouseEvent.setAccepted(false); + + switch (mouseEvent.type()) { + case QEvent::MouseMove: + mouseMoveEvent(&mouseEvent); + break; + case QEvent::MouseButtonPress: + mousePressEvent(&mouseEvent); + break; + case QEvent::MouseButtonRelease: + mouseReleaseEvent(&mouseEvent); + break; + default: + break; + } + grabber = c->mouseGrabberItem(); + if (grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this) + grabMouse(); + + return stealThisEvent; + } + if (event->type() == QEvent::MouseButtonRelease) { + _stealMouse = false; + if (c && c->mouseGrabberItem() == this) + ungrabMouse(); + setKeepMouseGrab(false); + } + return false; +} + +bool QQuickMultiPointTouchArea::childMouseEventFilter(QQuickItem *i, QEvent *event) +{ + if (!isEnabled() || !isVisible()) + return QQuickItem::childMouseEventFilter(i, event); + switch (event->type()) { + case QEvent::MouseButtonPress: + case QEvent::MouseMove: + case QEvent::MouseButtonRelease: + return sendMouseEvent(static_cast(event)); + break; + case QEvent::TouchBegin: + case QEvent::TouchUpdate: + if (!shouldFilter(event)) + return false; + updateTouchData(event); + return _stealMouse; + case QEvent::TouchEnd: { + if (!shouldFilter(event)) + return false; + updateTouchData(event); + //TODO: verify this behavior + _stealMouse = false; + setKeepMouseGrab(false); + QQuickCanvas *c = canvas(); + if (c && c->mouseGrabberItem() == this) + ungrabMouse(); + setKeepTouchGrab(false); + ungrabTouchPoints(); + } + break; + default: + break; + } + return QQuickItem::childMouseEventFilter(i, event); +} + +bool QQuickMultiPointTouchArea::shouldFilter(QEvent *event) +{ + QQuickCanvas *c = canvas(); + QQuickItem *grabber = c ? c->mouseGrabberItem() : 0; + bool disabledItem = grabber && !grabber->isEnabled(); + bool stealThisEvent = _stealMouse; + bool contains = false; + if (!stealThisEvent) { + switch (event->type()) { + case QEvent::MouseButtonPress: + case QEvent::MouseMove: + case QEvent::MouseButtonRelease: { + QMouseEvent *me = static_cast(event); + QRectF myRect = mapRectToScene(QRectF(0, 0, width(), height())); + contains = myRect.contains(me->windowPos()); + } + break; + case QEvent::TouchBegin: + case QEvent::TouchUpdate: + case QEvent::TouchEnd: { + QTouchEvent *te = static_cast(event); + QRectF myRect = mapRectToScene(QRectF(0, 0, width(), height())); + foreach (const QTouchEvent::TouchPoint &point, te->touchPoints()) { + if (myRect.contains(point.scenePos())) { + contains = true; + break; + } + } + } + break; + default: + break; + } + } + if ((stealThisEvent || contains) && (!grabber || !grabber->keepMouseGrab() || disabledItem)) { + return true; + } + ungrab(); + return false; +} + +QT_END_NAMESPACE diff --git a/src/declarative/items/qquickmultipointtoucharea_p.h b/src/declarative/items/qquickmultipointtoucharea_p.h new file mode 100644 index 0000000000..dac70453c5 --- /dev/null +++ b/src/declarative/items/qquickmultipointtoucharea_p.h @@ -0,0 +1,267 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKMULTIPOINTTOUCHAREA_H +#define QQUICKMULTIPOINTTOUCHAREA_H + +#include "qquickitem.h" +#include "qevent.h" + +#include +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QQuickMultiPointTouchArea; +class Q_AUTOTEST_EXPORT QQuickTouchPoint : public QObject +{ + Q_OBJECT + Q_PROPERTY(bool valid READ isValid NOTIFY validityChanged) + Q_PROPERTY(int pointId READ pointId NOTIFY pointIdChanged) + Q_PROPERTY(qreal x READ x NOTIFY xChanged) + Q_PROPERTY(qreal y READ y NOTIFY yChanged) + Q_PROPERTY(qreal pressure READ pressure NOTIFY pressureChanged) + Q_PROPERTY(QRectF area READ area NOTIFY areaChanged) + + Q_PROPERTY(qreal startX READ startX NOTIFY startXChanged) + Q_PROPERTY(qreal startY READ startY NOTIFY startYChanged) + Q_PROPERTY(qreal previousX READ previousX NOTIFY previousXChanged) + Q_PROPERTY(qreal previousY READ previousY NOTIFY previousYChanged) + Q_PROPERTY(qreal sceneX READ sceneX NOTIFY sceneXChanged) + Q_PROPERTY(qreal sceneY READ sceneY NOTIFY sceneYChanged) + +public: + QQuickTouchPoint(bool qmlDefined = true) + : _id(0), + _x(0.0), _y(0.0), + _pressure(0.0), + _qmlDefined(qmlDefined), + _valid(!qmlDefined), + _previousX(0.0), _previousY(0.0), + _sceneX(0.0), _sceneY(0.0) + {} + + int pointId() const { return _id; } + void setPointId(int id); + + qreal x() const { return _x; } + void setX(qreal x); + + qreal y() const { return _y; } + void setY(qreal y); + + qreal pressure() const { return _pressure; } + void setPressure(qreal pressure); + + QRectF area() const { return _area; } + void setArea(const QRectF &area); + + bool isQmlDefined() { return _qmlDefined; } + + bool isValid() { return _valid; } + void setValid(bool valid); + + qreal startX() const { return _startX; } + void setStartX(qreal startX); + + qreal startY() const { return _startY; } + void setStartY(qreal startY); + + qreal previousX() const { return _previousX; } + void setPreviousX(qreal previousX); + + qreal previousY() const { return _previousY; } + void setPreviousY(qreal previousY); + + qreal sceneX() const { return _sceneX; } + void setSceneX(qreal sceneX); + + qreal sceneY() const { return _sceneY; } + void setSceneY(qreal sceneY); + + +Q_SIGNALS: + void pointIdChanged(); + void xChanged(); + void yChanged(); + void pressureChanged(); + void areaChanged(); + void validityChanged(); + void startXChanged(); + void startYChanged(); + void previousXChanged(); + void previousYChanged(); + void sceneXChanged(); + void sceneYChanged(); + +private: + friend class QQuickMultiPointTouchArea; + int _id; + qreal _x; + qreal _y; + qreal _pressure; + QRectF _area; + bool _qmlDefined; + bool _valid; + qreal _startX; + qreal _startY; + qreal _previousX; + qreal _previousY; + qreal _sceneX; + qreal _sceneY; +}; + +class QQuickGrabGestureEvent : public QObject +{ + Q_OBJECT + Q_PROPERTY(QDeclarativeListProperty touchPoints READ touchPoints) + Q_PROPERTY(qreal dragThreshold READ dragThreshold) +public: + QQuickGrabGestureEvent() : _grab(false), _dragThreshold(qApp->styleHints()->startDragDistance()) {} + + Q_INVOKABLE void grab() { _grab = true; } + bool wantsGrab() const { return _grab; } + + QDeclarativeListProperty touchPoints() { + return QDeclarativeListProperty(this, _touchPoints); + } + qreal dragThreshold() const { return _dragThreshold; } + +private: + friend class QQuickMultiPointTouchArea; + bool _grab; + qreal _dragThreshold; + QList _touchPoints; +}; + +class Q_AUTOTEST_EXPORT QQuickMultiPointTouchArea : public QQuickItem +{ + Q_OBJECT + + Q_PROPERTY(QDeclarativeListProperty touchPoints READ touchPoints) + Q_PROPERTY(int minimumTouchPoints READ minimumTouchPoints WRITE setMinimumTouchPoints NOTIFY minimumTouchPointsChanged) + Q_PROPERTY(int maximumTouchPoints READ maximumTouchPoints WRITE setMaximumTouchPoints NOTIFY maximumTouchPointsChanged) + +public: + QQuickMultiPointTouchArea(QQuickItem *parent=0); + ~QQuickMultiPointTouchArea(); + + int minimumTouchPoints() const; + void setMinimumTouchPoints(int num); + int maximumTouchPoints() const; + void setMaximumTouchPoints(int num); + + QDeclarativeListProperty touchPoints() { + return QDeclarativeListProperty(this, 0, QQuickMultiPointTouchArea::touchPoint_append, QQuickMultiPointTouchArea::touchPoint_count, QQuickMultiPointTouchArea::touchPoint_at, 0); + } + + static void touchPoint_append(QDeclarativeListProperty *list, QQuickTouchPoint* touch) { + QQuickMultiPointTouchArea *q = static_cast(list->object); + q->addTouchPrototype(touch); + } + + static int touchPoint_count(QDeclarativeListProperty *list) { + QQuickMultiPointTouchArea *q = static_cast(list->object); + return q->_touchPrototypes.count(); + } + + static QQuickTouchPoint* touchPoint_at(QDeclarativeListProperty *list, int index) { + QQuickMultiPointTouchArea *q = static_cast(list->object); + return q->_touchPrototypes[index]; + } + +Q_SIGNALS: + void touchPointsPressed(const QList &touchPoints); + void touchPointsUpdated(const QList &touchPoints); + void touchPointsReleased(const QList &touchPoints); + void touchPointsCanceled(const QList &touchPoints); + void gestureStarted(QQuickGrabGestureEvent *gesture); + void touchUpdated(const QList &touchPoints); + void minimumTouchPointsChanged(); + void maximumTouchPointsChanged(); + +protected: + void touchEvent(QTouchEvent *); + bool childMouseEventFilter(QQuickItem *i, QEvent *event); + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + void mouseUngrabEvent(); + void touchUngrabEvent(); + + void addTouchPrototype(QQuickTouchPoint* prototype); + void addTouchPoint(const QTouchEvent::TouchPoint *p); + void clearTouchLists(); + + void updateTouchPoint(QQuickTouchPoint*, const QTouchEvent::TouchPoint*); + void updateTouchData(QEvent*); + + bool sendMouseEvent(QMouseEvent *event); + bool shouldFilter(QEvent *event); + void grabGesture(); + +private: + void ungrab(); + QMap _touchPrototypes; //TouchPoints defined in QML + QMap _touchPoints; //All current touch points + QList _releasedTouchPoints; + QList _pressedTouchPoints; + QList _movedTouchPoints; + int _minimumTouchPoints; + int _maximumTouchPoints; + bool _stealMouse; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickTouchPoint) +QML_DECLARE_TYPE(QQuickGrabGestureEvent) +QML_DECLARE_TYPE(QQuickMultiPointTouchArea) + +QT_END_HEADER + +#endif // QQUICKMULTIPOINTTOUCHAREA_H -- cgit v1.2.3