1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

Windowing: Store originator component when initiating a mouse drag

Before this change, when starting a mouse drag from a nested view such
as a webview, JUCE was unable to automatically determine which component
is associated with the drag.

Instead of relying on automatic detection, users can pass the
"sourceComponent" argument when initiating a drag to specify the parent
view that should receive associated drag events. However, previously the
sourceComponent was only used to find the view associated with the
mouse-down, but not the mouse-up. Automatic detection was always used
for the mouse-up, but this could fail in the case of a drag started from
a nested view.

Now, the drag event source will store a weak reference to the source
component provided by the user, and use the same component for both
mouse-down and mouse-up events.
This commit is contained in:
reuk 2025-01-07 19:27:27 +00:00
parent 08fcb744cc
commit 612c50f4a4
No known key found for this signature in database

View file

@ -62,73 +62,84 @@ static NSView* getNSViewForDragEvent (Component* sourceComp)
return nil; return nil;
} }
class NSDraggingSourceHelper final : public ObjCClass<NSObject<NSDraggingSource>> class NSDraggingSourceHelper final : public ObjCClass<NSObject<NSDraggingSource, NSPasteboardItemDataProvider>>
{ {
public: public:
static void setText (id self, const String& text) struct DataMembers
{ {
object_setInstanceVariable (self, "text", new String (text)); std::function<void()> callback;
} String text;
NSDragOperation operation;
Component::SafePointer<Component> originator;
};
static void setCompletionCallback (id self, std::function<void()> cb) static auto* create (DataMembers members)
{
object_setInstanceVariable (self, "callback", new std::function<void()> (cb));
}
static void setDragOperation (id self, NSDragOperation op)
{
object_setInstanceVariable (self, "operation", new NSDragOperation (op));
}
static NSDraggingSourceHelper& get()
{ {
static NSDraggingSourceHelper draggingSourceHelper; static NSDraggingSourceHelper draggingSourceHelper;
return draggingSourceHelper; auto* result = [[draggingSourceHelper.createInstance() init] autorelease];
object_setInstanceVariable (result, "members", new DataMembers (std::move (members)));
return result;
} }
private: private:
static DataMembers* getMembers (id self)
{
return getIvar<DataMembers*> (self, "members");
}
NSDraggingSourceHelper() NSDraggingSourceHelper()
: ObjCClass ("JUCENSDraggingSourceHelper_") : ObjCClass ("JUCENSDraggingSourceHelper_")
{ {
addIvar<std::function<void()>*> ("callback"); addIvar<DataMembers*> ("members");
addIvar<String*> ("text");
addIvar<NSDragOperation*> ("operation");
addMethod (@selector (dealloc), [] (id self, SEL) addMethod (@selector (dealloc), [] (id self, SEL)
{ {
delete getIvar<String*> (self, "text"); delete getMembers (self);
delete getIvar<std::function<void()>*> (self, "callback");
delete getIvar<NSDragOperation*> (self, "operation");
sendSuperclassMessage<void> (self, @selector (dealloc)); sendSuperclassMessage<void> (self, @selector (dealloc));
}); });
addMethod (@selector (pasteboard:item:provideDataForType:), [] (id self, SEL, NSPasteboard* sender, NSPasteboardItem*, NSString* type) addMethod (@selector (pasteboard:item:provideDataForType:),
[] (id self, SEL, NSPasteboard* sender, NSPasteboardItem*, NSString* type)
{ {
if ([type compare: NSPasteboardTypeString] == NSOrderedSame) if ([type compare: NSPasteboardTypeString] == NSOrderedSame)
if (auto* text = getIvar<String*> (self, "text")) if (auto* members = getMembers (self))
[sender setData: [juceStringToNS (*text) dataUsingEncoding: NSUTF8StringEncoding] [sender setData: [juceStringToNS (members->text) dataUsingEncoding: NSUTF8StringEncoding]
forType: NSPasteboardTypeString]; forType: NSPasteboardTypeString];
}); });
addMethod (@selector (draggingSession:sourceOperationMaskForDraggingContext:), [] (id self, SEL, NSDraggingSession*, NSDraggingContext) addMethod (@selector (draggingSession:sourceOperationMaskForDraggingContext:),
[] (id self, SEL, NSDraggingSession*, NSDraggingContext) -> NSDragOperation
{ {
return *getIvar<NSDragOperation*> (self, "operation"); if (auto* members = getMembers (self))
return members->operation;
return {};
}); });
addMethod (@selector (draggingSession:endedAtPoint:operation:), [] (id self, SEL, NSDraggingSession*, NSPoint p, NSDragOperation) addMethod (@selector (draggingSession:endedAtPoint:operation:),
[] (id self, SEL, NSDraggingSession*, NSPoint p, NSDragOperation)
{ {
// Our view doesn't receive a mouse up when the drag ends so we need to generate one here and send it... // Our view doesn't receive a mouse up when the drag ends so we need to generate one here and send it...
if (auto* view = getNSViewForDragEvent (nullptr)) auto* members = getMembers (self);
if (auto* cgEvent = CGEventCreateMouseEvent (nullptr, kCGEventLeftMouseUp, CGPointMake (p.x, p.y), kCGMouseButtonLeft))
if (id e = [NSEvent eventWithCGEvent: cgEvent]) if (members == nullptr)
return;
auto* cgEvent = CGEventCreateMouseEvent (nullptr,
kCGEventLeftMouseUp,
CGPointMake (p.x, p.y),
kCGMouseButtonLeft);
if (cgEvent != nullptr)
if (id e = [NSEvent eventWithCGEvent: cgEvent])
if (auto* view = getNSViewForDragEvent (members->originator))
[view mouseUp: e]; [view mouseUp: e];
if (auto* cb = getIvar<std::function<void()>*> (self, "callback")) NullCheckedInvocation::invoke (members->callback);
cb->operator()();
}); });
addProtocol (@protocol (NSPasteboardItemDataProvider)); addProtocol (@protocol (NSPasteboardItemDataProvider));
addProtocol (@protocol (NSDraggingSource));
registerClass(); registerClass();
} }
@ -146,12 +157,10 @@ bool DragAndDropContainer::performExternalDragDropOfText (const String& text, Co
{ {
if (auto event = [[view window] currentEvent]) if (auto event = [[view window] currentEvent])
{ {
id helper = [NSDraggingSourceHelper::get().createInstance() init]; auto* helper = NSDraggingSourceHelper::create ({ std::move (callback),
NSDraggingSourceHelper::setText (helper, text); text,
NSDraggingSourceHelper::setDragOperation (helper, NSDragOperationCopy); NSDragOperationCopy,
sourceComponent });
if (callback != nullptr)
NSDraggingSourceHelper::setCompletionCallback (helper, callback);
auto pasteboardItem = [[NSPasteboardItem new] autorelease]; auto pasteboardItem = [[NSPasteboardItem new] autorelease];
[pasteboardItem setDataProvider: helper [pasteboardItem setDataProvider: helper
@ -211,13 +220,10 @@ bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& fi
[dragItem release]; [dragItem release];
} }
auto helper = [NSDraggingSourceHelper::get().createInstance() autorelease]; auto* helper = NSDraggingSourceHelper::create ({ std::move (callback),
"",
if (callback != nullptr) canMoveFiles ? NSDragOperationMove : NSDragOperationCopy,
NSDraggingSourceHelper::setCompletionCallback (helper, callback); sourceComponent });
NSDraggingSourceHelper::setDragOperation (helper, canMoveFiles ? NSDragOperationMove
: NSDragOperationCopy);
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wnullable-to-nonnull-conversion") JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wnullable-to-nonnull-conversion")
return [view beginDraggingSessionWithItems: dragItems return [view beginDraggingSessionWithItems: dragItems