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 ++++++++ tests/auto/declarative/declarative.pro | 1 + .../declarative/qquickcanvas/tst_qquickcanvas.cpp | 5 +- .../qquickmultipointtoucharea/data/inFlickable.qml | 25 + .../qquickmultipointtoucharea/data/nested.qml | 27 + .../data/nonOverlapping.qml | 32 + .../qquickmultipointtoucharea/data/properties.qml | 15 + .../qquickmultipointtoucharea/data/signalTest.qml | 25 + .../qquickmultipointtoucharea.pro | 11 + .../tst_qquickmultipointtoucharea.cpp | 586 +++++++++++++++++ 18 files changed, 1839 insertions(+), 13 deletions(-) create mode 100644 src/declarative/items/qquickmultipointtoucharea.cpp create mode 100644 src/declarative/items/qquickmultipointtoucharea_p.h create mode 100644 tests/auto/declarative/qquickmultipointtoucharea/data/inFlickable.qml create mode 100644 tests/auto/declarative/qquickmultipointtoucharea/data/nested.qml create mode 100644 tests/auto/declarative/qquickmultipointtoucharea/data/nonOverlapping.qml create mode 100644 tests/auto/declarative/qquickmultipointtoucharea/data/properties.qml create mode 100644 tests/auto/declarative/qquickmultipointtoucharea/data/signalTest.qml create mode 100644 tests/auto/declarative/qquickmultipointtoucharea/qquickmultipointtoucharea.pro create mode 100644 tests/auto/declarative/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp 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 diff --git a/tests/auto/declarative/declarative.pro b/tests/auto/declarative/declarative.pro index cd4309486f..148c4ad854 100644 --- a/tests/auto/declarative/declarative.pro +++ b/tests/auto/declarative/declarative.pro @@ -77,6 +77,7 @@ QUICKTESTS = \ qquicklistview \ qquickloader \ qquickmousearea \ + qquickmultipointtoucharea \ qquickpathview \ qquickpincharea \ qquickpositioners \ diff --git a/tests/auto/declarative/qquickcanvas/tst_qquickcanvas.cpp b/tests/auto/declarative/qquickcanvas/tst_qquickcanvas.cpp index d2b691d461..640b4b4f40 100644 --- a/tests/auto/declarative/qquickcanvas/tst_qquickcanvas.cpp +++ b/tests/auto/declarative/qquickcanvas/tst_qquickcanvas.cpp @@ -153,8 +153,9 @@ protected: mousePressId = ++mousePressNum; } - bool childMouseEventFilter(QQuickItem *, QEvent *) { - mousePressId = ++mousePressNum; + bool childMouseEventFilter(QQuickItem *, QEvent *event) { + if (event->type() == QEvent::MouseButtonPress) + mousePressId = ++mousePressNum; return false; } diff --git a/tests/auto/declarative/qquickmultipointtoucharea/data/inFlickable.qml b/tests/auto/declarative/qquickmultipointtoucharea/data/inFlickable.qml new file mode 100644 index 0000000000..53a2bf87f9 --- /dev/null +++ b/tests/auto/declarative/qquickmultipointtoucharea/data/inFlickable.qml @@ -0,0 +1,25 @@ +import QtQuick 2.0 + +Flickable { + width: 240 + height: 320 + + contentWidth: width + contentHeight: height * 2 + + MultiPointTouchArea { + anchors.fill: parent + minimumTouchPoints: 2 + maximumTouchPoints: 2 + onGestureStarted: { + if ((Math.abs(point2.x - point2.startX) > gesture.dragThreshold/2) && (Math.abs(point1.x - point1.startX) > gesture.dragThreshold/2)) { + gesture.grab() + } + } + touchPoints: [ + TouchPoint { id: point1; objectName: "point1" }, + TouchPoint { id: point2; objectName: "point2" } + ] + } +} + diff --git a/tests/auto/declarative/qquickmultipointtoucharea/data/nested.qml b/tests/auto/declarative/qquickmultipointtoucharea/data/nested.qml new file mode 100644 index 0000000000..37b8820aa0 --- /dev/null +++ b/tests/auto/declarative/qquickmultipointtoucharea/data/nested.qml @@ -0,0 +1,27 @@ +import QtQuick 2.0 + +MultiPointTouchArea { + width: 240 + height: 320 + + property bool grabInnerArea: true + + minimumTouchPoints: 2 + maximumTouchPoints: 3 + touchPoints: [ + TouchPoint { objectName: "point11" }, + TouchPoint { objectName: "point12" } + ] + + MultiPointTouchArea { + anchors.fill: parent + minimumTouchPoints: 3 + maximumTouchPoints: 3 + onGestureStarted: if (grabInnerArea) gesture.grab() + touchPoints: [ + TouchPoint { objectName: "point21" }, + TouchPoint { objectName: "point22" }, + TouchPoint { objectName: "point23" } + ] + } +} diff --git a/tests/auto/declarative/qquickmultipointtoucharea/data/nonOverlapping.qml b/tests/auto/declarative/qquickmultipointtoucharea/data/nonOverlapping.qml new file mode 100644 index 0000000000..039607e26c --- /dev/null +++ b/tests/auto/declarative/qquickmultipointtoucharea/data/nonOverlapping.qml @@ -0,0 +1,32 @@ +import QtQuick 2.0 + +Rectangle { + width: 240 + height: 320 + + MultiPointTouchArea { + width: parent.width + height: 160 + minimumTouchPoints: 2 + maximumTouchPoints: 2 + onGestureStarted: gesture.grab() + touchPoints: [ + TouchPoint { objectName: "point11" }, + TouchPoint { objectName: "point12" } + ] + } + + MultiPointTouchArea { + width: parent.width + height: 160 + y: 160 + minimumTouchPoints: 3 + maximumTouchPoints: 3 + onGestureStarted: gesture.grab() + touchPoints: [ + TouchPoint { objectName: "point21" }, + TouchPoint { objectName: "point22" }, + TouchPoint { objectName: "point23" } + ] + } +} diff --git a/tests/auto/declarative/qquickmultipointtoucharea/data/properties.qml b/tests/auto/declarative/qquickmultipointtoucharea/data/properties.qml new file mode 100644 index 0000000000..98ef1a9cbe --- /dev/null +++ b/tests/auto/declarative/qquickmultipointtoucharea/data/properties.qml @@ -0,0 +1,15 @@ +import QtQuick 2.0 + +MultiPointTouchArea { + width: 240 + height: 320 + + minimumTouchPoints: 2 + maximumTouchPoints: 4 + touchPoints: [ + TouchPoint {}, + TouchPoint {}, + TouchPoint {}, + TouchPoint {} + ] +} diff --git a/tests/auto/declarative/qquickmultipointtoucharea/data/signalTest.qml b/tests/auto/declarative/qquickmultipointtoucharea/data/signalTest.qml new file mode 100644 index 0000000000..3a6aa86a1c --- /dev/null +++ b/tests/auto/declarative/qquickmultipointtoucharea/data/signalTest.qml @@ -0,0 +1,25 @@ +import QtQuick 2.0 + +MultiPointTouchArea { + width: 240 + height: 320 + + function clearCounts() { + touchPointPressCount = 0; + touchPointUpdateCount = 0; + touchPointReleaseCount = 0; + touchCount = 0; + } + + property int touchPointPressCount: 0 + property int touchPointUpdateCount: 0 + property int touchPointReleaseCount: 0 + property int touchCount: 0 + + maximumTouchPoints: 5 + + onTouchPointsPressed: { touchPointPressCount = touchPoints.length } + onTouchPointsUpdated: { touchPointUpdateCount = touchPoints.length } + onTouchPointsReleased: { touchPointReleaseCount = touchPoints.length } + onTouchUpdated: { touchCount = touchPoints.length } +} diff --git a/tests/auto/declarative/qquickmultipointtoucharea/qquickmultipointtoucharea.pro b/tests/auto/declarative/qquickmultipointtoucharea/qquickmultipointtoucharea.pro new file mode 100644 index 0000000000..06be9d4d41 --- /dev/null +++ b/tests/auto/declarative/qquickmultipointtoucharea/qquickmultipointtoucharea.pro @@ -0,0 +1,11 @@ +load(qttest_p4) +contains(QT_CONFIG,declarative): QT += declarative gui +macx:CONFIG -= app_bundle + +SOURCES += tst_qquickmultipointtoucharea.cpp + +importFiles.files = data +importFiles.path = . +DEPLOYMENT += importFiles + +QT += core-private gui-private declarative-private diff --git a/tests/auto/declarative/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp b/tests/auto/declarative/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp new file mode 100644 index 0000000000..9acef50645 --- /dev/null +++ b/tests/auto/declarative/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp @@ -0,0 +1,586 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite 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 +#include +#include +#include +#include + +class tst_QQuickMultiPointTouchArea: public QObject +{ + Q_OBJECT +private slots: + void initTestCase() {} + void cleanupTestCase() {} + + void properties(); + void signalTest(); + void nonOverlapping(); + void nested(); + void inFlickable(); + +private: + QQuickView *createAndShowView(const QString &file); +}; + +void tst_QQuickMultiPointTouchArea::properties() +{ + QQuickView *canvas = createAndShowView("properties.qml"); + QVERIFY(canvas->rootObject() != 0); + + QQuickMultiPointTouchArea *area = qobject_cast(canvas->rootObject()); + QVERIFY(area != 0); + + QCOMPARE(area->minimumTouchPoints(), 2); + QCOMPARE(area->maximumTouchPoints(), 4); + + QDeclarativeListReference ref(area, "touchPoints"); + QCOMPARE(ref.count(), 4); + + delete canvas; +} + +void tst_QQuickMultiPointTouchArea::signalTest() +{ + QQuickView *canvas = createAndShowView("signalTest.qml"); + QVERIFY(canvas->rootObject() != 0); + + QQuickMultiPointTouchArea *area = qobject_cast(canvas->rootObject()); + QVERIFY(area != 0); + + QPoint p1(20,100); + QPoint p2(40,100); + QPoint p3(60,100); + QPoint p4(80,100); + QPoint p5(100,100); + + QTest::QTouchEventSequence sequence = QTest::touchEvent(canvas); + + sequence.press(0, p1).press(1, p2).commit(); + + QCOMPARE(area->property("touchPointPressCount").toInt(), 2); + QCOMPARE(area->property("touchPointUpdateCount").toInt(), 0); + QCOMPARE(area->property("touchPointReleaseCount").toInt(), 0); + QCOMPARE(area->property("touchCount").toInt(), 2); + QMetaObject::invokeMethod(area, "clearCounts"); + + sequence.stationary(0).stationary(1).press(2, p3).commit(); + + QCOMPARE(area->property("touchPointPressCount").toInt(), 1); + QCOMPARE(area->property("touchPointUpdateCount").toInt(), 0); + QCOMPARE(area->property("touchPointReleaseCount").toInt(), 0); + QCOMPARE(area->property("touchCount").toInt(), 3); + QMetaObject::invokeMethod(area, "clearCounts"); + + p1 -= QPoint(10,10); + p2 += QPoint(10,10); + sequence.move(0, p1).move(1, p2).stationary(2).commit(); + + QCOMPARE(area->property("touchPointPressCount").toInt(), 0); + QCOMPARE(area->property("touchPointUpdateCount").toInt(), 2); + QCOMPARE(area->property("touchPointReleaseCount").toInt(), 0); + QCOMPARE(area->property("touchCount").toInt(), 3); + QMetaObject::invokeMethod(area, "clearCounts"); + + p3 += QPoint(10,10); + sequence.release(0, p1).release(1, p2) + .move(2, p3).press(3, p4).press(4, p5).commit(); + + QCOMPARE(area->property("touchPointPressCount").toInt(), 2); + QCOMPARE(area->property("touchPointUpdateCount").toInt(), 1); + QCOMPARE(area->property("touchPointReleaseCount").toInt(), 2); + QCOMPARE(area->property("touchCount").toInt(), 3); + QMetaObject::invokeMethod(area, "clearCounts"); + + sequence.release(2, p3).release(3, p4).release(4, p5).commit(); + + QCOMPARE(area->property("touchPointPressCount").toInt(), 0); + QCOMPARE(area->property("touchPointUpdateCount").toInt(), 0); + QCOMPARE(area->property("touchPointReleaseCount").toInt(), 3); + QCOMPARE(area->property("touchCount").toInt(), 0); + QMetaObject::invokeMethod(area, "clearCounts"); + + delete canvas; +} + +void tst_QQuickMultiPointTouchArea::nonOverlapping() +{ + QQuickView *canvas = createAndShowView("nonOverlapping.qml"); + QVERIFY(canvas->rootObject() != 0); + + QQuickTouchPoint *point11 = canvas->rootObject()->findChild("point11"); + QQuickTouchPoint *point12 = canvas->rootObject()->findChild("point12"); + QQuickTouchPoint *point21 = canvas->rootObject()->findChild("point21"); + QQuickTouchPoint *point22 = canvas->rootObject()->findChild("point22"); + QQuickTouchPoint *point23 = canvas->rootObject()->findChild("point23"); + + QCOMPARE(point11->isValid(), false); + QCOMPARE(point12->isValid(), false); + QCOMPARE(point21->isValid(), false); + QCOMPARE(point22->isValid(), false); + QCOMPARE(point23->isValid(), false); + + QPoint p1(20,100); + QPoint p2(40,100); + QPoint p3(60,180); + QPoint p4(80,180); + QPoint p5(100,180); + + QTest::QTouchEventSequence sequence = QTest::touchEvent(canvas); + + sequence.press(0, p1).commit(); + + QCOMPARE(point11->isValid(), false); + QCOMPARE(point12->isValid(), false); + QCOMPARE(point21->isValid(), false); + QCOMPARE(point22->isValid(), false); + QCOMPARE(point23->isValid(), false); + + sequence.stationary(0).press(1, p2).commit(); + + QCOMPARE(point11->isValid(), true); + QCOMPARE(point12->isValid(), true); + QCOMPARE(point21->isValid(), false); + QCOMPARE(point22->isValid(), false); + QCOMPARE(point23->isValid(), false); + + QCOMPARE(point11->x(), qreal(20)); QCOMPARE(point11->y(), qreal(100)); + QCOMPARE(point12->x(), qreal(40)); QCOMPARE(point12->y(), qreal(100)); + + p1 += QPoint(0,10); + p2 += QPoint(5,0); + sequence.move(0, p1).move(1, p2).commit(); + + QCOMPARE(point11->isValid(), true); + QCOMPARE(point12->isValid(), true); + QCOMPARE(point21->isValid(), false); + QCOMPARE(point22->isValid(), false); + QCOMPARE(point23->isValid(), false); + + QCOMPARE(point11->x(), qreal(20)); QCOMPARE(point11->y(), qreal(110)); + QCOMPARE(point12->x(), qreal(45)); QCOMPARE(point12->y(), qreal(100)); + + sequence.stationary(0).stationary(1).press(2, p3).commit(); + + QCOMPARE(point11->isValid(), true); + QCOMPARE(point12->isValid(), true); + QCOMPARE(point21->isValid(), false); + QCOMPARE(point22->isValid(), false); + QCOMPARE(point23->isValid(), false); + + sequence.stationary(0).stationary(1).stationary(2).press(3, p4).press(4, p5).commit(); + + QCOMPARE(point11->isValid(), true); + QCOMPARE(point12->isValid(), true); + QCOMPARE(point21->isValid(), true); + QCOMPARE(point22->isValid(), true); + QCOMPARE(point23->isValid(), true); + + QCOMPARE(point11->x(), qreal(20)); QCOMPARE(point11->y(), qreal(110)); + QCOMPARE(point12->x(), qreal(45)); QCOMPARE(point12->y(), qreal(100)); + QCOMPARE(point21->x(), qreal(60)); QCOMPARE(point21->y(), qreal(20)); + QCOMPARE(point22->x(), qreal(80)); QCOMPARE(point22->y(), qreal(20)); + QCOMPARE(point23->x(), qreal(100)); QCOMPARE(point23->y(), qreal(20)); + + p1 += QPoint(4,10); + p2 += QPoint(17,17); + p3 += QPoint(3,0); + p4 += QPoint(1,-1); + p5 += QPoint(-7,10); + sequence.move(0, p1).move(1, p2).move(2, p3).move(3, p4).move(4, p5).commit(); + + QCOMPARE(point11->isValid(), true); + QCOMPARE(point12->isValid(), true); + QCOMPARE(point21->isValid(), true); + QCOMPARE(point22->isValid(), true); + QCOMPARE(point23->isValid(), true); + + QCOMPARE(point11->x(), qreal(24)); QCOMPARE(point11->y(), qreal(120)); + QCOMPARE(point12->x(), qreal(62)); QCOMPARE(point12->y(), qreal(117)); + QCOMPARE(point21->x(), qreal(63)); QCOMPARE(point21->y(), qreal(20)); + QCOMPARE(point22->x(), qreal(81)); QCOMPARE(point22->y(), qreal(19)); + QCOMPARE(point23->x(), qreal(93)); QCOMPARE(point23->y(), qreal(30)); + + sequence.release(0, p1).release(1, p2).release(2, p3).release(3, p4).release(4, p5).commit(); + + //points remain valid immediately after release + QCOMPARE(point11->isValid(), true); + QCOMPARE(point12->isValid(), true); + QCOMPARE(point21->isValid(), true); + QCOMPARE(point22->isValid(), true); + QCOMPARE(point23->isValid(), true); + + delete canvas; +} + +void tst_QQuickMultiPointTouchArea::nested() +{ + QQuickView *canvas = createAndShowView("nested.qml"); + QVERIFY(canvas->rootObject() != 0); + + QQuickTouchPoint *point11 = canvas->rootObject()->findChild("point11"); + QQuickTouchPoint *point12 = canvas->rootObject()->findChild("point12"); + QQuickTouchPoint *point21 = canvas->rootObject()->findChild("point21"); + QQuickTouchPoint *point22 = canvas->rootObject()->findChild("point22"); + QQuickTouchPoint *point23 = canvas->rootObject()->findChild("point23"); + + QCOMPARE(point11->isValid(), false); + QCOMPARE(point12->isValid(), false); + QCOMPARE(point21->isValid(), false); + QCOMPARE(point22->isValid(), false); + QCOMPARE(point23->isValid(), false); + + QPoint p1(20,100); + QPoint p2(40,100); + QPoint p3(60,180); + + QTest::QTouchEventSequence sequence = QTest::touchEvent(canvas); + + sequence.press(0, p1).commit(); + + QCOMPARE(point11->isValid(), false); + QCOMPARE(point12->isValid(), false); + QCOMPARE(point21->isValid(), false); + QCOMPARE(point22->isValid(), false); + QCOMPARE(point23->isValid(), false); + + sequence.stationary(0).press(1, p2).commit(); + + QCOMPARE(point11->isValid(), true); + QCOMPARE(point12->isValid(), true); + QCOMPARE(point21->isValid(), false); + QCOMPARE(point22->isValid(), false); + QCOMPARE(point23->isValid(), false); + + QCOMPARE(point11->x(), qreal(20)); QCOMPARE(point11->y(), qreal(100)); + QCOMPARE(point12->x(), qreal(40)); QCOMPARE(point12->y(), qreal(100)); + + p1 += QPoint(0,10); + p2 += QPoint(5,0); + sequence.move(0, p1).move(1, p2).commit(); + + QCOMPARE(point11->isValid(), true); + QCOMPARE(point12->isValid(), true); + QCOMPARE(point21->isValid(), false); + QCOMPARE(point22->isValid(), false); + QCOMPARE(point23->isValid(), false); + + QCOMPARE(point11->x(), qreal(20)); QCOMPARE(point11->y(), qreal(110)); + QCOMPARE(point12->x(), qreal(45)); QCOMPARE(point12->y(), qreal(100)); + + sequence.stationary(0).stationary(1).press(2, p3).commit(); + + QCOMPARE(point11->isValid(), true); + QCOMPARE(point12->isValid(), true); + QCOMPARE(point21->isValid(), true); + QCOMPARE(point22->isValid(), true); + QCOMPARE(point23->isValid(), true); + + //point11 should be same as point21, point12 same as point22 + QCOMPARE(point11->x(), qreal(20)); QCOMPARE(point11->y(), qreal(110)); + QCOMPARE(point12->x(), qreal(45)); QCOMPARE(point12->y(), qreal(100)); + QCOMPARE(point21->x(), qreal(20)); QCOMPARE(point21->y(), qreal(110)); + QCOMPARE(point22->x(), qreal(45)); QCOMPARE(point22->y(), qreal(100)); + QCOMPARE(point23->x(), qreal(60)); QCOMPARE(point23->y(), qreal(180)); + + sequence.stationary(0).stationary(1).stationary(2).press(3, QPoint(80,180)).press(4, QPoint(100,180)).commit(); + + QCOMPARE(point11->isValid(), true); + QCOMPARE(point12->isValid(), true); + QCOMPARE(point21->isValid(), true); + QCOMPARE(point22->isValid(), true); + QCOMPARE(point23->isValid(), true); + + //new touch points should be ignored (have no impact on our existing touch points) + QCOMPARE(point11->x(), qreal(20)); QCOMPARE(point11->y(), qreal(110)); + QCOMPARE(point12->x(), qreal(45)); QCOMPARE(point12->y(), qreal(100)); + QCOMPARE(point21->x(), qreal(20)); QCOMPARE(point21->y(), qreal(110)); + QCOMPARE(point22->x(), qreal(45)); QCOMPARE(point22->y(), qreal(100)); + QCOMPARE(point23->x(), qreal(60)); QCOMPARE(point23->y(), qreal(180)); + + sequence.stationary(0).stationary(1).stationary(2).release(3, QPoint(80,180)).release(4, QPoint(100,180)).commit(); + + p1 += QPoint(4,10); + p2 += QPoint(17,17); + p3 += QPoint(3,0); + sequence.move(0, p1).move(1, p2).move(2, p3).commit(); + + QCOMPARE(point11->isValid(), true); + QCOMPARE(point12->isValid(), true); + QCOMPARE(point21->isValid(), true); + QCOMPARE(point22->isValid(), true); + QCOMPARE(point23->isValid(), true); + + QCOMPARE(point21->x(), qreal(24)); QCOMPARE(point21->y(), qreal(120)); + QCOMPARE(point22->x(), qreal(62)); QCOMPARE(point22->y(), qreal(117)); + QCOMPARE(point21->x(), qreal(24)); QCOMPARE(point21->y(), qreal(120)); + QCOMPARE(point22->x(), qreal(62)); QCOMPARE(point22->y(), qreal(117)); + QCOMPARE(point23->x(), qreal(63)); QCOMPARE(point23->y(), qreal(180)); + + p1 += QPoint(4,10); + p2 += QPoint(17,17); + p3 += QPoint(3,0); + sequence.move(0, p1).move(1, p2).move(2, p3).commit(); + + QCOMPARE(point11->isValid(), false); + QCOMPARE(point12->isValid(), false); + QCOMPARE(point21->isValid(), true); + QCOMPARE(point22->isValid(), true); + QCOMPARE(point23->isValid(), true); + + //first two remain the same (touches now grabbed by inner touch area) + QCOMPARE(point11->x(), qreal(24)); QCOMPARE(point11->y(), qreal(120)); + QCOMPARE(point12->x(), qreal(62)); QCOMPARE(point12->y(), qreal(117)); + QCOMPARE(point21->x(), qreal(28)); QCOMPARE(point21->y(), qreal(130)); + QCOMPARE(point22->x(), qreal(79)); QCOMPARE(point22->y(), qreal(134)); + QCOMPARE(point23->x(), qreal(66)); QCOMPARE(point23->y(), qreal(180)); + + sequence.release(0, p1).release(1, p2).release(2, p3).commit(); + + sequence.press(0, p1).commit(); + + QCOMPARE(point11->isValid(), false); + QCOMPARE(point12->isValid(), false); + QCOMPARE(point21->isValid(), false); + QCOMPARE(point22->isValid(), false); + QCOMPARE(point23->isValid(), false); + + sequence.release(0, p1).commit(); + + //test with grabbing turned off + canvas->rootObject()->setProperty("grabInnerArea", false); + + sequence.press(0, p1).press(1, p2).press(2, p3).commit(); + + QCOMPARE(point11->isValid(), true); + QCOMPARE(point12->isValid(), true); + QCOMPARE(point21->isValid(), true); + QCOMPARE(point22->isValid(), true); + QCOMPARE(point23->isValid(), true); + + p1 -= QPoint(4,10); + p2 -= QPoint(17,17); + p3 -= QPoint(3,0); + sequence.move(0, p1).move(1, p2).move(2, p3).commit(); + + QCOMPARE(point11->isValid(), true); + QCOMPARE(point12->isValid(), true); + QCOMPARE(point21->isValid(), true); + QCOMPARE(point22->isValid(), true); + QCOMPARE(point23->isValid(), true); + + QCOMPARE(point21->x(), qreal(24)); QCOMPARE(point21->y(), qreal(120)); + QCOMPARE(point22->x(), qreal(62)); QCOMPARE(point22->y(), qreal(117)); + QCOMPARE(point21->x(), qreal(24)); QCOMPARE(point21->y(), qreal(120)); + QCOMPARE(point22->x(), qreal(62)); QCOMPARE(point22->y(), qreal(117)); + QCOMPARE(point23->x(), qreal(63)); QCOMPARE(point23->y(), qreal(180)); + + p1 -= QPoint(4,10); + p2 -= QPoint(17,17); + p3 -= QPoint(3,0); + sequence.move(0, p1).move(1, p2).move(2, p3).commit(); + + QCOMPARE(point11->isValid(), true); + QCOMPARE(point12->isValid(), true); + QCOMPARE(point21->isValid(), true); + QCOMPARE(point22->isValid(), true); + QCOMPARE(point23->isValid(), true); + + //all change (touches not grabbed by inner touch area) + QCOMPARE(point11->x(), qreal(20)); QCOMPARE(point11->y(), qreal(110)); + QCOMPARE(point12->x(), qreal(45)); QCOMPARE(point12->y(), qreal(100)); + QCOMPARE(point21->x(), qreal(20)); QCOMPARE(point21->y(), qreal(110)); + QCOMPARE(point22->x(), qreal(45)); QCOMPARE(point22->y(), qreal(100)); + QCOMPARE(point23->x(), qreal(60)); QCOMPARE(point23->y(), qreal(180)); + + sequence.release(0, p1).release(1, p2).release(2, p3).commit(); + + delete canvas; +} + +void tst_QQuickMultiPointTouchArea::inFlickable() +{ + QQuickView *canvas = createAndShowView("inFlickable.qml"); + QVERIFY(canvas->rootObject() != 0); + + QQuickFlickable *flickable = qobject_cast(canvas->rootObject()); + QVERIFY(flickable != 0); + + QQuickTouchPoint *point11 = canvas->rootObject()->findChild("point1"); + QQuickTouchPoint *point12 = canvas->rootObject()->findChild("point2"); + + QCOMPARE(point11->isValid(), false); + QCOMPARE(point12->isValid(), false); + + QPoint p1(20,100); + QPoint p2(40,100); + + //moving one point vertically + QTest::touchEvent(canvas).press(0, p1); + QTest::mousePress(canvas, Qt::LeftButton, 0, p1); + + p1 += QPoint(0,15); + QTest::touchEvent(canvas).move(0, p1); + QTest::mouseMove(canvas, p1); + + p1 += QPoint(0,15); + QTest::touchEvent(canvas).move(0, p1); + QTest::mouseMove(canvas, p1); + + p1 += QPoint(0,15); + QTest::touchEvent(canvas).move(0, p1); + QTest::mouseMove(canvas, p1); + + p1 += QPoint(0,15); + QTest::touchEvent(canvas).move(0, p1); + QTest::mouseMove(canvas, p1); + + QVERIFY(flickable->contentY() < 0); + QCOMPARE(point11->isValid(), false); + QCOMPARE(point12->isValid(), false); + + QTest::touchEvent(canvas).release(0, p1); + QTest::mouseRelease(canvas,Qt::LeftButton, 0, p1); + QTest::qWait(50); + + QTRY_VERIFY(!flickable->isMoving()); + + //moving two points vertically + p1 = QPoint(20,100); + QTest::touchEvent(canvas).press(0, p1).press(1, p2); + QTest::mousePress(canvas, Qt::LeftButton, 0, p1); + + QCOMPARE(point11->isValid(), true); + QCOMPARE(point12->isValid(), true); + + p1 += QPoint(0,15); p2 += QPoint(0,15); + QTest::touchEvent(canvas).move(0, p1).move(1, p2); + QTest::mouseMove(canvas, p1); + + p1 += QPoint(0,15); p2 += QPoint(0,15); + QTest::touchEvent(canvas).move(0, p1).move(1, p2); + QTest::mouseMove(canvas, p1); + + p1 += QPoint(0,15); p2 += QPoint(0,15); + QTest::touchEvent(canvas).move(0, p1).move(1, p2); + QTest::mouseMove(canvas, p1); + + p1 += QPoint(0,15); p2 += QPoint(0,15); + QTest::touchEvent(canvas).move(0, p1).move(1, p2); + QTest::mouseMove(canvas, p1); + + QVERIFY(flickable->contentY() < 0); + QCOMPARE(point11->isValid(), false); + QCOMPARE(point12->isValid(), false); + + QTest::touchEvent(canvas).release(0, p1).release(1, p2); + QTest::mouseRelease(canvas,Qt::LeftButton, 0, p1); + QTest::qWait(50); + + QTRY_VERIFY(!flickable->isMoving()); + + //moving two points horizontally, then one point vertically + p1 = QPoint(20,100); + p2 = QPoint(40,100); + QTest::touchEvent(canvas).press(0, p1).press(1, p2); + QTest::mousePress(canvas, Qt::LeftButton, 0, p1); + + QCOMPARE(point11->isValid(), true); + QCOMPARE(point12->isValid(), true); + + p1 += QPoint(15,0); p2 += QPoint(15,0); + QTest::touchEvent(canvas).move(0, p1).move(1, p2); + QTest::mouseMove(canvas, p1); + + p1 += QPoint(15,0); p2 += QPoint(15,0); + QTest::touchEvent(canvas).move(0, p1).move(1, p2); + QTest::mouseMove(canvas, p1); + + p1 += QPoint(15,0); p2 += QPoint(15,0); + QTest::touchEvent(canvas).move(0, p1).move(1, p2); + QTest::mouseMove(canvas, p1); + + p1 += QPoint(15,0); p2 += QPoint(15,0); + QTest::touchEvent(canvas).move(0, p1).move(1, p2); + QTest::mouseMove(canvas, p1); + + p1 += QPoint(0,15); p2 += QPoint(0,15); + QTest::touchEvent(canvas).move(0, p1).move(1, p2); + QTest::mouseMove(canvas, p1); + + p1 += QPoint(0,15); p2 += QPoint(0,15); + QTest::touchEvent(canvas).move(0, p1).move(1, p2); + QTest::mouseMove(canvas, p1); + + p1 += QPoint(0,15); p2 += QPoint(0,15); + QTest::touchEvent(canvas).move(0, p1).move(1, p2); + QTest::mouseMove(canvas, p1); + + p1 += QPoint(0,15); p2 += QPoint(0,15); + QTest::touchEvent(canvas).move(0, p1).move(1, p2); + QTest::mouseMove(canvas, p1); + + QVERIFY(flickable->contentY() == 0); + QCOMPARE(point11->isValid(), true); + QCOMPARE(point12->isValid(), true); + + QTest::touchEvent(canvas).release(0, p1).release(1, p2); + QTest::mouseRelease(canvas,Qt::LeftButton, 0, p1); + QTest::qWait(50); + + delete canvas; +} + +QQuickView *tst_QQuickMultiPointTouchArea::createAndShowView(const QString &file) +{ + QQuickView *canvas = new QQuickView(0); + canvas->setSource(QUrl::fromLocalFile(QCoreApplication::applicationDirPath() + QLatin1String("/data/") + file)); + canvas->show(); + canvas->requestActivateWindow(); + QTest::qWaitForWindowShown(canvas); + + return canvas; +} + +QTEST_MAIN(tst_QQuickMultiPointTouchArea) + +#include "tst_qquickmultipointtoucharea.moc" -- cgit v1.2.3