diff options
Diffstat (limited to 'src/plugins/platforms/cocoa/qcocoadrag.mm')
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoadrag.mm | 90 |
1 files changed, 90 insertions, 0 deletions
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(); |