Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qsimpledrag.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qsimpledrag_p.h"
5
6#include "qbitmap.h"
7#include "qdrag.h"
8#include "qpixmap.h"
9#include "qevent.h"
10#include "qfile.h"
11#include "qguiapplication.h"
12#include "qpoint.h"
13#include "qbuffer.h"
14#include "qimage.h"
15#include "qdir.h"
16#include "qimagereader.h"
17#include "qimagewriter.h"
18#include "qplatformscreen.h"
19#include "qplatformwindow.h"
20
21#include <QtCore/QEventLoop>
22#include <QtCore/QDebug>
23#include <QtCore/QLoggingCategory>
24
25#include <private/qguiapplication_p.h>
26#include <private/qdnd_p.h>
27
28#include <private/qshapedpixmapdndwindow_p.h>
29#include <private/qhighdpiscaling_p.h>
30
32
33Q_LOGGING_CATEGORY(lcDnd, "qt.gui.dnd")
34
36{
38 const auto crend = list.crend();
39 for (auto it = list.crbegin(); it != crend; ++it) {
40 QWindow *w = *it;
41 if (w->isVisible() && w->handle() && w->geometry().contains(pos) && !qobject_cast<QShapedPixmapWindow*>(w))
42 return w;
43 }
44 return nullptr;
45}
46
63
65{
66 delete m_drag_icon_window;
67}
68
69void QBasicDrag::enableEventFilter()
70{
71 qApp->installEventFilter(this);
72}
73
74void QBasicDrag::disableEventFilter()
75{
76 qApp->removeEventFilter(this);
77}
78
79
81{
82 return QHighDpi::toNativePixels(static_cast<QMouseEvent *>(e)->globalPosition().toPoint(), window);
83}
84
86{
87 Q_UNUSED(o);
88
89 if (!m_drag) {
90 if (e->type() == QEvent::KeyRelease && static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape) {
91 disableEventFilter();
92 exitDndEventLoop();
93 return true; // block the key release
94 }
95 return false;
96 }
97
98 switch (e->type()) {
100 // prevent accelerators from firing while dragging
101 e->accept();
102 return true;
103
104 case QEvent::KeyPress:
106 {
107 QKeyEvent *ke = static_cast<QKeyEvent *>(e);
108 if (ke->key() == Qt::Key_Escape && e->type() == QEvent::KeyPress) {
109 cancel();
110 disableEventFilter();
111 exitDndEventLoop();
112
113 } else if (ke->modifiers() != QGuiApplication::keyboardModifiers()) {
114 move(m_lastPos, QGuiApplication::mouseButtons(), ke->modifiers());
115 }
116 return true; // Eat all key events
117 }
118
120 {
121 m_lastPos = getNativeMousePos(e, m_drag_icon_window);
122 auto mouseMove = static_cast<QMouseEvent *>(e);
123 move(m_lastPos, mouseMove->buttons(), mouseMove->modifiers());
124 return true; // Eat all mouse move events
125 }
127 {
128 QPointer<QObject> objGuard(o);
129 disableEventFilter();
130 if (canDrop()) {
131 QPoint nativePosition = getNativeMousePos(e, m_drag_icon_window);
132 auto mouseRelease = static_cast<QMouseEvent *>(e);
133 drop(nativePosition, mouseRelease->buttons(), mouseRelease->modifiers());
134 } else {
135 cancel();
136 }
137 exitDndEventLoop();
138 if (!objGuard)
139 return true;
140
141 // If a QShapedPixmapWindow (drag feedback) is being dragged along, the
142 // mouse event's localPos() will be relative to that, which is useless.
143 // We want a position relative to the window where the drag ends, if possible (?).
144 // If there is no such window (belonging to this Qt application),
145 // make the event relative to the window where the drag started. (QTBUG-66103)
146 const QMouseEvent *release = static_cast<QMouseEvent *>(e);
147 const QWindow *releaseWindow = topLevelAt(release->globalPosition().toPoint());
148 qCDebug(lcDnd) << "mouse released over" << releaseWindow << "after drag from" << m_sourceWindow << "globalPos" << release->globalPosition().toPoint();
149 if (!releaseWindow)
150 releaseWindow = m_sourceWindow;
151 QPointF releaseWindowPos = (releaseWindow ? releaseWindow->mapFromGlobal(release->globalPosition()) : release->globalPosition());
152 QMouseEvent *newRelease = new QMouseEvent(release->type(),
153 releaseWindowPos, releaseWindowPos, release->globalPosition(),
154 release->button(), release->buttons(),
155 release->modifiers(), release->source(), release->pointingDevice());
156 QCoreApplication::postEvent(o, newRelease);
157 return true; // defer mouse release events until drag event loop has returned
158 }
160 case QEvent::Wheel:
161 return true;
162 default:
163 break;
164 }
165 return false;
166}
167
169{
170 m_drag = o;
171 m_executed_drop_action = Qt::IgnoreAction;
172 m_can_drop = false;
173
174 startDrag();
175 m_eventLoop = new QEventLoop;
176 m_eventLoop->exec();
177 delete m_eventLoop;
178 m_eventLoop = nullptr;
179 m_drag = nullptr;
180 endDrag();
181
182 return m_executed_drop_action;
183}
184
186{
187 if (m_eventLoop) {
188 cancel();
189 m_eventLoop->quit();
190 }
191}
192
194{
195 QPoint pos;
196#ifndef QT_NO_CURSOR
197 pos = QCursor::pos();
198 if (pos.x() == int(qInf())) {
199 // ### fixme: no mouse pos registered. Get pos from touch...
200 pos = QPoint();
201 }
202#endif
203 m_lastPos = pos;
205 enableEventFilter();
206}
207
209{
210}
211
213{
214 delete m_drag_icon_window;
215 // ### TODO Check if its really necessary to have m_drag_icon_window
216 // when QDrag is used without a pixmap - QDrag::setPixmap()
217 m_drag_icon_window = new QShapedPixmapWindow(screen);
218
219 m_drag_icon_window->setUseCompositing(m_useCompositing);
220 m_drag_icon_window->setPixmap(m_drag->pixmap());
221 m_drag_icon_window->setHotspot(m_drag->hotSpot());
222 m_drag_icon_window->updateGeometry(pos);
223 m_drag_icon_window->setVisible(true);
224}
225
227{
228 disableEventFilter();
229 restoreCursor();
230 m_drag_icon_window->setVisible(false);
231}
232
239{
240 if (m_drag)
241 m_drag_icon_window->updateGeometry(globalPos);
242}
243
244void QBasicDrag::drop(const QPoint &, Qt::MouseButtons, Qt::KeyboardModifiers)
245{
246 disableEventFilter();
247 restoreCursor();
248 m_drag_icon_window->setVisible(false);
249}
250
251void QBasicDrag::exitDndEventLoop()
252{
253 if (m_eventLoop && m_eventLoop->isRunning())
254 m_eventLoop->exit();
255}
256
258{
259#ifndef QT_NO_CURSOR
261 if (canDrop()) {
262 switch (action) {
263 case Qt::CopyAction:
264 cursorShape = Qt::DragCopyCursor;
265 break;
266 case Qt::LinkAction:
267 cursorShape = Qt::DragLinkCursor;
268 break;
269 default:
270 cursorShape = Qt::DragMoveCursor;
271 break;
272 }
273 }
274
275 QPixmap pixmap = m_drag->dragCursor(action);
276
277 if (!m_dndHasSetOverrideCursor) {
278 QCursor newCursor = !pixmap.isNull() ? QCursor(pixmap) : QCursor(cursorShape);
280 m_dndHasSetOverrideCursor = true;
281 } else {
283 if (!cursor) {
285 } else {
286 if (!pixmap.isNull()) {
287 if (cursor->pixmap().cacheKey() != pixmap.cacheKey())
289 } else if (cursorShape != cursor->shape()) {
291 }
292 }
293 }
294#endif
295 updateAction(action);
296}
297
298void QBasicDrag::restoreCursor()
299{
300#ifndef QT_NO_CURSOR
301 if (m_dndHasSetOverrideCursor) {
303 m_dndHasSetOverrideCursor = false;
304 }
305#endif
306}
307
308static inline QPoint fromNativeGlobalPixels(const QPoint &point)
309{
310#ifndef QT_NO_HIGHDPISCALING
311 QPoint res = point;
313 for (const QScreen *s : std::as_const(QGuiApplicationPrivate::screen_list)) {
314 if (s->handle()->geometry().contains(point)) {
316 break;
317 }
318 }
319 }
320 return res;
321#else
322 return point;
323#endif
324}
325
341
343{
345
347 // Here we can be fairly sure that QGuiApplication::mouseButtons/keyboardModifiers() will
348 // contain sensible values as startDrag() normally is called from mouse event handlers
349 // by QDrag::exec(). A better API would be if we could pass something like "input device
350 // pointer" to QDrag::exec(). My guess is that something like that might be required for
351 // QTBUG-52430.
354 if (m_sourceWindow) {
355 auto nativePixelPos = QHighDpi::toNativePixels(QCursor::pos(), m_sourceWindow);
357 } else {
358 setCanDrop(false);
360 }
361
362 qCDebug(lcDnd) << "drag began from" << m_sourceWindow << "cursor pos" << QCursor::pos() << "can drop?" << canDrop();
363}
364
366{
367 QWindowSystemInterface::handleDrag(window, nullptr, QPoint(), Qt::IgnoreAction, { }, { });
368}
369
371{
373 if (drag() && m_sourceWindow) {
375 m_sourceWindow = nullptr;
376 }
377}
378
379void QSimpleDrag::move(const QPoint &nativeGlobalPos, Qt::MouseButtons buttons,
380 Qt::KeyboardModifiers modifiers)
381{
382 QPoint globalPos = fromNativeGlobalPixels(nativeGlobalPos);
383 moveShapedPixmapWindow(globalPos);
384 QWindow *window = topLevelAt(globalPos);
385
386 if (!window || window != m_windowUnderCursor) {
390 if (!window) {
391 // QSimpleDrag supports only in-process dnd, we can't drop anywhere else.
392 setCanDrop(false);
394 return;
395 }
396 }
397
398 const QPoint pos = nativeGlobalPos - window->handle()->geometry().topLeft();
399 const QPlatformDragQtResponse qt_response = QWindowSystemInterface::handleDrag(
400 window, drag()->mimeData(), pos, drag()->supportedActions(),
401 buttons, modifiers);
402
403 setCanDrop(qt_response.isAccepted());
404 updateCursor(qt_response.acceptedAction());
405}
406
407void QSimpleDrag::drop(const QPoint &nativeGlobalPos, Qt::MouseButtons buttons,
408 Qt::KeyboardModifiers modifiers)
409{
410 QPoint globalPos = fromNativeGlobalPixels(nativeGlobalPos);
411
412 QBasicDrag::drop(nativeGlobalPos, buttons, modifiers);
413 QWindow *window = topLevelAt(globalPos);
414 if (!window)
415 return;
416
417 const QPoint pos = nativeGlobalPos - window->handle()->geometry().topLeft();
418 const QPlatformDropQtResponse response = QWindowSystemInterface::handleDrop(
419 window, drag()->mimeData(), pos, drag()->supportedActions(),
420 buttons, modifiers);
421 if (response.isAccepted()) {
423 } else {
425 }
426}
427
void updateCursor(Qt::DropAction action)
QWindow * m_sourceWindow
bool canDrop() const
void moveShapedPixmapWindow(const QPoint &deviceIndependentPosition)
Move the drag label to globalPos, which is interpreted in device independent coordinates.
void cancelDrag() override
Cancels the currently active drag (only for drags of the current application initiated by QPlatformDr...
virtual bool eventFilter(QObject *o, QEvent *e) override
Filters events if this object has been installed as an event filter for the watched object.
virtual void drop(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardModifiers mods)=0
virtual void endDrag()
virtual void startDrag()
void recreateShapedPixmapWindow(QScreen *screen, const QPoint &pos)
void setExecutedDropAction(Qt::DropAction da)
QPointer< QWindow > m_windowUnderCursor
void setCanDrop(bool c)
virtual void move(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardModifiers mods)=0
QDrag * drag() const
virtual void cancel()
static void postEvent(QObject *receiver, QEvent *event, int priority=Qt::NormalEventPriority)
The QCursor class provides a mouse cursor with an arbitrary shape.
Definition qcursor.h:45
QPixmap pixmap() const
Returns the cursor pixmap.
Definition qcursor.cpp:584
Qt::CursorShape shape() const
Returns the cursor shape identifier.
Definition qcursor.cpp:498
static QPoint pos()
Returns the position of the cursor (hot spot) of the primary screen in global screen coordinates.
Definition qcursor.cpp:188
\inmodule QtGui
Definition qdrag.h:22
QPixmap pixmap() const
Returns the pixmap used to represent the data in a drag and drop operation.
Definition qdrag.cpp:134
QPixmap dragCursor(Qt::DropAction action) const
Returns the drag cursor for the action.
Definition qdrag.cpp:279
QPoint hotSpot() const
Returns the position of the hot spot relative to the top-left corner of the cursor.
Definition qdrag.cpp:158
\inmodule QtCore
Definition qeventloop.h:16
int exec(ProcessEventsFlags flags=AllEvents)
Enters the main event loop and waits until exit() is called.
void exit(int returnCode=0)
Tells the event loop to exit with a return code.
bool isRunning() const
Returns true if the event loop is running; otherwise returns false.
void quit()
Tells the event loop to exit normally.
\inmodule QtCore
Definition qcoreevent.h:45
@ ShortcutOverride
Definition qcoreevent.h:158
@ KeyRelease
Definition qcoreevent.h:65
@ MouseMove
Definition qcoreevent.h:63
@ KeyPress
Definition qcoreevent.h:64
@ MouseButtonDblClick
Definition qcoreevent.h:62
@ MouseButtonRelease
Definition qcoreevent.h:61
Type type() const
Returns the event type.
Definition qcoreevent.h:304
void accept()
Sets the accept flag of the event object, the equivalent of calling setAccepted(true).
Definition qcoreevent.h:310
static QList< QScreen * > screen_list
static QWindowList topLevelWindows()
Returns a list of the top-level windows in the application.
static QCursor * overrideCursor()
Returns the active application override cursor.
static void changeOverrideCursor(const QCursor &)
Changes the currently active application override cursor to cursor.
static Qt::KeyboardModifiers keyboardModifiers()
Returns the current state of the modifier keys on the keyboard.
static void setOverrideCursor(const QCursor &)
Sets the application override cursor to cursor.
static void restoreOverrideCursor()
Undoes the last setOverrideCursor().
static Qt::MouseButtons mouseButtons()
Returns the current state of the buttons on the mouse.
static bool isActive()
The QKeyEvent class describes a key event.
Definition qevent.h:424
int key() const
Returns the code of the key that was pressed or released.
Definition qevent.h:434
const_reverse_iterator crbegin() const noexcept
Definition qlist.h:638
const_reverse_iterator crend() const noexcept
Definition qlist.h:639
\inmodule QtGui
Definition qevent.h:196
\inmodule QtCore
Definition qobject.h:103
Returns a copy of the pixmap that is transformed using the given transformation transform and transfo...
Definition qpixmap.h:27
qint64 cacheKey() const
Returns a number that identifies this QPixmap.
Definition qpixmap.cpp:884
void updateAction(Qt::DropAction action)
Called to notify QDrag about changes of the current action.
Qt::DropAction acceptedAction() const
\inmodule QtCore\reentrant
Definition qpoint.h:217
\inmodule QtCore\reentrant
Definition qpoint.h:25
The QScreen class is used to query screen properties. \inmodule QtGui.
Definition qscreen.h:32
void setHotspot(const QPoint &hotspot)
void updateGeometry(const QPoint &pos)
void setPixmap(const QPixmap &pixmap)
virtual void cancel() override
virtual void drop(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardModifiers mods) override
virtual void move(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardModifiers mods) override
virtual void startDrag() override
\inmodule QtGui
Definition qwindow.h:63
void setVisible(bool visible)
Definition qwindow.cpp:689
EGLImageKHR int int EGLuint64KHR * modifiers
QCursor cursor
QSet< QString >::iterator it
T toNativePixels(const T &value, const C *context)
T fromNativePixels(const T &value, const C *context)
Combined button and popup list for selecting options.
CursorShape
@ DragCopyCursor
@ DragLinkCursor
@ DragMoveCursor
@ ForbiddenCursor
@ Key_Escape
Definition qnamespace.h:663
DropAction
@ CopyAction
@ IgnoreAction
@ LinkAction
#define qApp
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
Q_CORE_EXPORT Q_DECL_CONST_FUNCTION double qInf()
GLfloat GLfloat GLfloat w
[0]
GLdouble s
[6]
Definition qopenglext.h:235
GLuint res
static QPoint fromNativeGlobalPixels(const QPoint &point)
static void sendDragLeave(QWindow *window)
static QPoint getNativeMousePos(QEvent *e, QWindow *window)
static QT_BEGIN_NAMESPACE QWindow * topLevelAt(const QPoint &pos)
QScreen * screen
[1]
Definition main.cpp:29
#define Q_UNUSED(x)
QList< int > list
[14]
QMimeData * mimeData
sem release()
widget render & pixmap
aWidget window() -> setWindowTitle("New Window Title")
[2]