diff options
Diffstat (limited to 'src/plugins')
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoadrag.h | 6 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoadrag.mm | 90 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qnsview_dragging.mm | 9 |
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 |