aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/quick/items/qquickflickable.cpp138
-rw-r--r--src/quick/items/qquickflickable_p.h2
-rw-r--r--src/quick/items/qquickflickable_p_p.h7
-rw-r--r--tests/auto/quick/qquickflickable/data/nestedPressDelay.qml4
-rw-r--r--tests/auto/quick/qquickflickable/data/pressDelay.qml32
-rw-r--r--tests/auto/quick/qquickflickable/tst_qquickflickable.cpp61
6 files changed, 154 insertions, 90 deletions
diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp
index 105989c7f1..0a3dcd8039 100644
--- a/src/quick/items/qquickflickable.cpp
+++ b/src/quick/items/qquickflickable.cpp
@@ -265,12 +265,12 @@ QQuickFlickablePrivate::QQuickFlickablePrivate()
, vData(this, &QQuickFlickablePrivate::setViewportY)
, hMoved(false), vMoved(false)
, stealMouse(false), pressed(false), interactive(true), calcVelocity(false)
- , pixelAligned(false)
+ , pixelAligned(false), replayingPressEvent(false)
, lastPosTime(-1)
, lastPressTime(0)
, deceleration(QML_FLICK_DEFAULTDECELERATION)
, maxVelocity(QML_FLICK_DEFAULTMAXVELOCITY), reportedVelocitySmoothing(100)
- , delayedPressEvent(0), delayedPressTarget(0), pressDelay(0), fixupDuration(400)
+ , delayedPressEvent(0), pressDelay(0), fixupDuration(400)
, flickBoost(1.0), fixupMode(Normal), vTime(0), visibleArea(0)
, flickableDirection(QQuickFlickable::AutoFlickDirection)
, boundsBehavior(QQuickFlickable::DragAndOvershootBounds)
@@ -962,6 +962,7 @@ void QQuickFlickablePrivate::handleMousePressEvent(QMouseEvent *event)
flickBoost = 1.0;
}
q->setKeepMouseGrab(stealMouse);
+ clearDelayedPress();
pressed = true;
if (hData.transitionToBounds)
hData.transitionToBounds->stopTransition();
@@ -1087,8 +1088,10 @@ void QQuickFlickablePrivate::handleMouseMoveEvent(QMouseEvent *event)
}
stealMouse = stealX || stealY;
- if (stealMouse)
+ if (stealMouse) {
q->setKeepMouseGrab(true);
+ clearDelayedPress();
+ }
if (rejectY) {
vData.velocityBuffer.clear();
@@ -1228,6 +1231,15 @@ void QQuickFlickable::mouseMoveEvent(QMouseEvent *event)
{
Q_D(QQuickFlickable);
if (d->interactive) {
+ if (d->delayedPressEvent) {
+ // A move beyond the threshold replays the press to give nested Flickables
+ // the opportunity to grab the gesture.
+ QPointF delta = event->localPos() - d->delayedPressEvent->localPos();
+ if (QQuickWindowPrivate::dragOverThreshold(qAbs(delta.x()), Qt::XAxis, event)
+ || QQuickWindowPrivate::dragOverThreshold(qAbs(delta.y()), Qt::YAxis, event)) {
+ d->replayDelayedPress();
+ }
+ }
d->handleMouseMoveEvent(event);
event->accept();
} else {
@@ -1239,11 +1251,20 @@ void QQuickFlickable::mouseReleaseEvent(QMouseEvent *event)
{
Q_D(QQuickFlickable);
if (d->interactive) {
- d->clearDelayedPress();
+ if (d->delayedPressEvent) {
+ d->replayDelayedPress();
+
+ // Now send the release
+ window()->sendEvent(window()->mouseGrabberItem(), event);
+
+ // And the event has been consumed
+ d->stealMouse = false;
+ d->pressed = false;
+ return;
+ }
+
d->handleMouseReleaseEvent(event);
event->accept();
- if (window() && window()->mouseGrabberItem() == this)
- ungrabMouse();
} else {
QQuickItem::mouseReleaseEvent(event);
}
@@ -1304,28 +1325,32 @@ void QQuickFlickable::wheelEvent(QWheelEvent *event)
QQuickItem::wheelEvent(event);
}
-bool QQuickFlickablePrivate::isOutermostPressDelay() const
+bool QQuickFlickablePrivate::isInnermostPressDelay(QQuickItem *i) const
{
Q_Q(const QQuickFlickable);
- QQuickItem *item = q->parentItem();
+ QQuickItem *item = i;
while (item) {
QQuickFlickable *flick = qobject_cast<QQuickFlickable*>(item);
- if (flick && flick->pressDelay() > 0 && flick->isInteractive())
- return false;
+ if (flick && flick->pressDelay() > 0 && flick->isInteractive()) {
+ // Found the innermost flickable with press delay - is it me?
+ return (flick == q);
+ }
item = item->parentItem();
}
-
- return true;
+ return false;
}
-void QQuickFlickablePrivate::captureDelayedPress(QMouseEvent *event)
+void QQuickFlickablePrivate::captureDelayedPress(QQuickItem *item, QMouseEvent *event)
{
Q_Q(QQuickFlickable);
if (!q->window() || pressDelay <= 0)
return;
- if (!isOutermostPressDelay())
+
+ // Only the innermost flickable should handle the delayed press; this allows
+ // flickables up the parent chain to all see the events in their filter functions
+ if (!isInnermostPressDelay(item))
return;
- delayedPressTarget = q->window()->mouseGrabberItem();
+
delayedPressEvent = QQuickWindowPrivate::cloneMouseEvent(event);
delayedPressEvent->setAccepted(false);
delayedPressTimer.start(pressDelay, q);
@@ -1340,6 +1365,28 @@ void QQuickFlickablePrivate::clearDelayedPress()
}
}
+void QQuickFlickablePrivate::replayDelayedPress()
+{
+ Q_Q(QQuickFlickable);
+ if (delayedPressEvent) {
+ // Losing the grab will clear the delayed press event; take control of it here
+ QScopedPointer<QMouseEvent> mouseEvent(delayedPressEvent);
+ delayedPressEvent = 0;
+ delayedPressTimer.stop();
+
+ // If we have the grab, release before delivering the event
+ QQuickWindow *w = q->window();
+ if (w && (w->mouseGrabberItem() == q)) {
+ q->ungrabMouse();
+ }
+
+ // Use the event handler that will take care of finding the proper item to propagate the event
+ replayingPressEvent = true;
+ QQuickWindowPrivate::get(w)->deliverMouseEvent(mouseEvent.data());
+ replayingPressEvent = false;
+ }
+}
+
//XXX pixelAligned ignores the global position of the Flickable, i.e. assumes Flickable itself is pixel aligned.
void QQuickFlickablePrivate::setViewportX(qreal x)
{
@@ -1357,17 +1404,7 @@ void QQuickFlickable::timerEvent(QTimerEvent *event)
if (event->timerId() == d->delayedPressTimer.timerId()) {
d->delayedPressTimer.stop();
if (d->delayedPressEvent) {
- QQuickItem *grabber = window() ? window()->mouseGrabberItem() : 0;
- if (!grabber || grabber != this) {
- // We replay the mouse press but the grabber we had might not be interessted by the event (e.g. overlay)
- // so we reset the grabber
- if (window()->mouseGrabberItem() == d->delayedPressTarget)
- d->delayedPressTarget->ungrabMouse();
- // Use the event handler that will take care of finding the proper item to propagate the event
- QQuickWindowPrivate::get(window())->deliverMouseEvent(d->delayedPressEvent);
- }
- delete d->delayedPressEvent;
- d->delayedPressEvent = 0;
+ d->replayDelayedPress();
}
}
}
@@ -1992,7 +2029,7 @@ void QQuickFlickable::mouseUngrabEvent()
}
}
-bool QQuickFlickable::sendMouseEvent(QMouseEvent *event)
+bool QQuickFlickable::sendMouseEvent(QQuickItem *item, QMouseEvent *event)
{
Q_D(QQuickFlickable);
QPointF localPos = mapFromScene(event->windowPos());
@@ -2007,48 +2044,18 @@ bool QQuickFlickable::sendMouseEvent(QMouseEvent *event)
switch (mouseEvent->type()) {
case QEvent::MouseMove:
- if (d->delayedPressEvent) {
- // A move beyond the threshold replays the press to give nested Flickables
- // the opportunity to grab the gesture.
- QPointF delta = event->localPos() - d->delayedPressEvent->localPos();
- if (QQuickWindowPrivate::dragOverThreshold(qAbs(delta.x()), Qt::XAxis, event)
- || QQuickWindowPrivate::dragOverThreshold(qAbs(delta.y()), Qt::YAxis, event)) {
- // We replay the mouse press but the grabber we had might not be interested in the event (e.g. overlay)
- // so we reset the grabber
- if (c->mouseGrabberItem() == d->delayedPressTarget)
- d->delayedPressTarget->ungrabMouse();
- // Use the event handler that will take care of finding the proper item to propagate the event
- QQuickWindowPrivate::get(window())->deliverMouseEvent(d->delayedPressEvent);
- d->clearDelayedPress();
- // continue on to handle mouse move event
- }
- }
d->handleMouseMoveEvent(mouseEvent.data());
break;
case QEvent::MouseButtonPress:
- if (d->pressed) // we are already pressed - this is a delayed replay
+ // Don't process a replayed event during replay
+ if (d->replayingPressEvent)
return false;
d->handleMousePressEvent(mouseEvent.data());
- d->captureDelayedPress(event);
+ d->captureDelayedPress(item, event);
stealThisEvent = d->stealMouse; // Update stealThisEvent in case changed by function call above
break;
case QEvent::MouseButtonRelease:
- if (d->delayedPressEvent) {
- // We replay the mouse press but the grabber we had might not be interested in the event (e.g. overlay)
- // so we reset the grabber
- if (c->mouseGrabberItem() == d->delayedPressTarget)
- d->delayedPressTarget->ungrabMouse();
- // Use the event handler that will take care of finding the proper item to propagate the event
- QQuickWindowPrivate::get(window())->deliverMouseEvent(d->delayedPressEvent);
- d->clearDelayedPress();
- // We send the release
- window()->sendEvent(c->mouseGrabberItem(), event);
- // And the event has been consumed
- d->stealMouse = false;
- d->pressed = false;
- return true;
- }
d->handleMouseReleaseEvent(mouseEvent.data());
break;
default:
@@ -2060,7 +2067,12 @@ bool QQuickFlickable::sendMouseEvent(QMouseEvent *event)
grabMouse();
}
- return stealThisEvent || d->delayedPressEvent || grabberDisabled;
+ // Do not accept this event when filtering, as this would force the mouse grab to the child
+ const bool filtered = stealThisEvent || d->delayedPressEvent || grabberDisabled;
+ if (filtered) {
+ event->setAccepted(false);
+ }
+ return filtered;
} else if (d->lastPosTime != -1) {
d->lastPosTime = -1;
returnToBounds();
@@ -2084,7 +2096,7 @@ bool QQuickFlickable::childMouseEventFilter(QQuickItem *i, QEvent *e)
case QEvent::MouseButtonPress:
case QEvent::MouseMove:
case QEvent::MouseButtonRelease:
- return sendMouseEvent(static_cast<QMouseEvent *>(e));
+ return sendMouseEvent(i, static_cast<QMouseEvent *>(e));
case QEvent::UngrabMouse:
if (d->window && d->window->mouseGrabberItem() && d->window->mouseGrabberItem() != this) {
// The grab has been taken away from a child and given to some other item.
@@ -2250,7 +2262,7 @@ bool QQuickFlickablePrivate::isViewMoving() const
within the timeout, both the press and release will be delivered.
Note that for nested Flickables with pressDelay set, the pressDelay of
- inner Flickables is overridden by the outermost Flickable.
+ outer Flickables is overridden by the innermost Flickable.
*/
int QQuickFlickable::pressDelay() const
{
diff --git a/src/quick/items/qquickflickable_p.h b/src/quick/items/qquickflickable_p.h
index a97af9ce50..d5f97ad963 100644
--- a/src/quick/items/qquickflickable_p.h
+++ b/src/quick/items/qquickflickable_p.h
@@ -257,7 +257,7 @@ protected:
virtual void geometryChanged(const QRectF &newGeometry,
const QRectF &oldGeometry);
void mouseUngrabEvent();
- bool sendMouseEvent(QMouseEvent *event);
+ bool sendMouseEvent(QQuickItem *item, QMouseEvent *event);
bool xflick() const;
bool yflick() const;
diff --git a/src/quick/items/qquickflickable_p_p.h b/src/quick/items/qquickflickable_p_p.h
index ac1ecdb3cb..1aa4c4ab3e 100644
--- a/src/quick/items/qquickflickable_p_p.h
+++ b/src/quick/items/qquickflickable_p_p.h
@@ -183,9 +183,10 @@ public:
void updateBeginningEnd();
- bool isOutermostPressDelay() const;
- void captureDelayedPress(QMouseEvent *event);
+ bool isInnermostPressDelay(QQuickItem *item) const;
+ void captureDelayedPress(QQuickItem *item, QMouseEvent *event);
void clearDelayedPress();
+ void replayDelayedPress();
void setViewportX(qreal x);
void setViewportY(qreal y);
@@ -213,6 +214,7 @@ public:
bool interactive : 1;
bool calcVelocity : 1;
bool pixelAligned : 1;
+ bool replayingPressEvent : 1;
QElapsedTimer timer;
qint64 lastPosTime;
qint64 lastPressTime;
@@ -222,7 +224,6 @@ public:
qreal maxVelocity;
qreal reportedVelocitySmoothing;
QMouseEvent *delayedPressEvent;
- QQuickItem *delayedPressTarget;
QBasicTimer delayedPressTimer;
int pressDelay;
int fixupDuration;
diff --git a/tests/auto/quick/qquickflickable/data/nestedPressDelay.qml b/tests/auto/quick/qquickflickable/data/nestedPressDelay.qml
index 557b7c4117..742656641f 100644
--- a/tests/auto/quick/qquickflickable/data/nestedPressDelay.qml
+++ b/tests/auto/quick/qquickflickable/data/nestedPressDelay.qml
@@ -7,7 +7,7 @@ Flickable {
contentWidth: 480
contentHeight: 320
flickableDirection: Flickable.HorizontalFlick
- pressDelay: 50
+ pressDelay: 10000
Rectangle {
x: 20
y: 20
@@ -20,7 +20,7 @@ Flickable {
flickableDirection: Flickable.HorizontalFlick
contentWidth: 1480
contentHeight: 400
- pressDelay: 10000
+ pressDelay: 50
Rectangle {
y: 100
x: 80
diff --git a/tests/auto/quick/qquickflickable/data/pressDelay.qml b/tests/auto/quick/qquickflickable/data/pressDelay.qml
new file mode 100644
index 0000000000..18a5840504
--- /dev/null
+++ b/tests/auto/quick/qquickflickable/data/pressDelay.qml
@@ -0,0 +1,32 @@
+import QtQuick 2.0
+
+Flickable {
+ flickableDirection: Flickable.VerticalFlick
+ width: 480
+ height: 320
+ contentWidth: 480
+ contentHeight: 400
+ pressDelay: 100
+
+ MouseArea {
+ id: ma
+ objectName: "mouseArea"
+ y: 100
+ anchors.horizontalCenter: parent.horizontalCenter
+ width: 240
+ height: 100
+
+ Rectangle {
+ anchors.fill: parent
+ color: parent.pressed ? 'blue' : 'green'
+
+ Text {
+ anchors.fill: parent
+ verticalAlignment: Text.AlignVCenter
+ horizontalAlignment: Text.AlignHCenter
+ text: 'Hello'
+ }
+ }
+ }
+}
+
diff --git a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp
index ceed276305..66071e65d4 100644
--- a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp
+++ b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp
@@ -346,9 +346,13 @@ void tst_qquickflickable::flickDeceleration()
void tst_qquickflickable::pressDelay()
{
- QQmlComponent component(&engine);
- component.setData("import QtQuick 2.0; Flickable { pressDelay: 100; }", QUrl::fromLocalFile(""));
- QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(component.create());
+ QScopedPointer<QQuickView> window(new QQuickView);
+ window->setSource(testFileUrl("pressDelay.qml"));
+ window->show();
+ window->requestActivate();
+ QVERIFY(window->rootObject() != 0);
+
+ QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(window->rootObject());
QSignalSpy spy(flickable, SIGNAL(pressDelayChanged()));
QVERIFY(flickable);
@@ -360,13 +364,28 @@ void tst_qquickflickable::pressDelay()
flickable->setPressDelay(200);
QCOMPARE(spy.count(),1);
- delete flickable;
+ QQuickItem *mouseArea = window->rootObject()->findChild<QQuickItem*>("mouseArea");
+ QSignalSpy clickedSpy(mouseArea, SIGNAL(clicked(QQuickMouseEvent*)));
+
+ QTest::mousePress(window.data(), Qt::LeftButton, 0, QPoint(150, 150));
+
+ // The press should not occur immediately
+ QVERIFY(mouseArea->property("pressed").toBool() == false);
+
+ // But, it should occur eventually
+ QTRY_VERIFY(mouseArea->property("pressed").toBool() == true);
+
+ QCOMPARE(clickedSpy.count(),0);
+
+ // On release the clicked signal should be emitted
+ QTest::mouseRelease(window.data(), Qt::LeftButton, 0, QPoint(150, 150));
+ QCOMPARE(clickedSpy.count(),1);
}
// QTBUG-17361
void tst_qquickflickable::nestedPressDelay()
{
- QQuickView *window = new QQuickView;
+ QScopedPointer<QQuickView> window(new QQuickView);
window->setSource(testFileUrl("nestedPressDelay.qml"));
window->show();
window->requestActivate();
@@ -378,46 +397,46 @@ void tst_qquickflickable::nestedPressDelay()
QQuickFlickable *inner = window->rootObject()->findChild<QQuickFlickable*>("innerFlickable");
QVERIFY(inner != 0);
- QTest::mousePress(window, Qt::LeftButton, 0, QPoint(150, 150));
+ QTest::mousePress(window.data(), Qt::LeftButton, 0, QPoint(150, 150));
// the MouseArea is not pressed immediately
QVERIFY(outer->property("pressed").toBool() == false);
+ QVERIFY(inner->property("pressed").toBool() == false);
- // The outer pressDelay will prevail (50ms, vs. 10sec)
+ // The inner pressDelay will prevail (50ms, vs. 10sec)
// QTRY_VERIFY() has 5sec timeout, so will timeout well within 10sec.
QTRY_VERIFY(outer->property("pressed").toBool() == true);
- QTest::mouseRelease(window, Qt::LeftButton, 0, QPoint(150, 150));
+ QTest::mouseRelease(window.data(), Qt::LeftButton, 0, QPoint(150, 150));
// Dragging inner Flickable should work
- QTest::mousePress(window, Qt::LeftButton, 0, QPoint(80, 150));
+ QTest::mousePress(window.data(), Qt::LeftButton, 0, QPoint(80, 150));
// the MouseArea is not pressed immediately
QVERIFY(outer->property("pressed").toBool() == false);
+ QVERIFY(inner->property("pressed").toBool() == false);
- QTest::mouseMove(window, QPoint(60, 150));
- QTest::mouseMove(window, QPoint(40, 150));
- QTest::mouseMove(window, QPoint(20, 150));
+ QTest::mouseMove(window.data(), QPoint(60, 150));
+ QTest::mouseMove(window.data(), QPoint(40, 150));
+ QTest::mouseMove(window.data(), QPoint(20, 150));
- QVERIFY(outer->property("moving").toBool() == false);
QVERIFY(inner->property("moving").toBool() == true);
+ QVERIFY(outer->property("moving").toBool() == false);
- QTest::mouseRelease(window, Qt::LeftButton, 0, QPoint(20, 150));
+ QTest::mouseRelease(window.data(), Qt::LeftButton, 0, QPoint(20, 150));
// Dragging the MouseArea in the inner Flickable should move the inner Flickable
- QTest::mousePress(window, Qt::LeftButton, 0, QPoint(150, 150));
+ QTest::mousePress(window.data(), Qt::LeftButton, 0, QPoint(150, 150));
// the MouseArea is not pressed immediately
QVERIFY(outer->property("pressed").toBool() == false);
- QTest::mouseMove(window, QPoint(130, 150));
- QTest::mouseMove(window, QPoint(110, 150));
- QTest::mouseMove(window, QPoint(90, 150));
+ QTest::mouseMove(window.data(), QPoint(130, 150));
+ QTest::mouseMove(window.data(), QPoint(110, 150));
+ QTest::mouseMove(window.data(), QPoint(90, 150));
QVERIFY(outer->property("moving").toBool() == false);
QVERIFY(inner->property("moving").toBool() == true);
- QTest::mouseRelease(window, Qt::LeftButton, 0, QPoint(90, 150));
-
- delete window;
+ QTest::mouseRelease(window.data(), Qt::LeftButton, 0, QPoint(90, 150));
}
void tst_qquickflickable::flickableDirection()