diff options
-rw-r--r-- | src/quick/items/qquickitem.cpp | 12 | ||||
-rw-r--r-- | src/quick/items/qquickwindow.cpp | 61 | ||||
-rw-r--r-- | src/quick/items/qquickwindow_p.h | 3 | ||||
-rw-r--r-- | tests/auto/quick/qquickmousearea/data/twoMouseAreas.qml | 33 | ||||
-rw-r--r-- | tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp | 69 |
5 files changed, 137 insertions, 41 deletions
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 53f855e56d..bd37323ef9 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -7309,10 +7309,18 @@ void QQuickItem::unsetCursor() void QQuickItem::grabMouse() { Q_D(QQuickItem); - if (!d->window) + if (!d->window || d->window->mouseGrabberItem() == this) return; QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(d->window); - windowPriv->setMouseGrabber(this); + bool fromTouch = windowPriv->isDeliveringTouchAsMouse(); + auto point = fromTouch ? + windowPriv->pointerEventInstance(windowPriv->touchMouseDevice)->pointById(windowPriv->touchMouseId) : + windowPriv->pointerEventInstance(QQuickPointerDevice::genericMouseDevice())->point(0); + if (point) { + QQuickItem *oldGrabber = point->grabber(); + point->setGrabber(this); + windowPriv->sendUngrabEvent(oldGrabber, fromTouch); + } } /*! diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index fa251b7d78..fbcfa84948 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -738,40 +738,6 @@ bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QQuickPointerEve return false; } -void QQuickWindowPrivate::setMouseGrabber(QQuickItem *grabber) -{ - Q_Q(QQuickWindow); - if (q->mouseGrabberItem() == grabber) - return; - - qCDebug(DBG_MOUSE_TARGET) << "grabber" << q->mouseGrabberItem() << "->" << grabber; - QQuickItem *oldGrabber = q->mouseGrabberItem(); - bool fromTouch = false; - - if (grabber && touchMouseId != -1 && touchMouseDevice) { - // update the touch item for mouse touch id to the new grabber - qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << hex << touchMouseId << "->" << q->mouseGrabberItem(); - auto point = pointerEventInstance(touchMouseDevice)->pointById(touchMouseId); - if (point) - point->setGrabber(grabber); - fromTouch = true; - } else { - QQuickPointerEvent *event = pointerEventInstance(QQuickPointerDevice::genericMouseDevice()); - Q_ASSERT(event->pointCount() == 1); - event->point(0)->setGrabber(grabber); - } - - if (oldGrabber) { - QEvent e(QEvent::UngrabMouse); - QSet<QQuickItem *> hasFiltered; - if (!sendFilteredMouseEvent(oldGrabber->parentItem(), oldGrabber, &e, &hasFiltered)) { - oldGrabber->mouseUngrabEvent(); - if (fromTouch) - oldGrabber->touchUngrabEvent(); - } - } -} - void QQuickWindowPrivate::grabTouchPoints(QQuickItem *grabber, const QVector<int> &ids) { QSet<QQuickItem*> ungrab; @@ -817,8 +783,14 @@ void QQuickWindowPrivate::removeGrabber(QQuickItem *grabber, bool mouse, bool to { Q_Q(QQuickWindow); if (Q_LIKELY(mouse) && q->mouseGrabberItem() == grabber) { - qCDebug(DBG_MOUSE_TARGET) << "removeGrabber" << q->mouseGrabberItem() << "-> null"; - setMouseGrabber(nullptr); + bool fromTouch = isDeliveringTouchAsMouse(); + auto point = fromTouch ? + pointerEventInstance(touchMouseDevice)->pointById(touchMouseId) : + pointerEventInstance(QQuickPointerDevice::genericMouseDevice())->point(0); + QQuickItem *oldGrabber = point->grabber(); + qCDebug(DBG_MOUSE_TARGET) << "removeGrabber" << oldGrabber << "-> null"; + point->setGrabber(nullptr); + sendUngrabEvent(oldGrabber, fromTouch); } if (Q_LIKELY(touch)) { const auto touchDevices = QQuickPointerDevice::touchDevices(); @@ -836,6 +808,19 @@ void QQuickWindowPrivate::removeGrabber(QQuickItem *grabber, bool mouse, bool to } } +void QQuickWindowPrivate::sendUngrabEvent(QQuickItem *grabber, bool touch) +{ + if (!grabber) + return; + QEvent e(QEvent::UngrabMouse); + QSet<QQuickItem *> hasFiltered; + if (!sendFilteredMouseEvent(grabber->parentItem(), grabber, &e, &hasFiltered)) { + grabber->mouseUngrabEvent(); + if (touch) + grabber->touchUngrabEvent(); + } +} + /*! Translates the data in \a touchEvent to this window. This method leaves the item local positions in \a touchEvent untouched (these are filled in later). @@ -1494,7 +1479,7 @@ QQuickItem *QQuickWindow::mouseGrabberItem() const { Q_D(const QQuickWindow); - if (d->touchMouseId != -1 && d->touchMouseDevice) { + if (d->isDeliveringTouchAsMouse()) { if (QQuickPointerEvent *event = d->queryPointerEventInstance(d->touchMouseDevice)) { auto point = event->pointById(d->touchMouseId); return point ? point->grabber() : nullptr; @@ -1656,7 +1641,7 @@ void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEven auto point = pointerEvent->point(0); lastMousePosition = point->scenePos(); QQuickItem *grabber = point->grabber(); - if (!grabber && touchMouseId != -1 && touchMouseDevice) + if (!grabber && isDeliveringTouchAsMouse()) grabber = q->mouseGrabberItem(); if (grabber) { diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index e47dde6464..7dbfed099e 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -141,10 +141,11 @@ public: // Mouse positions are saved in widget coordinates QPointF lastMousePosition; bool deliverTouchAsMouse(QQuickItem *item, QQuickPointerEvent *pointerEvent); + bool isDeliveringTouchAsMouse() const { return touchMouseId != -1 && touchMouseDevice; } void translateTouchEvent(QTouchEvent *touchEvent); - void setMouseGrabber(QQuickItem *grabber); void grabTouchPoints(QQuickItem *grabber, const QVector<int> &ids); void removeGrabber(QQuickItem *grabber, bool mouse = true, bool touch = true); + void sendUngrabEvent(QQuickItem *grabber, bool touch); static QMouseEvent *cloneMouseEvent(QMouseEvent *event, QPointF *transformedLocalPos = 0); void deliverMouseEvent(QQuickPointerMouseEvent *pointerEvent); bool sendFilteredMouseEvent(QQuickItem *, QQuickItem *, QEvent *, QSet<QQuickItem *> *); diff --git a/tests/auto/quick/qquickmousearea/data/twoMouseAreas.qml b/tests/auto/quick/qquickmousearea/data/twoMouseAreas.qml new file mode 100644 index 0000000000..28f48c742a --- /dev/null +++ b/tests/auto/quick/qquickmousearea/data/twoMouseAreas.qml @@ -0,0 +1,33 @@ +import QtQuick 2.0 +import QtQuick.Window 2.0 + +Rectangle { + width: 400 + height: 300 + + property bool topPressed: top.pressed + property bool bottomPressed: bottom.pressed + + MouseArea { + id: top + objectName: "top" + width: parent.width + height: parent.height / 2 - 2 + Rectangle { + anchors.fill: parent + color: parent.pressed ? "MediumSeaGreen" : "beige" + } + } + + MouseArea { + id: bottom + objectName: "bottom" + y: parent.height / 2 + width: parent.width + height: parent.height / 2 + Rectangle { + anchors.fill: parent + color: parent.pressed ? "MediumSeaGreen" : "beige" + } + } +} diff --git a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp index 01bce46ccb..12c6233ded 100644 --- a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp +++ b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp @@ -130,6 +130,8 @@ private slots: void notPressedAfterStolenGrab(); void pressAndHold_data(); void pressAndHold(); + void pressOneAndTapAnother_data(); + void pressOneAndTapAnother(); private: int startDragDistance() const { @@ -2173,6 +2175,73 @@ void tst_QQuickMouseArea::pressAndHold() QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, QPoint(50, 50)); } +void tst_QQuickMouseArea::pressOneAndTapAnother_data() +{ + QTest::addColumn<bool>("pressMouseFirst"); + QTest::addColumn<bool>("releaseMouseFirst"); + + QTest::newRow("press mouse, tap touch, release mouse") << true << false; // QTBUG-64249 as written + QTest::newRow("press touch, press mouse, release touch, release mouse") << false << false; + QTest::newRow("press mouse, press touch, release mouse, release touch") << true << true; + QTest::newRow("press touch, click mouse, release touch") << false << true; +} + +void tst_QQuickMouseArea::pressOneAndTapAnother() +{ + QFETCH(bool, pressMouseFirst); + QFETCH(bool, releaseMouseFirst); + + QQuickView window; + QByteArray errorMessage; + QVERIFY2(initView(window, testFileUrl("twoMouseAreas.qml"), true, &errorMessage), errorMessage.constData()); + window.show(); + window.requestActivate(); + QVERIFY(QTest::qWaitForWindowExposed(&window)); + QQuickItem *root = window.rootObject(); + QVERIFY(root); + QQuickMouseArea *bottomMA = window.rootObject()->findChild<QQuickMouseArea*>("bottom"); + QVERIFY(bottomMA); + QQuickMouseArea *topMA = window.rootObject()->findChild<QQuickMouseArea*>("top"); + QVERIFY(topMA); + + QPoint upper(32, 32); + QPoint lower(32, window.height() - 32); + + // press them both + if (pressMouseFirst) { + QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, lower); + QTRY_COMPARE(bottomMA->pressed(), true); + + QTest::touchEvent(&window, device).press(0, lower, &window); + QQuickTouchUtils::flush(&window); + QTRY_COMPARE(bottomMA->pressed(), true); + } else { + QTest::touchEvent(&window, device).press(0, lower, &window); + QQuickTouchUtils::flush(&window); + QTRY_COMPARE(bottomMA->pressed(), true); + + QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, lower); + QTRY_COMPARE(bottomMA->pressed(), true); + } + + // release them both and make sure neither one gets stuck + if (releaseMouseFirst) { + QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, lower); + QTRY_COMPARE(bottomMA->pressed(), false); + + QTest::touchEvent(&window, device).release(0, upper, &window); + QQuickTouchUtils::flush(&window); + QTRY_COMPARE(topMA->pressed(), false); + } else { + QTest::touchEvent(&window, device).release(0, upper, &window); + QQuickTouchUtils::flush(&window); + + QTRY_COMPARE(topMA->pressed(), false); + QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, lower); + QTRY_COMPARE(bottomMA->pressed(), false); + } +} + QTEST_MAIN(tst_QQuickMouseArea) #include "tst_qquickmousearea.moc" |