aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-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
9 files changed, 464 insertions, 423 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);