Fix performance issue with drag retrigger events for qtquick items

The items that reject drag events (specifically onEntered in DropArea)
should not be retriggered with DragEnter event until entered once again.

Fixes: QTBUG-74496
Pick-to: 6.4 6.3 6.2 5.15
Change-Id: I241a6004da6382685be89fe8a001b98dfde5c8a2
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
This commit is contained in:
Santhosh Kumar 2022-11-01 13:28:30 +01:00
parent b09e9a0acf
commit 0b7374fefa
4 changed files with 71 additions and 10 deletions

View File

@ -72,9 +72,12 @@ public:
void grab(QQuickItem *item) { m_items.insert(new Item(item)); }
iterator release(iterator at) { Item *item = *at; at = at.erase(); delete item; return at; }
auto& ignoreList() { return m_ignoreDragItems; }
private:
ItemList m_items;
QVarLengthArray<QQuickItem *, 4> m_ignoreDragItems;
QObject *m_target;
};

View File

@ -2231,6 +2231,7 @@ void QQuickDeliveryAgentPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QE
QDragLeaveEvent leaveEvent;
for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem))
QCoreApplication::sendEvent(**grabItem, &leaveEvent);
grabber->ignoreList().clear();
return;
} else {
QDragMoveEvent *moveEvent = static_cast<QDragMoveEvent *>(event);
@ -2288,6 +2289,8 @@ void QQuickDeliveryAgentPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QE
e->modifiers());
QQuickDropEventEx::copyActions(&enterEvent, *e);
event->setAccepted(deliverDragEvent(grabber, rootItem, &enterEvent));
} else {
grabber->ignoreList().clear();
}
}
@ -2301,8 +2304,13 @@ bool QQuickDeliveryAgentPrivate::deliverDragEvent(
QPointF p = item->mapFromScene(event->position().toPoint());
bool itemContained = item->contains(p);
if (!itemContained && itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
return false;
const int itemIndex = grabber->ignoreList().indexOf(item);
if (!itemContained) {
if (itemIndex >= 0)
grabber->ignoreList().remove(itemIndex);
if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape)
return false;
}
QDragEnterEvent enterEvent(
@ -2332,15 +2340,19 @@ bool QQuickDeliveryAgentPrivate::deliverDragEvent(
}
if (event->type() == QEvent::DragMove || itemPrivate->flags & QQuickItem::ItemAcceptsDrops) {
if (event->type() == QEvent::DragEnter && formerTarget) {
QQuickItem *formerTargetItem = qobject_cast<QQuickItem *>(formerTarget);
if (formerTargetItem && currentGrabItems) {
QDragLeaveEvent leaveEvent;
QCoreApplication::sendEvent(formerTarget, &leaveEvent);
if (event->type() == QEvent::DragEnter) {
if (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);
// Remove the item from the currentGrabItems so a leave event won't be generated
// later on
currentGrabItems->removeAll(formerTarget);
}
} else if (itemIndex >= 0) {
return false;
}
}
@ -2356,6 +2368,8 @@ bool QQuickDeliveryAgentPrivate::deliverDragEvent(
grabber->grab(item);
grabber->setTarget(item);
return true;
} else if (itemIndex < 0) {
grabber->ignoreList().append(item);
}
} else {
return true;

View File

@ -0,0 +1,15 @@
import QtQuick 2.0
DropArea {
property int enterEvents: 0
property int exitEvents: 0
width: 100; height: 100
objectName: "dropArea"
onEntered: function (drag) { ++enterEvents; drag.accepted = false }
onExited: {++exitEvents}
Item {
objectName: "dragItem"
x: 50; y: 50
width: 10; height: 10
}
}

View File

@ -45,6 +45,9 @@ public:
private slots:
void containsDrag_internal();
void containsDrag_external();
void ignoreRetriggerEvent();
void keys_internal();
void keys_external();
void source_internal();
@ -807,6 +810,32 @@ void tst_QQuickDropArea::competingDrags()
QCOMPARE(evaluate<QString>(dropArea1, "statuslol"), QStringLiteral("parent"));
}
void tst_QQuickDropArea::ignoreRetriggerEvent()
{
QQuickView window;
QByteArray errorMessage;
QVERIFY2(QQuickTest::initView(window, testFileUrl("ignoreRetriggerEvent.qml"), true, &errorMessage), errorMessage.constData());
QQuickItem *dropArea = window.rootObject();
QVERIFY(dropArea);
QQuickItem *dragItem = dropArea->findChild<QQuickItem *>("dragItem");
QVERIFY(dragItem);
evaluate<void>(dragItem, "Drag.active = true");
// Drag the item within the drop area
dragItem->setPosition(QPointF(25, 25));
QCoreApplication::processEvents();
dragItem->setPosition(QPointF(50, 50));
QCoreApplication::processEvents();
dragItem->setPosition(QPointF(75, 75));
QCoreApplication::processEvents();
QCOMPARE(evaluate<bool>(dropArea, "containsDrag"), false);
QCOMPARE(evaluate<int>(dropArea, "enterEvents"), 1);
QCOMPARE(evaluate<int>(dropArea, "exitEvents"), 0);
}
void tst_QQuickDropArea::simultaneousDrags()
{
QQuickWindow window;