From 7a1282a9edd8afa4645d7d449ca2ed3746fb8d71 Mon Sep 17 00:00:00 2001 From: Andrew den Exter Date: Fri, 15 Feb 2013 21:29:32 +1000 Subject: [PATCH] MouseArea shouldn't grab the mouse until there is an effective drag. A MouseArea shouldn't prevent a parent MouseArea or Flickable from handling a drag event unless it is going to do something useful with it. Task-number: 29717 Change-Id: I24016994f6cf9116382ef7faeb50b10e5716e10e Reviewed-by: Alan Alpert --- src/quick/items/qquickmousearea.cpp | 49 ++++++--------- .../qquickmousearea/data/nestedStopAtBounds.qml | 44 +++++++++++++ .../quick/qquickmousearea/tst_qquickmousearea.cpp | 68 ++++++++++++++++++++ 3 files changed, 131 insertions(+), 30 deletions(-) create mode 100644 tests/auto/quick/qquickmousearea/data/nestedStopAtBounds.qml diff --git a/src/quick/items/qquickmousearea.cpp b/src/quick/items/qquickmousearea.cpp index e75a601..2fa52d9 100644 --- a/src/quick/items/qquickmousearea.cpp +++ b/src/quick/items/qquickmousearea.cpp @@ -797,9 +797,6 @@ void QQuickMouseArea::mouseMoveEvent(QMouseEvent *event) curLocalPos = event->windowPos(); } - qreal dx = qAbs(curLocalPos.x() - startLocalPos.x()); - qreal dy = qAbs(curLocalPos.y() - startLocalPos.y()); - if (keepMouseGrab() && d->stealMouse && !d->drag->active()) d->drag->setActive(true); @@ -807,38 +804,30 @@ void QQuickMouseArea::mouseMoveEvent(QMouseEvent *event) ? d->drag->target()->parentItem()->mapFromScene(d->targetStartPos) : d->targetStartPos; - QPointF dragPos = d->drag->target()->position(); - bool dragX = drag()->axis() & QQuickDrag::XAxis; bool dragY = drag()->axis() & QQuickDrag::YAxis; - if (dragX && d->drag->active()) { - qreal x = (curLocalPos.x() - startLocalPos.x()) + startPos.x(); - if (x < drag()->xmin()) - x = drag()->xmin(); - else if (x > drag()->xmax()) - x = drag()->xmax(); - dragPos.setX(x); + QPointF dragPos = d->drag->target()->position(); + if (dragX) { + dragPos.setX(qBound( + d->drag->xmin(), + startPos.x() + curLocalPos.x() - startLocalPos.x(), + d->drag->xmax())); } - if (dragY && d->drag->active()) { - qreal y = (curLocalPos.y() - startLocalPos.y()) + startPos.y(); - if (y < drag()->ymin()) - y = drag()->ymin(); - else if (y > drag()->ymax()) - y = drag()->ymax(); - dragPos.setY(y); + if (dragY) { + dragPos.setY(qBound( + d->drag->ymin(), + startPos.y() + curLocalPos.y() - startLocalPos.y(), + d->drag->ymax())); } - d->drag->target()->setPosition(dragPos); - - if (!keepMouseGrab()) { - bool xDragged = QQuickWindowPrivate::dragOverThreshold(dx, Qt::XAxis, event); - bool yDragged = QQuickWindowPrivate::dragOverThreshold(dy, Qt::YAxis, event); - if ((!dragY && !yDragged && dragX && xDragged) - || (!dragX && !xDragged && dragY && yDragged) - || (dragX && dragY && (xDragged || yDragged))) { - setKeepMouseGrab(true); - d->stealMouse = true; - } + if (d->drag->active()) + d->drag->target()->setPosition(dragPos); + + if (!keepMouseGrab() + && (QQuickWindowPrivate::dragOverThreshold(dragPos.x() - startPos.x(), Qt::XAxis, event) + || QQuickWindowPrivate::dragOverThreshold(dragPos.y() - startPos.y(), Qt::YAxis, event))) { + setKeepMouseGrab(true); + d->stealMouse = true; } d->moved = true; diff --git a/tests/auto/quick/qquickmousearea/data/nestedStopAtBounds.qml b/tests/auto/quick/qquickmousearea/data/nestedStopAtBounds.qml new file mode 100644 index 0000000..1fd39bb --- /dev/null +++ b/tests/auto/quick/qquickmousearea/data/nestedStopAtBounds.qml @@ -0,0 +1,44 @@ +import QtQuick 2.0 + +Rectangle { + width: 400 + height: 400 + + MouseArea { + id: outer + objectName: "outer" + x: 50 + y: 50 + width: 300 + height: 300 + + drag.target: outer + drag.filterChildren: true + + Rectangle { + anchors.fill: parent + color: "yellow" + } + + MouseArea { + id: inner + objectName: "inner" + + x: 0 + y: 0 + width: 200 + height: 200 + + drag.target: inner + drag.minimumX: 0 + drag.maximumX: 100 + drag.minimumY: 0 + drag.maximumY: 100 + + Rectangle { + anchors.fill: parent + color: "blue" + } + } + } +} diff --git a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp index 37ce0fd..ef7dc72 100644 --- a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp +++ b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp @@ -89,6 +89,8 @@ private slots: void cursorShape(); #endif void moveAndReleaseWithoutPress(); + void nestedStopAtBounds(); + void nestedStopAtBounds_data(); private: void acceptedButton_data(); @@ -1426,6 +1428,72 @@ void tst_QQuickMouseArea::moveAndReleaseWithoutPress() delete window; } +void tst_QQuickMouseArea::nestedStopAtBounds_data() +{ + QTest::addColumn("transpose"); + QTest::addColumn("invert"); + + QTest::newRow("left") << false << false; + QTest::newRow("right") << false << true; + QTest::newRow("top") << true << false; + QTest::newRow("bottom") << true << true; +} + +void tst_QQuickMouseArea::nestedStopAtBounds() +{ + QFETCH(bool, transpose); + QFETCH(bool, invert); + + QQuickView view; + view.setSource(testFileUrl("nestedStopAtBounds.qml")); + view.show(); + view.requestActivate(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + QVERIFY(view.rootObject()); + + QQuickMouseArea *outer = view.rootObject()->findChild("outer"); + QVERIFY(outer); + + QQuickMouseArea *inner = outer->findChild("inner"); + QVERIFY(inner); + inner->drag()->setAxis(transpose ? QQuickDrag::YAxis : QQuickDrag::XAxis); + inner->setX(invert ? 100 : 0); + inner->setY(invert ? 100 : 0); + + const int threshold = qApp->styleHints()->startDragDistance(); + + QPoint position(200, 200); + int &axis = transpose ? position.ry() : position.rx(); + + // drag toward the aligned boundary. Outer mouse area dragged. + QTest::mousePress(&view, Qt::LeftButton, 0, position); + QTest::qWait(10); + axis += invert ? threshold * 2 : -threshold * 2; + QTest::mouseMove(&view, position); + axis += invert ? threshold : -threshold; + QTest::mouseMove(&view, position); + QCOMPARE(outer->drag()->active(), true); + QCOMPARE(inner->drag()->active(), false); + QTest::mouseRelease(&view, Qt::LeftButton, 0, position); + + QVERIFY(!outer->drag()->active()); + + axis = 200; + outer->setX(50); + outer->setY(50); + + // drag away from the aligned boundary. Inner mouse area dragged. + QTest::mousePress(&view, Qt::LeftButton, 0, position); + QTest::qWait(10); + axis += invert ? -threshold * 2 : threshold * 2; + QTest::mouseMove(&view, position); + axis += invert ? -threshold : threshold; + QTest::mouseMove(&view, position); + QCOMPARE(outer->drag()->active(), false); + QCOMPARE(inner->drag()->active(), true); + QTest::mouseRelease(&view, Qt::LeftButton, 0, position); +} + QTEST_MAIN(tst_QQuickMouseArea) #include "tst_qquickmousearea.moc" -- 1.7.2.5