Make touch event delivery in the canvas reentrant
At the moment the unlikely case of spinning the event loop from a QQuickItem event handler for TouchBegin is not handled properly: It will end up not delivering the subsequent TouchUpdate and TouchEnd events to the item, leaving it in a state that should not normally happen. Change-Id: I668d065c9cf1a299bfd9268a9125d7a7e32f6786 Reviewed-by: Frederik Gladhorn <frederik.gladhorn@nokia.com>
This commit is contained in:
parent
9b1299e0db
commit
b8bb9d7561
|
@ -1493,14 +1493,19 @@ bool QQuickCanvasPrivate::deliverTouchPoints(QQuickItem *item, QTouchEvent *even
|
|||
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++) {
|
||||
itemForTouchPointId[matchingPoints[i].id()] = item;
|
||||
for (int i = 0; i < matchingPoints.count(); ++i)
|
||||
acceptedNewPoints->insert(matchingPoints[i].id());
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < matchingPoints.count(); ++i)
|
||||
if (itemForTouchPointId.value(matchingPoints[i].id()) == item)
|
||||
itemForTouchPointId.remove(matchingPoints[i].id());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -144,7 +144,8 @@ class TestTouchItem : public QQuickRectangle
|
|||
Q_OBJECT
|
||||
public:
|
||||
TestTouchItem(QQuickItem *parent = 0)
|
||||
: QQuickRectangle(parent), acceptEvents(true), mousePressId(0)
|
||||
: QQuickRectangle(parent), acceptEvents(true), mousePressId(0),
|
||||
spinLoopWhenPressed(false), touchEventCount(0)
|
||||
{
|
||||
border()->setWidth(1);
|
||||
setAcceptedMouseButtons(Qt::LeftButton);
|
||||
|
@ -164,17 +165,29 @@ public:
|
|||
mousePressNum = 0;
|
||||
}
|
||||
|
||||
void clearTouchEventCounter()
|
||||
{
|
||||
touchEventCount = 0;
|
||||
}
|
||||
|
||||
bool acceptEvents;
|
||||
TouchEventData lastEvent;
|
||||
int mousePressId;
|
||||
bool spinLoopWhenPressed;
|
||||
int touchEventCount;
|
||||
|
||||
protected:
|
||||
virtual void touchEvent(QTouchEvent *event) {
|
||||
if (!acceptEvents) {
|
||||
event->ignore();
|
||||
return;
|
||||
}
|
||||
++touchEventCount;
|
||||
lastEvent = makeTouchData(event->type(), event->window(), event->touchPointStates(), event->touchPoints());
|
||||
event->accept();
|
||||
if (spinLoopWhenPressed && event->touchPointStates().testFlag(Qt::TouchPointPressed)) {
|
||||
QCoreApplication::processEvents();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void mousePressEvent(QMouseEvent *) {
|
||||
|
@ -230,6 +243,7 @@ private slots:
|
|||
void touchEvent_propagation();
|
||||
void touchEvent_propagation_data();
|
||||
void touchEvent_cancel();
|
||||
void touchEvent_reentrant();
|
||||
|
||||
void clearCanvas();
|
||||
|
||||
|
@ -540,6 +554,42 @@ void tst_qquickcanvas::touchEvent_cancel()
|
|||
delete canvas;
|
||||
}
|
||||
|
||||
void tst_qquickcanvas::touchEvent_reentrant()
|
||||
{
|
||||
TestTouchItem::clearMousePressCounter();
|
||||
|
||||
QQuickCanvas *canvas = new QQuickCanvas;
|
||||
canvas->resize(250, 250);
|
||||
canvas->setPos(100, 100);
|
||||
canvas->show();
|
||||
|
||||
TestTouchItem *item = new TestTouchItem(canvas->rootItem());
|
||||
|
||||
item->spinLoopWhenPressed = true; // will call processEvents() from the touch handler
|
||||
|
||||
item->setPos(QPointF(50, 50));
|
||||
item->setSize(QSizeF(150, 150));
|
||||
QPointF pos(60, 60);
|
||||
|
||||
// None of these should commit from the dtor.
|
||||
QTest::QTouchEventSequence press = QTest::touchEvent(canvas, touchDevice, false).press(0, pos.toPoint(), canvas);
|
||||
pos += QPointF(2, 2);
|
||||
QTest::QTouchEventSequence move = QTest::touchEvent(canvas, touchDevice, false).move(0, pos.toPoint(), canvas);
|
||||
QTest::QTouchEventSequence release = QTest::touchEvent(canvas, touchDevice, false).release(0, pos.toPoint(), canvas);
|
||||
|
||||
// Now commit (i.e. call QWindowSystemInterface::handleTouchEvent), but do not process the events yet.
|
||||
press.commit(false);
|
||||
move.commit(false);
|
||||
release.commit(false);
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
QTRY_COMPARE(item->touchEventCount, 3);
|
||||
|
||||
delete item;
|
||||
delete canvas;
|
||||
}
|
||||
|
||||
void tst_qquickcanvas::clearCanvas()
|
||||
{
|
||||
QQuickCanvas *canvas = new QQuickCanvas;
|
||||
|
|
Loading…
Reference in New Issue