mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
Made the DragAndDropContainer::performExternalDragDropOfFiles() and ::performExternalDragDropOfText() methods asynchronous on Windows so that behaviour is consistent across all platforms and updated the documentation to reflect this
This commit is contained in:
parent
b6c615e6c4
commit
4280b51d09
8 changed files with 207 additions and 81 deletions
|
|
@ -6,7 +6,29 @@ Develop
|
||||||
|
|
||||||
Change
|
Change
|
||||||
------
|
------
|
||||||
AudioProcessor::getTailLengthSeconds can now return infinity for VST/VST3/AU/AUv3
|
DragAndDropContainer::performExternalDragDropOfFiles() and ::performExternalDragDropOfText()
|
||||||
|
are now asynchronous on Windows.
|
||||||
|
|
||||||
|
Possible Issues
|
||||||
|
---------------
|
||||||
|
Code that previously relied on these operations being synchronous and blocking until
|
||||||
|
completion will no longer work as the methods will return immediately and run
|
||||||
|
asynchronously.
|
||||||
|
|
||||||
|
Workaround
|
||||||
|
----------
|
||||||
|
Use the callback argument that has been added to these methods to register a lambda
|
||||||
|
that will be called when the operation has been completed.
|
||||||
|
|
||||||
|
Rationale
|
||||||
|
---------
|
||||||
|
The behaviour of these methods is now consistent across all platforms and the method
|
||||||
|
no longer blocks the message thread on Windows.
|
||||||
|
|
||||||
|
|
||||||
|
Change
|
||||||
|
------
|
||||||
|
AudioProcessor::getTailLengthSeconds can now return infinity for VST/VST3/AU/AUv3.
|
||||||
|
|
||||||
Possible Issues
|
Possible Issues
|
||||||
---------------
|
---------------
|
||||||
|
|
@ -20,7 +42,6 @@ a buffer.
|
||||||
|
|
||||||
Rationale
|
Rationale
|
||||||
---------
|
---------
|
||||||
|
|
||||||
Before this change there was no way for a JUCE plug-in to report an infinite tail time.
|
Before this change there was no way for a JUCE plug-in to report an infinite tail time.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -153,42 +153,46 @@ public:
|
||||||
|
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
/** This performs a synchronous drag-and-drop of a set of files to some external
|
/** This performs an asynchronous drag-and-drop of a set of files to some external
|
||||||
application.
|
application.
|
||||||
|
|
||||||
You can call this function in response to a mouseDrag callback, and it will
|
You can call this function in response to a mouseDrag callback, and it will
|
||||||
block, running its own internal message loop and tracking the mouse, while it
|
use a native operating system drag-and-drop operation to move or copy some
|
||||||
uses a native operating system drag-and-drop operation to move or copy some
|
|
||||||
files to another application.
|
files to another application.
|
||||||
|
|
||||||
@param files a list of filenames to drag
|
@param files a list of filenames to drag
|
||||||
@param canMoveFiles if true, the app that receives the files is allowed to move the files to a new location
|
@param canMoveFiles if true, the app that receives the files is allowed to move the files to a new location
|
||||||
(if this is appropriate). If false, the receiver is expected to make a copy of them.
|
(if this is appropriate). If false, the receiver is expected to make a copy of them.
|
||||||
@param sourceComponent Normally, JUCE will assume that the component under the mouse is the source component
|
@param sourceComponent normally, JUCE will assume that the component under the mouse is the source component
|
||||||
of the drag, but you can use this parameter to override this.
|
of the drag, but you can use this parameter to override this.
|
||||||
@returns true if the files were successfully dropped somewhere, or false if it
|
@param callback an optional completion callback that will be called when the operation has ended.
|
||||||
was interrupted
|
|
||||||
|
@returns true if the drag operation was successfully started, or false if it failed for some reason
|
||||||
|
|
||||||
@see performExternalDragDropOfText
|
@see performExternalDragDropOfText
|
||||||
*/
|
*/
|
||||||
static bool performExternalDragDropOfFiles (const StringArray& files, bool canMoveFiles,
|
static bool performExternalDragDropOfFiles (const StringArray& files, bool canMoveFiles,
|
||||||
Component* sourceComponent = nullptr);
|
Component* sourceComponent = nullptr,
|
||||||
|
std::function<void()> callback = nullptr);
|
||||||
|
|
||||||
/** This performs a synchronous drag-and-drop of a block of text to some external
|
/** This performs an asynchronous drag-and-drop of a block of text to some external
|
||||||
application.
|
application.
|
||||||
|
|
||||||
You can call this function in response to a mouseDrag callback, and it will
|
You can call this function in response to a mouseDrag callback, and it will
|
||||||
block, running its own internal message loop and tracking the mouse, while it
|
use a native operating system drag-and-drop operation to move or copy some
|
||||||
uses a native operating system drag-and-drop operation to move or copy some
|
|
||||||
text to another application.
|
text to another application.
|
||||||
|
|
||||||
@param text the text to copy
|
@param text the text to copy
|
||||||
@param sourceComponent Normally, JUCE will assume that the component under the mouse is the source component
|
@param sourceComponent Normally, JUCE will assume that the component under the mouse is the source component
|
||||||
of the drag, but you can use this parameter to override this.
|
of the drag, but you can use this parameter to override this.
|
||||||
@returns true if the text was successfully dropped somewhere, or false if it
|
@param callback an optional completion callback that will be called when the operation has ended.
|
||||||
was interrupted
|
|
||||||
|
@returns true if the drag operation was successfully started, or false if it failed for some reason
|
||||||
|
|
||||||
@see performExternalDragDropOfFiles
|
@see performExternalDragDropOfFiles
|
||||||
*/
|
*/
|
||||||
static bool performExternalDragDropOfText (const String& text, Component* sourceComponent = nullptr);
|
static bool performExternalDragDropOfText (const String& text, Component* sourceComponent = nullptr,
|
||||||
|
std::function<void()> callback = nullptr);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/** Override this if you want to be able to perform an external drag of a set of files
|
/** Override this if you want to be able to perform an external drag of a set of files
|
||||||
|
|
|
||||||
|
|
@ -50,9 +50,7 @@ public:
|
||||||
virtual ~DragAndDropTarget() {}
|
virtual ~DragAndDropTarget() {}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
/** Contains details about the source of a drag-and-drop operation.
|
/** Contains details about the source of a drag-and-drop operation. */
|
||||||
The contents of this
|
|
||||||
*/
|
|
||||||
class JUCE_API SourceDetails
|
class JUCE_API SourceDetails
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
|
||||||
|
|
@ -1077,13 +1077,16 @@ void MouseCursor::showInAllWindows() const {}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& /*files*/, const bool /*canMove*/,
|
bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& /*files*/, const bool /*canMove*/,
|
||||||
Component* /*srcComp*/)
|
Component* /*srcComp*/, std::function<void()> /*callback*/)
|
||||||
{
|
{
|
||||||
|
jassertfalse; // no such thing on Android!
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DragAndDropContainer::performExternalDragDropOfText (const String& /*text*/, Component* /*srcComp*/)
|
bool DragAndDropContainer::performExternalDragDropOfText (const String& /*text*/, Component* /*srcComp*/,
|
||||||
|
std::function<void()> /*callback*/)
|
||||||
{
|
{
|
||||||
|
jassertfalse; // no such thing on Android!
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -640,13 +640,13 @@ int JUCE_CALLTYPE NativeMessageBox::showYesNoBox (AlertWindow::AlertIconType /*i
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray&, bool, Component*)
|
bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray&, bool, Component*, std::function<void()>)
|
||||||
{
|
{
|
||||||
jassertfalse; // no such thing on iOS!
|
jassertfalse; // no such thing on iOS!
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DragAndDropContainer::performExternalDragDropOfText (const String&, Component*)
|
bool DragAndDropContainer::performExternalDragDropOfText (const String&, Component*, std::function<void()>)
|
||||||
{
|
{
|
||||||
jassertfalse; // no such thing on iOS!
|
jassertfalse; // no such thing on iOS!
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
|
|
@ -2471,15 +2471,15 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool externalDragTextInit (const String& text)
|
bool externalDragTextInit (const String& text, std::function<void()> cb)
|
||||||
{
|
{
|
||||||
if (dragState->dragging)
|
if (dragState->dragging)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return externalDragInit (true, text);
|
return externalDragInit (true, text, cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool externalDragFileInit (const StringArray& files, bool /*canMoveFiles*/)
|
bool externalDragFileInit (const StringArray& files, bool /*canMoveFiles*/, std::function<void()> cb)
|
||||||
{
|
{
|
||||||
if (dragState->dragging)
|
if (dragState->dragging)
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -2494,7 +2494,7 @@ public:
|
||||||
uriList.add ("file://" + f);
|
uriList.add ("file://" + f);
|
||||||
}
|
}
|
||||||
|
|
||||||
return externalDragInit (false, uriList.joinIntoString ("\r\n"));
|
return externalDragInit (false, uriList.joinIntoString ("\r\n"), cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
@ -3183,6 +3183,7 @@ private:
|
||||||
Rectangle<int> silentRect;
|
Rectangle<int> silentRect;
|
||||||
String textOrFiles;
|
String textOrFiles;
|
||||||
Array<Atom> allowedTypes;
|
Array<Atom> allowedTypes;
|
||||||
|
std::function<void()> completionCallback;
|
||||||
};
|
};
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
@ -3624,7 +3625,7 @@ private:
|
||||||
return externalFindDragTargetWindow (child);
|
return externalFindDragTargetWindow (child);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool externalDragInit (bool isText, const String& textOrFiles)
|
bool externalDragInit (bool isText, const String& textOrFiles, std::function<void()> cb)
|
||||||
{
|
{
|
||||||
ScopedXLock xlock (display);
|
ScopedXLock xlock (display);
|
||||||
|
|
||||||
|
|
@ -3632,6 +3633,7 @@ private:
|
||||||
dragState->isText = isText;
|
dragState->isText = isText;
|
||||||
dragState->textOrFiles = textOrFiles;
|
dragState->textOrFiles = textOrFiles;
|
||||||
dragState->targetWindow = windowH;
|
dragState->targetWindow = windowH;
|
||||||
|
dragState->completionCallback = cb;
|
||||||
|
|
||||||
const int pointerGrabMask = Button1MotionMask | ButtonReleaseMask;
|
const int pointerGrabMask = Button1MotionMask | ButtonReleaseMask;
|
||||||
|
|
||||||
|
|
@ -3664,6 +3666,9 @@ private:
|
||||||
XUngrabPointer (display, CurrentTime);
|
XUngrabPointer (display, CurrentTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (dragState->completionCallback != nullptr)
|
||||||
|
dragState->completionCallback();
|
||||||
|
|
||||||
resetExternalDragState();
|
resetExternalDragState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -4262,26 +4267,27 @@ static LinuxComponentPeer* getPeerForDragEvent (Component* sourceComp)
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, bool canMoveFiles,
|
bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, bool canMoveFiles,
|
||||||
Component* sourceComp)
|
Component* sourceComp, std::function<void()> callback)
|
||||||
{
|
{
|
||||||
if (files.isEmpty())
|
if (files.isEmpty())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (auto* lp = getPeerForDragEvent (sourceComp))
|
if (auto* lp = getPeerForDragEvent (sourceComp))
|
||||||
return lp->externalDragFileInit (files, canMoveFiles);
|
return lp->externalDragFileInit (files, canMoveFiles, callback);
|
||||||
|
|
||||||
// This method must be called in response to a component's mouseDown or mouseDrag event!
|
// This method must be called in response to a component's mouseDown or mouseDrag event!
|
||||||
jassertfalse;
|
jassertfalse;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DragAndDropContainer::performExternalDragDropOfText (const String& text, Component* sourceComp)
|
bool DragAndDropContainer::performExternalDragDropOfText (const String& text, Component* sourceComp,
|
||||||
|
std::function<void()> callback)
|
||||||
{
|
{
|
||||||
if (text.isEmpty())
|
if (text.isEmpty())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (auto* lp = getPeerForDragEvent (sourceComp))
|
if (auto* lp = getPeerForDragEvent (sourceComp))
|
||||||
return lp->externalDragTextInit (text);
|
return lp->externalDragTextInit (text, callback);
|
||||||
|
|
||||||
// This method must be called in response to a component's mouseDown or mouseDrag event!
|
// This method must be called in response to a component's mouseDown or mouseDrag event!
|
||||||
jassertfalse;
|
jassertfalse;
|
||||||
|
|
|
||||||
|
|
@ -177,14 +177,22 @@ static NSView* getNSViewForDragEvent (Component* sourceComp)
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TextDragDataProviderClass : public ObjCClass<NSObject>
|
struct NSDraggingSourceHelper : public ObjCClass<NSObject<NSDraggingSource>>
|
||||||
{
|
{
|
||||||
TextDragDataProviderClass() : ObjCClass<NSObject> ("JUCE_NSTextDragDataProvider_")
|
NSDraggingSourceHelper() : ObjCClass<NSObject<NSDraggingSource>> ("JUCENSDraggingSourceHelper_")
|
||||||
{
|
{
|
||||||
|
addIvar<std::function<void()>*> ("callback");
|
||||||
addIvar<String*> ("text");
|
addIvar<String*> ("text");
|
||||||
|
addIvar<NSDragOperation*> ("operation");
|
||||||
|
|
||||||
addMethod (@selector (dealloc), dealloc, "v@:");
|
addMethod (@selector (dealloc), dealloc, "v@:");
|
||||||
addMethod (@selector (pasteboard:item:provideDataForType:), provideDataForType, "v@:@@@");
|
addMethod (@selector (pasteboard:item:provideDataForType:), provideDataForType, "v@:@@@");
|
||||||
|
|
||||||
|
addMethod (@selector (draggingSession:sourceOperationMaskForDraggingContext:), sourceOperationMaskForDraggingContext, "c@:@@");
|
||||||
|
addMethod (@selector (draggingSession:endedAtPoint:operation:), draggingSessionEnded, "v@:@@@");
|
||||||
|
|
||||||
addProtocol (@protocol (NSPasteboardItemDataProvider));
|
addProtocol (@protocol (NSPasteboardItemDataProvider));
|
||||||
|
|
||||||
registerClass();
|
registerClass();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -193,10 +201,23 @@ struct TextDragDataProviderClass : public ObjCClass<NSObject>
|
||||||
object_setInstanceVariable (self, "text", new String (text));
|
object_setInstanceVariable (self, "text", new String (text));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void dealloc (id self, SEL)
|
static void dealloc (id self, SEL)
|
||||||
{
|
{
|
||||||
delete getIvar<String*> (self, "text");
|
delete getIvar<String*> (self, "text");
|
||||||
|
delete getIvar<std::function<void()>*> (self, "callback");
|
||||||
|
delete getIvar<NSDragOperation*> (self, "operation");
|
||||||
|
|
||||||
sendSuperclassMessage (self, @selector (dealloc));
|
sendSuperclassMessage (self, @selector (dealloc));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -207,9 +228,23 @@ private:
|
||||||
[sender setData: [juceStringToNS (*text) dataUsingEncoding: NSUTF8StringEncoding]
|
[sender setData: [juceStringToNS (*text) dataUsingEncoding: NSUTF8StringEncoding]
|
||||||
forType: NSPasteboardTypeString];
|
forType: NSPasteboardTypeString];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static NSDragOperation sourceOperationMaskForDraggingContext (id self, SEL, NSDraggingSession*, NSDraggingContext)
|
||||||
|
{
|
||||||
|
return *getIvar<NSDragOperation*> (self, "operation");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void draggingSessionEnded (id self, SEL, NSDraggingSession*, NSPoint, NSDragOperation)
|
||||||
|
{
|
||||||
|
if (auto* cb = getIvar<std::function<void()>*> (self, "callback"))
|
||||||
|
cb->operator()();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
bool DragAndDropContainer::performExternalDragDropOfText (const String& text, Component* sourceComponent)
|
static NSDraggingSourceHelper draggingSourceHelper;
|
||||||
|
|
||||||
|
bool DragAndDropContainer::performExternalDragDropOfText (const String& text, Component* sourceComponent,
|
||||||
|
std::function<void()> callback)
|
||||||
{
|
{
|
||||||
if (text.isEmpty())
|
if (text.isEmpty())
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -220,12 +255,15 @@ bool DragAndDropContainer::performExternalDragDropOfText (const String& text, Co
|
||||||
{
|
{
|
||||||
if (auto* event = [[view window] currentEvent])
|
if (auto* event = [[view window] currentEvent])
|
||||||
{
|
{
|
||||||
static TextDragDataProviderClass dataProviderClass;
|
id helper = [draggingSourceHelper.createInstance() init];
|
||||||
id delegate = [dataProviderClass.createInstance() init];
|
NSDraggingSourceHelper::setText (helper, text);
|
||||||
TextDragDataProviderClass::setText (delegate, text);
|
NSDraggingSourceHelper::setDragOperation (helper, NSDragOperationCopy);
|
||||||
|
|
||||||
|
if (callback != nullptr)
|
||||||
|
NSDraggingSourceHelper::setCompletionCallback (helper, callback);
|
||||||
|
|
||||||
auto* pasteboardItem = [[NSPasteboardItem new] autorelease];
|
auto* pasteboardItem = [[NSPasteboardItem new] autorelease];
|
||||||
[pasteboardItem setDataProvider: delegate
|
[pasteboardItem setDataProvider: helper
|
||||||
forTypes: [NSArray arrayWithObjects: NSPasteboardTypeString, nil]];
|
forTypes: [NSArray arrayWithObjects: NSPasteboardTypeString, nil]];
|
||||||
|
|
||||||
auto* dragItem = [[[NSDraggingItem alloc] initWithPasteboardWriter: pasteboardItem] autorelease];
|
auto* dragItem = [[[NSDraggingItem alloc] initWithPasteboardWriter: pasteboardItem] autorelease];
|
||||||
|
|
@ -233,13 +271,15 @@ bool DragAndDropContainer::performExternalDragDropOfText (const String& text, Co
|
||||||
NSImage* image = [[NSWorkspace sharedWorkspace] iconForFile: nsEmptyString()];
|
NSImage* image = [[NSWorkspace sharedWorkspace] iconForFile: nsEmptyString()];
|
||||||
[dragItem setDraggingFrame: getDragRect (view, event) contents: image];
|
[dragItem setDraggingFrame: getDragRect (view, event) contents: image];
|
||||||
|
|
||||||
auto* draggingSession = [view beginDraggingSessionWithItems: [NSArray arrayWithObject: dragItem]
|
if (auto* session = [view beginDraggingSessionWithItems: [NSArray arrayWithObject: dragItem]
|
||||||
event: event
|
event: event
|
||||||
source: delegate];
|
source: helper])
|
||||||
|
{
|
||||||
|
session.animatesToStartingPositionsOnCancelOrFail = YES;
|
||||||
|
session.draggingFormation = NSDraggingFormationNone;
|
||||||
|
|
||||||
draggingSession.animatesToStartingPositionsOnCancelOrFail = YES;
|
return true;
|
||||||
draggingSession.draggingFormation = NSDraggingFormationNone;
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -247,24 +287,8 @@ bool DragAndDropContainer::performExternalDragDropOfText (const String& text, Co
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct NSDraggingSourceHelper : public ObjCClass<NSObject<NSDraggingSource>>
|
bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, bool canMoveFiles,
|
||||||
{
|
Component* sourceComponent, std::function<void()> callback)
|
||||||
NSDraggingSourceHelper() : ObjCClass<NSObject<NSDraggingSource>> ("JUCENSDraggingSourceHelper_")
|
|
||||||
{
|
|
||||||
addMethod (@selector (draggingSession:sourceOperationMaskForDraggingContext:), sourceOperationMaskForDraggingContext, "c@:@@");
|
|
||||||
registerClass();
|
|
||||||
}
|
|
||||||
|
|
||||||
static NSDragOperation sourceOperationMaskForDraggingContext (id, SEL, NSDraggingSession*, NSDraggingContext)
|
|
||||||
{
|
|
||||||
return NSDragOperationCopy;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static NSDraggingSourceHelper draggingSourceHelper;
|
|
||||||
|
|
||||||
bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, bool /*canMoveFiles*/,
|
|
||||||
Component* sourceComponent)
|
|
||||||
{
|
{
|
||||||
if (files.isEmpty())
|
if (files.isEmpty())
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -296,9 +320,16 @@ bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& fi
|
||||||
|
|
||||||
auto* helper = [draggingSourceHelper.createInstance() autorelease];
|
auto* helper = [draggingSourceHelper.createInstance() autorelease];
|
||||||
|
|
||||||
return [view beginDraggingSessionWithItems: dragItems
|
if (callback != nullptr)
|
||||||
event: event
|
NSDraggingSourceHelper::setCompletionCallback (helper, callback);
|
||||||
source: helper];
|
|
||||||
|
NSDraggingSourceHelper::setDragOperation (helper, canMoveFiles ? NSDragOperationMove
|
||||||
|
: NSDragOperationCopy);
|
||||||
|
|
||||||
|
if (auto* session = [view beginDraggingSessionWithItems: dragItems
|
||||||
|
event: event
|
||||||
|
source: helper])
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -241,49 +241,112 @@ namespace DragAndDropHelpers
|
||||||
return hDrop;
|
return hDrop;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool performDragDrop (FORMATETC* const format, STGMEDIUM* const medium, const DWORD whatToDo)
|
struct DragAndDropJob : public ThreadPoolJob
|
||||||
{
|
{
|
||||||
auto source = new JuceDropSource();
|
DragAndDropJob (FORMATETC f, STGMEDIUM m, DWORD d, std::function<void()> cb)
|
||||||
auto data = new JuceDataObject (source, format, medium);
|
: ThreadPoolJob ("DragAndDrop"),
|
||||||
|
format (f), medium (m), whatToDo (d),
|
||||||
|
completionCallback (cb)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
DWORD effect;
|
JobStatus runJob() override
|
||||||
auto res = DoDragDrop (data, source, whatToDo, &effect);
|
{
|
||||||
|
OleInitialize (0);
|
||||||
|
|
||||||
data->Release();
|
auto source = new JuceDropSource();
|
||||||
source->Release();
|
auto data = new JuceDataObject (source, &format, &medium);
|
||||||
|
|
||||||
return res == DRAGDROP_S_DROP;
|
DWORD effect;
|
||||||
}
|
DoDragDrop (data, source, whatToDo, &effect);
|
||||||
|
|
||||||
|
data->Release();
|
||||||
|
source->Release();
|
||||||
|
|
||||||
|
if (completionCallback != nullptr)
|
||||||
|
MessageManager::callAsync (completionCallback);
|
||||||
|
|
||||||
|
return jobHasFinished;
|
||||||
|
}
|
||||||
|
|
||||||
|
FORMATETC format;
|
||||||
|
STGMEDIUM medium;
|
||||||
|
DWORD whatToDo;
|
||||||
|
|
||||||
|
std::function<void()> completionCallback;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ThreadPoolHolder : private DeletedAtShutdown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ThreadPoolHolder() = default;
|
||||||
|
|
||||||
|
~ThreadPoolHolder()
|
||||||
|
{
|
||||||
|
// Wait forever if there's a job running. The user needs to cancel the transfer
|
||||||
|
// in the GUI.
|
||||||
|
pool.removeAllJobs (true, -1);
|
||||||
|
|
||||||
|
clearSingletonInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
juce_DeclareSingleton_SingleThreaded (ThreadPoolHolder, true)
|
||||||
|
|
||||||
|
// We need to make sure we don't do simultaneous text and file drag and drops,
|
||||||
|
// so use a pool that can only run a single job.
|
||||||
|
ThreadPool pool { 1 };
|
||||||
|
};
|
||||||
|
|
||||||
|
juce_ImplementSingleton_SingleThreaded (ThreadPoolHolder)
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, const bool canMove, Component*)
|
bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, const bool canMove,
|
||||||
|
Component*, std::function<void()> callback)
|
||||||
{
|
{
|
||||||
|
if (files.isEmpty())
|
||||||
|
return false;
|
||||||
|
|
||||||
FORMATETC format = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
|
FORMATETC format = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
|
||||||
STGMEDIUM medium = { TYMED_HGLOBAL, { 0 }, 0 };
|
STGMEDIUM medium = { TYMED_HGLOBAL, { 0 }, 0 };
|
||||||
|
|
||||||
medium.hGlobal = DragAndDropHelpers::createHDrop (files);
|
medium.hGlobal = DragAndDropHelpers::createHDrop (files);
|
||||||
|
|
||||||
return DragAndDropHelpers::performDragDrop (&format, &medium, canMove ? (DWORD) (DROPEFFECT_COPY | DROPEFFECT_MOVE)
|
auto& pool = DragAndDropHelpers::ThreadPoolHolder::getInstance()->pool;
|
||||||
: (DWORD) DROPEFFECT_COPY);
|
pool.addJob (new DragAndDropHelpers::DragAndDropJob (format, medium,
|
||||||
|
canMove ? (DROPEFFECT_COPY | DROPEFFECT_MOVE) : DROPEFFECT_COPY,
|
||||||
|
callback),
|
||||||
|
true);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DragAndDropContainer::performExternalDragDropOfText (const String& text, Component*)
|
bool DragAndDropContainer::performExternalDragDropOfText (const String& text, Component*, std::function<void()> callback)
|
||||||
{
|
{
|
||||||
|
if (text.isEmpty())
|
||||||
|
return false;
|
||||||
|
|
||||||
FORMATETC format = { CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
|
FORMATETC format = { CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
|
||||||
STGMEDIUM medium = { TYMED_HGLOBAL, { 0 }, 0 };
|
STGMEDIUM medium = { TYMED_HGLOBAL, { 0 }, 0 };
|
||||||
|
|
||||||
auto numBytes = CharPointer_UTF16::getBytesRequiredFor (text.getCharPointer());
|
auto numBytes = CharPointer_UTF16::getBytesRequiredFor (text.getCharPointer());
|
||||||
|
|
||||||
medium.hGlobal = GlobalAlloc (GMEM_MOVEABLE | GMEM_ZEROINIT, numBytes + 2);
|
medium.hGlobal = GlobalAlloc (GMEM_MOVEABLE | GMEM_ZEROINIT, numBytes + 2);
|
||||||
WCHAR* const data = static_cast<WCHAR*> (GlobalLock (medium.hGlobal));
|
auto* data = static_cast<WCHAR*> (GlobalLock (medium.hGlobal));
|
||||||
|
|
||||||
text.copyToUTF16 (data, numBytes);
|
text.copyToUTF16 (data, numBytes + 2);
|
||||||
format.cfFormat = CF_UNICODETEXT;
|
format.cfFormat = CF_UNICODETEXT;
|
||||||
|
|
||||||
GlobalUnlock (medium.hGlobal);
|
GlobalUnlock (medium.hGlobal);
|
||||||
|
|
||||||
return DragAndDropHelpers::performDragDrop (&format, &medium, DROPEFFECT_COPY | DROPEFFECT_MOVE);
|
auto& pool = DragAndDropHelpers::ThreadPoolHolder::getInstance()->pool;
|
||||||
|
pool.addJob (new DragAndDropHelpers::DragAndDropJob (format,
|
||||||
|
medium,
|
||||||
|
DROPEFFECT_COPY | DROPEFFECT_MOVE,
|
||||||
|
callback),
|
||||||
|
true);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace juce
|
} // namespace juce
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue