summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFriedemann Kleint <Friedemann.Kleint@nokia.com>2012-08-29 16:06:05 +0200
committerQt by Nokia <qt-info@nokia.com>2012-08-31 16:10:25 +0200
commit1e34d3525f175340f03bad83600306c23703a92b (patch)
tree40bb433d6cabc6c37d1248e9547b1db822333364
parent9aa67cf0c48ff8e9e73fc19c4dcd950961b5ad54 (diff)
Fix updating of drag icons.
Try to find a target widget that accepts drops; ignore the event if none can be found. Split the handleDrag*() functions to reduce indentation. Add an autotest. Task-number: QTBUG-22987 Change-Id: I516ac5f0c002caaf83c52ac16f821246e565230f Reviewed-by: Lars Knoll <lars.knoll@nokia.com>
-rw-r--r--src/widgets/kernel/qwidgetwindow.cpp115
-rw-r--r--src/widgets/kernel/qwidgetwindow_qpa_p.h4
-rw-r--r--tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp186
3 files changed, 251 insertions, 54 deletions
diff --git a/src/widgets/kernel/qwidgetwindow.cpp b/src/widgets/kernel/qwidgetwindow.cpp
index 66313424a0..81a341c78c 100644
--- a/src/widgets/kernel/qwidgetwindow.cpp
+++ b/src/widgets/kernel/qwidgetwindow.cpp
@@ -165,10 +165,14 @@ bool QWidgetWindow::event(QEvent *event)
#ifndef QT_NO_DRAGANDDROP
case QEvent::DragEnter:
- case QEvent::DragLeave:
case QEvent::DragMove:
+ handleDragEnterMoveEvent(static_cast<QDragMoveEvent *>(event));
+ return true;
+ case QEvent::DragLeave:
+ handleDragLeaveEvent(static_cast<QDragLeaveEvent *>(event));
+ return true;
case QEvent::Drop:
- handleDragEvent(event);
+ handleDropEvent(static_cast<QDropEvent *>(event));
return true;
#endif
@@ -426,62 +430,69 @@ void QWidgetWindow::handleWheelEvent(QWheelEvent *event)
#ifndef QT_NO_DRAGANDDROP
-void QWidgetWindow::handleDragEvent(QEvent *event)
+void QWidgetWindow::handleDragEnterMoveEvent(QDragMoveEvent *event)
{
- switch (event->type()) {
- case QEvent::DragEnter:
- Q_ASSERT(!m_dragTarget);
- // fall through
- case QEvent::DragMove:
- {
- QDragMoveEvent *de = static_cast<QDragMoveEvent *>(event);
- QWidget *widget = m_widget->childAt(de->pos());
- if (!widget)
- widget = m_widget;
-
- if (widget != m_dragTarget.data()) {
- if (m_dragTarget.data()) {
- QDragLeaveEvent le;
- QGuiApplication::sendSpontaneousEvent(m_dragTarget.data(), &le);
- }
- m_dragTarget = widget;
- QPoint mapped = widget->mapFrom(m_widget, de->pos());
- QDragEnterEvent translated(mapped, de->possibleActions(), de->mimeData(), de->mouseButtons(), de->keyboardModifiers());
- QGuiApplication::sendSpontaneousEvent(widget, &translated);
- if (translated.isAccepted())
- event->accept();
- de->setDropAction(translated.dropAction());
+ 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());
+ if (!widget)
+ widget = m_widget;
+ for ( ; widget && widget != m_widget && !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->mapFrom(m_widget, event->pos());
+ QDragMoveEvent translated(mapped, event->possibleActions(), event->mimeData(), event->mouseButtons(), event->keyboardModifiers());
+ translated.setDropAction(event->dropAction());
+ QGuiApplication::sendSpontaneousEvent(widget, &translated);
+ if (translated.isAccepted()) {
+ event->accept();
} else {
- Q_ASSERT(event->type() == QEvent::DragMove);
- QPoint mapped = widget->mapFrom(m_widget, de->pos());
- QDragMoveEvent translated(mapped, de->possibleActions(), de->mimeData(), de->mouseButtons(), de->keyboardModifiers());
- translated.setDropAction(de->dropAction());
- QGuiApplication::sendSpontaneousEvent(widget, &translated);
- if (translated.isAccepted())
- event->accept();
- de->setDropAction(translated.dropAction());
+ event->ignore();
}
- break;
+ event->setDropAction(translated.dropAction());
+ return;
}
- case QEvent::DragLeave:
- if (m_dragTarget)
- QGuiApplication::sendSpontaneousEvent(m_dragTarget.data(), event);
- m_dragTarget = (QWidget *)0;
- break;
- case QEvent::Drop:
- {
- QDropEvent *de = static_cast<QDropEvent *>(event);
- QPoint mapped = m_dragTarget.data()->mapFrom(m_widget, de->pos());
- QDropEvent translated(mapped, de->possibleActions(), de->mimeData(), de->mouseButtons(), de->keyboardModifiers());
- QGuiApplication::sendSpontaneousEvent(m_dragTarget.data(), &translated);
- if (translated.isAccepted())
- event->accept();
- de->setDropAction(translated.dropAction());
- m_dragTarget = (QWidget *)0;
+ // Target widget changed: Send DragLeave to previous, DragEnter to new if there is any
+ if (m_dragTarget.data()) {
+ QDragLeaveEvent le;
+ QGuiApplication::sendSpontaneousEvent(m_dragTarget.data(), &le);
+ m_dragTarget = 0;
}
- default:
- break;
+ if (!widget) {
+ event->ignore();
+ return;
+ }
+ m_dragTarget = widget;
+ const QPoint mapped = widget->mapFrom(m_widget, event->pos());
+ QDragEnterEvent translated(mapped, event->possibleActions(), event->mimeData(), event->mouseButtons(), event->keyboardModifiers());
+ QGuiApplication::sendSpontaneousEvent(widget, &translated);
+ if (translated.isAccepted()) {
+ event->accept();
+ } else {
+ event->ignore();
}
+ event->setDropAction(translated.dropAction());
+}
+
+void QWidgetWindow::handleDragLeaveEvent(QDragLeaveEvent *event)
+{
+ if (m_dragTarget)
+ QGuiApplication::sendSpontaneousEvent(m_dragTarget.data(), event);
+ m_dragTarget = 0;
+}
+
+void QWidgetWindow::handleDropEvent(QDropEvent *event)
+{
+ const QPoint mapped = m_dragTarget.data()->mapFrom(m_widget, event->pos());
+ QDropEvent translated(mapped, event->possibleActions(), event->mimeData(), event->mouseButtons(), event->keyboardModifiers());
+ QGuiApplication::sendSpontaneousEvent(m_dragTarget.data(), &translated);
+ if (translated.isAccepted())
+ event->accept();
+ event->setDropAction(translated.dropAction());
+ m_dragTarget = 0;
}
#endif // QT_NO_DRAGANDDROP
diff --git a/src/widgets/kernel/qwidgetwindow_qpa_p.h b/src/widgets/kernel/qwidgetwindow_qpa_p.h
index 5750a05d40..f9cd539dbc 100644
--- a/src/widgets/kernel/qwidgetwindow_qpa_p.h
+++ b/src/widgets/kernel/qwidgetwindow_qpa_p.h
@@ -80,7 +80,9 @@ protected:
void handleResizeEvent(QResizeEvent *);
void handleWheelEvent(QWheelEvent *);
#ifndef QT_NO_DRAGANDDROP
- void handleDragEvent(QEvent *);
+ void handleDragEnterMoveEvent(QDragMoveEvent *);
+ void handleDragLeaveEvent(QDragLeaveEvent *);
+ void handleDropEvent(QDropEvent *);
#endif
void handleExposeEvent(QExposeEvent *);
void handleWindowStateChangedEvent(QWindowStateChangeEvent *event);
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 bccd9a5951..17a8d7911d 100644
--- a/tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp
+++ b/tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp
@@ -42,6 +42,10 @@
#include <QtTest/QtTest>
#include <QtGui/QtGui>
+#include <QtCore/QTextStream>
+#include <QtCore/QStringList>
+#include <QtCore/QMimeData>
+#include <QtCore/QPoint>
#include <qeventloop.h>
#include <qlist.h>
@@ -79,6 +83,10 @@ private slots:
void tst_showWithoutActivating();
void tst_paintEventOnSecondShow();
+
+#ifndef QT_NO_DRAGANDDROP
+ void tst_dnd();
+#endif
};
void tst_QWidget_window::initTestCase()
@@ -90,7 +98,8 @@ void tst_QWidget_window::cleanupTestCase()
}
/* Test if the maximum/minimum size constraints
- * are propagated from the widget to the QWidgetWindow
+ * are propagated from the wid src/widgets/kernel/qwidgetwindow_qpa_p.h
+get to the QWidgetWindow
* independently of whether they were set before or after
* window creation (QTBUG-26745). */
@@ -363,5 +372,180 @@ void tst_QWidget_window::tst_paintEventOnSecondShow()
QTRY_VERIFY(w.paintEventReceived);
}
+#ifndef QT_NO_DRAGANDDROP
+
+/* DnD test for QWidgetWindow (handleDrag*Event() functions).
+ * Simulates a drop onto a QWidgetWindow of a top level widget
+ * that has 3 child widgets in a vertical layout with a frame. Only the lower 2
+ * child widgets accepts drops (QTBUG-22987), the bottom child has another child
+ * that does not accept drops.
+ * Sends a series of DnD events to the QWidgetWindow,
+ * entering the top level at the top frame and move
+ * down in steps of 5 pixels, drop onto the bottom widget.
+ * The test compares the sequences of events received by the widgets in readable format.
+ * It also checks whether the address of the mimedata received is the same as the
+ * sending one, that is, no conversion/serialization of text mime data occurs in the
+ * process. */
+
+static const char *expectedLogC[] = {
+ "Event at 11,1 ignored",
+ "Event at 11,21 ignored",
+ "Event at 11,41 ignored",
+ "Event at 11,61 ignored",
+ "Event at 11,81 ignored",
+ "Event at 11,101 ignored",
+ "acceptingDropsWidget1::dragEnterEvent 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",
+ "acceptingDropsWidget1::dragMoveEvent at 1,51 action=1 MIME_DATA_ADDRESS 'testmimetext'",
+ "Event at 11,161 accepted",
+ "acceptingDropsWidget1::dragMoveEvent at 1,71 action=1 MIME_DATA_ADDRESS 'testmimetext'",
+ "Event at 11,181 accepted",
+ "acceptingDropsWidget1::dragLeaveEvent QDragLeaveEvent",
+ "Event at 11,201 ignored",
+ "acceptingDropsWidget2::dragEnterEvent 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",
+ "acceptingDropsWidget2::dropEvent at 1,51 action=1 MIME_DATA_ADDRESS 'testmimetext'",
+ "Event at 11,261 accepted"
+};
+
+// A widget that logs the DnD events it receives into a QStringList.
+class DnDEventLoggerWidget : public QWidget
+{
+public:
+ DnDEventLoggerWidget(QStringList *log, QWidget *w = 0) : QWidget(w), m_log(log) {}
+
+protected:
+ void dragEnterEvent(QDragEnterEvent *);
+ void dragMoveEvent(QDragMoveEvent *);
+ void dragLeaveEvent(QDragLeaveEvent *);
+ void dropEvent(QDropEvent *);
+
+private:
+ void formatDropEvent(const char *function, const QDropEvent *e, QTextStream &str) const;
+ QStringList *m_log;
+};
+
+void DnDEventLoggerWidget::formatDropEvent(const char *function, const QDropEvent *e, QTextStream &str) const
+{
+ str << objectName() << "::" << function << " at " << e->pos().x() << ',' << e->pos().y()
+ << " action=" << e->dropAction()
+ << ' ' << quintptr(e->mimeData()) << " '" << e->mimeData()->text() << '\'';
+}
+
+void DnDEventLoggerWidget::dragEnterEvent(QDragEnterEvent *e)
+{
+ e->accept();
+ QString message;
+ QTextStream str(&message);
+ formatDropEvent("dragEnterEvent", e, str);
+ m_log->push_back(message);
+}
+
+void DnDEventLoggerWidget::dragMoveEvent(QDragMoveEvent *e)
+{
+ e->accept();
+ QString message;
+ QTextStream str(&message);
+ formatDropEvent("dragMoveEvent", e, str);
+ m_log->push_back(message);
+}
+
+void DnDEventLoggerWidget::dragLeaveEvent(QDragLeaveEvent *e)
+{
+ e->accept();
+ m_log->push_back(objectName() + QLatin1String("::") + QLatin1String("dragLeaveEvent") + QLatin1String(" QDragLeaveEvent"));
+}
+
+void DnDEventLoggerWidget::dropEvent(QDropEvent *e)
+{
+ e->accept();
+ QString message;
+ QTextStream str(&message);
+ formatDropEvent("dropEvent", e, str);
+ m_log->push_back(message);
+}
+
+static QString msgEventAccepted(const QDropEvent &e)
+{
+ QString message;
+ QTextStream str(&message);
+ str << "Event at " << e.pos().x() << ',' << e.pos().y() << ' ' << (e.isAccepted() ? "accepted" : "ignored");
+ return message;
+}
+
+void tst_QWidget_window::tst_dnd()
+{
+ QStringList log;
+ DnDEventLoggerWidget dndTestWidget(&log);
+
+ dndTestWidget.setObjectName(QLatin1String("dndTestWidget"));
+ dndTestWidget.setWindowTitle(dndTestWidget.objectName());
+ dndTestWidget.resize(200, 300);
+
+ QWidget *dropsRefusingWidget1 = new DnDEventLoggerWidget(&log, &dndTestWidget);
+ dropsRefusingWidget1->setObjectName(QLatin1String("dropsRefusingWidget1"));
+ dropsRefusingWidget1->resize(180, 80);
+ dropsRefusingWidget1->move(10, 10);
+
+ QWidget *dropsAcceptingWidget1 = new DnDEventLoggerWidget(&log, &dndTestWidget);
+ dropsAcceptingWidget1->setAcceptDrops(true);
+ dropsAcceptingWidget1->setObjectName(QLatin1String("acceptingDropsWidget1"));
+ dropsAcceptingWidget1->resize(180, 80);
+ dropsAcceptingWidget1->move(10, 110);
+
+ QWidget *dropsAcceptingWidget2 = new DnDEventLoggerWidget(&log, &dndTestWidget);
+ dropsAcceptingWidget2->setAcceptDrops(true);
+ dropsAcceptingWidget2->setObjectName(QLatin1String("acceptingDropsWidget2"));
+ dropsAcceptingWidget2->resize(180, 80);
+ dropsAcceptingWidget2->move(10, 210);
+
+ QWidget *dropsRefusingWidget2 = new DnDEventLoggerWidget(&log, dropsAcceptingWidget2);
+ dropsRefusingWidget2->setObjectName(QLatin1String("dropsRefusingDropsWidget2"));
+ dropsRefusingWidget2->resize(160, 60);
+ dropsRefusingWidget2->move(10, 10);
+
+ dndTestWidget.show();
+ qApp->setActiveWindow(&dndTestWidget);
+ QVERIFY(QTest::qWaitForWindowActive(&dndTestWidget));
+
+ QMimeData mimeData;
+ mimeData.setText(QLatin1String("testmimetext"));
+
+ // Simulate DnD events on the QWidgetWindow.
+ QPoint position = QPoint(11, 1);
+ QDragEnterEvent e(position, Qt::CopyAction, &mimeData, Qt::LeftButton, Qt::NoModifier);
+ QWindow *window = dndTestWidget.windowHandle();
+ qApp->sendEvent(window, &e);
+ log.push_back(msgEventAccepted(e));
+ while (true) {
+ position.ry() += 20;
+ if (position.y() >= 250) {
+ QDropEvent e(position, Qt::CopyAction, &mimeData, Qt::LeftButton, Qt::NoModifier);
+ qApp->sendEvent(window, &e);
+ log.push_back(msgEventAccepted(e));
+ break;
+ } else {
+ QDragMoveEvent e(position, Qt::CopyAction, &mimeData, Qt::LeftButton, Qt::NoModifier);
+ qApp->sendEvent(window, &e);
+ log.push_back(msgEventAccepted(e));
+ }
+ }
+
+ // Compare logs.
+ QStringList expectedLog;
+ const int expectedLogSize = int(sizeof(expectedLogC) / sizeof(expectedLogC[0]));
+ const QString mimeDataAddress = QString::number(quintptr(&mimeData));
+ const QString mimeDataAddressPlaceHolder = QLatin1String("MIME_DATA_ADDRESS");
+ for (int i= 0; i < expectedLogSize; ++i)
+ expectedLog.push_back(QString::fromLatin1(expectedLogC[i]).replace(mimeDataAddressPlaceHolder, mimeDataAddress));
+
+ QCOMPARE(log, expectedLog);
+}
+#endif
+
QTEST_MAIN(tst_QWidget_window)
#include "tst_qwidget_window.moc"