summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/cocoa
diff options
context:
space:
mode:
authorTimur Pocheptsov <timur.pocheptsov@qt.io>2020-04-22 12:00:01 +0200
committerTimur Pocheptsov <timur.pocheptsov@qt.io>2020-05-08 18:10:35 +0200
commit8481a9fc974a1f1dd44a9f82decb18fe2290689f (patch)
treec77ee86c162fc806a47392f77491ae7b387c682a /src/plugins/platforms/cocoa
parent7cb4c50e73cf611cf9e888013de7ae6f5ab481c7 (diff)
QCocoaDrag - avoid using the deprecated API if possible
The -dragImage stopped to work in 10.14 first with change in behavior documented and the new API (AppKit) proposed/advised. Then after some update the old behavior was restored and D&D for multiple URLs was working again without us having to change anything. In 10.15 it's not working anymore and we have to properly fix it and switch to the API advised. The new API is non-blocking, but QCocoaDrag::drag is expected to be blocking so we have to add a nested event loop to emulate the old behavior. Dragging image is a bit problematic (the exception is thrown because number of images (1) and number of items in NSPasteboard are not equal): creating separate images for different drag items looks really messy. So ... we use the same image again and again with the same frame. Fixes: QTBUG-71939 Change-Id: I02c2a10eab280cf4a55513adaf29c22fff934c01 Pick-to: 5.15 Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
Diffstat (limited to 'src/plugins/platforms/cocoa')
-rw-r--r--src/plugins/platforms/cocoa/qcocoadrag.h6
-rw-r--r--src/plugins/platforms/cocoa/qcocoadrag.mm90
-rw-r--r--src/plugins/platforms/cocoa/qnsview_dragging.mm9
3 files changed, 104 insertions, 1 deletions
diff --git a/src/plugins/platforms/cocoa/qcocoadrag.h b/src/plugins/platforms/cocoa/qcocoadrag.h
index 5a5b985c6e..975741c270 100644
--- a/src/plugins/platforms/cocoa/qcocoadrag.h
+++ b/src/plugins/platforms/cocoa/qcocoadrag.h
@@ -48,6 +48,8 @@
#include <QtGui/private/qdnd_p.h>
#include <QtGui/private/qinternalmimedata_p.h>
+#include <QtCore/qeventloop.h>
+
QT_BEGIN_NAMESPACE
class QCocoaDrag : public QPlatformDrag
@@ -69,11 +71,15 @@ public:
void setLastMouseEvent(NSEvent *event, NSView *view);
void setAcceptedAction(Qt::DropAction act);
+ void exitDragLoop();
private:
QDrag *m_drag;
NSEvent *m_lastEvent;
NSView *m_lastView;
Qt::DropAction m_executed_drop_action;
+ QEventLoop internalDragLoop;
+
+ bool maybeDragMultipleItems();
QPixmap dragPixmap(QDrag *drag, QPoint &hotSpot) const;
};
diff --git a/src/plugins/platforms/cocoa/qcocoadrag.mm b/src/plugins/platforms/cocoa/qcocoadrag.mm
index 3525fddfd1..cf48c50caf 100644
--- a/src/plugins/platforms/cocoa/qcocoadrag.mm
+++ b/src/plugins/platforms/cocoa/qcocoadrag.mm
@@ -41,6 +41,9 @@
#include "qmacclipboard.h"
#include "qcocoahelpers.h"
#include <QtGui/private/qcoregraphics_p.h>
+#include <QtCore/qsysinfo.h>
+
+#include <vector>
QT_BEGIN_NAMESPACE
@@ -125,6 +128,9 @@ Qt::DropAction QCocoaDrag::drag(QDrag *o)
m_drag = o;
m_executed_drop_action = Qt::IgnoreAction;
+ if (maybeDragMultipleItems())
+ return m_executed_drop_action;
+
QPoint hotSpot = m_drag->hotSpot();
QPixmap pm = dragPixmap(m_drag, hotSpot);
NSImage *dragImage = [NSImage imageFromQImage:pm.toImage()];
@@ -155,11 +161,95 @@ Qt::DropAction QCocoaDrag::drag(QDrag *o)
return m_executed_drop_action;
}
+bool QCocoaDrag::maybeDragMultipleItems()
+{
+ Q_ASSERT(m_drag && m_drag->mimeData());
+ Q_ASSERT(m_executed_drop_action == Qt::IgnoreAction);
+
+ if (QOperatingSystemVersion::current() < QOperatingSystemVersion::MacOSMojave) {
+ // -dragImage: stopped working in 10.14 first.
+ return false;
+ }
+
+ const QMacAutoReleasePool pool;
+
+ NSWindow *theWindow = [m_lastEvent window];
+ Q_ASSERT(theWindow);
+
+ if (![theWindow.contentView respondsToSelector:@selector(draggingSession:sourceOperationMaskForDraggingContext:)])
+ return false;
+
+ auto *sourceView = static_cast<NSView<NSDraggingSource>*>(theWindow.contentView);
+
+ const auto &qtUrls = m_drag->mimeData()->urls();
+ NSPasteboard *dragBoard = [NSPasteboard pasteboardWithName:NSPasteboardNameDrag];
+
+ if (int(dragBoard.pasteboardItems.count) == 1 && qtUrls.size() <= 1) {
+ // Good old -dragImage: works perfectly for this ...
+ return false;
+ }
+
+ std::vector<NSPasteboardItem *> nonUrls;
+ for (NSPasteboardItem *item in dragBoard.pasteboardItems) {
+ bool isUrl = false;
+ for (NSPasteboardType type in item.types) {
+ using NSStringRef = NSString *;
+ if ([type isEqualToString:NSStringRef(kUTTypeFileURL)]) {
+ isUrl = true;
+ break;
+ }
+ }
+
+ if (!isUrl)
+ nonUrls.push_back(item);
+ }
+
+ QPoint hotSpot = m_drag->hotSpot();
+ const auto pixmap = dragPixmap(m_drag, hotSpot);
+ NSImage *dragImage = [NSImage imageFromQImage:pixmap.toImage()];
+ Q_ASSERT(dragImage);
+
+ NSMutableArray<NSDraggingItem *> *dragItems = [[[NSMutableArray alloc] init] autorelease];
+ const NSPoint itemLocation = m_drag->hotSpot().toCGPoint();
+ // 0. We start from URLs, which can be actually in a list (thus technically
+ // only ONE item in the pasteboard. The fact it's only one does not help, we are
+ // still getting an exception because of the number of items/images mismatch ...
+ for (const auto &qtUrl : qtUrls) {
+ NSURL *nsUrl = qtUrl.toNSURL();
+ auto *newItem = [[[NSDraggingItem alloc] initWithPasteboardWriter:nsUrl] autorelease];
+ const NSRect itemFrame = NSMakeRect(itemLocation.x, itemLocation.y,
+ dragImage.size.width,
+ dragImage.size.height);
+ [newItem setDraggingFrame:itemFrame contents:dragImage];
+ [dragItems addObject:newItem];
+ }
+ // 1. Repeat for non-url items, if any:
+ for (auto *pbItem : nonUrls) {
+ auto *newItem = [[[NSDraggingItem alloc] initWithPasteboardWriter:pbItem] autorelease];
+ const NSRect itemFrame = NSMakeRect(itemLocation.x, itemLocation.y,
+ dragImage.size.width,
+ dragImage.size.height);
+ [newItem setDraggingFrame:itemFrame contents:dragImage];
+ [dragItems addObject:newItem];
+ }
+
+ [sourceView beginDraggingSessionWithItems:dragItems event:m_lastEvent source:sourceView];
+ internalDragLoop.exec();
+ return true;
+}
+
void QCocoaDrag::setAcceptedAction(Qt::DropAction act)
{
m_executed_drop_action = act;
}
+void QCocoaDrag::exitDragLoop()
+{
+ if (internalDragLoop.isRunning())
+ internalDragLoop.exit();
+}
+
+
QPixmap QCocoaDrag::dragPixmap(QDrag *drag, QPoint &hotSpot) const
{
const QMimeData* data = drag->mimeData();
diff --git a/src/plugins/platforms/cocoa/qnsview_dragging.mm b/src/plugins/platforms/cocoa/qnsview_dragging.mm
index 2e88b6a08f..44ffc87f7a 100644
--- a/src/plugins/platforms/cocoa/qnsview_dragging.mm
+++ b/src/plugins/platforms/cocoa/qnsview_dragging.mm
@@ -231,6 +231,10 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin
if (!target)
return;
+ auto *nativeDrag = QCocoaIntegration::instance()->drag();
+ Q_ASSERT(nativeDrag);
+ nativeDrag->exitDragLoop();
+
QPoint windowPoint = QPointF::fromCGPoint([self convertPoint:sender.draggingLocation fromView:nil]).toPoint();
qCDebug(lcQpaMouse) << QEvent::DragLeave << self << "at" << windowPoint;
@@ -289,7 +293,10 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin
if (!target)
return;
- QCocoaIntegration::instance()->drag();
+ QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag();
+ Q_ASSERT(nativeDrag);
+ nativeDrag->exitDragLoop();
+ nativeDrag->setAcceptedAction(qt_mac_mapNSDragOperation(operation));
// Qt starts drag-and-drop on a mouse button press event. Cococa in
// this case won't send the matching release event, so we have to