aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShawn Rutledge <shawn.rutledge@nokia.com>2012-06-26 18:00:59 +0200
committerQt by Nokia <qt-info@nokia.com>2012-06-28 16:47:42 +0200
commit468626e99a90d6ac21cb311cde05c658ccb3b781 (patch)
tree7ac0039994e4489ea19e27cb0840dfa000486cbb
parent4c1addd2c3b019d66b5c19fcd8ba9e0918e92978 (diff)
Propagate synthesized mouse events in parallel with touch.
The old way of event propagation inside QQuickCanvas was to send the touch event through all elements, and if it was accepted along the way, stop. Otherwise generate a mouse event and propagate it through the items in the same way. With this patch the behavior is changed instead to do the propagation in parallel. The idea is to first send a touch, then a mouse event to each QML item (in paint order) that can potentially handle the events. When items filter their child elements, the same logic applies. Other changes/clarifications: - mouse events no longer get synthesized for more than one touch point - TouchPoints can be distributed to multiple Items - if an item accepts a touch point, it always gets updates, even if the point is stationary - events containing only stationary TouchPoints are discarded - PinchArea must accept any initial single TouchPoint in order to receive subsequent updates, even though it's not pinching yet. This means if PA is on top, items underneath don't get touches. New unit tests showing this behavior were added. This patch was written by Frederik Gladhorn, Laszlo Agocs and Shawn Rutledge. Due to the complexity of the logic some refactoring was done. QQuickMouseEventEx has been removed because it inherently relied on using the QEvent d pointer. Change-Id: If19ef687d7602e83cc11b18d2fecfbbdb4e44f5c Reviewed-by: Frederik Gladhorn <frederik.gladhorn@nokia.com>
-rw-r--r--src/quick/items/qquickcanvas.cpp595
-rw-r--r--src/quick/items/qquickcanvas_p.h11
-rw-r--r--src/quick/items/qquickevents_p_p.h69
-rw-r--r--src/quick/items/qquickflickable.cpp58
-rw-r--r--src/quick/items/qquickitem.cpp7
-rw-r--r--src/quick/items/qquickitem.h2
-rw-r--r--src/quick/items/qquickmultipointtoucharea.cpp2
-rw-r--r--src/quick/items/qquickpincharea.cpp138
-rw-r--r--src/quick/items/qquickpincharea_p.h5
-rw-r--r--tests/auto/quick/qquickcanvas/tst_qquickcanvas.cpp229
-rw-r--r--tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp14
-rw-r--r--tests/auto/quick/qquickpincharea/data/pinchproperties.qml3
-rw-r--r--tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp156
-rw-r--r--tests/auto/quick/quick.pro1
-rw-r--r--tests/auto/quick/touchmouse/data/buttononflickable.qml42
-rw-r--r--tests/auto/quick/touchmouse/data/buttonontouch.qml100
-rw-r--r--tests/auto/quick/touchmouse/data/flickableonpinch.qml37
-rw-r--r--tests/auto/quick/touchmouse/data/mouseonflickableonpinch.qml47
-rw-r--r--tests/auto/quick/touchmouse/data/pinchonflickable.qml35
-rw-r--r--tests/auto/quick/touchmouse/data/singleitem.qml18
-rw-r--r--tests/auto/quick/touchmouse/data/twoitems.qml22
-rw-r--r--tests/auto/quick/touchmouse/touchmouse.pro17
-rw-r--r--tests/auto/quick/touchmouse/tst_touchmouse.cpp923
23 files changed, 2007 insertions, 524 deletions
diff --git a/src/quick/items/qquickcanvas.cpp b/src/quick/items/qquickcanvas.cpp
index 7c59a9df9e..37c238fd21 100644
--- a/src/quick/items/qquickcanvas.cpp
+++ b/src/quick/items/qquickcanvas.cpp
@@ -72,8 +72,6 @@
QT_BEGIN_NAMESPACE
-DEFINE_BOOL_CONFIG_OPTION(qmlTranslateTouchToMouse, QML_TRANSLATE_TOUCH_TO_MOUSE)
-
void QQuickCanvasPrivate::updateFocusItemTransform()
{
Q_Q(QQuickCanvas);
@@ -395,25 +393,39 @@ void QQuickCanvasPrivate::initRootItem()
rootItem->setHeight(q->height());
}
-static QQuickMouseEventEx touchToMouseEvent(QEvent::Type type, const QTouchEvent::TouchPoint &p)
+static QMouseEvent *touchToMouseEvent(QEvent::Type type, const QTouchEvent::TouchPoint &p, QTouchEvent *event, QQuickItem *item, bool transformNeeded = true)
{
- QQuickMouseEventEx me(type, p.pos(), p.scenePos(), p.screenPos(),
- Qt::LeftButton, Qt::LeftButton, 0);
- me.setVelocity(p.velocity());
+ // The touch point local position and velocity are not yet transformed.
+ QMouseEvent *me = new QMouseEvent(type, transformNeeded ? item->mapFromScene(p.scenePos()) : p.pos(), p.scenePos(), p.screenPos(),
+ Qt::LeftButton, Qt::LeftButton, event->modifiers());
+ me->setAccepted(true);
+ me->setTimestamp(event->timestamp());
+ QVector2D transformedVelocity = p.velocity();
+ if (transformNeeded) {
+ QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
+ QMatrix4x4 transformMatrix(itemPrivate->canvasToItemTransform());
+ transformedVelocity = transformMatrix.mapVector(p.velocity()).toVector2D();
+ }
+ QGuiApplicationPrivate::setMouseEventCapsAndVelocity(me, event->device()->capabilities(), transformedVelocity);
return me;
}
-void QQuickCanvasPrivate::translateTouchToMouse(QTouchEvent *event)
+bool QQuickCanvasPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *event)
{
- if (event->type() == QEvent::TouchCancel) {
- touchMouseId = -1;
- if (mouseGrabberItem)
- mouseGrabberItem->ungrabMouse();
- return;
- }
+ Q_Q(QQuickCanvas);
+ // For each point, check if it is accepted, if not, try the next point.
+ // Any of the fingers can become the mouse one.
+ // This can happen because a mouse area might not accept an event at some point but another.
for (int i = 0; i < event->touchPoints().count(); ++i) {
- QTouchEvent::TouchPoint p = event->touchPoints().at(i);
+ const QTouchEvent::TouchPoint &p = event->touchPoints().at(i);
+ // A new touch point
if (touchMouseId == -1 && p.state() & Qt::TouchPointPressed) {
+ QPointF pos = item->mapFromScene(p.scenePos());
+
+ // probably redundant, we check bounds in the calling function (matchingNewPoints)
+ if (!item->contains(pos))
+ break;
+
bool doubleClick = event->timestamp() - touchMousePressTimestamp
< static_cast<ulong>(qApp->styleHints()->mouseDoubleClickInterval());
touchMousePressTimestamp = event->timestamp();
@@ -421,72 +433,89 @@ void QQuickCanvasPrivate::translateTouchToMouse(QTouchEvent *event)
// accepted. Cannot defer setting the new value because otherwise if the event
// handler spins the event loop all subsequent moves and releases get lost.
touchMouseId = p.id();
- QQuickMouseEventEx me = touchToMouseEvent(QEvent::MouseButtonPress, p);
- me.setTimestamp(event->timestamp());
- me.setAccepted(false);
- me.setCapabilities(event->device()->capabilities());
- deliverMouseEvent(&me);
- if (me.isAccepted())
- event->setAccepted(true);
- else
+ itemForTouchPointId[touchMouseId] = item;
+ QScopedPointer<QMouseEvent> mousePress(touchToMouseEvent(QEvent::MouseButtonPress, p, event, item));
+
+ // Send a single press and see if that's accepted
+ if (!mouseGrabberItem)
+ item->grabMouse();
+ item->grabTouchPoints(QVector<int>() << touchMouseId);
+
+ q->sendEvent(item, mousePress.data());
+ event->setAccepted(mousePress->isAccepted());
+ if (!mousePress->isAccepted()) {
touchMouseId = -1;
- if (doubleClick && me.isAccepted()) {
+ if (itemForTouchPointId.value(p.id()) == item)
+ itemForTouchPointId.remove(p.id());
+
+ if (mouseGrabberItem == item)
+ item->ungrabMouse();
+ }
+
+ if (doubleClick && mousePress->isAccepted()) {
touchMousePressTimestamp = 0;
- QQuickMouseEventEx me = touchToMouseEvent(QEvent::MouseButtonDblClick, p);
- me.setTimestamp(event->timestamp());
- me.setAccepted(false);
- me.setCapabilities(event->device()->capabilities());
- if (!mouseGrabberItem) {
- if (deliverInitialMousePressEvent(rootItem, &me))
- event->setAccepted(true);
- else
- touchMouseId = -1;
+ QScopedPointer<QMouseEvent> mouseDoubleClick(touchToMouseEvent(QEvent::MouseButtonDblClick, p, event, item));
+ q->sendEvent(item, mouseDoubleClick.data());
+ event->setAccepted(mouseDoubleClick->isAccepted());
+ if (mouseDoubleClick->isAccepted()) {
+ return true;
} else {
- deliverMouseEvent(&me);
- if (me.isAccepted())
- event->setAccepted(true);
- else
- touchMouseId = -1;
+ touchMouseId = -1;
}
}
+ // The event was accepted, we are done.
+ if (mousePress->isAccepted())
+ return true;
+ // The event was not accepted but touchMouseId was set.
if (touchMouseId != -1)
- break;
+ return false;
+ // try the next point
+
+ // Touch point was there before and moved
} else if (p.id() == touchMouseId) {
if (p.state() & Qt::TouchPointMoved) {
- QQuickMouseEventEx me = touchToMouseEvent(QEvent::MouseMove, p);
- me.setTimestamp(event->timestamp());
- me.setCapabilities(event->device()->capabilities());
- if (!mouseGrabberItem) {
+ if (mouseGrabberItem) {
+ QScopedPointer<QMouseEvent> me(touchToMouseEvent(QEvent::MouseMove, p, event, mouseGrabberItem));
+ q->sendEvent(mouseGrabberItem, me.data());
+ event->setAccepted(me->isAccepted());
+ if (me->isAccepted()) {
+ itemForTouchPointId[p.id()] = mouseGrabberItem; // N.B. the mouseGrabberItem may be different after returning from sendEvent()
+ return true;
+ }
+ } else {
+ // no grabber, check if we care about mouse hover
+ // FIXME: this should only happen once, not recursively... I'll ignore it just ignore hover now.
+ // hover for touch???
+ QScopedPointer<QMouseEvent> me(touchToMouseEvent(QEvent::MouseMove, p, event, item));
if (lastMousePosition.isNull())
- lastMousePosition = me.windowPos();
+ lastMousePosition = me->windowPos();
QPointF last = lastMousePosition;
- lastMousePosition = me.windowPos();
+ lastMousePosition = me->windowPos();
- bool accepted = me.isAccepted();
- bool delivered = deliverHoverEvent(rootItem, me.windowPos(), last, me.modifiers(), accepted);
+ bool accepted = me->isAccepted();
+ bool delivered = deliverHoverEvent(rootItem, me->windowPos(), last, me->modifiers(), accepted);
if (!delivered) {
//take care of any exits
accepted = clearHover();
}
- me.setAccepted(accepted);
+ me->setAccepted(accepted);
break;
}
-
- deliverMouseEvent(&me);
} else if (p.state() & Qt::TouchPointReleased) {
+ // currently handled point was released
touchMouseId = -1;
- if (!mouseGrabberItem)
- return;
- QQuickMouseEventEx me = touchToMouseEvent(QEvent::MouseButtonRelease, p);
- me.setTimestamp(event->timestamp());
- me.setCapabilities(event->device()->capabilities());
- deliverMouseEvent(&me);
- if (mouseGrabberItem)
- mouseGrabberItem->ungrabMouse();
+ if (mouseGrabberItem) {
+ QScopedPointer<QMouseEvent> me(touchToMouseEvent(QEvent::MouseButtonRelease, p, event, mouseGrabberItem));
+ q->sendEvent(mouseGrabberItem, me.data());
+ if (mouseGrabberItem) // might have ungrabbed due to event
+ mouseGrabberItem->ungrabMouse();
+ return me->isAccepted();
+ }
}
break;
}
}
+ return false;
}
void QQuickCanvasPrivate::transformTouchPoints(QList<QTouchEvent::TouchPoint> &touchPoints, const QTransform &transform)
@@ -1047,17 +1076,17 @@ bool QQuickCanvas::event(QEvent *e)
case QEvent::TouchBegin:
case QEvent::TouchUpdate:
- case QEvent::TouchEnd:
- case QEvent::TouchCancel:
- {
- QTouchEvent *touch = static_cast<QTouchEvent *>(e);
+ case QEvent::TouchEnd: {
+ QTouchEvent *touch = static_cast<QTouchEvent*>(e);
d->translateTouchEvent(touch);
- d->deliverTouchEvent(touch);
- if (qmlTranslateTouchToMouse())
- d->translateTouchToMouse(touch);
-
- return touch->isAccepted();
+ // return in order to avoid the QWindow::event below
+ return d->deliverTouchEvent(touch);
}
+ break;
+ case QEvent::TouchCancel:
+ // return in order to avoid the QWindow::event below
+ return d->deliverTouchCancelEvent(static_cast<QTouchEvent*>(e));
+ break;
case QEvent::Leave:
d->clearHover();
d->lastMousePosition = QPoint();
@@ -1102,6 +1131,19 @@ void QQuickCanvas::keyReleaseEvent(QKeyEvent *e)
sendEvent(d->activeFocusItem, e);
}
+QMouseEvent *QQuickCanvasPrivate::cloneMouseEvent(QMouseEvent *event, QPointF *transformedLocalPos)
+{
+ int caps = QGuiApplicationPrivate::mouseEventCaps(event);
+ QVector2D velocity = QGuiApplicationPrivate::mouseEventVelocity(event);
+ QMouseEvent *me = new QMouseEvent(event->type(),
+ transformedLocalPos ? *transformedLocalPos : event->localPos(),
+ event->windowPos(), event->screenPos(),
+ event->button(), event->buttons(), event->modifiers());
+ QGuiApplicationPrivate::setMouseEventCapsAndVelocity(me, caps, velocity);
+ me->setTimestamp(event->timestamp());
+ return me;
+}
+
bool QQuickCanvasPrivate::deliverInitialMousePressEvent(QQuickItem *item, QMouseEvent *event)
{
Q_Q(QQuickCanvas);
@@ -1126,16 +1168,14 @@ bool QQuickCanvasPrivate::deliverInitialMousePressEvent(QQuickItem *item, QMouse
}
if (itemPrivate->acceptedMouseButtons() & event->button()) {
- QPointF p = item->mapFromScene(event->windowPos());
- if (item->contains(p)) {
- QMouseEvent me(event->type(), p, event->windowPos(), event->screenPos(),
- event->button(), event->buttons(), event->modifiers());
- me.setTimestamp(event->timestamp());
- me.accept();
+ QPointF localPos = item->mapFromScene(event->windowPos());
+ if (item->contains(localPos)) {
+ QScopedPointer<QMouseEvent> me(cloneMouseEvent(event, &localPos));
+ me->accept();
item->grabMouse();
- q->sendEvent(item, &me);
- event->setAccepted(me.isAccepted());
- if (me.isAccepted())
+ q->sendEvent(item, me.data());
+ event->setAccepted(me->isAccepted());
+ if (me->isAccepted())
return true;
if (mouseGrabberItem)
mouseGrabberItem->ungrabMouse();
@@ -1162,21 +1202,12 @@ bool QQuickCanvasPrivate::deliverMouseEvent(QMouseEvent *event)
}
if (mouseGrabberItem) {
- QQuickItemPrivate *mgPrivate = QQuickItemPrivate::get(mouseGrabberItem);
- const QTransform &transform = mgPrivate->canvasToItemTransform();
- QQuickMouseEventEx me(event->type(), transform.map(event->windowPos()),
- event->windowPos(), event->screenPos(),
- event->button(), event->buttons(), event->modifiers());
- QQuickMouseEventEx *eventEx = QQuickMouseEventEx::extended(event);
- if (eventEx) {
- me.setVelocity(QMatrix4x4(transform).mapVector(eventEx->velocity()).toVector2D());
- me.setCapabilities(eventEx->capabilities());
- }
- me.setTimestamp(event->timestamp());
- me.accept();
- q->sendEvent(mouseGrabberItem, &me);
- event->setAccepted(me.isAccepted());
- if (me.isAccepted())
+ QPointF localPos = mouseGrabberItem->mapFromScene(event->windowPos());
+ QScopedPointer<QMouseEvent> me(cloneMouseEvent(event, &localPos));
+ me->accept();
+ q->sendEvent(mouseGrabberItem, me.data());
+ event->setAccepted(me->isAccepted());
+ if (me->isAccepted())
return true;
}
@@ -1187,8 +1218,6 @@ bool QQuickCanvasPrivate::deliverMouseEvent(QMouseEvent *event)
void QQuickCanvas::mousePressEvent(QMouseEvent *event)
{
Q_D(QQuickCanvas);
- if (qmlTranslateTouchToMouse())
- return; // We are using touch events
#ifdef MOUSE_DEBUG
qWarning() << "QQuickCanvas::mousePressEvent()" << event->pos() << event->button() << event->buttons();
#endif
@@ -1200,8 +1229,6 @@ void QQuickCanvas::mousePressEvent(QMouseEvent *event)
void QQuickCanvas::mouseReleaseEvent(QMouseEvent *event)
{
Q_D(QQuickCanvas);
- if (qmlTranslateTouchToMouse())
- return; // We are using touch events
#ifdef MOUSE_DEBUG
qWarning() << "QQuickCanvas::mouseReleaseEvent()" << event->pos() << event->button() << event->buttons();
#endif
@@ -1220,9 +1247,6 @@ void QQuickCanvas::mouseReleaseEvent(QMouseEvent *event)
void QQuickCanvas::mouseDoubleClickEvent(QMouseEvent *event)
{
Q_D(QQuickCanvas);
- if (qmlTranslateTouchToMouse())
- return; // We are using touch events
-
#ifdef MOUSE_DEBUG
qWarning() << "QQuickCanvas::mouseDoubleClickEvent()" << event->pos() << event->button() << event->buttons();
#endif
@@ -1258,8 +1282,6 @@ bool QQuickCanvasPrivate::sendHoverEvent(QEvent::Type type, QQuickItem *item,
void QQuickCanvas::mouseMoveEvent(QMouseEvent *event)
{
Q_D(QQuickCanvas);
- if (qmlTranslateTouchToMouse())
- return; // We are using touch events
#ifdef MOUSE_DEBUG
qWarning() << "QQuickCanvas::mouseMoveEvent()" << event->pos() << event->button() << event->buttons();
#endif
@@ -1402,98 +1424,100 @@ void QQuickCanvas::wheelEvent(QWheelEvent *event)
}
#endif // QT_NO_WHEELEVENT
+
+bool QQuickCanvasPrivate::deliverTouchCancelEvent(QTouchEvent *event)
+{
+#ifdef TOUCH_DEBUG
+ qWarning("touchCancelEvent");
+#endif
+ Q_Q(QQuickCanvas);
+ // A TouchCancel event will typically not contain any points.
+ // Deliver it to all items that have active touches.
+ QSet<QQuickItem *> cancelDelivered;
+ foreach (QQuickItem *item, itemForTouchPointId) {
+ if (cancelDelivered.contains(item))
+ continue;
+ cancelDelivered.insert(item);
+ q->sendEvent(item, event);
+ }
+ touchMouseId = -1;
+ if (mouseGrabberItem)
+ mouseGrabberItem->ungrabMouse();
+ // The next touch event can only be a TouchBegin so clean up.
+ itemForTouchPointId.clear();
+ return true;
+}
+
+// check what kind of touch we have (begin/update) and
+// call deliverTouchPoints to actually dispatch the points
bool QQuickCanvasPrivate::deliverTouchEvent(QTouchEvent *event)
{
#ifdef TOUCH_DEBUG
if (event->type() == QEvent::TouchBegin)
- qWarning("touchBeginEvent");
+ qWarning() << "touchBeginEvent";
else if (event->type() == QEvent::TouchUpdate)
- qWarning("touchUpdateEvent");
+ qWarning() << "touchUpdateEvent points";
else if (event->type() == QEvent::TouchEnd)
qWarning("touchEndEvent");
- else if (event->type() == QEvent::TouchCancel)
- qWarning("touchCancelEvent");
#endif
- Q_Q(QQuickCanvas);
-
+ // List of all items that received an event before
+ // When we have TouchBegin this is and will stay empty
QHash<QQuickItem *, QList<QTouchEvent::TouchPoint> > updatedPoints;
- if (event->type() == QTouchEvent::TouchBegin) { // all points are new touch points
- QSet<int> acceptedNewPoints;
- deliverTouchPoints(rootItem, event, event->touchPoints(), &acceptedNewPoints, &updatedPoints);
- if (acceptedNewPoints.count() > 0)
- event->accept();
- else
- event->ignore();
- return event->isAccepted();
- }
-
- if (event->type() == QTouchEvent::TouchCancel) {
- // A TouchCancel event will typically not contain any points.
- // Deliver it to all items that have active touches.
- QSet<QQuickItem *> cancelDelivered;
- foreach (QQuickItem *item, itemForTouchPointId) {
- if (cancelDelivered.contains(item))
- continue;
- cancelDelivered.insert(item);
- q->sendEvent(item, event);
- }
- // The next touch event can only be a TouchBegin so clean up.
- itemForTouchPointId.clear();
- return true;
- }
-
+ // Figure out who accepted a touch point last and put it in updatedPoints
+ // Add additional item to newPoints
const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
QList<QTouchEvent::TouchPoint> newPoints;
- QQuickItem *item = 0;
for (int i=0; i<touchPoints.count(); i++) {
- const QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
- switch (touchPoint.state()) {
- case Qt::TouchPointPressed:
- newPoints << touchPoint;
- break;
- case Qt::TouchPointMoved:
- case Qt::TouchPointStationary:
- case Qt::TouchPointReleased:
- if (itemForTouchPointId.contains(touchPoint.id())) {
- item = itemForTouchPointId[touchPoint.id()];
- if (item)
- updatedPoints[item].append(touchPoint);
- }
- break;
- default:
- break;
+ const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i);
+ if (touchPoint.state() == Qt::TouchPointPressed) {
+ newPoints << touchPoint;
+ } else {
+ // TouchPointStationary is relevant only to items which
+ // are also receiving touch points with some other state.
+ // But we have not yet decided which points go to which item,
+ // so for now we must include all non-new points in updatedPoints.
+ if (itemForTouchPointId.contains(touchPoint.id())) {
+ QQuickItem *item = itemForTouchPointId.value(touchPoint.id());
+ if (item)
+ updatedPoints[item].append(touchPoint);
+ }
}
}
+ // Deliver the event, but only if there is at least one new point
+ // or some item accepted a point and should receive an update
if (newPoints.count() > 0 || updatedPoints.count() > 0) {
QSet<int> acceptedNewPoints;
- int prevCount = updatedPoints.count();
- deliverTouchPoints(rootItem, event, newPoints, &acceptedNewPoints, &updatedPoints);
- if (acceptedNewPoints.count() > 0 || updatedPoints.count() != prevCount)
- event->accept();
- else
- event->ignore();
+ event->setAccepted(deliverTouchPoints(rootItem, event, newPoints, &acceptedNewPoints, &updatedPoints));
} else
event->ignore();
+ // Remove released points from itemForTouchPointId
if (event->touchPointStates() & Qt::TouchPointReleased) {
for (int i=0; i<touchPoints.count(); i++) {
- if (touchPoints[i].state() == Qt::TouchPointReleased)
+ if (touchPoints[i].state() == Qt::TouchPointReleased) {
itemForTouchPointId.remove(touchPoints[i].id());
+ if (touchPoints[i].id() == touchMouseId)
+ touchMouseId = -1;
+ }
}
}
+ if (event->type() == QEvent::TouchEnd) {
+ Q_ASSERT(itemForTouchPointId.isEmpty());
+ }
+
return event->isAccepted();
}
+// This function recurses and sends the events to the individual items
bool QQuickCanvasPrivate::deliverTouchPoints(QQuickItem *item, QTouchEvent *event, const QList<QTouchEvent::TouchPoint> &newPoints, QSet<int> *acceptedNewPoints, QHash<QQuickItem *, QList<QTouchEvent::TouchPoint> > *updatedPoints)
{
- Q_Q(QQuickCanvas);
QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
- if (itemPrivate->opacity() == 0.0)
+ if (qFuzzyIsNull(itemPrivate->opacity()))
return false;
if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
@@ -1504,6 +1528,8 @@ bool QQuickCanvasPrivate::deliverTouchPoints(QQuickItem *item, QTouchEvent *even
}
}
+ // Check if our children want the event (or parts of it)
+ // This is the only point where touch event delivery recurses!
QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
for (int ii = children.count() - 1; ii >= 0; --ii) {
QQuickItem *child = children.at(ii);
@@ -1513,71 +1539,161 @@ bool QQuickCanvasPrivate::deliverTouchPoints(QQuickItem *item, QTouchEvent *even
return true;
}
- QList<QTouchEvent::TouchPoint> matchingPoints;
+ // None of the children accepted the event, so check the given item itself.
+ // First, construct matchingPoints as a list of TouchPoints which the
+ // given item might be interested in. Any newly-pressed point which is
+ // inside the item's bounds will be interesting, and also any updated point
+ // which was already accepted by that item when it was first pressed.
+ // (A point which was already accepted is effectively "grabbed" by the item.)
+
+ // set of IDs of "interesting" new points
+ QSet<int> matchingNewPoints;
+ // set of points which this item has previously accepted, for starters
+ QList<QTouchEvent::TouchPoint> matchingPoints = (*updatedPoints)[item];
+ // now add the new points which are inside this item's bounds
if (newPoints.count() > 0 && acceptedNewPoints->count() < newPoints.count()) {
- for (int i=0; i<newPoints.count(); i++) {
+ for (int i = 0; i < newPoints.count(); i++) {
if (acceptedNewPoints->contains(newPoints[i].id()))
continue;
QPointF p = item->mapFromScene(newPoints[i].scenePos());
- if (item->contains(p))
+ if (item->contains(p)) {
+ matchingNewPoints.insert(newPoints[i].id());
matchingPoints << newPoints[i];
+ }
}
}
+ // If there are no matching new points, and the existing points are all stationary,
+ // there's no need to send an event to this item. This is required by a test in
+ // tst_qquickcanvas::touchEvent_basic:
+ // a single stationary press on an item shouldn't cause an event
+ if (matchingNewPoints.isEmpty()) {
+ bool stationaryOnly = true;
+ Q_FOREACH (QTouchEvent::TouchPoint tp, matchingPoints)
+ if (tp.state() != Qt::TouchPointStationary)
+ stationaryOnly = false;
+ if (stationaryOnly)
+ matchingPoints.clear();
+ }
+
+ if (!matchingPoints.isEmpty()) {
+ // Now we know this item might be interested in the event. Copy and send it, but
+ // with only the subset of TouchPoints which are relevant to that item: that's matchingPoints.
+ QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
+ transformTouchPoints(matchingPoints, itemPrivate->canvasToItemTransform());
+ deliverMatchingPointsToItem(item, event, acceptedNewPoints, matchingNewPoints, matchingPoints);
+ }
- if (matchingPoints.count() > 0 || (*updatedPoints)[item].count() > 0) {
- QList<QTouchEvent::TouchPoint> &eventPoints = (*updatedPoints)[item];
- eventPoints.append(matchingPoints);
- transformTouchPoints(eventPoints, itemPrivate->canvasToItemTransform());
+ // record the fact that this item has been visited already
+ updatedPoints->remove(item);
- Qt::TouchPointStates eventStates;
- for (int i=0; i<eventPoints.count(); i++)
- eventStates |= eventPoints[i].state();
- // if all points have the same state, set the event type accordingly
- QEvent::Type eventType;
- switch (eventStates) {
- case Qt::TouchPointPressed:
- eventType = QEvent::TouchBegin;
- break;
- case Qt::TouchPointReleased:
- eventType = QEvent::TouchEnd;
- break;
- default:
- eventType = QEvent::TouchUpdate;
- break;
+ // recursion is done only if ALL touch points have been delivered
+ return (acceptedNewPoints->count() == newPoints.count() && updatedPoints->isEmpty());
+}
+
+// touchEventForItemBounds has no means to generate a touch event that contains
+// only the points that are relevant for this item. Thus the need for
+// matchingPoints to already be that set of interesting points.
+// They are all pre-transformed, too.
+bool QQuickCanvasPrivate::deliverMatchingPointsToItem(QQuickItem *item, QTouchEvent *event, QSet<int> *acceptedNewPoints, const QSet<int> &matchingNewPoints, const QList<QTouchEvent::TouchPoint> &matchingPoints)
+{
+ QScopedPointer<QTouchEvent> touchEvent(touchEventWithPoints(*event, matchingPoints));
+ touchEvent.data()->setTarget(item);
+ bool touchEventAccepted = false;
+
+ // First check whether the parent wants to be a filter,
+ // and if the parent accepts the event we are done.
+ if (sendFilteredTouchEvent(item->parentItem(), item, event)) {
+ event->accept();
+ return true;
+ }
+
+ // Since it can change in sendEvent, update itemForTouchPointId now
+ foreach (int id, matchingNewPoints)
+ itemForTouchPointId[id] = item;
+
+ // Deliver the touch event to the given item
+ QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
+ itemPrivate->deliverTouchEvent(touchEvent.data());
+ touchEventAccepted = touchEvent->isAccepted();
+
+ // If the touch event wasn't accepted, synthesize a mouse event and see if the item wants it.
+ if (!touchEventAccepted && (itemPrivate->acceptedMouseButtons() & Qt::LeftButton)) {
+ // send mouse event
+ event->setAccepted(translateTouchToMouse(item, event));
+ if (event->isAccepted()) {
+ touchEventAccepted = true;
}
+ }
- if (eventStates != Qt::TouchPointStationary) {
- QTouchEvent touchEvent(eventType);
- touchEvent.setWindow(event->window());
- touchEvent.setTarget(item);
- touchEvent.setDevice(event->device());
- touchEvent.setModifiers(event->modifiers());
- touchEvent.setTouchPointStates(eventStates);
- touchEvent.setTouchPoints(eventPoints);
- touchEvent.setTimestamp(event->timestamp());
-
- for (int i = 0; i < matchingPoints.count(); ++i)
- itemForTouchPointId[matchingPoints[i].id()] = item;
-
- touchEvent.accept();
- q->sendEvent(item, &touchEvent);
-
- if (touchEvent.isAccepted()) {
- for (int i = 0; i < matchingPoints.count(); ++i)
- acceptedNewPoints->insert(matchingPoints[i].id());
+ if (touchEventAccepted) {
+ // If the touch was accepted (regardless by whom or in what form),
+ // update acceptedNewPoints.
+ foreach (int id, matchingNewPoints)
+ acceptedNewPoints->insert(id);
+ } else {
+ // But if the event was not accepted then we know this item
+ // will not be interested in further updates for those touchpoint IDs either.
+ foreach (int id, matchingNewPoints)
+ if (itemForTouchPointId[id] == item)
+ itemForTouchPointId.remove(id);
+ }
+
+ return touchEventAccepted;
+}
+
+QTouchEvent *QQuickCanvasPrivate::touchEventForItemBounds(QQuickItem *target, const QTouchEvent &originalEvent)
+{
+ const QList<QTouchEvent::TouchPoint> &touchPoints = originalEvent.touchPoints();
+ QList<QTouchEvent::TouchPoint> pointsInBounds;
+ // if all points are stationary, the list of points should be empty to signal a no-op
+ if (originalEvent.touchPointStates() != Qt::TouchPointStationary) {
+ for (int i = 0; i < touchPoints.count(); ++i) {
+ const QTouchEvent::TouchPoint &tp = touchPoints.at(i);
+ if (tp.state() == Qt::TouchPointPressed) {
+ QPointF p = target->mapFromScene(tp.scenePos());
+ if (target->contains(p))
+ pointsInBounds.append(tp);
} else {
- for (int i = 0; i < matchingPoints.count(); ++i)
- if (itemForTouchPointId.value(matchingPoints[i].id()) == item)
- itemForTouchPointId.remove(matchingPoints[i].id());
+ pointsInBounds.append(tp);
}
}
+ transformTouchPoints(pointsInBounds, QQuickItemPrivate::get(target)->canvasToItemTransform());
}
- updatedPoints->remove(item);
- if (acceptedNewPoints->count() == newPoints.count() && updatedPoints->isEmpty())
- return true;
+ QTouchEvent* touchEvent = touchEventWithPoints(originalEvent, pointsInBounds);
+ touchEvent->setTarget(target);
+ return touchEvent;
+}
- return false;
+QTouchEvent *QQuickCanvasPrivate::touchEventWithPoints(const QTouchEvent &event, const QList<QTouchEvent::TouchPoint> &newPoints)
+{
+ Qt::TouchPointStates eventStates;
+ for (int i=0; i<newPoints.count(); i++)
+ eventStates |= newPoints[i].state();
+ // if all points have the same state, set the event type accordingly
+ QEvent::Type eventType = event.type();
+ switch (eventStates) {
+ case Qt::TouchPointPressed:
+ eventType = QEvent::TouchBegin;
+ break;
+ case Qt::TouchPointReleased:
+ eventType = QEvent::TouchEnd;
+ break;
+ default:
+ eventType = QEvent::TouchUpdate;
+ break;
+ }
+
+ QTouchEvent *touchEvent = new QTouchEvent(eventType);
+ touchEvent->setWindow(event.window());
+ touchEvent->setTarget(event.target());
+ touchEvent->setDevice(event.device());
+ touchEvent->setModifiers(event.modifiers());
+ touchEvent->setTouchPoints(newPoints);
+ touchEvent->setTouchPointStates(eventStates);
+ touchEvent->setTimestamp(event.timestamp());
+ touchEvent->accept();
+ return touchEvent;
}
#ifndef QT_NO_DRAGANDDROP
@@ -1702,6 +1818,59 @@ bool QQuickCanvasPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQuickIte
}
#endif // QT_NO_DRAGANDDROP
+bool QQuickCanvasPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QTouchEvent *event)
+{
+ if (!target)
+ return false;
+
+ QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(target);
+ if (targetPrivate->filtersChildMouseEvents) {
+ QScopedPointer<QTouchEvent> targetEvent(touchEventForItemBounds(target, *event));
+ if (!targetEvent->touchPoints().isEmpty()) {
+ QVector<int> touchIds;
+ for (int i = 0; i < event->touchPoints().size(); ++i)
+ touchIds.append(event->touchPoints().at(i).id());
+ if (target->childMouseEventFilter(item, targetEvent.data())) {
+ target->grabTouchPoints(touchIds);
+ if (mouseGrabberItem) {
+ mouseGrabberItem->ungrabMouse();
+ touchMouseId = -1;
+ }
+ return true;
+ }
+
+ // Only offer a mouse event to the filter if we have one point
+ if (targetEvent->touchPoints().count() == 1) {
+ QEvent::Type t;
+ const QTouchEvent::TouchPoint &tp = targetEvent->touchPoints().first();
+ switch (tp.state()) {
+ case Qt::TouchPointPressed:
+ t = QEvent::MouseButtonPress;
+ break;
+ case Qt::TouchPointReleased:
+ t = QEvent::MouseButtonRelease;
+ break;
+ default:
+ // move or stationary
+ t = QEvent::MouseMove;
+ break;
+ }
+
+ // targetEvent is already transformed wrt local position, velocity, etc.
+ QScopedPointer<QMouseEvent> mouseEvent(touchToMouseEvent(t, targetEvent->touchPoints().first(), event, item, false));
+ if (target->childMouseEventFilter(item, mouseEvent.data())) {
+ itemForTouchPointId[tp.id()] = target;
+ touchMouseId = tp.id();
+ target->grabMouse();
+ return true;
+ }
+ }
+ }
+ }
+
+ return sendFilteredTouchEvent(target->parentItem(), item, event);
+}
+
bool QQuickCanvasPrivate::sendFilteredMouseEvent(QQuickItem *target, QQuickItem *item, QEvent *event)
{
if (!target)
@@ -1721,14 +1890,13 @@ bool QQuickCanvasPrivate::sendFilteredMouseEvent(QQuickItem *target, QQuickItem
bool QQuickCanvasPrivate::dragOverThreshold(qreal d, Qt::Axis axis, QMouseEvent *event)
{
QStyleHints *styleHints = qApp->styleHints();
- QQuickMouseEventEx *extended = QQuickMouseEventEx::extended(event);
- bool dragVelocityLimitAvailable = extended
- && extended->capabilities().testFlag(QTouchDevice::Velocity)
+ int caps = QGuiApplicationPrivate::mouseEventCaps(event);
+ bool dragVelocityLimitAvailable = (caps & QTouchDevice::Velocity)
&& styleHints->startDragVelocity();
bool overThreshold = qAbs(d) > styleHints->startDragDistance();
if (dragVelocityLimitAvailable) {
- qreal velocity = axis == Qt::XAxis ? extended->velocity().x()
- : extended->velocity().y();
+ QVector2D velocityVec = QGuiApplicationPrivate::mouseEventVelocity(event);
+ qreal velocity = axis == Qt::XAxis ? velocityVec.x() : velocityVec.y();
overThreshold |= qAbs(velocity) > styleHints->startDragVelocity();
}
return overThreshold;
@@ -1768,6 +1936,7 @@ bool QQuickCanvas::sendEvent(QQuickItem *item, QEvent *e)
case QEvent::MouseMove:
// XXX todo - should sendEvent be doing this? how does it relate to forwarded events?
if (!d->sendFilteredMouseEvent(item->parentItem(), item, e)) {
+ // accept because qml items by default accept and have to explicitly opt out of accepting
e->accept();
QQuickItemPrivate::get(item)->deliverMouseEvent(static_cast<QMouseEvent *>(e));
}
@@ -1789,12 +1958,10 @@ bool QQuickCanvas::sendEvent(QQuickItem *item, QEvent *e)
case QEvent::TouchBegin:
case QEvent::TouchUpdate:
case QEvent::TouchEnd:
+ d->sendFilteredTouchEvent(item->parentItem(), item, static_cast<QTouchEvent *>(e));
+ break;
case QEvent::TouchCancel:
- // 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<QTouchEvent *>(e));
- }
+ QQuickItemPrivate::get(item)->deliverTouchEvent(static_cast<QTouchEvent *>(e));
break;
#ifndef QT_NO_DRAGANDDROP
case QEvent::DragEnter:
diff --git a/src/quick/items/qquickcanvas_p.h b/src/quick/items/qquickcanvas_p.h
index e08c342c44..1bbf36f541 100644
--- a/src/quick/items/qquickcanvas_p.h
+++ b/src/quick/items/qquickcanvas_p.h
@@ -110,6 +110,8 @@ public:
QQmlListProperty<QObject> data();
QQuickItem *activeFocusItem;
+
+ // Keeps track of the item currently receiving mouse events
QQuickItem *mouseGrabberItem;
#ifndef QT_NO_DRAGANDDROP
QQuickDragGrabber dragGrabber;
@@ -119,9 +121,10 @@ public:
// Mouse positions are saved in widget coordinates
QPointF lastMousePosition;
- void translateTouchToMouse(QTouchEvent *event);
+ bool translateTouchToMouse(QQuickItem *item, QTouchEvent *event);
void translateTouchEvent(QTouchEvent *touchEvent);
static void transformTouchPoints(QList<QTouchEvent::TouchPoint> &touchPoints, const QTransform &transform);
+ static QMouseEvent *cloneMouseEvent(QMouseEvent *event, QPointF *transformedLocalPos = 0);
bool deliverInitialMousePressEvent(QQuickItem *, QMouseEvent *);
bool deliverMouseEvent(QMouseEvent *);
bool sendFilteredMouseEvent(QQuickItem *, QQuickItem *, QEvent *);
@@ -129,7 +132,12 @@ public:
bool deliverTouchPoints(QQuickItem *, QTouchEvent *, const QList<QTouchEvent::TouchPoint> &, QSet<int> *,
QHash<QQuickItem *, QList<QTouchEvent::TouchPoint> > *);
bool deliverTouchEvent(QTouchEvent *);
+ bool deliverTouchCancelEvent(QTouchEvent *);
bool deliverHoverEvent(QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, bool &accepted);
+ bool deliverMatchingPointsToItem(QQuickItem *item, QTouchEvent *event, QSet<int> *acceptedNewPoints, const QSet<int> &matchingNewPoints, const QList<QTouchEvent::TouchPoint> &matchingPoints);
+ QTouchEvent *touchEventForItemBounds(QQuickItem *target, const QTouchEvent &originalEvent);
+ QTouchEvent *touchEventWithPoints(const QTouchEvent &event, const QList<QTouchEvent::TouchPoint> &newPoints);
+ bool sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QTouchEvent *event);
bool sendHoverEvent(QEvent::Type, QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos,
Qt::KeyboardModifiers modifiers, bool accepted);
bool clearHover();
@@ -195,6 +203,7 @@ public:
uint renderTargetId;
QSize renderTargetSize;
+ // Keeps track of which touch point (int) was last accepted by which item
QHash<int, QQuickItem *> itemForTouchPointId;
mutable QQuickCanvasIncubationController *incubationController;
diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h
index 1809394ed4..0cf53f6a76 100644
--- a/src/quick/items/qquickevents_p_p.h
+++ b/src/quick/items/qquickevents_p_p.h
@@ -137,75 +137,6 @@ private:
bool _accepted;
};
-class QQuickMouseEventEx : public QMouseEvent
-{
-public:
- QQuickMouseEventEx(Type type, const QPointF &pos, Qt::MouseButton button,
- Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers)
- : QMouseEvent(type,pos,button,buttons,modifiers)
- {
- }
-
- QQuickMouseEventEx(Type type, const QPointF &pos, const QPointF &globalPos,
- Qt::MouseButton button, Qt::MouseButtons buttons,
- Qt::KeyboardModifiers modifiers)
- : QMouseEvent(type,pos,globalPos,button,buttons,modifiers)
- {
- }
-
- QQuickMouseEventEx(Type type, const QPointF &pos, const QPointF &windowPos, const QPointF &globalPos,
- Qt::MouseButton button, Qt::MouseButtons buttons,
- Qt::KeyboardModifiers modifiers)
- : QMouseEvent(type,pos,windowPos,globalPos,button,buttons,modifiers)
- {
- }
-
- QQuickMouseEventEx(const QMouseEvent &event)
- : QMouseEvent(event)
- {
- const QQuickMouseEventEx *eventEx = extended(&event);
- if (eventEx) {
- setVelocity(eventEx->velocity());
- setCapabilities(eventEx->capabilities());
- }
- }
-
- ~QQuickMouseEventEx()
- {
- d = 0;
- }
-
- static const QQuickMouseEventEx *extended(const QMouseEvent *e) {
- const QQuickMouseEventEx *ex = static_cast<const QQuickMouseEventEx*>(e);
- return reinterpret_cast<const QMouseEvent*>(ex->d) == e ? ex : 0;
- }
- static QQuickMouseEventEx *extended(QMouseEvent *e) {
- QQuickMouseEventEx *ex = static_cast<QQuickMouseEventEx*>(e);
- return reinterpret_cast<QMouseEvent*>(ex->d) == e ? ex : 0;
- }
-
- void setExtended() {
- d = reinterpret_cast<QEventPrivate*>(this);
- }
-
- void setVelocity(const QVector2D &v) {
- setExtended();
- _velocity = v;
- }
- QVector2D velocity() const { return _velocity; }
-
- void setCapabilities(QTouchDevice::Capabilities caps) {
- setExtended();
- _capabilities = caps;
- }
- QTouchDevice::Capabilities capabilities() const { return _capabilities; }
-
-private:
- QVector2D _velocity;
- QTouchDevice::Capabilities _capabilities;
-};
-
-
class QQuickWheelEvent : public QObject
{
Q_OBJECT
diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp
index 9d690347b1..3a56dd1c23 100644
--- a/src/quick/items/qquickflickable.cpp
+++ b/src/quick/items/qquickflickable.cpp
@@ -51,6 +51,7 @@
#include <QtQml/qqmlinfo.h>
#include <QtGui/qevent.h>
#include <QtGui/qguiapplication.h>
+#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/qstylehints.h>
#include "qplatformdefs.h"
@@ -970,7 +971,7 @@ void QQuickFlickablePrivate::handleMousePressEvent(QMouseEvent *event)
vData.dragMaxBound = q->maxYExtent();
fixupMode = Normal;
lastPos = QPointF();
- pressPos = event->localPos();
+ pressPos = event->windowPos();
hData.pressPos = hData.move.value();
vData.pressPos = vData.move.value();
bool wasFlicking = hData.flicking || vData.flicking;
@@ -1005,7 +1006,7 @@ void QQuickFlickablePrivate::handleMouseMoveEvent(QMouseEvent *event)
qint64 elapsedSincePress = computeCurrentTime(event) - lastPressTime;
if (q->yflick()) {
- qreal dy = event->localPos().y() - pressPos.y();
+ qreal dy = event->windowPos().y() - pressPos.y();
bool overThreshold = QQuickCanvasPrivate::dragOverThreshold(dy, Qt::YAxis, event);
if (overThreshold || elapsedSincePress > 200) {
if (!vMoved)
@@ -1039,7 +1040,7 @@ void QQuickFlickablePrivate::handleMouseMoveEvent(QMouseEvent *event)
}
if (q->xflick()) {
- qreal dx = event->localPos().x() - pressPos.x();
+ qreal dx = event->windowPos().x() - pressPos.x();
bool overThreshold = QQuickCanvasPrivate::dragOverThreshold(dx, Qt::XAxis, event);
if (overThreshold || elapsedSincePress > 200) {
if (!hMoved)
@@ -1096,25 +1097,24 @@ void QQuickFlickablePrivate::handleMouseMoveEvent(QMouseEvent *event)
if (elapsed <= 0)
return;
lastPosTime = currentTimestamp;
- QQuickMouseEventEx *extended = QQuickMouseEventEx::extended(event);
if (q->yflick() && !rejectY) {
- if (extended && extended->capabilities().testFlag(QTouchDevice::Velocity)) {
- vData.addVelocitySample(extended->velocity().y(), maxVelocity);
+ if (QGuiApplicationPrivate::mouseEventCaps(event) & QTouchDevice::Velocity) {
+ vData.addVelocitySample(QGuiApplicationPrivate::mouseEventVelocity(event).y(), maxVelocity);
} else {
- qreal dy = event->localPos().y() - (lastPos.isNull() ? pressPos.y() : lastPos.y());
+ qreal dy = event->windowPos().y() - (lastPos.isNull() ? pressPos.y() : lastPos.y());
vData.addVelocitySample(dy/elapsed, maxVelocity);
}
}
if (q->xflick() && !rejectX) {
- if (extended && extended->capabilities().testFlag(QTouchDevice::Velocity)) {
- hData.addVelocitySample(extended->velocity().x(), maxVelocity);
+ if (QGuiApplicationPrivate::mouseEventCaps(event) & QTouchDevice::Velocity) {
+ hData.addVelocitySample(QGuiApplicationPrivate::mouseEventVelocity(event).x(), maxVelocity);
} else {
- qreal dx = event->localPos().x() - (lastPos.isNull() ? pressPos.x() : lastPos.x());
+ qreal dx = event->windowPos().x() - (lastPos.isNull() ? pressPos.x() : lastPos.x());
hData.addVelocitySample(dx/elapsed, maxVelocity);
}
}
- lastPos = event->localPos();
+ lastPos = event->windowPos();
}
void QQuickFlickablePrivate::handleMouseReleaseEvent(QMouseEvent *event)
@@ -1141,9 +1141,8 @@ void QQuickFlickablePrivate::handleMouseReleaseEvent(QMouseEvent *event)
qreal vVelocity = 0;
if (elapsed < 100 && vData.velocity != 0.) {
- QQuickMouseEventEx *extended = QQuickMouseEventEx::extended(event);
- vVelocity = (extended && extended->capabilities().testFlag(QTouchDevice::Velocity))
- ? extended->velocity().y() : vData.velocity;
+ vVelocity = (QGuiApplicationPrivate::mouseEventCaps(event) & QTouchDevice::Velocity)
+ ? QGuiApplicationPrivate::mouseEventVelocity(event).y() : vData.velocity;
}
if ((vData.atBeginning && vVelocity > 0.) || (vData.atEnd && vVelocity < 0.)) {
vVelocity /= 2;
@@ -1157,9 +1156,8 @@ void QQuickFlickablePrivate::handleMouseReleaseEvent(QMouseEvent *event)
qreal hVelocity = 0;
if (elapsed < 100 && hData.velocity != 0.) {
- QQuickMouseEventEx *extended = QQuickMouseEventEx::extended(event);
- hVelocity = (extended && extended->capabilities().testFlag(QTouchDevice::Velocity))
- ? extended->velocity().x() : hData.velocity;
+ hVelocity = (QGuiApplicationPrivate::mouseEventCaps(event) & QTouchDevice::Velocity)
+ ? QGuiApplicationPrivate::mouseEventVelocity(event).x() : hData.velocity;
}
if ((hData.atBeginning && hVelocity > 0.) || (hData.atEnd && hVelocity < 0.)) {
hVelocity /= 2;
@@ -1175,7 +1173,7 @@ void QQuickFlickablePrivate::handleMouseReleaseEvent(QMouseEvent *event)
bool flickedV = false;
vVelocity *= flickBoost;
- if (q->yflick() && qAbs(vVelocity) > MinimumFlickVelocity && qAbs(event->localPos().y() - pressPos.y()) > FlickThreshold) {
+ if (q->yflick() && qAbs(vVelocity) > MinimumFlickVelocity && qAbs(event->windowPos().y() - pressPos.y()) > FlickThreshold) {
velocityTimeline.reset(vData.smoothVelocity);
vData.smoothVelocity.setValue(-vVelocity);
flickedV = flickY(vVelocity);
@@ -1185,7 +1183,7 @@ void QQuickFlickablePrivate::handleMouseReleaseEvent(QMouseEvent *event)
bool flickedH = false;
hVelocity *= flickBoost;
- if (q->xflick() && qAbs(hVelocity) > MinimumFlickVelocity && qAbs(event->localPos().x() - pressPos.x()) > FlickThreshold) {
+ if (q->xflick() && qAbs(hVelocity) > MinimumFlickVelocity && qAbs(event->windowPos().x() - pressPos.x()) > FlickThreshold) {
velocityTimeline.reset(hData.smoothVelocity);
hData.smoothVelocity.setValue(-hVelocity);
flickedH = flickX(hVelocity);
@@ -1305,7 +1303,7 @@ void QQuickFlickablePrivate::captureDelayedPress(QMouseEvent *event)
if (!isOutermostPressDelay())
return;
delayedPressTarget = q->canvas()->mouseGrabberItem();
- delayedPressEvent = new QQuickMouseEventEx(*event);
+ delayedPressEvent = QQuickCanvasPrivate::cloneMouseEvent(event);
delayedPressEvent->setAccepted(false);
delayedPressTimer.start(pressDelay, q);
}
@@ -1974,26 +1972,18 @@ bool QQuickFlickable::sendMouseEvent(QMouseEvent *event)
bool grabberDisabled = grabber && !grabber->isEnabled();
bool stealThisEvent = d->stealMouse;
if ((stealThisEvent || contains(localPos)) && (!grabber || !grabber->keepMouseGrab() || grabberDisabled)) {
- QQuickMouseEventEx mouseEvent(event->type(), localPos,
- event->windowPos(), event->screenPos(),
- event->button(), event->buttons(), event->modifiers());
- QQuickMouseEventEx *eventEx = QQuickMouseEventEx::extended(event);
- if (eventEx) {
- mouseEvent.setVelocity(eventEx->velocity());
- mouseEvent.setCapabilities(eventEx->capabilities());
- }
- mouseEvent.setTimestamp(event->timestamp());
- mouseEvent.setAccepted(false);
+ QScopedPointer<QMouseEvent> mouseEvent(QQuickCanvasPrivate::cloneMouseEvent(event, &localPos));
+ mouseEvent->setAccepted(false);
- switch (mouseEvent.type()) {
+ switch (mouseEvent->type()) {
case QEvent::MouseMove:
- d->handleMouseMoveEvent(&mouseEvent);
+ d->handleMouseMoveEvent(mouseEvent.data());
break;
case QEvent::MouseButtonPress:
if (d->pressed) // we are already pressed - this is a delayed replay
return false;
- d->handleMousePressEvent(&mouseEvent);
+ d->handleMousePressEvent(mouseEvent.data());
d->captureDelayedPress(event);
stealThisEvent = d->stealMouse; // Update stealThisEvent in case changed by function call above
break;
@@ -2013,7 +2003,7 @@ bool QQuickFlickable::sendMouseEvent(QMouseEvent *event)
d->pressed = false;
return true;
}
- d->handleMouseReleaseEvent(&mouseEvent);
+ d->handleMouseReleaseEvent(mouseEvent.data());
break;
default:
break;
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp
index 02a0c22c5d..2cf12b6c03 100644
--- a/src/quick/items/qquickitem.cpp
+++ b/src/quick/items/qquickitem.cpp
@@ -2290,6 +2290,11 @@ void QQuickItemPrivate::derefCanvas()
QQuickCanvasPrivate *c = QQuickCanvasPrivate::get(canvas);
if (polishScheduled)
c->itemsToPolish.remove(q);
+ QMutableHashIterator<int, QQuickItem *> itemTouchMapIt(c->itemForTouchPointId);
+ while (itemTouchMapIt.hasNext()) {
+ if (itemTouchMapIt.next().value() == q)
+ itemTouchMapIt.remove();
+ }
if (c->mouseGrabberItem == q)
c->mouseGrabberItem = 0;
if ( hoverEnabled )
@@ -5131,7 +5136,7 @@ void QQuickItem::setKeepMouseGrab(bool keep)
\sa ungrabTouchPoints(), setKeepTouchGrab()
*/
-void QQuickItem::grabTouchPoints(const QList<int> &ids)
+void QQuickItem::grabTouchPoints(const QVector<int> &ids)
{
Q_D(QQuickItem);
if (!d->canvas)
diff --git a/src/quick/items/qquickitem.h b/src/quick/items/qquickitem.h
index c0fd01fb7e..7174edd057 100644
--- a/src/quick/items/qquickitem.h
+++ b/src/quick/items/qquickitem.h
@@ -284,7 +284,7 @@ public:
bool filtersChildMouseEvents() const;
void setFiltersChildMouseEvents(bool filter);
- void grabTouchPoints(const QList<int> &ids);
+ void grabTouchPoints(const QVector<int> &ids);
void ungrabTouchPoints();
bool keepTouchGrab() const;
void setKeepTouchGrab(bool);
diff --git a/src/quick/items/qquickmultipointtoucharea.cpp b/src/quick/items/qquickmultipointtoucharea.cpp
index f980fdce6c..f4e49bd66c 100644
--- a/src/quick/items/qquickmultipointtoucharea.cpp
+++ b/src/quick/items/qquickmultipointtoucharea.cpp
@@ -418,7 +418,7 @@ void QQuickMultiPointTouchArea::grabGesture()
grabMouse();
setKeepMouseGrab(true);
- grabTouchPoints(_touchPoints.keys());
+ grabTouchPoints(_touchPoints.keys().toVector());
setKeepTouchGrab(true);
}
diff --git a/src/quick/items/qquickpincharea.cpp b/src/quick/items/qquickpincharea.cpp
index 8c1c7bd096..f58990485e 100644
--- a/src/quick/items/qquickpincharea.cpp
+++ b/src/quick/items/qquickpincharea.cpp
@@ -247,6 +247,8 @@ QQuickPinchArea::QQuickPinchArea(QQuickItem *parent)
{
Q_D(QQuickPinchArea);
d->init();
+ setAcceptedMouseButtons(Qt::LeftButton);
+ setFiltersChildMouseEvents(true);
}
QQuickPinchArea::~QQuickPinchArea()
@@ -281,6 +283,20 @@ void QQuickPinchArea::touchEvent(QTouchEvent *event)
return;
}
+ // A common non-trivial starting scenario is the user puts down one finger,
+ // then that finger remains stationary while putting down a second one.
+ // However QQuickCanvas will not send TouchUpdates for TouchPoints which
+ // were not initially accepted; that would be inefficient and noisy.
+ // So even if there is only one touchpoint so far, it's important to accept it
+ // in order to get updates later on (and it's accepted by default anyway).
+ // If the user puts down one finger, we're waiting for the other finger to drop.
+ // Therefore updatePinch() must do the right thing for any combination of
+ // points and states that may occur, and there is no reason to ignore any event.
+ // One consequence though is that if PinchArea is on top of something else,
+ // it's always going to accept the touches, and that means the item underneath
+ // will not get them (unless the PA's parent is doing parent filtering,
+ // as the Flickable does, for example).
+
switch (event->type()) {
case QEvent::TouchBegin:
case QEvent::TouchUpdate:
@@ -329,17 +345,27 @@ void QQuickPinchArea::updatePinch()
setKeepMouseGrab(false);
return;
}
+
+ if (d->touchPoints.count() == 1) {
+ setKeepMouseGrab(false);
+ }
+
QTouchEvent::TouchPoint touchPoint1 = d->touchPoints.at(0);
QTouchEvent::TouchPoint touchPoint2 = d->touchPoints.at(d->touchPoints. count() >= 2 ? 1 : 0);
+ QRectF bounds = clipRect();
+ // Pinch is not started unless there are exactly two touch points
+ // AND one or more of the points has just now been pressed (wasn't pressed already)
+ // AND both points are inside the bounds.
if (d->touchPoints.count() == 2
- && (touchPoint1.state() & Qt::TouchPointPressed || touchPoint2.state() & Qt::TouchPointPressed)) {
+ && (touchPoint1.state() & Qt::TouchPointPressed || touchPoint2.state() & Qt::TouchPointPressed) &&
+ bounds.contains(touchPoint1.pos()) && bounds.contains(touchPoint2.pos())) {
d->id1 = touchPoint1.id();
d->sceneStartPoint1 = touchPoint1.scenePos();
d->sceneStartPoint2 = touchPoint2.scenePos();
d->pinchActivated = true;
d->initPinch = true;
}
- if (d->pinchActivated && !d->pinchRejected){
+ if (d->pinchActivated && !d->pinchRejected) {
const int dragThreshold = qApp->styleHints()->startDragDistance();
QPointF p1 = touchPoint1.scenePos();
QPointF p2 = touchPoint2.scenePos();
@@ -361,10 +387,10 @@ void QQuickPinchArea::updatePinch()
angle -= 360;
if (!d->inPinch || d->initPinch) {
if (d->touchPoints.count() >= 2
- && (qAbs(p1.x()-d->sceneStartPoint1.x()) > dragThreshold
- || qAbs(p1.y()-d->sceneStartPoint1.y()) > dragThreshold
- || qAbs(p2.x()-d->sceneStartPoint2.x()) > dragThreshold
- || qAbs(p2.y()-d->sceneStartPoint2.y()) > dragThreshold)) {
+ && (qAbs(p1.x()-d->sceneStartPoint1.x()) >= dragThreshold
+ || qAbs(p1.y()-d->sceneStartPoint1.y()) >= dragThreshold
+ || qAbs(p2.x()-d->sceneStartPoint2.x()) >= dragThreshold
+ || qAbs(p2.y()-d->sceneStartPoint2.y()) >= dragThreshold)) {
d->initPinch = false;
d->sceneStartCenter = sceneCenter;
d->sceneLastCenter = sceneCenter;
@@ -461,106 +487,24 @@ void QQuickPinchArea::updatePinch()
}
}
-void QQuickPinchArea::mousePressEvent(QMouseEvent *event)
-{
- Q_D(QQuickPinchArea);
- d->stealMouse = false;
- if (!d->enabled)
- QQuickItem::mousePressEvent(event);
- else {
- setKeepMouseGrab(false);
- event->setAccepted(true);
- }
-}
-
-void QQuickPinchArea::mouseMoveEvent(QMouseEvent *event)
-{
- Q_D(QQuickPinchArea);
- if (!d->enabled) {
- QQuickItem::mouseMoveEvent(event);
- return;
- }
-}
-
-void QQuickPinchArea::mouseReleaseEvent(QMouseEvent *event)
-{
- Q_D(QQuickPinchArea);
- d->stealMouse = false;
- if (!d->enabled) {
- QQuickItem::mouseReleaseEvent(event);
- } else {
- QQuickCanvas *c = canvas();
- if (c && c->mouseGrabberItem() == this)
- ungrabMouse();
- setKeepMouseGrab(false);
- }
-}
-
-void QQuickPinchArea::mouseUngrabEvent()
-{
- setKeepMouseGrab(false);
-}
-
-bool QQuickPinchArea::sendMouseEvent(QMouseEvent *event)
-{
- Q_D(QQuickPinchArea);
- QPointF localPos = mapFromScene(event->windowPos());
-
- QQuickCanvas *c = canvas();
- QQuickItem *grabber = c ? c->mouseGrabberItem() : 0;
- bool stealThisEvent = d->stealMouse;
- if ((stealThisEvent || contains(localPos)) && (!grabber || !grabber->keepMouseGrab())) {
- QMouseEvent mouseEvent(event->type(), localPos, 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) {
- d->stealMouse = false;
- if (c && c->mouseGrabberItem() == this)
- ungrabMouse();
- setKeepMouseGrab(false);
- }
- return false;
-}
-
bool QQuickPinchArea::childMouseEventFilter(QQuickItem *i, QEvent *e)
{
Q_D(QQuickPinchArea);
if (!d->enabled || !isVisible())
return QQuickItem::childMouseEventFilter(i, e);
switch (e->type()) {
- case QEvent::MouseButtonPress:
- case QEvent::MouseMove:
- case QEvent::MouseButtonRelease:
- return sendMouseEvent(static_cast<QMouseEvent *>(e));
- break;
case QEvent::TouchBegin:
case QEvent::TouchUpdate: {
QTouchEvent *touch = static_cast<QTouchEvent*>(e);
- d->touchPoints.clear();
- for (int i = 0; i < touch->touchPoints().count(); ++i)
- if (!(touch->touchPoints().at(i).state() & Qt::TouchPointReleased))
- d->touchPoints << touch->touchPoints().at(i);
- updatePinch();
+ if (touch->touchPoints().count() > 1) {
+ touchEvent(touch);
+ } else {
+ d->touchPoints.clear();
+ for (int i = 0; i < touch->touchPoints().count(); ++i)
+ if (!(touch->touchPoints().at(i).state() & Qt::TouchPointReleased))
+ d->touchPoints << touch->touchPoints().at(i);
+ updatePinch();
+ }
}
return d->inPinch;
case QEvent::TouchEnd:
diff --git a/src/quick/items/qquickpincharea_p.h b/src/quick/items/qquickpincharea_p.h
index b7328b3cb8..3abe3bbc45 100644
--- a/src/quick/items/qquickpincharea_p.h
+++ b/src/quick/items/qquickpincharea_p.h
@@ -278,11 +278,6 @@ Q_SIGNALS:
void pinchFinished(QQuickPinchEvent *pinch);
protected:
- virtual void mousePressEvent(QMouseEvent *event);
- virtual void mouseReleaseEvent(QMouseEvent *event);
- virtual void mouseMoveEvent(QMouseEvent *event);
- virtual void mouseUngrabEvent();
- virtual bool sendMouseEvent(QMouseEvent *event);
virtual bool childMouseEventFilter(QQuickItem *i, QEvent *e);
virtual void touchEvent(QTouchEvent *event);
diff --git a/tests/auto/quick/qquickcanvas/tst_qquickcanvas.cpp b/tests/auto/quick/qquickcanvas/tst_qquickcanvas.cpp
index ed3dceb403..82af3678da 100644
--- a/tests/auto/quick/qquickcanvas/tst_qquickcanvas.cpp
+++ b/tests/auto/quick/qquickcanvas/tst_qquickcanvas.cpp
@@ -51,6 +51,7 @@
#include "../../shared/util.h"
#include <QSignalSpy>
#include <private/qquickcanvas_p.h>
+#include <private/qguiapplication_p.h>
struct TouchEventData {
QEvent::Type type;
@@ -144,7 +145,8 @@ class TestTouchItem : public QQuickRectangle
Q_OBJECT
public:
TestTouchItem(QQuickItem *parent = 0)
- : QQuickRectangle(parent), acceptEvents(true), mousePressId(0),
+ : QQuickRectangle(parent), acceptTouchEvents(true), acceptMouseEvents(true),
+ mousePressId(0),
spinLoopWhenPressed(false), touchEventCount(0)
{
border()->setWidth(1);
@@ -153,16 +155,20 @@ public:
}
void reset() {
- acceptEvents = true;
+ acceptTouchEvents = acceptMouseEvents = true;
setEnabled(true);
setOpacity(1.0);
lastEvent = makeTouchData(QEvent::None, canvas(), 0, QList<QTouchEvent::TouchPoint>());//CHECK_VALID
+
+ lastVelocity = lastVelocityFromMouseMove = QVector2D();
+ lastMousePos = QPointF();
+ lastMouseCapabilityFlags = 0;
}
static void clearMousePressCounter()
{
- mousePressNum = 0;
+ mousePressNum = mouseMoveNum = mouseReleaseNum = 0;
}
void clearTouchEventCounter()
@@ -170,40 +176,78 @@ public:
touchEventCount = 0;
}
- bool acceptEvents;
+ bool acceptTouchEvents;
+ bool acceptMouseEvents;
TouchEventData lastEvent;
int mousePressId;
bool spinLoopWhenPressed;
int touchEventCount;
+ QVector2D lastVelocity;
+ QVector2D lastVelocityFromMouseMove;
+ QPointF lastMousePos;
+ int lastMouseCapabilityFlags;
-protected:
- virtual void touchEvent(QTouchEvent *event) {
- if (!acceptEvents) {
+ void touchEvent(QTouchEvent *event) {
+ if (!acceptTouchEvents) {
event->ignore();
return;
}
++touchEventCount;
lastEvent = makeTouchData(event->type(), event->window(), event->touchPointStates(), event->touchPoints());
- event->accept();
+ if (event->device()->capabilities().testFlag(QTouchDevice::Velocity) && !event->touchPoints().isEmpty()) {
+ lastVelocity = event->touchPoints().first().velocity();
+ } else {
+ lastVelocity = QVector2D();
+ }
if (spinLoopWhenPressed && event->touchPointStates().testFlag(Qt::TouchPointPressed)) {
QCoreApplication::processEvents();
}
}
- virtual void mousePressEvent(QMouseEvent *) {
+ void mousePressEvent(QMouseEvent *e) {
+ if (!acceptMouseEvents) {
+ e->ignore();
+ return;
+ }
mousePressId = ++mousePressNum;
+ lastMousePos = e->pos();
+ lastMouseCapabilityFlags = QGuiApplicationPrivate::mouseEventCaps(e);
+ }
+
+ void mouseMoveEvent(QMouseEvent *e) {
+ if (!acceptMouseEvents) {
+ e->ignore();
+ return;
+ }
+ ++mouseMoveNum;
+ lastVelocityFromMouseMove = QGuiApplicationPrivate::mouseEventVelocity(e);
+ lastMouseCapabilityFlags = QGuiApplicationPrivate::mouseEventCaps(e);
+ lastMousePos = e->pos();
+ }
+
+ void mouseReleaseEvent(QMouseEvent *e) {
+ if (!acceptMouseEvents) {
+ e->ignore();
+ return;
+ }
+ ++mouseReleaseNum;
+ lastMousePos = e->pos();
+ lastMouseCapabilityFlags = QGuiApplicationPrivate::mouseEventCaps(e);
}
bool childMouseEventFilter(QQuickItem *, QEvent *event) {
+ // TODO Is it a bug if a QTouchEvent comes here?
if (event->type() == QEvent::MouseButtonPress)
mousePressId = ++mousePressNum;
return false;
}
- static int mousePressNum;
+ static int mousePressNum, mouseMoveNum, mouseReleaseNum;
};
int TestTouchItem::mousePressNum = 0;
+int TestTouchItem::mouseMoveNum = 0;
+int TestTouchItem::mouseReleaseNum = 0;
class ConstantUpdateItem : public QQuickItem
{
@@ -229,9 +273,13 @@ private slots:
void initTestCase()
{
QQmlDataTest::initTestCase();
- touchDevice = new QTouchDevice();
+ touchDevice = new QTouchDevice;
touchDevice->setType(QTouchDevice::TouchScreen);
QWindowSystemInterface::registerTouchDevice(touchDevice);
+ touchDeviceWithVelocity = new QTouchDevice;
+ touchDeviceWithVelocity->setType(QTouchDevice::TouchScreen);
+ touchDeviceWithVelocity->setCapabilities(QTouchDevice::Position | QTouchDevice::Velocity);
+ QWindowSystemInterface::registerTouchDevice(touchDeviceWithVelocity);
}
@@ -244,6 +292,9 @@ private slots:
void touchEvent_propagation_data();
void touchEvent_cancel();
void touchEvent_reentrant();
+ void touchEvent_velocity();
+
+ void mouseFromTouch_basic();
void clearCanvas();
@@ -262,6 +313,7 @@ private slots:
void ownershipRootItem();
private:
QTouchDevice *touchDevice;
+ QTouchDevice *touchDeviceWithVelocity;
};
//If the item calls update inside updatePaintNode, it should schedule another update
@@ -282,6 +334,7 @@ void tst_qquickcanvas::touchEvent_basic()
canvas->resize(250, 250);
canvas->setPos(100, 100);
canvas->show();
+ QTest::qWaitForWindowShown(canvas);
TestTouchItem *bottomItem = new TestTouchItem(canvas->rootItem());
bottomItem->setObjectName("Bottom Item");
@@ -307,7 +360,8 @@ void tst_qquickcanvas::touchEvent_basic()
QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
QVERIFY(bottomItem->lastEvent.touchPoints.isEmpty());
- TouchEventData d = makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed, makeTouchPoint(topItem,pos));
+ // At one point this was failing with kwin (KDE window manager) because canvas->setPos(100, 100)
+ // would put the decorated window at that position rather than the canvas itself.
COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed, makeTouchPoint(topItem, pos)));
topItem->reset();
@@ -356,6 +410,10 @@ void tst_qquickcanvas::touchEvent_basic()
COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchBegin, canvas, Qt::TouchPointPressed, makeTouchPoint(bottomItem, pos)));
topItem->reset();
bottomItem->reset();
+ // cleanup: what is pressed must be released
+ // Otherwise you will get an assertion failure:
+ // ASSERT: "itemForTouchPointId.isEmpty()" in file items/qquickcanvas.cpp
+ QTest::touchEvent(canvas, touchDevice).release(0, pos.toPoint(), canvas).release(1, pos.toPoint(), canvas);
// move touch point from top item to bottom, and release
QTest::touchEvent(canvas, touchDevice).press(0, topItem->mapToScene(pos).toPoint(),canvas);
@@ -395,7 +453,8 @@ void tst_qquickcanvas::touchEvent_propagation()
{
TestTouchItem::clearMousePressCounter();
- QFETCH(bool, acceptEvents);
+ QFETCH(bool, acceptTouchEvents);
+ QFETCH(bool, acceptMouseEvents);
QFETCH(bool, enableItem);
QFETCH(qreal, itemOpacity);
@@ -403,6 +462,7 @@ void tst_qquickcanvas::touchEvent_propagation()
canvas->resize(250, 250);
canvas->setPos(100, 100);
canvas->show();
+ QTest::qWaitForWindowShown(canvas);
TestTouchItem *bottomItem = new TestTouchItem(canvas->rootItem());
bottomItem->setObjectName("Bottom Item");
@@ -424,7 +484,8 @@ void tst_qquickcanvas::touchEvent_propagation()
QPoint pointInTopItem = topItem->mapToScene(pos).toPoint(); // (110, 110) overlaps with bottom & top items
// disable topItem
- topItem->acceptEvents = acceptEvents;
+ topItem->acceptTouchEvents = acceptTouchEvents;
+ topItem->acceptMouseEvents = acceptMouseEvents;
topItem->setEnabled(enableItem);
topItem->setOpacity(itemOpacity);
@@ -450,7 +511,8 @@ void tst_qquickcanvas::touchEvent_propagation()
middleItem->reset();
// disable middleItem as well
- middleItem->acceptEvents = acceptEvents;
+ middleItem->acceptTouchEvents = acceptTouchEvents;
+ middleItem->acceptMouseEvents = acceptMouseEvents;
middleItem->setEnabled(enableItem);
middleItem->setOpacity(itemOpacity);
@@ -467,7 +529,7 @@ void tst_qquickcanvas::touchEvent_propagation()
bottomItem->reset();
// disable bottom item as well
- bottomItem->acceptEvents = acceptEvents;
+ bottomItem->acceptTouchEvents = acceptTouchEvents;
bottomItem->setEnabled(enableItem);
bottomItem->setOpacity(itemOpacity);
@@ -485,7 +547,7 @@ void tst_qquickcanvas::touchEvent_propagation()
bottomItem->reset();
// disable middle item, touch on top item
- middleItem->acceptEvents = acceptEvents;
+ middleItem->acceptTouchEvents = acceptTouchEvents;
middleItem->setEnabled(enableItem);
middleItem->setOpacity(itemOpacity);
QTest::touchEvent(canvas, touchDevice).press(0, pointInTopItem, canvas);
@@ -514,13 +576,14 @@ void tst_qquickcanvas::touchEvent_propagation()
void tst_qquickcanvas::touchEvent_propagation_data()
{
- QTest::addColumn<bool>("acceptEvents");
+ QTest::addColumn<bool>("acceptTouchEvents");
+ QTest::addColumn<bool>("acceptMouseEvents");
QTest::addColumn<bool>("enableItem");
QTest::addColumn<qreal>("itemOpacity");
- QTest::newRow("disable events") << false << true << 1.0;
- QTest::newRow("disable item") << true << false << 1.0;
- QTest::newRow("opacity of 0") << true << true << 0.0;
+ QTest::newRow("disable events") << false << false << true << 1.0;
+ QTest::newRow("disable item") << true << true << false << 1.0;
+ QTest::newRow("opacity of 0") << true << true << true << 0.0;
}
void tst_qquickcanvas::touchEvent_cancel()
@@ -531,6 +594,7 @@ void tst_qquickcanvas::touchEvent_cancel()
canvas->resize(250, 250);
canvas->setPos(100, 100);
canvas->show();
+ QTest::qWaitForWindowShown(canvas);
TestTouchItem *item = new TestTouchItem(canvas->rootItem());
item->setPos(QPointF(50, 50));
@@ -562,6 +626,7 @@ void tst_qquickcanvas::touchEvent_reentrant()
canvas->resize(250, 250);
canvas->setPos(100, 100);
canvas->show();
+ QTest::qWaitForWindowShown(canvas);
TestTouchItem *item = new TestTouchItem(canvas->rootItem());
@@ -590,6 +655,126 @@ void tst_qquickcanvas::touchEvent_reentrant()
delete canvas;
}
+void tst_qquickcanvas::touchEvent_velocity()
+{
+ TestTouchItem::clearMousePressCounter();
+
+ QQuickCanvas *canvas = new QQuickCanvas;
+ canvas->resize(250, 250);
+ canvas->setPos(100, 100);
+ canvas->show();
+ QTest::qWaitForWindowShown(canvas);
+ QTest::qWait(10);
+
+ TestTouchItem *item = new TestTouchItem(canvas->rootItem());
+ item->setPos(QPointF(50, 50));
+ item->setSize(QSizeF(150, 150));
+
+ QList<QWindowSystemInterface::TouchPoint> points;
+ QWindowSystemInterface::TouchPoint tp;
+ tp.id = 1;
+ tp.state = Qt::TouchPointPressed;
+ QPoint pos = canvas->mapToGlobal(item->mapToScene(QPointF(10, 10)).toPoint());
+ tp.area = QRectF(pos, QSizeF(4, 4));
+ points << tp;
+ QWindowSystemInterface::handleTouchEvent(canvas, touchDeviceWithVelocity, points);
+ points[0].state = Qt::TouchPointMoved;
+ points[0].area.adjust(5, 5, 5, 5);
+ QVector2D velocity(1.5, 2.5);
+ points[0].velocity = velocity;
+ QWindowSystemInterface::handleTouchEvent(canvas, touchDeviceWithVelocity, points);
+ QCoreApplication::processEvents();
+ QCOMPARE(item->touchEventCount, 2);
+ QCOMPARE(item->lastEvent.touchPoints.count(), 1);
+ QCOMPARE(item->lastVelocity, velocity);
+
+ // Now have a transformation on the item and check if velocity and position are transformed accordingly.
+ item->setRotation(90); // clockwise
+ QMatrix4x4 transformMatrix;
+ transformMatrix.rotate(-90, 0, 0, 1); // counterclockwise
+ QVector2D transformedVelocity = transformMatrix.mapVector(velocity).toVector2D();
+ points[0].area.adjust(5, 5, 5, 5);
+ QWindowSystemInterface::handleTouchEvent(canvas, touchDeviceWithVelocity, points);
+ QCoreApplication::processEvents();
+ QCOMPARE(item->lastVelocity, transformedVelocity);
+ QPoint itemLocalPos = item->mapFromScene(canvas->mapFromGlobal(points[0].area.center().toPoint())).toPoint();
+ QPoint itemLocalPosFromEvent = item->lastEvent.touchPoints[0].pos().toPoint();
+ QCOMPARE(itemLocalPos, itemLocalPosFromEvent);
+
+ points[0].state = Qt::TouchPointReleased;
+ QWindowSystemInterface::handleTouchEvent(canvas, touchDeviceWithVelocity, points);
+ QCoreApplication::processEvents();
+ delete item;
+ delete canvas;
+}
+
+void tst_qquickcanvas::mouseFromTouch_basic()
+{
+ // Turn off accepting touch events with acceptTouchEvents. This
+ // should result in sending mouse events generated from the touch
+ // with the new event propagation system.
+
+ TestTouchItem::clearMousePressCounter();
+ QQuickCanvas *canvas = new QQuickCanvas;
+ canvas->resize(250, 250);
+ canvas->setPos(100, 100);
+ canvas->show();
+ QTest::qWaitForWindowShown(canvas);
+ QTest::qWait(10);
+
+ TestTouchItem *item = new TestTouchItem(canvas->rootItem());
+ item->setPos(QPointF(50, 50));
+ item->setSize(QSizeF(150, 150));
+ item->acceptTouchEvents = false;
+
+ QList<QWindowSystemInterface::TouchPoint> points;
+ QWindowSystemInterface::TouchPoint tp;
+ tp.id = 1;
+ tp.state = Qt::TouchPointPressed;
+ QPoint pos = canvas->mapToGlobal(item->mapToScene(QPointF(10, 10)).toPoint());
+ tp.area = QRectF(pos, QSizeF(4, 4));
+ points << tp;
+ QWindowSystemInterface::handleTouchEvent(canvas, touchDeviceWithVelocity, points);
+ points[0].state = Qt::TouchPointMoved;
+ points[0].area.adjust(5, 5, 5, 5);
+ QVector2D velocity(1.5, 2.5);
+ points[0].velocity = velocity;
+ QWindowSystemInterface::handleTouchEvent(canvas, touchDeviceWithVelocity, points);
+ points[0].state = Qt::TouchPointReleased;
+ QWindowSystemInterface::handleTouchEvent(canvas, touchDeviceWithVelocity, points);
+ QCoreApplication::processEvents();
+
+ // The item should have received a mouse press, move, and release.
+ QCOMPARE(item->mousePressNum, 1);
+ QCOMPARE(item->mouseMoveNum, 1);
+ QCOMPARE(item->mouseReleaseNum, 1);
+ QCOMPARE(item->lastMousePos.toPoint(), item->mapFromScene(canvas->mapFromGlobal(points[0].area.center().toPoint())).toPoint());
+ QCOMPARE(item->lastVelocityFromMouseMove, velocity);
+ QVERIFY((item->lastMouseCapabilityFlags & QTouchDevice::Velocity) != 0);
+
+ // Now the same with a transformation.
+ item->setRotation(90); // clockwise
+ QMatrix4x4 transformMatrix;
+ transformMatrix.rotate(-90, 0, 0, 1); // counterclockwise
+ QVector2D transformedVelocity = transformMatrix.mapVector(velocity).toVector2D();
+ points[0].state = Qt::TouchPointPressed;
+ points[0].velocity = velocity;
+ points[0].area = QRectF(pos, QSizeF(4, 4));
+ QWindowSystemInterface::handleTouchEvent(canvas, touchDeviceWithVelocity, points);
+ points[0].state = Qt::TouchPointMoved;
+ points[0].area.adjust(5, 5, 5, 5);
+ QWindowSystemInterface::handleTouchEvent(canvas, touchDeviceWithVelocity, points);
+ QCoreApplication::processEvents();
+ QCOMPARE(item->lastMousePos.toPoint(), item->mapFromScene(canvas->mapFromGlobal(points[0].area.center().toPoint())).toPoint());
+ QCOMPARE(item->lastVelocityFromMouseMove, transformedVelocity);
+
+ points[0].state = Qt::TouchPointReleased;
+ QWindowSystemInterface::handleTouchEvent(canvas, touchDeviceWithVelocity, points);
+ QCoreApplication::processEvents();
+ delete item;
+ delete canvas;
+}
+
void tst_qquickcanvas::clearCanvas()
{
QQuickCanvas *canvas = new QQuickCanvas;
@@ -613,6 +798,7 @@ void tst_qquickcanvas::mouseFiltering()
canvas->resize(250, 250);
canvas->setPos(100, 100);
canvas->show();
+ QTest::qWaitForWindowShown(canvas);
TestTouchItem *bottomItem = new TestTouchItem(canvas->rootItem());
bottomItem->setObjectName("Bottom Item");
@@ -820,6 +1006,7 @@ void tst_qquickcanvas::ignoreUnhandledMouseEvents()
QQuickCanvas* canvas = new QQuickCanvas;
canvas->resize(100, 100);
canvas->show();
+ QTest::qWaitForWindowShown(canvas);
QQuickItem* item = new QQuickItem;
item->setSize(QSizeF(100, 100));
diff --git a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp
index c5ede2629e..9745e20d10 100644
--- a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp
+++ b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp
@@ -570,30 +570,24 @@ void tst_QQuickMultiPointTouchArea::inFlickable()
//moving one point vertically
QTest::touchEvent(canvas, device).press(0, p1);
- QTest::mousePress(canvas, Qt::LeftButton, 0, p1);
p1 += QPoint(0,15);
QTest::touchEvent(canvas, device).move(0, p1);
- QTest::mouseMove(canvas, p1);
p1 += QPoint(0,15);
QTest::touchEvent(canvas, device).move(0, p1);
- QTest::mouseMove(canvas, p1);
p1 += QPoint(0,15);
QTest::touchEvent(canvas, device).move(0, p1);
- QTest::mouseMove(canvas, p1);
p1 += QPoint(0,15);
QTest::touchEvent(canvas, device).move(0, p1);
- QTest::mouseMove(canvas, p1);
QVERIFY(flickable->contentY() < 0);
QCOMPARE(point11->pressed(), false);
QCOMPARE(point12->pressed(), false);
QTest::touchEvent(canvas, device).release(0, p1);
- QTest::mouseRelease(canvas,Qt::LeftButton, 0, p1);
QTest::qWait(50);
QTRY_VERIFY(!flickable->isMoving());
@@ -736,32 +730,26 @@ void tst_QQuickMultiPointTouchArea::inFlickable2()
// Check that we can still move the Flickable
p1 = QPoint(50,100);
QTest::touchEvent(canvas, device).press(0, p1);
- QTest::mousePress(canvas, Qt::LeftButton, 0, p1);
QCOMPARE(point11->pressed(), true);
p1 += QPoint(0,15);
QTest::touchEvent(canvas, device).move(0, p1);
- QTest::mouseMove(canvas, p1);
p1 += QPoint(0,15);
QTest::touchEvent(canvas, device).move(0, p1);
- QTest::mouseMove(canvas, p1);
p1 += QPoint(0,15);
QTest::touchEvent(canvas, device).move(0, p1);
- QTest::mouseMove(canvas, p1);
p1 += QPoint(0,15);
QTest::touchEvent(canvas, device).move(0, p1);
- QTest::mouseMove(canvas, p1);
QVERIFY(flickable->contentY() < 0);
QVERIFY(flickable->isMoving());
- QCOMPARE(point11->pressed(), false);
+ QCOMPARE(point11->pressed(), true);
QTest::touchEvent(canvas, device).release(0, p1);
- QTest::mouseRelease(canvas,Qt::LeftButton, 0, p1);
QTest::qWait(50);
QTRY_VERIFY(!flickable->isMoving());
diff --git a/tests/auto/quick/qquickpincharea/data/pinchproperties.qml b/tests/auto/quick/qquickpincharea/data/pinchproperties.qml
index 44d116184e..6665e2f597 100644
--- a/tests/auto/quick/qquickpincharea/data/pinchproperties.qml
+++ b/tests/auto/quick/qquickpincharea/data/pinchproperties.qml
@@ -4,6 +4,7 @@ Rectangle {
property variant center
property real scale
property int pointCount: 0
+ property bool pinchActive: false
width: 240; height: 320
color: "white"
Rectangle {
@@ -34,6 +35,7 @@ Rectangle {
whiteRect.center = pinch.center
whiteRect.scale = pinch.scale
whiteRect.pointCount = pinch.pointCount;
+ whiteRect.pinchActive = true;
}
onPinchUpdated: {
whiteRect.center = pinch.center
@@ -44,6 +46,7 @@ Rectangle {
whiteRect.center = pinch.center
whiteRect.scale = pinch.scale
whiteRect.pointCount = pinch.pointCount;
+ whiteRect.pinchActive = false;
}
}
}
diff --git a/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp b/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp
index 27efdf7e28..e0e4b6c4d8 100644
--- a/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp
+++ b/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp
@@ -229,31 +229,41 @@ void tst_QQuickPinchArea::scale()
QPoint p1(80, 80);
QPoint p2(100, 100);
-
- QTest::touchEvent(canvas, device).press(0, p1, canvas);
- QTest::touchEvent(canvas, device).stationary(0).press(1, p2, canvas);
- p1 -= QPoint(10,10);
- p2 += QPoint(10,10);
- QTest::touchEvent(canvas, device).move(0, p1,canvas).move(1, p2,canvas);
-
- QCOMPARE(root->property("scale").toReal(), 1.0);
-
- p1 -= QPoint(10,10);
- p2 += QPoint(10,10);
- QTest::touchEvent(canvas, device).move(0, p1,canvas).move(1, p2,canvas);
-
- QCOMPARE(root->property("scale").toReal(), 1.5);
- QCOMPARE(root->property("center").toPointF(), QPointF(40, 40)); // blackrect is at 50,50
- QCOMPARE(blackRect->scale(), 1.5);
+ {
+ QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(canvas, device);
+ pinchSequence.press(0, p1, canvas).commit();
+ // In order for the stationary point to remember its previous position,
+ // we have to reuse the same pinchSequence object. Otherwise if we let it
+ // be destroyed and then start a new sequence, point 0 will default to being
+ // stationary at 0, 0, and PinchArea will filter out that touchpoint because
+ // it is outside its bounds.
+ pinchSequence.stationary(0).press(1, p2, canvas).commit();
+ p1 -= QPoint(10,10);
+ p2 += QPoint(10,10);
+ pinchSequence.move(0, p1,canvas).move(1, p2,canvas).commit();
+
+ QCOMPARE(root->property("scale").toReal(), 1.0);
+ QVERIFY(root->property("pinchActive").toBool());
+
+ p1 -= QPoint(10,10);
+ p2 += QPoint(10,10);
+ pinchSequence.move(0, p1,canvas).move(1, p2,canvas).commit();
+
+ QCOMPARE(root->property("scale").toReal(), 1.5);
+ QCOMPARE(root->property("center").toPointF(), QPointF(40, 40)); // blackrect is at 50,50
+ QCOMPARE(blackRect->scale(), 1.5);
+ }
// scale beyond bound
p1 -= QPoint(50,50);
p2 += QPoint(50,50);
- QTest::touchEvent(canvas, device).move(0, p1, canvas).move(1, p2, canvas);
-
- QCOMPARE(blackRect->scale(), 2.0);
-
- QTest::touchEvent(canvas, device).release(0, p1, canvas).release(1, p2, canvas);
+ {
+ QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(canvas, device);
+ pinchSequence.move(0, p1, canvas).move(1, p2, canvas).commit();
+ QCOMPARE(blackRect->scale(), 2.0);
+ pinchSequence.release(0, p1, canvas).release(1, p2, canvas).commit();
+ }
+ QVERIFY(!root->property("pinchActive").toBool());
delete canvas;
}
@@ -282,21 +292,25 @@ void tst_QQuickPinchArea::pan()
QPoint p1(80, 80);
QPoint p2(100, 100);
-
- QTest::touchEvent(canvas, device).press(0, p1, canvas);
- QTest::touchEvent(canvas, device).stationary(0).press(1, p2, canvas);
- p1 += QPoint(10,10);
- p2 += QPoint(10,10);
- QTest::touchEvent(canvas, device).move(0, p1, canvas).move(1, p2, canvas);
-
- QCOMPARE(root->property("scale").toReal(), 1.0);
-
- p1 += QPoint(10,10);
- p2 += QPoint(10,10);
- QTest::touchEvent(canvas, device).move(0, p1, canvas).move(1, p2, canvas);
+ {
+ QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(canvas, device);
+ pinchSequence.press(0, p1, canvas).commit();
+ // In order for the stationary point to remember its previous position,
+ // we have to reuse the same pinchSequence object.
+ pinchSequence.stationary(0).press(1, p2, canvas).commit();
+ p1 += QPoint(10,10);
+ p2 += QPoint(10,10);
+ pinchSequence.move(0, p1,canvas).move(1, p2,canvas).commit();
+
+ QCOMPARE(root->property("scale").toReal(), 1.0);
+ QVERIFY(root->property("pinchActive").toBool());
+
+ p1 += QPoint(10,10);
+ p2 += QPoint(10,10);
+ pinchSequence.move(0, p1,canvas).move(1, p2,canvas).commit();
+ }
QCOMPARE(root->property("center").toPointF(), QPointF(60, 60)); // blackrect is at 50,50
-
QCOMPARE(blackRect->x(), 60.0);
QCOMPARE(blackRect->y(), 60.0);
@@ -309,6 +323,7 @@ void tst_QQuickPinchArea::pan()
QCOMPARE(blackRect->y(), 160.0);
QTest::touchEvent(canvas, device).release(0, p1, canvas).release(1, p2, canvas);
+ QVERIFY(!root->property("pinchActive").toBool());
delete canvas;
}
@@ -341,57 +356,64 @@ void tst_QQuickPinchArea::retouch()
QPoint p1(80, 80);
QPoint p2(100, 100);
+ {
+ QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(canvas, device);
+ pinchSequence.press(0, p1, canvas).commit();
+ // In order for the stationary point to remember its previous position,
+ // we have to reuse the same pinchSequence object.
+ pinchSequence.stationary(0).press(1, p2, canvas).commit();
+ p1 -= QPoint(10,10);
+ p2 += QPoint(10,10);
+ pinchSequence.move(0, p1,canvas).move(1, p2,canvas).commit();
- QTest::touchEvent(canvas, device).press(0, p1, canvas);
- QTest::touchEvent(canvas, device).stationary(0).press(1, p2, canvas);
- p1 -= QPoint(10,10);
- p2 += QPoint(10,10);
- QTest::touchEvent(canvas, device).move(0, p1, canvas).move(1, p2, canvas);
-
- QCOMPARE(root->property("scale").toReal(), 1.0);
+ QCOMPARE(root->property("scale").toReal(), 1.0);
+ QVERIFY(root->property("pinchActive").toBool());
- p1 -= QPoint(10,10);
- p2 += QPoint(10,10);
- QTest::touchEvent(canvas, device).move(0, p1, canvas).move(1, p2, canvas);
+ p1 -= QPoint(10,10);
+ p2 += QPoint(10,10);
+ pinchSequence.move(0, p1,canvas).move(1, p2,canvas).commit();
- QCOMPARE(startedSpy.count(), 1);
+ QCOMPARE(startedSpy.count(), 1);
- QCOMPARE(root->property("scale").toReal(), 1.5);
- QCOMPARE(root->property("center").toPointF(), QPointF(40, 40)); // blackrect is at 50,50
- QCOMPARE(blackRect->scale(), 1.5);
+ QCOMPARE(root->property("scale").toReal(), 1.5);
+ QCOMPARE(root->property("center").toPointF(), QPointF(40, 40)); // blackrect is at 50,50
+ QCOMPARE(blackRect->scale(), 1.5);
- QCOMPARE(canvas->rootObject()->property("pointCount").toInt(), 2);
+ QCOMPARE(canvas->rootObject()->property("pointCount").toInt(), 2);
- QCOMPARE(startedSpy.count(), 1);
- QCOMPARE(finishedSpy.count(), 0);
+ QCOMPARE(startedSpy.count(), 1);
+ QCOMPARE(finishedSpy.count(), 0);
- QTest::touchEvent(canvas, device).stationary(0).release(1, p2, canvas);
+ // Hold down the first finger but release the second one
+ pinchSequence.stationary(0).release(1, p2, canvas).commit();
- QCOMPARE(startedSpy.count(), 1);
- QCOMPARE(finishedSpy.count(), 0);
+ QCOMPARE(startedSpy.count(), 1);
+ QCOMPARE(finishedSpy.count(), 0);
- QCOMPARE(canvas->rootObject()->property("pointCount").toInt(), 1);
+ QCOMPARE(canvas->rootObject()->property("pointCount").toInt(), 1);
- QTest::touchEvent(canvas, device).stationary(0).press(1, p2, canvas);
- p1 -= QPoint(10,10);
- p2 += QPoint(10,10);
- QTest::touchEvent(canvas, device).move(0, p1, canvas).move(1, p2, canvas);
+ // Keep holding down the first finger and re-touch the second one, then move them both
+ pinchSequence.stationary(0).press(1, p2, canvas).commit();
+ p1 -= QPoint(10,10);
+ p2 += QPoint(10,10);
+ pinchSequence.move(0, p1, canvas).move(1, p2, canvas).commit();
- // Lifting and retouching results in onPinchStarted being called again
- QCOMPARE(startedSpy.count(), 2);
- QCOMPARE(finishedSpy.count(), 0);
+ // Lifting and retouching results in onPinchStarted being called again
+ QCOMPARE(startedSpy.count(), 2);
+ QCOMPARE(finishedSpy.count(), 0);
- QCOMPARE(canvas->rootObject()->property("pointCount").toInt(), 2);
+ QCOMPARE(canvas->rootObject()->property("pointCount").toInt(), 2);
- QTest::touchEvent(canvas, device).release(0, p1, canvas).release(1, p2, canvas);
+ pinchSequence.release(0, p1, canvas).release(1, p2, canvas).commit();
- QCOMPARE(startedSpy.count(), 2);
- QCOMPARE(finishedSpy.count(), 1);
+ QVERIFY(!root->property("pinchActive").toBool());
+ QCOMPARE(startedSpy.count(), 2);
+ QCOMPARE(finishedSpy.count(), 1);
+ }
delete canvas;
}
-
QQuickView *tst_QQuickPinchArea::createView()
{
QQuickView *canvas = new QQuickView(0);
diff --git a/tests/auto/quick/quick.pro b/tests/auto/quick/quick.pro
index 2ef6dc0357..fd78da2746 100644
--- a/tests/auto/quick/quick.pro
+++ b/tests/auto/quick/quick.pro
@@ -70,6 +70,7 @@ QUICKTESTS = \
qquickview \
qquickcanvasitem \
qquickscreen \
+ touchmouse \
SUBDIRS += $$PUBLICTESTS
diff --git a/tests/auto/quick/touchmouse/data/buttononflickable.qml b/tests/auto/quick/touchmouse/data/buttononflickable.qml
new file mode 100644
index 0000000000..95a993f806
--- /dev/null
+++ b/tests/auto/quick/touchmouse/data/buttononflickable.qml
@@ -0,0 +1,42 @@
+import QtQuick 2.0
+import Qt.test 1.0
+
+Rectangle {
+ id: root
+ width: 300
+ height: 500
+ color: "green"
+
+ Flickable {
+ objectName: "flickable"
+ anchors.fill: parent
+ contentHeight: 1000
+
+ Rectangle {
+ objectName: "button"
+ y: 100
+ height: 100
+ width: parent.width
+
+ EventItem {
+ objectName: "eventItem1"
+ height: 100
+ width: 300
+ }
+ }
+
+ Rectangle {
+ objectName: "button2"
+ y: 300
+ height: 100
+ width: parent.width
+
+ EventItem {
+ objectName: "eventItem2"
+ height: 100
+ width: 300
+ }
+ }
+ }
+}
+
diff --git a/tests/auto/quick/touchmouse/data/buttonontouch.qml b/tests/auto/quick/touchmouse/data/buttonontouch.qml
new file mode 100644
index 0000000000..dcd2573f2e
--- /dev/null
+++ b/tests/auto/quick/touchmouse/data/buttonontouch.qml
@@ -0,0 +1,100 @@
+import QtQuick 2.0
+import Qt.test 1.0
+
+Rectangle {
+ id: root
+ width: 300
+ height: 800
+ color: "green"
+
+ Rectangle {
+ color: "blue"
+ height: 400
+ width: parent.width
+
+
+ PinchArea {
+ pinch.target: button1
+ objectName: "pincharea"
+ anchors.fill: parent
+
+ pinch.minimumScale: 0.1
+ pinch.maximumScale: 10.0
+ }
+
+ Rectangle {
+
+ id: button1
+ objectName: "button1"
+ y: 100
+ height: 100
+ width: parent.width
+ Text { text: "Button 1" }
+
+ EventItem {
+ objectName: "eventItem1"
+ height: 100
+ width: 300
+ }
+ }
+
+ Rectangle {
+ objectName: "button2"
+ y: 300
+ height: 100
+ width: parent.width
+ Text { text: "Button 2" }
+
+ EventItem {
+ objectName: "eventItem2"
+ height: 100
+ width: 300
+ }
+ }
+ }
+
+ Rectangle {
+ y: 400
+ width: parent.width
+ height: parent.height
+ color: "red"
+
+ MultiPointTouchArea {
+ objectName: "toucharea"
+ anchors.fill: parent
+
+ y: 400
+ height: 400
+
+ Rectangle {
+ objectName: "button3"
+ y: 100
+ height: 100
+ width: parent.width
+ Text { text: "Button 3" }
+
+ EventItem {
+ objectName: "eventItem3"
+ height: 100
+ width: 300
+ }
+ }
+
+ Rectangle {
+ objectName: "button4"
+ y: 300
+ height: 100
+ width: parent.width
+ Text { text: "Button 4" }
+
+ EventItem {
+ objectName: "eventItem4"
+ height: 100
+ width: 300
+ }
+ }
+
+ }
+ }
+}
+
diff --git a/tests/auto/quick/touchmouse/data/flickableonpinch.qml b/tests/auto/quick/touchmouse/data/flickableonpinch.qml
new file mode 100644
index 0000000000..9c9a197d66
--- /dev/null
+++ b/tests/auto/quick/touchmouse/data/flickableonpinch.qml
@@ -0,0 +1,37 @@
+import QtQuick 2.0
+import Qt.test 1.0
+
+Rectangle {
+ id: root
+ width: 600
+ height: 600
+ color: "green"
+
+ PinchArea {
+ objectName: "pincharea"
+ pinch.target: rect
+ anchors.fill: parent
+
+ pinch.minimumScale: 1.0
+ pinch.maximumScale: 10.0
+
+ Flickable {
+ objectName: "flickable"
+ anchors.fill: parent
+ contentHeight: 1000
+ contentWidth: 1000
+
+ Rectangle {
+ objectName: "rect"
+ id: rect
+ color: "blue"
+ x: 200
+ y: 200
+ width: 400
+ height: 400
+ }
+ }
+ }
+
+}
+
diff --git a/tests/auto/quick/touchmouse/data/mouseonflickableonpinch.qml b/tests/auto/quick/touchmouse/data/mouseonflickableonpinch.qml
new file mode 100644
index 0000000000..015391f291
--- /dev/null
+++ b/tests/auto/quick/touchmouse/data/mouseonflickableonpinch.qml
@@ -0,0 +1,47 @@
+import QtQuick 2.0
+import Qt.test 1.0
+
+Rectangle {
+ id: root
+ width: 600
+ height: 600
+ color: "green"
+
+ PinchArea {
+ objectName: "pincharea"
+ pinch.target: rect
+ anchors.fill: parent
+
+ pinch.minimumScale: 1.0
+ pinch.maximumScale: 10.0
+
+ Flickable {
+ objectName: "flickable"
+ anchors.fill: parent
+ contentHeight: 1000
+ contentWidth: 1000
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ if (rect.color == "#000000")
+ rect.color = "#00ff00"
+ else
+ rect.color = "#000000"
+ }
+ }
+
+ Rectangle {
+ objectName: "rect"
+ id: rect
+ color: "blue"
+ x: 200
+ y: 200
+ width: 400
+ height: 400
+ }
+ }
+ }
+
+}
+
diff --git a/tests/auto/quick/touchmouse/data/pinchonflickable.qml b/tests/auto/quick/touchmouse/data/pinchonflickable.qml
new file mode 100644
index 0000000000..2a7a91006e
--- /dev/null
+++ b/tests/auto/quick/touchmouse/data/pinchonflickable.qml
@@ -0,0 +1,35 @@
+import QtQuick 2.0
+//import Qt.test 1.0
+
+Rectangle {
+ id: root
+ width: 600
+ height: 600
+ color: "green"
+
+ Flickable {
+ objectName: "flickable"
+ anchors.fill: parent
+ contentHeight: 1000
+ contentWidth: 1000
+
+ Rectangle {
+ objectName: "rect"
+ id: rect
+ color: "blue"
+ x: 200
+ y: 200
+ width: 400
+ height: 400
+ }
+ PinchArea {
+ objectName: "pincharea"
+ pinch.target: rect
+ anchors.fill: parent
+
+ pinch.minimumScale: 1.0
+ pinch.maximumScale: 10.0
+ }
+ }
+}
+
diff --git a/tests/auto/quick/touchmouse/data/singleitem.qml b/tests/auto/quick/touchmouse/data/singleitem.qml
new file mode 100644
index 0000000000..76d3a51da9
--- /dev/null
+++ b/tests/auto/quick/touchmouse/data/singleitem.qml
@@ -0,0 +1,18 @@
+import QtQuick 2.0
+import Qt.test 1.0
+
+Rectangle {
+ id: root
+ width: 320
+ height: 480
+ color: "green"
+
+ EventItem {
+ objectName: "eventItem1"
+ x: 5
+ y: 5
+ height: 30
+ width: 30
+ }
+}
+
diff --git a/tests/auto/quick/touchmouse/data/twoitems.qml b/tests/auto/quick/touchmouse/data/twoitems.qml
new file mode 100644
index 0000000000..afbf35fe1a
--- /dev/null
+++ b/tests/auto/quick/touchmouse/data/twoitems.qml
@@ -0,0 +1,22 @@
+import QtQuick 2.0
+import Qt.test 1.0
+
+Rectangle {
+ id: root
+ width: 320
+ height: 480
+ color: "green"
+
+ EventItem {
+ objectName: "eventItem1"
+ height: 200
+ width: 100
+
+ EventItem {
+ objectName: "eventItem2"
+ height: 100
+ width: 100
+ }
+ }
+}
+
diff --git a/tests/auto/quick/touchmouse/touchmouse.pro b/tests/auto/quick/touchmouse/touchmouse.pro
new file mode 100644
index 0000000000..d0b0fa79f2
--- /dev/null
+++ b/tests/auto/quick/touchmouse/touchmouse.pro
@@ -0,0 +1,17 @@
+CONFIG += testcase
+
+TARGET = tst_touchmouse
+QT += core-private gui-private qml-private quick-private v8-private testlib
+
+macx:CONFIG -= app_bundle
+
+SOURCES += tst_touchmouse.cpp
+
+include (../../shared/util.pri)
+
+TESTDATA = data/*
+
+# OTHER_FILES += data/foo.qml
+
+CONFIG += parallel_test
+
diff --git a/tests/auto/quick/touchmouse/tst_touchmouse.cpp b/tests/auto/quick/touchmouse/tst_touchmouse.cpp
new file mode 100644
index 0000000000..7ab9387ea7
--- /dev/null
+++ b/tests/auto/quick/touchmouse/tst_touchmouse.cpp
@@ -0,0 +1,923 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQml 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 <QtTest/QtTest>
+
+#include <QtGui/qstylehints.h>
+
+#include <QtQuick/qquickview.h>
+#include <QtQuick/qquickitem.h>
+#include <QtQuick/private/qquickmousearea_p.h>
+#include <QtQuick/private/qquickmultipointtoucharea_p.h>
+#include <QtQuick/private/qquickpincharea_p.h>
+#include <QtQuick/private/qquickflickable_p.h>
+
+#include <private/qquickcanvas_p.h>
+
+#include <QtQml/qqmlengine.h>
+#include <QtQml/qqmlproperty.h>
+
+#include "../../shared/util.h"
+
+struct Event
+{
+ Event(QEvent::Type t, QPoint mouse, QPoint global)
+ :type(t), mousePos(mouse), mousePosGlobal(global)
+ {}
+
+ Event(QEvent::Type t, QList<QTouchEvent::TouchPoint> touch)
+ :type(t), points(touch)
+ {}
+
+ QEvent::Type type;
+ QPoint mousePos;
+ QPoint mousePosGlobal;
+ QList<QTouchEvent::TouchPoint> points;
+};
+
+class EventItem : public QQuickItem
+{
+ Q_OBJECT
+public:
+ EventItem(QQuickItem *parent = 0)
+ : QQuickItem(parent), acceptMouse(false), acceptTouch(false), filterTouch(false)
+ {}
+
+ void touchEvent(QTouchEvent *event)
+ {
+ eventList.append(Event(event->type(), event->touchPoints()));
+ event->setAccepted(acceptTouch);
+ }
+ void mousePressEvent(QMouseEvent *event)
+ {
+ eventList.append(Event(event->type(), event->pos(), event->globalPos()));
+ event->setAccepted(acceptMouse);
+ }
+ void mouseMoveEvent(QMouseEvent *event)
+ {
+ eventList.append(Event(event->type(), event->pos(), event->globalPos()));
+ event->setAccepted(acceptMouse);
+ }
+ void mouseReleaseEvent(QMouseEvent *event)
+ {
+ eventList.append(Event(event->type(), event->pos(), event->globalPos()));
+ event->setAccepted(acceptMouse);
+ }
+ void mouseDoubleClickEvent(QMouseEvent *event)
+ {
+ eventList.append(Event(event->type(), event->pos(), event->globalPos()));
+ event->setAccepted(acceptMouse);
+ }
+ bool event(QEvent *event) {
+ if (event->type() == QEvent::UngrabMouse) {
+ eventList.append(Event(event->type(), QPoint(0,0), QPoint(0,0)));
+ }
+ return QQuickItem::event(event);
+ }
+
+ QList<Event> eventList;
+ bool acceptMouse;
+ bool acceptTouch;
+ bool filterTouch; // when used as event filter
+
+ bool eventFilter(QObject *, QEvent *event)
+ {
+ if (event->type() == QEvent::TouchBegin ||
+ event->type() == QEvent::TouchUpdate ||
+ event->type() == QEvent::TouchCancel ||
+ event->type() == QEvent::TouchEnd) {
+ QTouchEvent *touch = static_cast<QTouchEvent*>(event);
+ eventList.append(Event(event->type(), touch->touchPoints()));
+ if (filterTouch)
+ event->accept();
+ return true;
+ }
+ return false;
+ }
+};
+
+class tst_TouchMouse : public QQmlDataTest
+{
+ Q_OBJECT
+public:
+ tst_TouchMouse()
+ :device(0)
+ {}
+
+private slots:
+ void initTestCase();
+
+ void simpleTouchEvent();
+ void eventFilter();
+ void mouse();
+ void touchOverMouse();
+ void mouseOverTouch();
+
+ void buttonOnFlickable();
+ void buttonOnTouch();
+
+ void pinchOnFlickable();
+ void flickableOnPinch();
+ void mouseOnFlickableOnPinch();
+
+private:
+ QQuickView *createView();
+ QTouchDevice *device;
+};
+
+QQuickView *tst_TouchMouse::createView()
+{
+ QQuickView *canvas = new QQuickView(0);
+ canvas->setGeometry(0,0,240,320);
+
+ return canvas;
+}
+
+void tst_TouchMouse::initTestCase()
+{
+ // This test assumes that we don't get synthesized mouse events from QGuiApplication
+ qApp->setAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents, false);
+
+ QQmlDataTest::initTestCase();
+ qmlRegisterType<EventItem>("Qt.test", 1, 0, "EventItem");
+ if (!device) {
+ device = new QTouchDevice;
+ device->setType(QTouchDevice::TouchScreen);
+ QWindowSystemInterface::registerTouchDevice(device);
+ }
+}
+
+void tst_TouchMouse::simpleTouchEvent()
+{
+ QQuickView *canvas = createView();
+
+ canvas->setSource(testFileUrl("singleitem.qml"));
+ canvas->show();
+ canvas->requestActivateWindow();
+ QTest::qWaitForWindowShown(canvas);
+ QVERIFY(canvas->rootObject() != 0);
+
+ EventItem *eventItem1 = canvas->rootObject()->findChild<EventItem*>("eventItem1");
+ QVERIFY(eventItem1);
+
+ // Do not accept touch or mouse
+ QPoint p1;
+ p1 = QPoint(20, 20);
+ QTest::touchEvent(canvas, device).press(0, p1, canvas);
+ QCOMPARE(eventItem1->eventList.size(), 1);
+ QCOMPARE(eventItem1->eventList.at(0).type, QEvent::TouchBegin);
+ p1 += QPoint(10, 0);
+ QTest::touchEvent(canvas, device).move(0, p1, canvas);
+ QCOMPARE(eventItem1->eventList.size(), 1);
+ QTest::touchEvent(canvas, device).release(0, p1, canvas);
+ QCOMPARE(eventItem1->eventList.size(), 1);
+ eventItem1->eventList.clear();
+
+ // Accept touch
+ eventItem1->acceptTouch = true;
+ p1 = QPoint(20, 20);
+ QTest::touchEvent(canvas, device).press(0, p1, canvas);
+ QCOMPARE(eventItem1->eventList.size(), 1);
+ p1 += QPoint(10, 0);
+ QTest::touchEvent(canvas, device).move(0, p1, canvas);
+ QCOMPARE(eventItem1->eventList.size(), 2);
+ QTest::touchEvent(canvas, device).release(0, p1, canvas);
+ QCOMPARE(eventItem1->eventList.size(), 3);
+ eventItem1->eventList.clear();
+
+ // wait to avoid getting a double click event
+ QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10);
+
+ // Accept mouse
+ eventItem1->acceptTouch = false;
+ eventItem1->acceptMouse = true;
+ eventItem1->setAcceptedMouseButtons(Qt::LeftButton);
+ p1 = QPoint(20, 20);
+ QTest::touchEvent(canvas, device).press(0, p1, canvas);
+ QCOMPARE(eventItem1->eventList.size(), 2);
+ QCOMPARE(eventItem1->eventList.at(0).type, QEvent::TouchBegin);
+ QCOMPARE(eventItem1->eventList.at(1).type, QEvent::MouseButtonPress);
+ QQuickCanvasPrivate *canvasPriv = QQuickCanvasPrivate::get(canvas);
+ QCOMPARE(canvasPriv->mouseGrabberItem, eventItem1);
+
+ QPoint localPos = eventItem1->mapFromScene(p1).toPoint();
+ QPoint globalPos = canvas->mapToGlobal(p1);
+ QPoint scenePos = p1; // item is at 0,0
+ QCOMPARE(eventItem1->eventList.at(0).points.at(0).pos().toPoint(), localPos);
+ QCOMPARE(eventItem1->eventList.at(0).points.at(0).scenePos().toPoint(), scenePos);
+ QCOMPARE(eventItem1->eventList.at(0).points.at(0).screenPos().toPoint(), globalPos);
+ QCOMPARE(eventItem1->eventList.at(1).mousePos, localPos);
+ QCOMPARE(eventItem1->eventList.at(1).mousePosGlobal, globalPos);
+
+ p1 += QPoint(10, 0);
+ QTest::touchEvent(canvas, device).move(0, p1, canvas);
+ QCOMPARE(eventItem1->eventList.size(), 4);
+ QCOMPARE(eventItem1->eventList.at(2).type, QEvent::TouchUpdate);
+ QCOMPARE(eventItem1->eventList.at(3).type, QEvent::MouseMove);
+ QTest::touchEvent(canvas, device).release(0, p1, canvas);
+ QCOMPARE(eventItem1->eventList.size(), 6);
+ QCOMPARE(eventItem1->eventList.at(4).type, QEvent::TouchEnd);
+ QCOMPARE(eventItem1->eventList.at(5).type, QEvent::MouseButtonRelease);
+ eventItem1->eventList.clear();
+
+ // wait to avoid getting a double click event
+ QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10);
+
+ // Accept mouse buttons but not the event
+ eventItem1->acceptTouch = false;
+ eventItem1->acceptMouse = false;
+ eventItem1->setAcceptedMouseButtons(Qt::LeftButton);
+ p1 = QPoint(20, 20);
+ QTest::touchEvent(canvas, device).press(0, p1, canvas);
+ QCOMPARE(eventItem1->eventList.size(), 2);
+ QCOMPARE(eventItem1->eventList.at(0).type, QEvent::TouchBegin);
+ QCOMPARE(eventItem1->eventList.at(1).type, QEvent::MouseButtonPress);
+ p1 += QPoint(10, 0);
+ QTest::touchEvent(canvas, device).move(0, p1, canvas);
+ QCOMPARE(eventItem1->eventList.size(), 2);
+ QTest::touchEvent(canvas, device).release(0, p1, canvas);
+ QCOMPARE(eventItem1->eventList.size(), 2);
+ eventItem1->eventList.clear();
+
+ // wait to avoid getting a double click event
+ QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10);
+
+ // Accept touch and mouse
+ eventItem1->acceptTouch = true;
+ eventItem1->setAcceptedMouseButtons(Qt::LeftButton);
+ p1 = QPoint(20, 20);
+ QTest::touchEvent(canvas, device).press(0, p1, canvas);
+ QCOMPARE(eventItem1->eventList.size(), 1);
+ QCOMPARE(eventItem1->eventList.at(0).type, QEvent::TouchBegin);
+ p1 += QPoint(10, 0);
+ QTest::touchEvent(canvas, device).move(0, p1, canvas);
+ QCOMPARE(eventItem1->eventList.size(), 2);
+ QCOMPARE(eventItem1->eventList.at(1).type, QEvent::TouchUpdate);
+ QTest::touchEvent(canvas, device).release(0, p1, canvas);
+ QCOMPARE(eventItem1->eventList.size(), 3);
+ QCOMPARE(eventItem1->eventList.at(2).type, QEvent::TouchEnd);
+ eventItem1->eventList.clear();
+
+ delete canvas;
+}
+
+void tst_TouchMouse::eventFilter()
+{
+// // install event filter on item and see that it can grab events
+// QQuickView *canvas = createView();
+
+// canvas->setSource(testFileUrl("singleitem.qml"));
+// canvas->show();
+// canvas->requestActivateWindow();
+// QVERIFY(canvas->rootObject() != 0);
+
+// EventItem *eventItem1 = canvas->rootObject()->findChild<EventItem*>("eventItem1");
+// QVERIFY(eventItem1);
+// eventItem1->acceptTouch = true;
+
+// EventItem *filter = new EventItem;
+// filter->filterTouch = true;
+// eventItem1->installEventFilter(filter);
+
+// QPoint p1 = QPoint(20, 20);
+// QTest::touchEvent(canvas, device).press(0, p1, canvas);
+// // QEXPECT_FAIL("", "We do not implement event filters correctly", Abort);
+// QCOMPARE(eventItem1->eventList.size(), 0);
+// QCOMPARE(filter->eventList.size(), 1);
+// QTest::touchEvent(canvas, device).release(0, p1, canvas);
+// QCOMPARE(eventItem1->eventList.size(), 0);
+// QCOMPARE(filter->eventList.size(), 2);
+
+// delete filter;
+// delete canvas;
+}
+
+void tst_TouchMouse::mouse()
+{
+ // eventItem1
+ // - eventItem2
+
+ QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10);
+ QQuickView *canvas = createView();
+
+ canvas->setSource(testFileUrl("twoitems.qml"));
+ canvas->show();
+ canvas->requestActivateWindow();
+ QVERIFY(canvas->rootObject() != 0);
+
+ EventItem *eventItem1 = canvas->rootObject()->findChild<EventItem*>("eventItem1");
+ QVERIFY(eventItem1);
+ EventItem *eventItem2 = canvas->rootObject()->findChild<EventItem*>("eventItem2");
+ QVERIFY(eventItem2);
+ QTest::qWaitForWindowShown(canvas);
+
+ // bottom item likes mouse, top likes touch
+ eventItem1->setAcceptedMouseButtons(Qt::LeftButton);
+ eventItem1->acceptMouse = true;
+ // item 2 doesn't accept anything, thus it sees a touch pass by
+ QPoint p1 = QPoint(30, 30);
+ QTest::touchEvent(canvas, device).press(0, p1, canvas);
+
+ QCOMPARE(eventItem1->eventList.size(), 2);
+ QCOMPARE(eventItem1->eventList.at(0).type, QEvent::TouchBegin);
+ QCOMPARE(eventItem1->eventList.at(1).type, QEvent::MouseButtonPress);
+
+ delete canvas;
+}
+
+void tst_TouchMouse::touchOverMouse()
+{
+ // eventItem1
+ // - eventItem2
+
+ QQuickView *canvas = createView();
+
+ canvas->setSource(testFileUrl("twoitems.qml"));
+ canvas->show();
+ canvas->requestActivateWindow();
+ QVERIFY(canvas->rootObject() != 0);
+
+ EventItem *eventItem1 = canvas->rootObject()->findChild<EventItem*>("eventItem1");
+ QVERIFY(eventItem1);
+ EventItem *eventItem2 = canvas->rootObject()->findChild<EventItem*>("eventItem2");
+ QVERIFY(eventItem2);
+
+ // bottom item likes mouse, top likes touch
+ eventItem1->setAcceptedMouseButtons(Qt::LeftButton);
+ eventItem2->acceptTouch = true;
+
+ QTest::qWaitForWindowShown(canvas);
+
+ QCOMPARE(eventItem1->eventList.size(), 0);
+ QPoint p1 = QPoint(20, 20);
+ QTest::touchEvent(canvas, device).press(0, p1, canvas);
+ QCOMPARE(eventItem1->eventList.size(), 0);
+ QCOMPARE(eventItem2->eventList.size(), 1);
+ QCOMPARE(eventItem2->eventList.at(0).type, QEvent::TouchBegin);
+ p1 += QPoint(10, 0);
+ QTest::touchEvent(canvas, device).move(0, p1, canvas);
+ QCOMPARE(eventItem2->eventList.size(), 2);
+ QCOMPARE(eventItem2->eventList.at(1).type, QEvent::TouchUpdate);
+ QTest::touchEvent(canvas, device).release(0, p1, canvas);
+ QCOMPARE(eventItem2->eventList.size(), 3);
+ QCOMPARE(eventItem2->eventList.at(2).type, QEvent::TouchEnd);
+ eventItem2->eventList.clear();
+
+ delete canvas;
+}
+
+void tst_TouchMouse::mouseOverTouch()
+{
+ // eventItem1
+ // - eventItem2
+
+ QQuickView *canvas = createView();
+
+ canvas->setSource(testFileUrl("twoitems.qml"));
+ canvas->show();
+ canvas->requestActivateWindow();
+ QVERIFY(canvas->rootObject() != 0);
+
+ EventItem *eventItem1 = canvas->rootObject()->findChild<EventItem*>("eventItem1");
+ QVERIFY(eventItem1);
+ EventItem *eventItem2 = canvas->rootObject()->findChild<EventItem*>("eventItem2");
+ QVERIFY(eventItem2);
+
+ // bottom item likes mouse, top likes touch
+ eventItem1->acceptTouch = true;
+ eventItem2->setAcceptedMouseButtons(Qt::LeftButton);
+ eventItem2->acceptMouse = true;
+
+ QTest::qWaitForWindowShown(canvas);
+
+ QPoint p1 = QPoint(20, 20);
+ QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10);
+ QTest::touchEvent(canvas, device).press(0, p1, canvas);
+ QCOMPARE(eventItem1->eventList.size(), 0);
+ QCOMPARE(eventItem2->eventList.size(), 2);
+ QCOMPARE(eventItem2->eventList.at(0).type, QEvent::TouchBegin);
+ QCOMPARE(eventItem2->eventList.at(1).type, QEvent::MouseButtonPress);
+
+
+// p1 += QPoint(10, 0);
+// QTest::touchEvent(canvas, device).move(0, p1, canvas);
+// QCOMPARE(eventItem2->eventList.size(), 1);
+// QTest::touchEvent(canvas, device).release(0, p1, canvas);
+// QCOMPARE(eventItem2->eventList.size(), 1);
+// eventItem2->eventList.clear();
+
+ delete canvas;
+}
+
+void tst_TouchMouse::buttonOnFlickable()
+{
+ // flickable - height 500 / 1000
+ // - eventItem1 y: 100, height 100
+ // - eventItem2 y: 300, height 100
+
+ QQuickView *canvas = createView();
+
+ canvas->setSource(testFileUrl("buttononflickable.qml"));
+ canvas->show();
+ canvas->requestActivateWindow();
+ QVERIFY(canvas->rootObject() != 0);
+
+ QQuickFlickable *flickable = canvas->rootObject()->findChild<QQuickFlickable*>("flickable");
+ QVERIFY(flickable);
+
+ // should a mouse area button be clickable on top of flickable? yes :)
+ EventItem *eventItem1 = canvas->rootObject()->findChild<EventItem*>("eventItem1");
+ QVERIFY(eventItem1);
+ eventItem1->setAcceptedMouseButtons(Qt::LeftButton);
+ eventItem1->acceptMouse = true;
+
+ // should a touch button be touchable on top of flickable? yes :)
+ EventItem *eventItem2 = canvas->rootObject()->findChild<EventItem*>("eventItem2");
+ QVERIFY(eventItem2);
+ QCOMPARE(eventItem2->eventList.size(), 0);
+ eventItem2->acceptTouch = true;
+
+ // wait to avoid getting a double click event
+ QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10);
+
+ // check that buttons are clickable
+ // mouse button
+ QCOMPARE(eventItem1->eventList.size(), 0);
+ QPoint p1 = QPoint(20, 130);
+ QTest::touchEvent(canvas, device).press(0, p1, canvas);
+ QCOMPARE(eventItem1->eventList.size(), 2);
+ QCOMPARE(eventItem1->eventList.at(0).type, QEvent::TouchBegin);
+ QCOMPARE(eventItem1->eventList.at(1).type, QEvent::MouseButtonPress);
+ QTest::touchEvent(canvas, device).release(0, p1, canvas);
+ QCOMPARE(eventItem1->eventList.size(), 4);
+ QCOMPARE(eventItem1->eventList.at(2).type, QEvent::TouchEnd);
+ QCOMPARE(eventItem1->eventList.at(3).type, QEvent::MouseButtonRelease);
+ eventItem1->eventList.clear();
+
+ // touch button
+ p1 = QPoint(10, 310);
+ QTest::touchEvent(canvas, device).press(0, p1, canvas);
+ QCOMPARE(eventItem2->eventList.size(), 1);
+ QCOMPARE(eventItem2->eventList.at(0).type, QEvent::TouchBegin);
+ QTest::touchEvent(canvas, device).release(0, p1, canvas);
+ QCOMPARE(eventItem2->eventList.size(), 2);
+ QCOMPARE(eventItem2->eventList.at(1).type, QEvent::TouchEnd);
+ QCOMPARE(eventItem1->eventList.size(), 0);
+ eventItem2->eventList.clear();
+
+ // wait to avoid getting a double click event
+ QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10);
+
+ // click above button, no events please
+ p1 = QPoint(10, 90);
+ QTest::touchEvent(canvas, device).press(0, p1, canvas);
+ QCOMPARE(eventItem1->eventList.size(), 0);
+ QTest::touchEvent(canvas, device).release(0, p1, canvas);
+ QCOMPARE(eventItem1->eventList.size(), 0);
+ eventItem1->eventList.clear();
+
+ // wait to avoid getting a double click event
+ QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10);
+
+ // check that flickable moves - mouse button
+ QCOMPARE(eventItem1->eventList.size(), 0);
+ p1 = QPoint(10, 110);
+ QTest::touchEvent(canvas, device).press(0, p1, canvas);
+ QCOMPARE(eventItem1->eventList.size(), 2);
+ QCOMPARE(eventItem1->eventList.at(0).type, QEvent::TouchBegin);
+ QCOMPARE(eventItem1->eventList.at(1).type, QEvent::MouseButtonPress);
+
+ QQuickCanvasPrivate *canvasPriv = QQuickCanvasPrivate::get(canvas);
+ QCOMPARE(canvasPriv->touchMouseId, 0);
+ QCOMPARE(canvasPriv->itemForTouchPointId[0], eventItem1);
+ QCOMPARE(canvasPriv->mouseGrabberItem, eventItem1);
+
+ p1 += QPoint(0, -10);
+ QPoint p2 = p1 + QPoint(0, -10);
+ QPoint p3 = p2 + QPoint(0, -10);
+ QTest::qWait(10);
+ QTest::touchEvent(canvas, device).move(0, p1, canvas);
+ QTest::qWait(10);
+ QTest::touchEvent(canvas, device).move(0, p2, canvas);
+ QTest::qWait(10);
+ QTest::touchEvent(canvas, device).move(0, p3, canvas);
+
+ // we cannot really know when the events get grabbed away
+ QVERIFY(eventItem1->eventList.size() >= 4);
+ QCOMPARE(eventItem1->eventList.at(2).type, QEvent::TouchUpdate);
+ QCOMPARE(eventItem1->eventList.at(3).type, QEvent::MouseMove);
+
+ QCOMPARE(canvasPriv->mouseGrabberItem, flickable);
+ QVERIFY(flickable->isMovingVertically());
+
+ QTest::touchEvent(canvas, device).release(0, p3, canvas);
+ delete canvas;
+}
+
+void tst_TouchMouse::buttonOnTouch()
+{
+ // 400x800
+ // PinchArea - height 400
+ // - eventItem1 y: 100, height 100
+ // - eventItem2 y: 300, height 100
+ // MultiPointTouchArea - height 400
+ // - eventItem1 y: 100, height 100
+ // - eventItem2 y: 300, height 100
+
+ QQuickView *canvas = createView();
+ canvas->setSource(testFileUrl("buttonontouch.qml"));
+ canvas->show();
+ canvas->requestActivateWindow();
+ QVERIFY(canvas->rootObject() != 0);
+
+ QQuickPinchArea *pinchArea = canvas->rootObject()->findChild<QQuickPinchArea*>("pincharea");
+ QVERIFY(pinchArea);
+ QQuickItem *button1 = canvas->rootObject()->findChild<QQuickItem*>("button1");
+ QVERIFY(button1);
+ EventItem *eventItem1 = canvas->rootObject()->findChild<EventItem*>("eventItem1");
+ QVERIFY(eventItem1);
+ EventItem *eventItem2 = canvas->rootObject()->findChild<EventItem*>("eventItem2");
+ QVERIFY(eventItem2);
+
+ QQuickMultiPointTouchArea *touchArea = canvas->rootObject()->findChild<QQuickMultiPointTouchArea*>("toucharea");
+ QVERIFY(touchArea);
+ EventItem *eventItem3 = canvas->rootObject()->findChild<EventItem*>("eventItem3");
+ QVERIFY(eventItem3);
+ EventItem *eventItem4 = canvas->rootObject()->findChild<EventItem*>("eventItem4");
+ QVERIFY(eventItem4);
+
+
+ // Test the common case of a mouse area on top of pinch
+ eventItem1->setAcceptedMouseButtons(Qt::LeftButton);
+ eventItem1->acceptMouse = true;
+
+
+ // wait to avoid getting a double click event
+ QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10);
+
+ // Normal touch click
+ QPoint p1 = QPoint(10, 110);
+ QTest::touchEvent(canvas, device).press(0, p1, canvas);
+ QTest::touchEvent(canvas, device).release(0, p1, canvas);
+ QCOMPARE(eventItem1->eventList.size(), 4);
+ QCOMPARE(eventItem1->eventList.at(0).type, QEvent::TouchBegin);
+ QCOMPARE(eventItem1->eventList.at(1).type, QEvent::MouseButtonPress);
+ QCOMPARE(eventItem1->eventList.at(2).type, QEvent::TouchEnd);
+ QCOMPARE(eventItem1->eventList.at(3).type, QEvent::MouseButtonRelease);
+ eventItem1->eventList.clear();
+
+ // Normal mouse click
+ QTest::mouseClick(canvas, Qt::LeftButton, 0, p1);
+ QCOMPARE(eventItem1->eventList.size(), 2);
+ QCOMPARE(eventItem1->eventList.at(0).type, QEvent::MouseButtonPress);
+ QCOMPARE(eventItem1->eventList.at(1).type, QEvent::MouseButtonRelease);
+ eventItem1->eventList.clear();
+
+ // Pinch starting on the PinchArea should work
+ p1 = QPoint(40, 10);
+ QPoint p2 = QPoint(60, 10);
+
+ // Start the events after each other
+ QTest::touchEvent(canvas, device).press(0, p1, canvas);
+ QTest::touchEvent(canvas, device).stationary(0).press(1, p2, canvas);
+
+ QCOMPARE(button1->scale(), 1.0);
+
+ // This event seems to be discarded, let's ignore it for now until someone digs into pincharea
+ p1 -= QPoint(10, 0);
+ p2 += QPoint(10, 0);
+ QTest::touchEvent(canvas, device).move(0, p1, canvas).move(1, p2, canvas);
+
+ p1 -= QPoint(10, 0);
+ p2 += QPoint(10, 0);
+ QTest::touchEvent(canvas, device).move(0, p1, canvas).move(1, p2, canvas);
+// QCOMPARE(button1->scale(), 1.5);
+ qDebug() << "Button scale: " << button1->scale();
+
+ p1 -= QPoint(10, 0);
+ p2 += QPoint(10, 0);
+ QTest::touchEvent(canvas, device).move(0, p1, canvas).move(1, p2, canvas);
+// QCOMPARE(button1->scale(), 2.0);
+ qDebug() << "Button scale: " << button1->scale();
+
+ QTest::touchEvent(canvas, device).release(0, p1, canvas).release(1, p2, canvas);
+// QVERIFY(eventItem1->eventList.isEmpty());
+// QCOMPARE(button1->scale(), 2.0);
+ qDebug() << "Button scale: " << button1->scale();
+
+
+ // wait to avoid getting a double click event
+ QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10);
+
+ // Start pinching while on the button
+ button1->setScale(1.0);
+ p1 = QPoint(40, 110);
+ p2 = QPoint(60, 110);
+ QTest::touchEvent(canvas, device).press(0, p1, canvas).press(1, p2, canvas);
+ QCOMPARE(button1->scale(), 1.0);
+ QCOMPARE(eventItem1->eventList.count(), 2);
+ QCOMPARE(eventItem1->eventList.at(0).type, QEvent::TouchBegin);
+ QCOMPARE(eventItem1->eventList.at(1).type, QEvent::MouseButtonPress);
+
+ // This event seems to be discarded, let's ignore it for now until someone digs into pincharea
+ p1 -= QPoint(10, 0);
+ p2 += QPoint(10, 0);
+ QTest::touchEvent(canvas, device).move(0, p1, canvas).move(1, p2, canvas);
+
+ p1 -= QPoint(10, 0);
+ p2 += QPoint(10, 0);
+ QTest::touchEvent(canvas, device).move(0, p1, canvas).move(1, p2, canvas);
+ //QCOMPARE(button1->scale(), 1.5);
+ qDebug() << button1->scale();
+
+ p1 -= QPoint(10, 0);
+ p2 += QPoint(10, 0);
+ QTest::touchEvent(canvas, device).move(0, p1, canvas).move(1, p2, canvas);
+ qDebug() << button1->scale();
+ //QCOMPARE(button1->scale(), 2.0);
+
+ QTest::touchEvent(canvas, device).release(0, p1, canvas).release(1, p2, canvas);
+// QCOMPARE(eventItem1->eventList.size(), 99);
+ qDebug() << button1->scale();
+ //QCOMPARE(button1->scale(), 2.0);
+
+ delete canvas;
+}
+
+void tst_TouchMouse::pinchOnFlickable()
+{
+ QQuickView *canvas = createView();
+ canvas->setSource(testFileUrl("pinchonflickable.qml"));
+ canvas->show();
+ canvas->requestActivateWindow();
+ QVERIFY(canvas->rootObject() != 0);
+
+ QQuickPinchArea *pinchArea = canvas->rootObject()->findChild<QQuickPinchArea*>("pincharea");
+ QVERIFY(pinchArea);
+ QQuickFlickable *flickable = canvas->rootObject()->findChild<QQuickFlickable*>("flickable");
+ QVERIFY(flickable);
+ QQuickItem *rect = canvas->rootObject()->findChild<QQuickItem*>("rect");
+ QVERIFY(rect);
+
+ // flickable - single touch point
+ QVERIFY(flickable->contentX() == 0.0);
+ QPoint p = QPoint(100, 100);
+ QTest::touchEvent(canvas, device).press(0, p, canvas);
+ QCOMPARE(rect->pos(), QPointF(200.0, 200.0));
+ p -= QPoint(10, 0);
+ QTest::touchEvent(canvas, device).move(0, p, canvas);
+ p -= QPoint(10, 0);
+ QTest::touchEvent(canvas, device).move(0, p, canvas);
+ QTest::qWait(10);
+ p -= QPoint(10, 0);
+ QTest::touchEvent(canvas, device).move(0, p, canvas);
+ QTest::qWait(10);
+ p -= QPoint(10, 0);
+ QTest::touchEvent(canvas, device).move(0, p, canvas);
+ QTest::touchEvent(canvas, device).release(0, p, canvas);
+
+ QGuiApplication::processEvents();
+ QTest::qWait(10);
+ QVERIFY(!flickable->isAtXBeginning());
+ // wait until flicking is done
+ QTRY_VERIFY(!flickable->isFlicking());
+
+ // pinch
+ QPoint p1 = QPoint(40, 20);
+ QPoint p2 = QPoint(60, 20);
+
+ QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(canvas, device);
+ pinchSequence.press(0, p1, canvas).commit();
+ // In order for the stationary point to remember its previous position,
+ // we have to reuse the same pinchSequence object. Otherwise if we let it
+ // be destroyed and then start a new sequence, point 0 will default to being
+ // stationary at 0, 0, and PinchArea will filter out that touchpoint because
+ // it is outside its bounds.
+ pinchSequence.stationary(0).press(1, p2, canvas).commit();
+ p1 -= QPoint(10,10);
+ p2 += QPoint(10,10);
+ pinchSequence.move(0, p1, canvas).move(1, p2, canvas).commit();
+ QCOMPARE(rect->scale(), 1.0);
+ p1 -= QPoint(10, 0);
+ p2 += QPoint(10, 0);
+ pinchSequence.move(0, p1, canvas).move(1, p2, canvas).commit();
+ p1 -= QPoint(10, 0);
+ p2 += QPoint(10, 0);
+ pinchSequence.move(0, p1, canvas).move(1, p2, canvas).commit();
+ p1 -= QPoint(10, 0);
+ p2 += QPoint(10, 0);
+ pinchSequence.move(0, p1, canvas).move(1, p2, canvas).commit();
+ pinchSequence.release(0, p1, canvas).release(1, p2, canvas).commit();
+ QVERIFY(rect->scale() > 1.0);
+}
+
+void tst_TouchMouse::flickableOnPinch()
+{
+ QQuickView *canvas = createView();
+ canvas->setSource(testFileUrl("flickableonpinch.qml"));
+ canvas->show();
+ canvas->requestActivateWindow();
+ QVERIFY(canvas->rootObject() != 0);
+
+ QQuickPinchArea *pinchArea = canvas->rootObject()->findChild<QQuickPinchArea*>("pincharea");
+ QVERIFY(pinchArea);
+ QQuickFlickable *flickable = canvas->rootObject()->findChild<QQuickFlickable*>("flickable");
+ QVERIFY(flickable);
+ QQuickItem *rect = canvas->rootObject()->findChild<QQuickItem*>("rect");
+ QVERIFY(rect);
+
+ // flickable - single touch point
+ QVERIFY(flickable->contentX() == 0.0);
+ QPoint p = QPoint(100, 100);
+ QTest::touchEvent(canvas, device).press(0, p, canvas);
+ QCOMPARE(rect->pos(), QPointF(200.0, 200.0));
+ p -= QPoint(10, 0);
+ QTest::touchEvent(canvas, device).move(0, p, canvas);
+ p -= QPoint(10, 0);
+ QTest::touchEvent(canvas, device).move(0, p, canvas);
+
+ QTest::qWait(1000);
+
+ p -= QPoint(10, 0);
+ QTest::touchEvent(canvas, device).move(0, p, canvas);
+ QTest::touchEvent(canvas, device).release(0, p, canvas);
+
+ QTest::qWait(1000);
+
+ //QVERIFY(flickable->isMovingHorizontally());
+ qDebug() << "Pos: " << rect->pos();
+ // wait until flicking is done
+ QTRY_VERIFY(!flickable->isFlicking());
+
+ // pinch
+ QPoint p1 = QPoint(40, 20);
+ QPoint p2 = QPoint(60, 20);
+ QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(canvas, device);
+ pinchSequence.press(0, p1, canvas).commit();
+ // In order for the stationary point to remember its previous position,
+ // we have to reuse the same pinchSequence object. Otherwise if we let it
+ // be destroyed and then start a new sequence, point 0 will default to being
+ // stationary at 0, 0, and PinchArea will filter out that touchpoint because
+ // it is outside its bounds.
+ pinchSequence.stationary(0).press(1, p2, canvas).commit();
+ p1 -= QPoint(10,10);
+ p2 += QPoint(10,10);
+ pinchSequence.move(0, p1, canvas).move(1, p2, canvas).commit();
+ QCOMPARE(rect->scale(), 1.0);
+ p1 -= QPoint(10, 0);
+ p2 += QPoint(10, 0);
+ pinchSequence.move(0, p1, canvas).move(1, p2, canvas).commit();
+ p1 -= QPoint(10, 0);
+ p2 += QPoint(10, 0);
+ pinchSequence.move(0, p1, canvas).move(1, p2, canvas).commit();
+ p1 -= QPoint(10, 0);
+ p2 += QPoint(10, 0);
+ pinchSequence.move(0, p1, canvas).move(1, p2, canvas).commit();
+ pinchSequence.release(0, p1, canvas).release(1, p2, canvas).commit();
+ QVERIFY(rect->scale() > 1.0);
+}
+
+void tst_TouchMouse::mouseOnFlickableOnPinch()
+{
+ QQuickView *canvas = createView();
+ canvas->setSource(testFileUrl("mouseonflickableonpinch.qml"));
+ canvas->show();
+ canvas->requestActivateWindow();
+ QVERIFY(canvas->rootObject() != 0);
+
+ QQuickPinchArea *pinchArea = canvas->rootObject()->findChild<QQuickPinchArea*>("pincharea");
+ QVERIFY(pinchArea);
+ QQuickFlickable *flickable = canvas->rootObject()->findChild<QQuickFlickable*>("flickable");
+ QVERIFY(flickable);
+ QQuickItem *rect = canvas->rootObject()->findChild<QQuickItem*>("rect");
+ QVERIFY(rect);
+
+ // flickable - single touch point
+ QVERIFY(flickable->contentX() == 0.0);
+ QPoint p = QPoint(100, 100);
+ QTest::touchEvent(canvas, device).press(0, p, canvas);
+ QCOMPARE(rect->pos(), QPointF(200.0, 200.0));
+ p -= QPoint(10, 0);
+ QTest::touchEvent(canvas, device).move(0, p, canvas);
+ p -= QPoint(10, 0);
+ QTest::touchEvent(canvas, device).move(0, p, canvas);
+
+ QTest::qWait(1000);
+
+ p -= QPoint(10, 0);
+ QTest::touchEvent(canvas, device).move(0, p, canvas);
+ QTest::touchEvent(canvas, device).release(0, p, canvas);
+
+ QTest::qWait(1000);
+
+ //QVERIFY(flickable->isMovingHorizontally());
+ qDebug() << "Pos: " << rect->pos();
+
+ // pinch
+ QPoint p1 = QPoint(40, 20);
+ QPoint p2 = QPoint(60, 20);
+ QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(canvas, device);
+ pinchSequence.press(0, p1, canvas).commit();
+ // In order for the stationary point to remember its previous position,
+ // we have to reuse the same pinchSequence object. Otherwise if we let it
+ // be destroyed and then start a new sequence, point 0 will default to being
+ // stationary at 0, 0, and PinchArea will filter out that touchpoint because
+ // it is outside its bounds.
+ pinchSequence.stationary(0).press(1, p2, canvas).commit();
+ p1 -= QPoint(10,10);
+ p2 += QPoint(10,10);
+ pinchSequence.move(0, p1, canvas).move(1, p2, canvas).commit();
+ QCOMPARE(rect->scale(), 1.0);
+ p1 -= QPoint(10, 0);
+ p2 += QPoint(10, 0);
+ pinchSequence.move(0, p1, canvas).move(1, p2, canvas).commit();
+ p1 -= QPoint(10, 0);
+ p2 += QPoint(10, 0);
+ pinchSequence.move(0, p1, canvas).move(1, p2, canvas).commit();
+ p1 -= QPoint(10, 0);
+ p2 += QPoint(10, 0);
+ pinchSequence.move(0, p1, canvas).move(1, p2, canvas).commit();
+ pinchSequence.release(0, p1, canvas).release(1, p2, canvas).commit();
+ QVERIFY(rect->scale() > 1.0);
+
+ // PinchArea should steal the event after flicking started
+ rect->setScale(1.0);
+ flickable->setContentX(0.0);
+ p = QPoint(100, 100);
+ pinchSequence.press(0, p, canvas).commit();
+ QCOMPARE(rect->pos(), QPointF(200.0, 200.0));
+ p -= QPoint(10, 0);
+ pinchSequence.move(0, p, canvas).commit();
+ p -= QPoint(10, 0);
+ pinchSequence.move(0, p, canvas).commit();
+ QTest::qWait(1000);
+ p -= QPoint(10, 0);
+ pinchSequence.move(0, p, canvas).commit();
+
+ QQuickCanvasPrivate *canvasPriv = QQuickCanvasPrivate::get(canvas);
+ QCOMPARE(canvasPriv->mouseGrabberItem, flickable);
+ qDebug() << "Mouse Grabber: " << canvasPriv->mouseGrabberItem << " itemForTouchPointId: " << canvasPriv->itemForTouchPointId;
+
+ // Add a second finger, this should lead to stealing
+ p1 = QPoint(40, 100);
+ p2 = QPoint(60, 100);
+ pinchSequence.stationary(0).press(1, p2, canvas).commit();
+ QCOMPARE(rect->scale(), 1.0);
+
+ p1 -= QPoint(5, 0);
+ p2 += QPoint(5, 0);
+ pinchSequence.move(0, p1, canvas).move(1, p2, canvas).commit();
+ p1 -= QPoint(5, 0);
+ p2 += QPoint(5, 0);
+ pinchSequence.move(0, p1, canvas).move(1, p2, canvas).commit();
+ p1 -= QPoint(5, 0);
+ p2 += QPoint(5, 0);
+ pinchSequence.move(0, p1, canvas).move(1, p2, canvas).commit();
+ pinchSequence.release(0, p1, canvas).release(1, p2, canvas).commit();
+ QVERIFY(rect->scale() > 1.0);
+ pinchSequence.release(0, p, canvas).commit();
+}
+
+QTEST_MAIN(tst_TouchMouse)
+
+#include "tst_touchmouse.moc"
+