summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/gui/kernel/qguiapplication.cpp9
-rw-r--r--src/gui/kernel/qguiapplication_p.h1
-rw-r--r--src/gui/kernel/qplatformintegration.cpp4
-rw-r--r--src/gui/kernel/qsimpledrag.cpp105
-rw-r--r--src/gui/kernel/qsimpledrag_p.h24
-rw-r--r--src/plugins/platforms/xcb/qxcbdrag.cpp3
-rw-r--r--src/plugins/platforms/xcb/qxcbintegration.cpp9
-rw-r--r--tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp87
8 files changed, 177 insertions, 65 deletions
diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp
index 1ebdde3426..10defabd4f 100644
--- a/src/gui/kernel/qguiapplication.cpp
+++ b/src/gui/kernel/qguiapplication.cpp
@@ -143,6 +143,8 @@ Qt::ApplicationState QGuiApplicationPrivate::applicationState = Qt::ApplicationI
bool QGuiApplicationPrivate::highDpiScalingUpdated = false;
+QPointer<QWindow> QGuiApplicationPrivate::currentDragWindow;
+
QVector<QGuiApplicationPrivate::TabletPointData> QGuiApplicationPrivate::tabletDevicePoints;
QPlatformIntegration *QGuiApplicationPrivate::platform_integration = 0;
@@ -668,6 +670,7 @@ QGuiApplication::~QGuiApplication()
QGuiApplicationPrivate::currentMousePressWindow = QGuiApplicationPrivate::currentMouseWindow = nullptr;
QGuiApplicationPrivate::applicationState = Qt::ApplicationInactive;
QGuiApplicationPrivate::highDpiScalingUpdated = false;
+ QGuiApplicationPrivate::currentDragWindow = nullptr;
QGuiApplicationPrivate::tabletDevicePoints.clear();
#ifndef QT_NO_SESSIONMANAGER
QGuiApplicationPrivate::is_fallback_session_management_enabled = true;
@@ -3092,7 +3095,6 @@ QPlatformDragQtResponse QGuiApplicationPrivate::processDrag(QWindow *w, const QM
{
updateMouseAndModifierButtonState(buttons, modifiers);
- static QPointer<QWindow> currentDragWindow;
static Qt::DropAction lastAcceptedDropAction = Qt::IgnoreAction;
QPlatformDrag *platformDrag = platformIntegration()->drag();
if (!platformDrag || (w && w->d_func()->blockedByModalWindow)) {
@@ -3101,8 +3103,7 @@ QPlatformDragQtResponse QGuiApplicationPrivate::processDrag(QWindow *w, const QM
}
if (!dropData) {
- if (currentDragWindow.data() == w)
- currentDragWindow = 0;
+ currentDragWindow = nullptr;
QDragLeaveEvent e;
QGuiApplication::sendEvent(w, &e);
lastAcceptedDropAction = Qt::IgnoreAction;
@@ -3141,6 +3142,8 @@ QPlatformDropQtResponse QGuiApplicationPrivate::processDrop(QWindow *w, const QM
{
updateMouseAndModifierButtonState(buttons, modifiers);
+ currentDragWindow = nullptr;
+
QDropEvent de(p, supportedActions, dropData, buttons, modifiers);
QGuiApplication::sendEvent(w, &de);
diff --git a/src/gui/kernel/qguiapplication_p.h b/src/gui/kernel/qguiapplication_p.h
index b7ee9f86b5..145e764244 100644
--- a/src/gui/kernel/qguiapplication_p.h
+++ b/src/gui/kernel/qguiapplication_p.h
@@ -217,6 +217,7 @@ public:
static QWindow *currentMousePressWindow;
static Qt::ApplicationState applicationState;
static bool highDpiScalingUpdated;
+ static QPointer<QWindow> currentDragWindow;
struct TabletPointData {
TabletPointData(qint64 devId = 0) : deviceId(devId), state(Qt::NoButton), target(nullptr) {}
diff --git a/src/gui/kernel/qplatformintegration.cpp b/src/gui/kernel/qplatformintegration.cpp
index dfb8f60915..6e285a8fa5 100644
--- a/src/gui/kernel/qplatformintegration.cpp
+++ b/src/gui/kernel/qplatformintegration.cpp
@@ -99,8 +99,8 @@ QPlatformClipboard *QPlatformIntegration::clipboard() const
/*!
Accessor for the platform integration's drag object.
- Default implementation returns 0, implying no drag and drop support.
-
+ Default implementation returns QSimpleDrag. This class supports only drag
+ and drop operations within the same Qt application.
*/
QPlatformDrag *QPlatformIntegration::drag() const
{
diff --git a/src/gui/kernel/qsimpledrag.cpp b/src/gui/kernel/qsimpledrag.cpp
index 35896514b0..a84f873437 100644
--- a/src/gui/kernel/qsimpledrag.cpp
+++ b/src/gui/kernel/qsimpledrag.cpp
@@ -94,11 +94,7 @@ static QWindow* topLevelAt(const QPoint &pos)
(within the Qt application or outside) accepts the drag and sets the state accordingly.
*/
-QBasicDrag::QBasicDrag() :
- m_current_window(nullptr), m_restoreCursor(false), m_eventLoop(nullptr),
- m_executed_drop_action(Qt::IgnoreAction), m_can_drop(false),
- m_drag(nullptr), m_drag_icon_window(nullptr), m_useCompositing(true),
- m_screen(nullptr)
+QBasicDrag::QBasicDrag()
{
}
@@ -181,9 +177,9 @@ bool QBasicDrag::eventFilter(QObject *o, QEvent *e)
// make the event relative to the window where the drag started. (QTBUG-66103)
const QMouseEvent *release = static_cast<QMouseEvent *>(e);
const QWindow *releaseWindow = topLevelAt(release->globalPos());
- qCDebug(lcDnd) << "mouse released over" << releaseWindow << "after drag from" << m_current_window << "globalPos" << release->globalPos();
+ qCDebug(lcDnd) << "mouse released over" << releaseWindow << "after drag from" << m_sourceWindow << "globalPos" << release->globalPos();
if (!releaseWindow)
- releaseWindow = m_current_window;
+ releaseWindow = m_sourceWindow;
QPoint releaseWindowPos = (releaseWindow ? releaseWindow->mapFromGlobal(release->globalPos()) : release->globalPos());
QMouseEvent *newRelease = new QMouseEvent(release->type(),
releaseWindowPos, releaseWindowPos, release->screenPos(),
@@ -206,18 +202,15 @@ Qt::DropAction QBasicDrag::drag(QDrag *o)
m_drag = o;
m_executed_drop_action = Qt::IgnoreAction;
m_can_drop = false;
- m_restoreCursor = true;
-#ifndef QT_NO_CURSOR
- qApp->setOverrideCursor(Qt::DragCopyCursor);
- updateCursor(m_executed_drop_action);
-#endif
+
startDrag();
m_eventLoop = new QEventLoop;
m_eventLoop->exec();
delete m_eventLoop;
- m_eventLoop = 0;
- m_drag = 0;
+ m_eventLoop = nullptr;
+ m_drag = nullptr;
endDrag();
+
return m_executed_drop_action;
}
@@ -229,16 +222,6 @@ void QBasicDrag::cancelDrag()
}
}
-void QBasicDrag::restoreCursor()
-{
- if (m_restoreCursor) {
-#ifndef QT_NO_CURSOR
- QGuiApplication::restoreOverrideCursor();
-#endif
- m_restoreCursor = false;
- }
-}
-
void QBasicDrag::startDrag()
{
QPoint pos;
@@ -320,25 +303,34 @@ void QBasicDrag::updateCursor(Qt::DropAction action)
}
}
- QCursor *cursor = QGuiApplication::overrideCursor();
QPixmap pixmap = m_drag->dragCursor(action);
- if (!cursor) {
- QGuiApplication::changeOverrideCursor((pixmap.isNull()) ? QCursor(cursorShape) : QCursor(pixmap));
+
+ if (!m_dndHasSetOverrideCursor) {
+ QCursor newCursor = !pixmap.isNull() ? QCursor(pixmap) : QCursor(cursorShape);
+ QGuiApplication::setOverrideCursor(newCursor);
+ m_dndHasSetOverrideCursor = true;
} else {
+ QCursor *cursor = QGuiApplication::overrideCursor();
if (!pixmap.isNull()) {
- if ((cursor->pixmap().cacheKey() != pixmap.cacheKey())) {
+ if (cursor->pixmap().cacheKey() != pixmap.cacheKey())
QGuiApplication::changeOverrideCursor(QCursor(pixmap));
- }
- } else {
- if (cursorShape != cursor->shape()) {
- QGuiApplication::changeOverrideCursor(QCursor(cursorShape));
- }
+ } else if (cursorShape != cursor->shape()) {
+ QGuiApplication::changeOverrideCursor(QCursor(cursorShape));
}
}
#endif
updateAction(action);
}
+void QBasicDrag::restoreCursor()
+{
+#ifndef QT_NO_CURSOR
+ if (m_dndHasSetOverrideCursor) {
+ QGuiApplication::restoreOverrideCursor();
+ m_dndHasSetOverrideCursor = false;
+ }
+#endif
+}
static inline QPoint fromNativeGlobalPixels(const QPoint &point)
{
@@ -376,35 +368,38 @@ QSimpleDrag::QSimpleDrag()
void QSimpleDrag::startDrag()
{
+ setExecutedDropAction(Qt::IgnoreAction);
+
QBasicDrag::startDrag();
// Here we can be fairly sure that QGuiApplication::mouseButtons/keyboardModifiers() will
// contain sensible values as startDrag() normally is called from mouse event handlers
// by QDrag::exec(). A better API would be if we could pass something like "input device
// pointer" to QDrag::exec(). My guess is that something like that might be required for
// QTBUG-52430.
- m_current_window = topLevelAt(QCursor::pos());
- if (m_current_window) {
- auto nativePixelPos = QHighDpi::toNativePixels(QCursor::pos(), m_current_window);
- QPlatformDragQtResponse response = QWindowSystemInterface::handleDrag(
- m_current_window, drag()->mimeData(), nativePixelPos,
- drag()->supportedActions(), QGuiApplication::mouseButtons(),
- QGuiApplication::keyboardModifiers());
- setCanDrop(response.isAccepted());
- updateCursor(response.acceptedAction());
+ m_sourceWindow = topLevelAt(QCursor::pos());
+ m_windowUnderCursor = m_sourceWindow;
+ if (m_sourceWindow) {
+ auto nativePixelPos = QHighDpi::toNativePixels(QCursor::pos(), m_sourceWindow);
+ move(nativePixelPos, QGuiApplication::mouseButtons(), QGuiApplication::keyboardModifiers());
} else {
setCanDrop(false);
updateCursor(Qt::IgnoreAction);
}
- setExecutedDropAction(Qt::IgnoreAction);
- qCDebug(lcDnd) << "drag began from" << m_current_window<< "cursor pos" << QCursor::pos() << "can drop?" << canDrop();
+
+ qCDebug(lcDnd) << "drag began from" << m_sourceWindow << "cursor pos" << QCursor::pos() << "can drop?" << canDrop();
+}
+
+static void sendDragLeave(QWindow *window)
+{
+ QWindowSystemInterface::handleDrag(window, nullptr, QPoint(), Qt::IgnoreAction, 0, 0);
}
void QSimpleDrag::cancel()
{
QBasicDrag::cancel();
- if (drag() && m_current_window) {
- QWindowSystemInterface::handleDrag(m_current_window, nullptr, QPoint(), Qt::IgnoreAction, 0, 0);
- m_current_window = nullptr;
+ if (drag() && m_sourceWindow) {
+ sendDragLeave(m_sourceWindow);
+ m_sourceWindow = nullptr;
}
}
@@ -414,16 +409,26 @@ void QSimpleDrag::move(const QPoint &nativeGlobalPos, Qt::MouseButtons buttons,
QPoint globalPos = fromNativeGlobalPixels(nativeGlobalPos);
moveShapedPixmapWindow(globalPos);
QWindow *window = topLevelAt(globalPos);
- if (!window)
- return;
+
+ if (!window || window != m_windowUnderCursor) {
+ if (m_windowUnderCursor)
+ sendDragLeave(m_windowUnderCursor);
+ m_windowUnderCursor = window;
+ if (!window) {
+ // QSimpleDrag supports only in-process dnd, we can't drop anywhere else.
+ setCanDrop(false);
+ updateCursor(Qt::IgnoreAction);
+ return;
+ }
+ }
const QPoint pos = nativeGlobalPos - window->handle()->geometry().topLeft();
const QPlatformDragQtResponse qt_response = QWindowSystemInterface::handleDrag(
window, drag()->mimeData(), pos, drag()->supportedActions(),
buttons, modifiers);
- updateCursor(qt_response.acceptedAction());
setCanDrop(qt_response.isAccepted());
+ updateCursor(qt_response.acceptedAction());
}
void QSimpleDrag::drop(const QPoint &nativeGlobalPos, Qt::MouseButtons buttons,
diff --git a/src/gui/kernel/qsimpledrag_p.h b/src/gui/kernel/qsimpledrag_p.h
index ddd32bc5a5..f9e8a83a39 100644
--- a/src/gui/kernel/qsimpledrag_p.h
+++ b/src/gui/kernel/qsimpledrag_p.h
@@ -55,13 +55,14 @@
#include <qpa/qplatformdrag.h>
#include <QtCore/QObject>
+#include <QtCore/QPointer>
+#include <QtGui/QWindow>
QT_REQUIRE_CONFIG(draganddrop);
QT_BEGIN_NAMESPACE
class QMouseEvent;
-class QWindow;
class QEventLoop;
class QDropData;
class QShapedPixmapWindow;
@@ -106,7 +107,8 @@ protected:
QDrag *drag() const { return m_drag; }
protected:
- QWindow *m_current_window;
+ QWindow *m_sourceWindow = nullptr;
+ QPointer<QWindow> m_windowUnderCursor = nullptr;
private:
void enableEventFilter();
@@ -114,14 +116,16 @@ private:
void restoreCursor();
void exitDndEventLoop();
- bool m_restoreCursor;
- QEventLoop *m_eventLoop;
- Qt::DropAction m_executed_drop_action;
- bool m_can_drop;
- QDrag *m_drag;
- QShapedPixmapWindow *m_drag_icon_window;
- bool m_useCompositing;
- QScreen *m_screen;
+#ifndef QT_NO_CURSOR
+ bool m_dndHasSetOverrideCursor = false;
+#endif
+ QEventLoop *m_eventLoop = nullptr;
+ Qt::DropAction m_executed_drop_action = Qt::IgnoreAction;
+ bool m_can_drop = false;
+ QDrag *m_drag = nullptr;
+ QShapedPixmapWindow *m_drag_icon_window = nullptr;
+ bool m_useCompositing = true;
+ QScreen *m_screen = nullptr;
};
class Q_GUI_EXPORT QSimpleDrag : public QBasicDrag
diff --git a/src/plugins/platforms/xcb/qxcbdrag.cpp b/src/plugins/platforms/xcb/qxcbdrag.cpp
index 999ae16897..20b01225cf 100644
--- a/src/plugins/platforms/xcb/qxcbdrag.cpp
+++ b/src/plugins/platforms/xcb/qxcbdrag.cpp
@@ -201,6 +201,9 @@ void QXcbDrag::startDrag()
QBasicDrag::startDrag();
if (connection()->mouseGrabber() == nullptr)
shapedPixmapWindow()->setMouseGrabEnabled(true);
+
+ auto nativePixelPos = QHighDpi::toNativePixels(QCursor::pos(), initiatorWindow);
+ move(nativePixelPos, QGuiApplication::mouseButtons(), QGuiApplication::keyboardModifiers());
}
void QXcbDrag::endDrag()
diff --git a/src/plugins/platforms/xcb/qxcbintegration.cpp b/src/plugins/platforms/xcb/qxcbintegration.cpp
index b8a0e85465..49649eb816 100644
--- a/src/plugins/platforms/xcb/qxcbintegration.cpp
+++ b/src/plugins/platforms/xcb/qxcbintegration.cpp
@@ -379,8 +379,17 @@ QPlatformClipboard *QXcbIntegration::clipboard() const
#endif
#if QT_CONFIG(draganddrop)
+#include <private/qsimpledrag_p.h>
QPlatformDrag *QXcbIntegration::drag() const
{
+ static const bool useSimpleDrag = qEnvironmentVariableIsSet("QT_XCB_USE_SIMPLE_DRAG");
+ if (Q_UNLIKELY(useSimpleDrag)) { // This is useful for testing purposes
+ static QSimpleDrag *simpleDrag = nullptr;
+ if (!simpleDrag)
+ simpleDrag = new QSimpleDrag();
+ return simpleDrag;
+ }
+
return m_connections.at(0)->drag();
}
#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 0813a13693..f0b4bd6dd9 100644
--- a/tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp
+++ b/tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp
@@ -83,6 +83,7 @@ private slots:
#if QT_CONFIG(draganddrop)
void tst_dnd();
+ void tst_dnd_events();
#endif
void tst_qtbug35600();
@@ -597,6 +598,92 @@ void tst_QWidget_window::tst_dnd()
QCOMPARE(log, expectedLog);
}
+
+class DnDEventRecorder : public QWidget
+{
+ Q_OBJECT
+public:
+ QString _dndEvents;
+ DnDEventRecorder() { setAcceptDrops(true); }
+
+protected:
+ void mousePressEvent(QMouseEvent *)
+ {
+ QMimeData *mimeData = new QMimeData;
+ mimeData->setData("application/x-dnditemdata", "some data");
+ QDrag *drag = new QDrag(this);
+ drag->setMimeData(mimeData);
+ drag->exec();
+ }
+
+ void dragEnterEvent(QDragEnterEvent *e)
+ {
+ e->accept();
+ _dndEvents.append(QStringLiteral("DragEnter "));
+ }
+ void dragMoveEvent(QDragMoveEvent *e)
+ {
+ e->accept();
+ _dndEvents.append(QStringLiteral("DragMove "));
+ emit releaseMouseButton();
+ }
+ void dragLeaveEvent(QDragLeaveEvent *e)
+ {
+ e->accept();
+ _dndEvents.append(QStringLiteral("DragLeave "));
+ }
+ void dropEvent(QDropEvent *e)
+ {
+ e->accept();
+ _dndEvents.append(QStringLiteral("DropEvent "));
+ }
+
+signals:
+ void releaseMouseButton();
+};
+
+void tst_QWidget_window::tst_dnd_events()
+{
+ // Note: This test is somewhat a hack as testing DnD with qtestlib is not
+ // supported at the moment. The test verifies that we get an expected event
+ // sequence on dnd operation that does not move a mouse. This logic is implemented
+ // in QGuiApplication, so we have to go via QWindowSystemInterface API (QTest::mouse*).
+ const auto platformName = QGuiApplication::platformName().toLower();
+ // The test is known to work with XCB and platforms that use the default dnd
+ // implementation QSimpleDrag (e.g. qnx). Running on XCB should be sufficient to
+ // catch regressions at cross platform code: QGuiApplication::processDrag/Leave().
+ if (platformName != "xcb")
+ return;
+
+ const QString expectedDndEvents = "DragEnter DragMove DropEvent DragEnter DragMove "
+ "DropEvent DragEnter DragMove DropEvent ";
+ DnDEventRecorder dndWidget;
+ dndWidget.setGeometry(100, 100, 200, 200);
+ dndWidget.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&dndWidget));
+ QVERIFY(QTest::qWaitForWindowActive(&dndWidget));
+
+ // ### FIXME - QTBUG-35117 ???
+ auto targetCenter = QPoint(dndWidget.width(), dndWidget.height()) / 2;
+ auto targetCenterGlobal = dndWidget.mapToGlobal(targetCenter);
+ QCursor::setPos(targetCenterGlobal);
+ QVERIFY(QTest::qWaitFor([&]() { return QCursor::pos() == targetCenterGlobal; }));
+ QCoreApplication::processEvents(); // clear mouse events generated from cursor
+
+ auto window = dndWidget.window()->windowHandle();
+
+ // Some dnd implementation rely on running internal event loops, so we have to use
+ // the following queued signal hack to simulate mouse clicks in the widget.
+ QObject::connect(&dndWidget, &DnDEventRecorder::releaseMouseButton, this, [=]() {
+ QTest::mouseRelease(window, Qt::LeftButton);
+ }, Qt::QueuedConnection);
+
+ QTest::mousePress(window, Qt::LeftButton);
+ QTest::mousePress(window, Qt::LeftButton);
+ QTest::mousePress(window, Qt::LeftButton);
+
+ QCOMPARE(dndWidget._dndEvents, expectedDndEvents);
+}
#endif
void tst_QWidget_window::tst_qtbug35600()