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
qnsview_dragging.mm
Go to the documentation of this file.
1// Copyright (C) 2018 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// This file is included from qnsview.mm, and only used to organize the code
5
6@implementation QNSView (Dragging)
7
9{
11
12 NSString * const mimeTypeGeneric = @"com.trolltech.qt.MimeTypeName";
13 NSMutableArray<NSString *> *supportedTypes = [NSMutableArray<NSString *> arrayWithArray:@[
14 NSPasteboardTypeColor, NSPasteboardTypeString,
15 NSPasteboardTypeFileURL, @"com.adobe.encapsulated-postscript", NSPasteboardTypeTIFF,
16 NSPasteboardTypeRTF, NSPasteboardTypeTabularText, NSPasteboardTypeFont,
17 NSPasteboardTypeRuler, NSFileContentsPboardType,
18 NSPasteboardTypeRTFD , NSPasteboardTypeHTML,
19 NSPasteboardTypeURL, NSPasteboardTypePDF, UTTypeVCard.identifier,
20 (NSString *)kPasteboardTypeFileURLPromise,
21 NSPasteboardTypeMultipleTextSelection, mimeTypeGeneric]];
22
23 // Add custom types supported by the application
24 for (const QString &customType : QMacMimeRegistry::enabledDraggedTypes())
25 [supportedTypes addObject:customType.toNSString()];
26
27 [self registerForDraggedTypes:supportedTypes];
28}
29
30static QWindow *findEventTargetWindow(QWindow *candidate)
31{
32 while (candidate) {
33 if (!(candidate->flags() & Qt::WindowTransparentForInput))
34 return candidate;
35 candidate = candidate->parent();
36 }
37 return candidate;
38}
39
40static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint point)
41{
42 return target->mapFromGlobal(source->mapToGlobal(point));
43}
44
45- (NSDragOperation)draggingSession:(NSDraggingSession *)session sourceOperationMaskForDraggingContext:(NSDraggingContext)context
46{
47 Q_UNUSED(session);
48
50 QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag();
51 return qt_mac_mapDropActions(nativeDrag->currentDrag()->supportedActions());
52}
53
54- (BOOL)ignoreModifierKeysForDraggingSession:(NSDraggingSession *)session
55{
56 Q_UNUSED(session);
57 // According to the "Dragging Sources" chapter on Cocoa DnD Programming
58 // (https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/DragandDrop/Concepts/dragsource.html),
59 // if the control, option, or command key is pressed, the source’s
60 // operation mask is filtered to only contain a reduced set of operations.
61 //
62 // Since Qt already takes care of tracking the keyboard modifiers, we
63 // don't need (or want) Cocoa to filter anything. Instead, we'll let
64 // the application do the actual filtering. But only while dragging
65 // within application, otherwise ignored modifiers may end up in a
66 // wrong drop operation executed.
67
68 return m_lastSeenContext == NSDraggingContextWithinApplication;
69}
70
71- (BOOL)wantsPeriodicDraggingUpdates
72{
73 // From the documentation:
74 //
75 // "If the destination returns NO, these messages are sent only when the mouse moves
76 // or a modifier flag changes. Otherwise the destination gets the default behavior,
77 // where it receives periodic dragging-updated messages even if nothing changes."
78 //
79 // We do not want these constant drag update events while mouse is stationary,
80 // since we do all animations (autoscroll) with timers.
81 return NO;
82}
83
84
85- (BOOL)wantsPeriodicDraggingUpdates:(void *)dummy
86{
87 // This method never gets called. It's a workaround for Apple's
88 // bug: they first respondsToSelector : @selector(wantsPeriodicDraggingUpdates:)
89 // (note ':') and then call -wantsPeriodicDraggingUpdate (without colon).
90 // So, let's make them happy.
91 Q_UNUSED(dummy);
92
93 return NO;
94}
95
96- (void)updateCursorFromDragResponse:(QPlatformDragQtResponse)response drag:(QCocoaDrag *)drag
97{
98 const QPixmap pixmapCursor = drag->currentDrag()->dragCursor(response.acceptedAction());
99 NSCursor *nativeCursor = nil;
100
101 if (pixmapCursor.isNull()) {
102 switch (response.acceptedAction()) {
103 case Qt::CopyAction:
104 nativeCursor = [NSCursor dragCopyCursor];
105 break;
106 case Qt::LinkAction:
107 nativeCursor = [NSCursor dragLinkCursor];
108 break;
109 case Qt::IgnoreAction:
110 // Uncomment the next lines if forbidden cursor is wanted on undroppable targets.
111 /*nativeCursor = [NSCursor operationNotAllowedCursor];
112 break;*/
113 case Qt::MoveAction:
114 default:
115 nativeCursor = [NSCursor arrowCursor];
116 break;
117 }
118 } else {
119 auto *nsimage = [NSImage imageFromQImage:pixmapCursor.toImage()];
120 nativeCursor = [[NSCursor alloc] initWithImage:nsimage hotSpot:NSZeroPoint];
121 }
122
123 // Change the cursor
124 [nativeCursor set];
125
126 // Make sure the cursor is updated correctly if the mouse does not move and window is under cursor
127 // by creating a fake move event, unless on 10.14 and later where doing so will trigger a security
128 // warning dialog. FIXME: Find a way to update the cursor without fake mouse events.
130 return;
131
132 if (m_updatingDrag)
133 return;
134
135 QCFType<CGEventRef> moveEvent = CGEventCreateMouseEvent(
136 nullptr, kCGEventMouseMoved, QCursor::pos().toCGPoint(),
137 kCGMouseButtonLeft // ignored
138 );
139 CGEventPost(kCGHIDEventTap, moveEvent);
140}
141
142- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender
143{
144 return [self handleDrag:(QEvent::DragEnter) sender:sender];
145}
146
147- (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)sender
148{
149 QScopedValueRollback<bool> rollback(m_updatingDrag, true);
150 return [self handleDrag:(QEvent::DragMove) sender:sender];
151}
152
153// Sends drag update to Qt, return the action
154- (NSDragOperation)handleDrag:(QEvent::Type)dragType sender:(id<NSDraggingInfo>)sender
155{
156 if (!m_platformWindow)
157 return NSDragOperationNone;
158
159 QPoint windowPoint = QPointF::fromCGPoint([self convertPoint:sender.draggingLocation fromView:nil]).toPoint();
160
161 Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations(sender.draggingSourceOperationMask);
162
163 QWindow *target = findEventTargetWindow(m_platformWindow->window());
164 if (!target)
165 return NSDragOperationNone;
166
167 const auto modifiers = QAppleKeyMapper::fromCocoaModifiers(NSApp.currentEvent.modifierFlags);
168 const auto buttons = currentlyPressedMouseButtons();
169 const auto point = mapWindowCoordinates(m_platformWindow->window(), target, windowPoint);
170
171 if (dragType == QEvent::DragEnter)
172 qCDebug(lcQpaMouse) << dragType << self << "at" << windowPoint;
173 else
174 qCDebug(lcQpaMouse) << dragType << "at" << windowPoint << "with" << buttons;
175
177 QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag();
178 if (nativeDrag->currentDrag()) {
179 // The drag was started from within the application
180 response = QWindowSystemInterface::handleDrag(target, nativeDrag->dragMimeData(),
181 point, qtAllowed, buttons, modifiers);
182 [self updateCursorFromDragResponse:response drag:nativeDrag];
183 } else {
184 QCocoaDropData mimeData(sender.draggingPasteboard);
185 response = QWindowSystemInterface::handleDrag(target, &mimeData,
186 point, qtAllowed, buttons, modifiers);
187 }
188
189 return qt_mac_mapDropAction(response.acceptedAction());
190}
191
192- (void)draggingExited:(id<NSDraggingInfo>)sender
193{
194 if (!m_platformWindow)
195 return;
196
197 QWindow *target = findEventTargetWindow(m_platformWindow->window());
198 if (!target)
199 return;
200
201 auto *nativeDrag = QCocoaIntegration::instance()->drag();
202 Q_ASSERT(nativeDrag);
203 nativeDrag->exitDragLoop();
204
205 QPoint windowPoint = QPointF::fromCGPoint([self convertPoint:sender.draggingLocation fromView:nil]).toPoint();
206
207 qCDebug(lcQpaMouse) << QEvent::DragLeave << self << "at" << windowPoint;
208
209 // Send 0 mime data to indicate drag exit
210 QWindowSystemInterface::handleDrag(target, nullptr,
211 mapWindowCoordinates(m_platformWindow->window(), target, windowPoint),
213}
214
215// Called on drop, send the drop to Qt and return if it was accepted
216- (BOOL)performDragOperation:(id<NSDraggingInfo>)sender
217{
218 if (!m_platformWindow)
219 return false;
220
221 QWindow *target = findEventTargetWindow(m_platformWindow->window());
222 if (!target)
223 return false;
224
225 QPoint windowPoint = QPointF::fromCGPoint([self convertPoint:sender.draggingLocation fromView:nil]).toPoint();
226
227 Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations(sender.draggingSourceOperationMask);
228
230 QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag();
231 const auto modifiers = QAppleKeyMapper::fromCocoaModifiers(NSApp.currentEvent.modifierFlags);
232 const auto buttons = currentlyPressedMouseButtons();
233 const auto point = mapWindowCoordinates(m_platformWindow->window(), target, windowPoint);
234
235 qCDebug(lcQpaMouse) << QEvent::Drop << "at" << windowPoint << "with" << buttons;
236
237 if (nativeDrag->currentDrag()) {
238 // The drag was started from within the application
239 response = QWindowSystemInterface::handleDrop(target, nativeDrag->dragMimeData(),
240 point, qtAllowed, buttons, modifiers);
241 nativeDrag->setAcceptedAction(response.acceptedAction());
242 } else {
243 QCocoaDropData mimeData(sender.draggingPasteboard);
244 response = QWindowSystemInterface::handleDrop(target, &mimeData,
245 point, qtAllowed, buttons, modifiers);
246 }
247 return response.isAccepted();
248}
249
250- (void)draggingSession:(NSDraggingSession *)session endedAtPoint:(NSPoint)screenPoint operation:(NSDragOperation)operation
251{
252 Q_UNUSED(session);
253 Q_UNUSED(screenPoint);
254 Q_UNUSED(operation);
255
256 m_lastSeenContext = NSDraggingContextWithinApplication;
257
258 if (!m_platformWindow)
259 return;
260
261 QWindow *target = findEventTargetWindow(m_platformWindow->window());
262 if (!target)
263 return;
264
265 QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag();
266 Q_ASSERT(nativeDrag);
267 nativeDrag->exitDragLoop();
268 // for internal drag'n'drop, don't override the action the drop event accepted
269 if (!nativeDrag->currentDrag())
270 nativeDrag->setAcceptedAction(qt_mac_mapNSDragOperation(operation));
271
272 // Qt starts drag-and-drop on a mouse button press event. Cococa in
273 // this case won't send the matching release event, so we have to
274 // synthesize it here.
276 const auto modifiers = QAppleKeyMapper::fromCocoaModifiers(NSApp.currentEvent.modifierFlags);
277
278 NSPoint windowPoint = [self.window convertRectFromScreen:NSMakeRect(screenPoint.x, screenPoint.y, 1, 1)].origin;
279 NSPoint nsViewPoint = [self convertPoint: windowPoint fromView: nil];
280
281 QPoint qtWindowPoint(nsViewPoint.x, nsViewPoint.y);
282 QPoint qtScreenPoint = QCocoaScreen::mapFromNative(screenPoint).toPoint();
283
285 target,
286 mapWindowCoordinates(m_platformWindow->window(), target, qtWindowPoint),
287 qtScreenPoint,
288 m_buttons,
291 modifiers);
292
293 qCDebug(lcQpaMouse) << "Drag session" << session << "ended, with" << m_buttons;
294}
295
296@end
static QCocoaIntegration * instance()
static QPointF mapFromNative(CGPoint pos, QCocoaScreen *screen=QCocoaScreen::primaryScreen())
static QPoint pos()
Returns the position of the cursor (hot spot) of the primary screen in global screen coordinates.
Definition qcursor.cpp:188
QPixmap dragCursor(Qt::DropAction action) const
Returns the drag cursor for the action.
Definition qdrag.cpp:279
\inmodule QtCore
Definition qcoreevent.h:45
@ DragEnter
Definition qcoreevent.h:101
@ DragLeave
Definition qcoreevent.h:103
@ MouseButtonRelease
Definition qcoreevent.h:61
static Q_CORE_EXPORT QOperatingSystemVersionBase current()
static constexpr QOperatingSystemVersionBase MacOSMojave
\variable QOperatingSystemVersion::MacOSMojave
Returns a copy of the pixmap that is transformed using the given transformation transform and transfo...
Definition qpixmap.h:27
QDrag * currentDrag() const
Qt::DropAction acceptedAction() const
\inmodule QtCore\reentrant
Definition qpoint.h:25
\inmodule QtCore\reentrant
Definition qrect.h:30
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static bool handleMouseEvent(QWindow *window, const QPointF &local, const QPointF &global, Qt::MouseButtons state, Qt::MouseButton button, QEvent::Type type, Qt::KeyboardModifiers mods=Qt::NoModifier, Qt::MouseEventSource source=Qt::MouseEventNotSynthesized)
\inmodule QtGui
Definition qwindow.h:63
Qt::WindowFlags flags
the window flags of the window
Definition qwindow.h:79
EGLImageKHR int int EGLuint64KHR * modifiers
const QStringList & enabledDraggedTypes()
@ NoButton
Definition qnamespace.h:57
@ NoModifier
@ CopyAction
@ IgnoreAction
@ MoveAction
@ LinkAction
@ WindowTransparentForInput
Definition qnamespace.h:234
QString self
Definition language.cpp:58
static void * context
Qt::DropActions qt_mac_mapNSDragOperations(NSDragOperation nsActions)
NSDragOperation qt_mac_mapDropActions(Qt::DropActions actions)
Qt::DropAction qt_mac_mapNSDragOperation(NSDragOperation nsActions)
NSDragOperation qt_mac_mapDropAction(Qt::DropAction action)
Qt::MouseButtons currentlyPressedMouseButtons()
Returns the Qt::MouseButtons that corresponds to an NSEvent.pressedMouseButtons.
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage void
#define qCDebug(category,...)
bool m_updatingDrag
Definition qnsview.mm:109
Qt::MouseButtons m_buttons
Definition qnsview.mm:102
NSDraggingContext m_lastSeenContext
Definition qnsview.mm:121
GLenum target
GLsizei GLsizei GLchar * source
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define Q_UNUSED(x)
QMimeData * mimeData
Definition moc.h:23