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:
parent
08fcb744cc
commit
612c50f4a4
1 changed files with 53 additions and 47 deletions
|
|
@ -62,73 +62,84 @@ static NSView* getNSViewForDragEvent (Component* sourceComp)
|
|||
return nil;
|
||||
}
|
||||
|
||||
class NSDraggingSourceHelper final : public ObjCClass<NSObject<NSDraggingSource>>
|
||||
class NSDraggingSourceHelper final : public ObjCClass<NSObject<NSDraggingSource, NSPasteboardItemDataProvider>>
|
||||
{
|
||||
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)
|
||||
{
|
||||
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 auto* create (DataMembers members)
|
||||
{
|
||||
static NSDraggingSourceHelper draggingSourceHelper;
|
||||
return draggingSourceHelper;
|
||||
auto* result = [[draggingSourceHelper.createInstance() init] autorelease];
|
||||
object_setInstanceVariable (result, "members", new DataMembers (std::move (members)));
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
static DataMembers* getMembers (id self)
|
||||
{
|
||||
return getIvar<DataMembers*> (self, "members");
|
||||
}
|
||||
|
||||
NSDraggingSourceHelper()
|
||||
: ObjCClass ("JUCENSDraggingSourceHelper_")
|
||||
{
|
||||
addIvar<std::function<void()>*> ("callback");
|
||||
addIvar<String*> ("text");
|
||||
addIvar<NSDragOperation*> ("operation");
|
||||
addIvar<DataMembers*> ("members");
|
||||
|
||||
addMethod (@selector (dealloc), [] (id self, SEL)
|
||||
{
|
||||
delete getIvar<String*> (self, "text");
|
||||
delete getIvar<std::function<void()>*> (self, "callback");
|
||||
delete getIvar<NSDragOperation*> (self, "operation");
|
||||
|
||||
delete getMembers (self);
|
||||
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 (auto* text = getIvar<String*> (self, "text"))
|
||||
[sender setData: [juceStringToNS (*text) dataUsingEncoding: NSUTF8StringEncoding]
|
||||
if (auto* members = getMembers (self))
|
||||
[sender setData: [juceStringToNS (members->text) dataUsingEncoding: NSUTF8StringEncoding]
|
||||
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...
|
||||
if (auto* view = getNSViewForDragEvent (nullptr))
|
||||
if (auto* cgEvent = CGEventCreateMouseEvent (nullptr, kCGEventLeftMouseUp, CGPointMake (p.x, p.y), kCGMouseButtonLeft))
|
||||
if (id e = [NSEvent eventWithCGEvent: cgEvent])
|
||||
auto* members = getMembers (self);
|
||||
|
||||
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];
|
||||
|
||||
if (auto* cb = getIvar<std::function<void()>*> (self, "callback"))
|
||||
cb->operator()();
|
||||
NullCheckedInvocation::invoke (members->callback);
|
||||
});
|
||||
|
||||
addProtocol (@protocol (NSPasteboardItemDataProvider));
|
||||
addProtocol (@protocol (NSDraggingSource));
|
||||
|
||||
registerClass();
|
||||
}
|
||||
|
|
@ -146,12 +157,10 @@ bool DragAndDropContainer::performExternalDragDropOfText (const String& text, Co
|
|||
{
|
||||
if (auto event = [[view window] currentEvent])
|
||||
{
|
||||
id helper = [NSDraggingSourceHelper::get().createInstance() init];
|
||||
NSDraggingSourceHelper::setText (helper, text);
|
||||
NSDraggingSourceHelper::setDragOperation (helper, NSDragOperationCopy);
|
||||
|
||||
if (callback != nullptr)
|
||||
NSDraggingSourceHelper::setCompletionCallback (helper, callback);
|
||||
auto* helper = NSDraggingSourceHelper::create ({ std::move (callback),
|
||||
text,
|
||||
NSDragOperationCopy,
|
||||
sourceComponent });
|
||||
|
||||
auto pasteboardItem = [[NSPasteboardItem new] autorelease];
|
||||
[pasteboardItem setDataProvider: helper
|
||||
|
|
@ -211,13 +220,10 @@ bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& fi
|
|||
[dragItem release];
|
||||
}
|
||||
|
||||
auto helper = [NSDraggingSourceHelper::get().createInstance() autorelease];
|
||||
|
||||
if (callback != nullptr)
|
||||
NSDraggingSourceHelper::setCompletionCallback (helper, callback);
|
||||
|
||||
NSDraggingSourceHelper::setDragOperation (helper, canMoveFiles ? NSDragOperationMove
|
||||
: NSDragOperationCopy);
|
||||
auto* helper = NSDraggingSourceHelper::create ({ std::move (callback),
|
||||
"",
|
||||
canMoveFiles ? NSDragOperationMove : NSDragOperationCopy,
|
||||
sourceComponent });
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wnullable-to-nonnull-conversion")
|
||||
return [view beginDraggingSessionWithItems: dragItems
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue