diff options
author | Maximilian Goldstein <max.goldstein@qt.io> | 2021-05-03 16:55:25 +0200 |
---|---|---|
committer | Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> | 2021-12-03 02:56:31 +0000 |
commit | 9e6274e180312e9e6c9d26322668f1427abfe4b9 (patch) | |
tree | a62078e581c6f809db31fae354924d7beaab16fd | |
parent | dd806b30dc57e0c91dadf090cdd2d8f51a9f6f41 (diff) |
qquickdeliveryagent: Fix drag events being sent in the wrong order
When a former drag event target is on a higher or the same z-level as the new target, send the QDragLeaveEvent before the QDragEnterEvent.
[ChangeLog][Quick][Fix] Now sends DragArea leave events before enter events when appropriate (QTBUG-82263)
Fixes: QTBUG-82263
Change-Id: Ibe76000cbe76748ee8928e4b98a92c38eff5b59c
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
(cherry picked from commit 4a5b0ad84fbc0f814c38b0abcccb7b5421f41a19)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-rw-r--r-- | src/quick/util/qquickdeliveryagent.cpp | 33 | ||||
-rw-r--r-- | src/quick/util/qquickdeliveryagent_p_p.h | 4 | ||||
-rw-r--r-- | tests/auto/quick/qquickdroparea/tst_qquickdroparea.cpp | 59 |
3 files changed, 76 insertions, 20 deletions
diff --git a/src/quick/util/qquickdeliveryagent.cpp b/src/quick/util/qquickdeliveryagent.cpp index 0cea513b78..f613e74d53 100644 --- a/src/quick/util/qquickdeliveryagent.cpp +++ b/src/quick/util/qquickdeliveryagent.cpp @@ -2052,6 +2052,7 @@ void QQuickDeliveryAgentPrivate::deliverMatchingPointsToItem(QQuickItem *item, b #if QT_CONFIG(quick_draganddrop) void QQuickDeliveryAgentPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *event) { + QObject *formerTarget = grabber->target(); grabber->resetTarget(); QQuickDragGrabber::iterator grabItem = grabber->begin(); if (grabItem != grabber->end()) { @@ -2095,7 +2096,8 @@ void QQuickDeliveryAgentPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QE moveEvent->buttons(), moveEvent->modifiers()); QQuickDropEventEx::copyActions(&enterEvent, *moveEvent); - event->setAccepted(deliverDragEvent(grabber, rootItem, &enterEvent, ¤tGrabItems)); + event->setAccepted(deliverDragEvent(grabber, rootItem, &enterEvent, ¤tGrabItems, + formerTarget)); for (grabItem = grabber->begin(); grabItem != grabber->end(); ++grabItem) { int i = currentGrabItems.indexOf(**grabItem); @@ -2136,7 +2138,9 @@ void QQuickDeliveryAgentPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QE } } -bool QQuickDeliveryAgentPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQuickItem *item, QDragMoveEvent *event, QVarLengthArray<QQuickItem*, 64> *currentGrabItems) +bool QQuickDeliveryAgentPrivate::deliverDragEvent( + QQuickDragGrabber *grabber, QQuickItem *item, QDragMoveEvent *event, + QVarLengthArray<QQuickItem *, 64> *currentGrabItems, QObject *formerTarget) { QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); if (!item->isVisible() || !item->isEnabled() || QQuickItemPrivate::get(item)->culled) @@ -2161,7 +2165,7 @@ bool QQuickDeliveryAgentPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQ for (int ii = children.count() - 1; ii >= 0; --ii) { if (children.at(ii)->z() < 0) continue; - if (deliverDragEvent(grabber, children.at(ii), &enterEvent, currentGrabItems)) + if (deliverDragEvent(grabber, children.at(ii), &enterEvent, currentGrabItems, formerTarget)) return true; } @@ -2175,13 +2179,20 @@ bool QQuickDeliveryAgentPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQ } if (event->type() == QEvent::DragMove || itemPrivate->flags & QQuickItem::ItemAcceptsDrops) { - QDragMoveEvent translatedEvent( - p.toPoint(), - event->possibleActions(), - event->mimeData(), - event->buttons(), - event->modifiers(), - event->type()); + if (event->type() == QEvent::DragEnter && formerTarget) { + QQuickItem *formerTargetItem = qobject_cast<QQuickItem *>(formerTarget); + if (formerTargetItem && currentGrabItems) { + QDragLeaveEvent leaveEvent; + QCoreApplication::sendEvent(formerTarget, &leaveEvent); + + // Remove the item from the currentGrabItems so a leave event won't be generated + // later on + currentGrabItems->removeAll(formerTarget); + } + } + + QDragMoveEvent translatedEvent(p.toPoint(), event->possibleActions(), event->mimeData(), + event->buttons(), event->modifiers(), event->type()); QQuickDropEventEx::copyActions(&translatedEvent, *event); translatedEvent.setAccepted(event->isAccepted()); QCoreApplication::sendEvent(item, &translatedEvent); @@ -2203,7 +2214,7 @@ bool QQuickDeliveryAgentPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQ for (int ii = children.count() - 1; ii >= 0; --ii) { if (children.at(ii)->z() >= 0) continue; - if (deliverDragEvent(grabber, children.at(ii), &enterEvent, currentGrabItems)) + if (deliverDragEvent(grabber, children.at(ii), &enterEvent, currentGrabItems, formerTarget)) return true; } diff --git a/src/quick/util/qquickdeliveryagent_p_p.h b/src/quick/util/qquickdeliveryagent_p_p.h index f9095158e8..4ea1ef0d36 100644 --- a/src/quick/util/qquickdeliveryagent_p_p.h +++ b/src/quick/util/qquickdeliveryagent_p_p.h @@ -199,7 +199,9 @@ public: #if QT_CONFIG(quick_draganddrop) void deliverDragEvent(QQuickDragGrabber *, QEvent *); - bool deliverDragEvent(QQuickDragGrabber *, QQuickItem *, QDragMoveEvent *, QVarLengthArray<QQuickItem*, 64> *currentGrabItems = nullptr); + bool deliverDragEvent(QQuickDragGrabber *, QQuickItem *, QDragMoveEvent *, + QVarLengthArray<QQuickItem *, 64> *currentGrabItems = nullptr, + QObject *formerTarget = nullptr); #endif static bool dragOverThreshold(qreal d, Qt::Axis axis, QMouseEvent *event, int startDragThreshold = -1); diff --git a/tests/auto/quick/qquickdroparea/tst_qquickdroparea.cpp b/tests/auto/quick/qquickdroparea/tst_qquickdroparea.cpp index dbf090bfed..6bbe0d522b 100644 --- a/tests/auto/quick/qquickdroparea/tst_qquickdroparea.cpp +++ b/tests/auto/quick/qquickdroparea/tst_qquickdroparea.cpp @@ -80,6 +80,7 @@ private slots: void dropStuff(); void nestedDropAreas_data(); void nestedDropAreas(); + void signalOrder(); private: QQmlEngine engine; @@ -958,8 +959,8 @@ void tst_QQuickDropArea::simultaneousDrags() Qt::MouseButtons(), Qt::KeyboardModifiers()); //Same as in the first case, dropArea2 already contains a drag, dropArea1 will get the event QCOMPARE(evaluate<bool>(dropArea1, "containsDrag"), true); - QCOMPARE(evaluate<int>(dropArea1, "enterEvents"), 1); - QCOMPARE(evaluate<int>(dropArea1, "exitEvents"), 0); + QCOMPARE(evaluate<int>(dropArea1, "enterEvents"), 2); + QCOMPARE(evaluate<int>(dropArea1, "exitEvents"), 1); QCOMPARE(evaluate<bool>(dropArea2, "containsDrag"), true); QCOMPARE(evaluate<int>(dropArea2, "enterEvents"), 0); QCOMPARE(evaluate<int>(dropArea2, "exitEvents"), 0); @@ -967,8 +968,8 @@ void tst_QQuickDropArea::simultaneousDrags() QWindowSystemInterface::handleDrag(&alternateWindow, &data, QPoint(50, 50), Qt::CopyAction, Qt::MouseButtons(), Qt::KeyboardModifiers()); QCOMPARE(evaluate<bool>(dropArea1, "containsDrag"), false); - QCOMPARE(evaluate<int>(dropArea1, "enterEvents"), 1); - QCOMPARE(evaluate<int>(dropArea1, "exitEvents"), 1); + QCOMPARE(evaluate<int>(dropArea1, "enterEvents"), 2); + QCOMPARE(evaluate<int>(dropArea1, "exitEvents"), 2); QCOMPARE(evaluate<bool>(dropArea2, "containsDrag"), true); QCOMPARE(evaluate<int>(dropArea2, "enterEvents"), 0); QCOMPARE(evaluate<int>(dropArea2, "exitEvents"), 0); @@ -976,16 +977,16 @@ void tst_QQuickDropArea::simultaneousDrags() QWindowSystemInterface::handleDrag(&window, &data, QPoint(50, 50), Qt::CopyAction, Qt::MouseButtons(), Qt::KeyboardModifiers()); QCOMPARE(evaluate<bool>(dropArea1, "containsDrag"), true); - QCOMPARE(evaluate<int>(dropArea1, "enterEvents"), 2); - QCOMPARE(evaluate<int>(dropArea1, "exitEvents"), 1); + QCOMPARE(evaluate<int>(dropArea1, "enterEvents"), 4); + QCOMPARE(evaluate<int>(dropArea1, "exitEvents"), 3); QCOMPARE(evaluate<bool>(dropArea2, "containsDrag"), true); QCOMPARE(evaluate<int>(dropArea2, "enterEvents"), 0); QCOMPARE(evaluate<int>(dropArea2, "exitEvents"), 0); evaluate<void>(dragItem1, "Drag.active = false"); QCOMPARE(evaluate<bool>(dropArea1, "containsDrag"), true); - QCOMPARE(evaluate<int>(dropArea1, "enterEvents"), 2); - QCOMPARE(evaluate<int>(dropArea1, "exitEvents"), 1); + QCOMPARE(evaluate<int>(dropArea1, "enterEvents"), 4); + QCOMPARE(evaluate<int>(dropArea1, "exitEvents"), 3); QCOMPARE(evaluate<bool>(dropArea2, "containsDrag"), false); QCOMPARE(evaluate<int>(dropArea2, "enterEvents"), 0); QCOMPARE(evaluate<int>(dropArea2, "exitEvents"), 1); @@ -1290,6 +1291,48 @@ void tst_QQuickDropArea::nestedDropAreas() QCOMPARE(window.rootObject()->property("innerExitEvents"), 2); } +void tst_QQuickDropArea::signalOrder() +{ + QQuickWindow window; + QQmlComponent component(&engine); + component.setData("import QtQuick\n" + "Item {\n" + " id: root\n" + " property var eventOrder: []\n" + " DropArea {\n" + " width: 100; height: 100\n" + " x: 0; y: 0\n" + " onEntered: eventOrder.push('entered1');\n" + " onExited: eventOrder.push('exited1');\n" + " }\n" + " DropArea {\n" + " width: 100; height: 100\n" + " x: 0; y: 100\n" + " onEntered: eventOrder.push('entered2');\n" + " onExited: eventOrder.push('exited2');\n" + " }\n" + "}", + QUrl()); + + QScopedPointer<QObject> object(component.create()); + QQuickItem *item = qobject_cast<QQuickItem *>(object.data()); + QVERIFY(item); + item->setParentItem(window.contentItem()); + + QMimeData data; + + QWindowSystemInterface::handleDrag(&window, &data, QPoint(50, 50), Qt::CopyAction, + Qt::MouseButtons(), Qt::KeyboardModifiers()); + QWindowSystemInterface::handleDrag(&window, &data, QPoint(50, 150), Qt::CopyAction, + Qt::MouseButtons(), Qt::KeyboardModifiers()); + QWindowSystemInterface::handleDrag(&window, &data, QPoint(50, 250), Qt::CopyAction, + Qt::MouseButtons(), Qt::KeyboardModifiers()); + + const QList<QVariant> eventOrder = item->property("eventOrder").toList(); + QCOMPARE(eventOrder, + QList<QVariant>({ u"entered1"_qs, u"exited1"_qs, u"entered2"_qs, u"exited2"_qs })); +} + QTEST_MAIN(tst_QQuickDropArea) #include "tst_qquickdroparea.moc" |