diff --git a/src/widgets/kernel/qwidgetwindow.cpp b/src/widgets/kernel/qwidgetwindow.cpp index 5603c7cb20..1c73588b7c 100644 --- a/src/widgets/kernel/qwidgetwindow.cpp +++ b/src/widgets/kernel/qwidgetwindow.cpp @@ -311,8 +311,10 @@ bool QWidgetWindow::event(QEvent *event) #if QT_CONFIG(draganddrop) case QEvent::DragEnter: + handleDragEnterEvent(static_cast(event)); + return true; case QEvent::DragMove: - handleDragEnterMoveEvent(static_cast(event)); + handleDragMoveEvent(static_cast(event)); return true; case QEvent::DragLeave: handleDragLeaveEvent(static_cast(event)); @@ -849,62 +851,80 @@ void QWidgetWindow::handleWheelEvent(QWheelEvent *event) #if QT_CONFIG(draganddrop) -void QWidgetWindow::handleDragEnterMoveEvent(QDragMoveEvent *event) +static QWidget *findDnDTarget(QWidget *parent, const QPoint &pos) { - Q_ASSERT(event->type() ==QEvent::DragMove || !m_dragTarget); // Find a target widget under mouse that accepts drops (QTBUG-22987). - QWidget *widget = m_widget->childAt(event->pos()); + QWidget *widget = parent->childAt(pos); if (!widget) - widget = m_widget; + widget = parent; for ( ; widget && !widget->isWindow() && !widget->acceptDrops(); widget = widget->parentWidget()) ; if (widget && !widget->acceptDrops()) - widget = 0; - // Target widget unchanged: DragMove - if (widget && widget == m_dragTarget.data()) { - Q_ASSERT(event->type() == QEvent::DragMove); - const QPoint mapped = widget->mapFromGlobal(m_widget->mapToGlobal(event->pos())); - QDragMoveEvent translated(mapped, event->possibleActions(), event->mimeData(), event->mouseButtons(), event->keyboardModifiers()); - translated.setDropAction(event->dropAction()); - if (event->isAccepted()) { // Handling 'DragEnter' should suffice for the application. - translated.accept(); - translated.setDropAction(event->dropAction()); - } - QGuiApplication::forwardEvent(widget, &translated, event); - if (translated.isAccepted()) { - event->accept(); - } else { - event->ignore(); - } - event->setDropAction(translated.dropAction()); + widget = nullptr; + return widget; +} + +void QWidgetWindow::handleDragEnterEvent(QDragEnterEvent *event, QWidget *widget) +{ + Q_ASSERT(m_dragTarget == nullptr); + if (!widget) + widget = findDnDTarget(m_widget, event->pos()); + if (!widget) { + event->ignore(); return; } - // Target widget changed: Send DragLeave to previous, DragEnter to new if there is any - if (m_dragTarget.data()) { - QDragLeaveEvent le; - QGuiApplication::forwardEvent(m_dragTarget.data(), &le, event); - m_dragTarget = 0; - } - if (!widget) { - event->ignore(); - return; - } m_dragTarget = widget; + const QPoint mapped = widget->mapFromGlobal(m_widget->mapToGlobal(event->pos())); - QDragEnterEvent translated(mapped, event->possibleActions(), event->mimeData(), event->mouseButtons(), event->keyboardModifiers()); - QGuiApplication::forwardEvent(widget, &translated, event); - if (translated.isAccepted()) { - event->accept(); - } else { - event->ignore(); - } + QDragEnterEvent translated(mapped, event->possibleActions(), event->mimeData(), + event->mouseButtons(), event->keyboardModifiers()); + translated.setDropAction(event->dropAction()); + translated.setAccepted(event->isAccepted()); + QGuiApplication::forwardEvent(m_dragTarget, &translated, event); + event->setAccepted(translated.isAccepted()); event->setDropAction(translated.dropAction()); } +void QWidgetWindow::handleDragMoveEvent(QDragMoveEvent *event) +{ + auto *widget = findDnDTarget(m_widget, event->pos()); + if (!widget) { + event->ignore(); + if (m_dragTarget) { // Send DragLeave to previous + QDragLeaveEvent leaveEvent; + QGuiApplication::forwardEvent(m_dragTarget, &leaveEvent, event); + m_dragTarget = nullptr; + } + } else { + const QPoint mapped = widget->mapFromGlobal(m_widget->mapToGlobal(event->pos())); + QDragMoveEvent translated(mapped, event->possibleActions(), event->mimeData(), + event->mouseButtons(), event->keyboardModifiers()); + translated.setDropAction(event->dropAction()); + translated.setAccepted(event->isAccepted()); + + if (widget == m_dragTarget) { // Target widget unchanged: Send DragMove + QGuiApplication::forwardEvent(m_dragTarget, &translated, event); + } else { + if (m_dragTarget) { // Send DragLeave to previous + QDragLeaveEvent leaveEvent; + QGuiApplication::forwardEvent(m_dragTarget, &leaveEvent, event); + m_dragTarget = nullptr; + } + // Send DragEnter to new widget. + handleDragEnterEvent(static_cast(event), widget); + // The drag enter event is always immediately followed by a drag move event, + // see QDragEnterEvent documentation. + QGuiApplication::forwardEvent(m_dragTarget, &translated, event); + } + event->setAccepted(translated.isAccepted()); + event->setDropAction(translated.dropAction()); + } +} + void QWidgetWindow::handleDragLeaveEvent(QDragLeaveEvent *event) { if (m_dragTarget) - QGuiApplication::forwardEvent(m_dragTarget.data(), event); - m_dragTarget = 0; + QGuiApplication::forwardEvent(m_dragTarget, event); + m_dragTarget = nullptr; } void QWidgetWindow::handleDropEvent(QDropEvent *event) @@ -914,13 +934,12 @@ void QWidgetWindow::handleDropEvent(QDropEvent *event) event->ignore(); return; } - const QPoint mapped = m_dragTarget.data()->mapFromGlobal(m_widget->mapToGlobal(event->pos())); + const QPoint mapped = m_dragTarget->mapFromGlobal(m_widget->mapToGlobal(event->pos())); QDropEvent translated(mapped, event->possibleActions(), event->mimeData(), event->mouseButtons(), event->keyboardModifiers()); - QGuiApplication::forwardEvent(m_dragTarget.data(), &translated, event); - if (translated.isAccepted()) - event->accept(); + QGuiApplication::forwardEvent(m_dragTarget, &translated, event); + event->setAccepted(translated.isAccepted()); event->setDropAction(translated.dropAction()); - m_dragTarget = 0; + m_dragTarget = nullptr; } #endif // QT_CONFIG(draganddrop) diff --git a/src/widgets/kernel/qwidgetwindow_p.h b/src/widgets/kernel/qwidgetwindow_p.h index ead099390e..0728135467 100644 --- a/src/widgets/kernel/qwidgetwindow_p.h +++ b/src/widgets/kernel/qwidgetwindow_p.h @@ -96,7 +96,8 @@ protected: void handleWheelEvent(QWheelEvent *); #endif #if QT_CONFIG(draganddrop) - void handleDragEnterMoveEvent(QDragMoveEvent *); + void handleDragEnterEvent(QDragEnterEvent *, QWidget *widget = nullptr); + void handleDragMoveEvent(QDragMoveEvent *); void handleDragLeaveEvent(QDragLeaveEvent *); void handleDropEvent(QDropEvent *); #endif diff --git a/tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp b/tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp index f0b4bd6dd9..2da04397d0 100644 --- a/tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp +++ b/tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp @@ -423,6 +423,7 @@ static const char *expectedLogC[] = { "Event at 11,81 ignored", "Event at 11,101 ignored", "acceptingDropsWidget1::dragEnterEvent at 1,11 action=1 MIME_DATA_ADDRESS 'testmimetext'", + "acceptingDropsWidget1::dragMoveEvent at 1,11 action=1 MIME_DATA_ADDRESS 'testmimetext'", "Event at 11,121 accepted", "acceptingDropsWidget1::dragMoveEvent at 1,31 action=1 MIME_DATA_ADDRESS 'testmimetext'", "Event at 11,141 accepted", @@ -433,6 +434,7 @@ static const char *expectedLogC[] = { "acceptingDropsWidget1::dragLeaveEvent QDragLeaveEvent", "Event at 11,201 ignored", "acceptingDropsWidget2::dragEnterEvent at 1,11 action=1 MIME_DATA_ADDRESS 'testmimetext'", + "acceptingDropsWidget2::dragMoveEvent at 1,11 action=1 MIME_DATA_ADDRESS 'testmimetext'", "Event at 11,221 accepted", "acceptingDropsWidget2::dragMoveEvent at 1,31 action=1 MIME_DATA_ADDRESS 'testmimetext'", "Event at 11,241 accepted",