forked from microsoft/vcpkg
-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #46 from daschuer/macos_qt_drag_and_drop
Macos qt drag and drop fix
- Loading branch information
Showing
4 changed files
with
458 additions
and
1 deletion.
There are no files selected for viewing
278 changes: 278 additions & 0 deletions
278
overlay/osx/qt5-base/patches/QCocoaDrag-avoid-using-the-deprecated-API-if-possibl.patch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,278 @@ | ||
From c220a61c0324ef32e85523494d09edc6be54ceff Mon Sep 17 00:00:00 2001 | ||
From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= <daschuer@mixxx.org> | ||
Date: Mon, 3 Jan 2022 10:23:23 +0100 | ||
Subject: [PATCH] QCocoaDrag - avoid using the deprecated API if possible | ||
MIME-Version: 1.0 | ||
Content-Type: text/plain; charset=UTF-8 | ||
Content-Transfer-Encoding: 8bit | ||
|
||
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 | ||
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io> | ||
(cherry picked from commit 8481a9f) | ||
Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io> | ||
--- | ||
src/plugins/platforms/cocoa/qcocoadrag.h | 6 + | ||
src/plugins/platforms/cocoa/qcocoadrag.mm | 105 ++++++++++++++++-- | ||
.../platforms/cocoa/qnsview_dragging.mm | 14 ++- | ||
3 files changed, 112 insertions(+), 13 deletions(-) | ||
|
||
diff --git a/src/plugins/platforms/cocoa/qcocoadrag.h b/src/plugins/platforms/cocoa/qcocoadrag.h | ||
index 5a5b985c..975741c2 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 09433194..ab6863a4 100644 | ||
--- a/src/plugins/platforms/cocoa/qcocoadrag.mm | ||
+++ b/src/plugins/platforms/cocoa/qcocoadrag.mm | ||
@@ -44,6 +44,9 @@ | ||
#include <QtWidgets/qwidget.h> | ||
#endif | ||
#include <QtGui/private/qcoregraphics_p.h> | ||
+#include <QtCore/qsysinfo.h> | ||
+ | ||
+#include <vector> | ||
|
||
QT_BEGIN_NAMESPACE | ||
|
||
@@ -128,13 +131,15 @@ 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); | ||
- QSize pmDeviceIndependentSize = pm.size() / pm.devicePixelRatio(); | ||
- NSImage *nsimage = qt_mac_create_nsimage(pm); | ||
- [nsimage setSize:NSSizeFromCGSize(pmDeviceIndependentSize.toCGSize())]; | ||
+ NSImage *dragImage = [NSImage imageFromQImage:pm.toImage()]; | ||
+ Q_ASSERT(dragImage); | ||
|
||
- QMacPasteboard dragBoard((CFStringRef) NSDragPboard, QMacInternalPasteboardMime::MIME_DND); | ||
+ QMacPasteboard dragBoard(CFStringRef(NSDragPboard), QMacInternalPasteboardMime::MIME_DND); | ||
m_drag->mimeData()->setData(QLatin1String("application/x-qt-mime-type-name"), QByteArray("dummy")); | ||
dragBoard.setMimeData(m_drag->mimeData(), QMacPasteboard::LazyRequest); | ||
|
||
@@ -142,12 +147,12 @@ Qt::DropAction QCocoaDrag::drag(QDrag *o) | ||
NSWindow *theWindow = [m_lastEvent window]; | ||
Q_ASSERT(theWindow); | ||
event_location.x -= hotSpot.x(); | ||
- CGFloat flippedY = pmDeviceIndependentSize.height() - hotSpot.y(); | ||
+ CGFloat flippedY = dragImage.size.height - hotSpot.y(); | ||
event_location.y -= flippedY; | ||
NSSize mouseOffset_unused = NSMakeSize(0.0, 0.0); | ||
- NSPasteboard *pboard = [NSPasteboard pasteboardWithName:NSDragPboard]; | ||
+ NSPasteboard *pboard = [NSPasteboard pasteboardWithName:NSDragPboard]; | ||
|
||
- [theWindow dragImage:nsimage | ||
+ [theWindow dragImage:dragImage | ||
at:event_location | ||
offset:mouseOffset_unused | ||
event:m_lastEvent | ||
@@ -155,17 +160,99 @@ Qt::DropAction QCocoaDrag::drag(QDrag *o) | ||
source:m_lastView | ||
slideBack:YES]; | ||
|
||
- [nsimage release]; | ||
- | ||
m_drag = nullptr; | ||
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:NSDragPboard]; | ||
+ | ||
+ 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 37e972db..978d73f7 100644 | ||
--- a/src/plugins/platforms/cocoa/qnsview_dragging.mm | ||
+++ b/src/plugins/platforms/cocoa/qnsview_dragging.mm | ||
@@ -39,7 +39,7 @@ | ||
|
||
// This file is included from qnsview.mm, and only used to organize the code | ||
|
||
-@implementation QT_MANGLE_NAMESPACE(QNSView) (Dragging) | ||
+@implementation QNSView (Dragging) | ||
|
||
-(void)registerDragTypes | ||
{ | ||
@@ -150,10 +150,8 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin | ||
break; | ||
} | ||
} else { | ||
- NSImage *nsimage = qt_mac_create_nsimage(pixmapCursor); | ||
- nsimage.size = NSSizeFromCGSize((pixmapCursor.size() / pixmapCursor.devicePixelRatioF()).toCGSize()); | ||
+ auto *nsimage = [NSImage imageFromQImage:pixmapCursor.toImage()]; | ||
nativeCursor = [[NSCursor alloc] initWithImage:nsimage hotSpot:NSZeroPoint]; | ||
- [nsimage release]; | ||
} | ||
|
||
// Change the cursor | ||
@@ -234,6 +232,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; | ||
@@ -270,6 +272,7 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin | ||
// The drag was started from within the application | ||
response = QWindowSystemInterface::handleDrop(target, nativeDrag->dragMimeData(), | ||
point, qtAllowed, buttons, modifiers); | ||
+ nativeDrag->setAcceptedAction(response.acceptedAction()); | ||
} else { | ||
QCocoaDropData mimeData(sender.draggingPasteboard); | ||
response = QWindowSystemInterface::handleDrop(target, &mimeData, | ||
@@ -282,6 +285,7 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin | ||
{ | ||
Q_UNUSED(session); | ||
Q_UNUSED(screenPoint); | ||
+ Q_UNUSED(operation); | ||
|
||
if (!m_platformWindow) | ||
return; | ||
@@ -291,6 +295,8 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin | ||
return; | ||
|
||
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 | ||
-- | ||
2.25.1 | ||
|
Oops, something went wrong.