A Little Bit Confused About Drag and Drop

Jules can we please re-consider my proposal (reference counted “drag data” object with any number of user-defined representations obtained through dynamic_cast and what not)?

Every time I pick up the tip I have to waste time re-applying my patch…

It’s just too over-engineered for my liking… Can’t think of any concrete arguments against it, but implementing such a complicated class just to pass an object from one component to another feels all wrong.

I can live with a solution where the “DragAndDropData” object lives outside the juce source tree (i.e. in my own project).

But in order to do that we still need to change over from a String to a dynamically allocated object with lifetime management (ReferenceCountedObject?)…can we do that?

Jules how about a DragAndDropData object that looks like this:

//==============================================================================
/**
    Used to pass data for drag and drop operations.

    @see DragAndDropContainer
*/
class JUCE_API  DragAndDropData : public ReferenceCountedObject
{
public:
    //==============================================================================
    /** Convenience type
    */
    typedef ReferenceCountedObjectPtr<DragAndDropData> Ptr;

    /** Creates a DragAndDropData.

        The string is passed to legacy functions that don't understand this object.
    */
    DragAndDropData (const String& sourceDescription_=String::empty);

    //==============================================================================
    /** Destructor. */
    virtual ~DragAndDropData();

    //==============================================================================
    /** Returns the legacy source description for this data.
    */
    const String getSourceDescription ();

private:
    String sourceDescription;
};

This is still a huge improvement over a plain String because I could then subclass DragAndDropData to overlay my storage scheme, and use dynamic_cast to downcast the DragAndDropData provided by Juce to my subclass in functions like itemDragEnter, itemDragExit, itemDropped, etc…

fwiw, i’ve been using ValueTrees for my drag&drop data. It’d be cool if it just used them, but for the time being I can live with converting to/from strings, as I’m not doing anything complicated in the background. I have two simple functions that convert ValueTrees to/from single-line no-header XML text - this also means it’s at least possible to peek at the start of a string to see if the base tag matches before bothering to convert it. Works fine for me like this, though it would be super if the functionality were adapted to directly use ValueTrees (and they’re automatically reference counted anyway).

EDIT: Alternatively, it’d be cool if ValueTree had toXmlString/fromXmlString functions to save having to use my own :slight_smile: [just to skip the step of creating a document from a serialised XML element, and conversely parsing to XML and deserialising]. Also, perhaps a static peeking function bool xmlStringHasRootType(xmlString, typeName) would be nice :slight_smile:

The problem with String/ValueTree is that you cannot use dynamic objects (i.e. lifetime exists only for the duration of the drag).

well, if it were pure ValueTrees (without needing conversion) then it’d be a reference counted object just as you describe - and could hold any number of things (using DynamicObjects for complex data). You can’t do that with string conversion, mind, but you could if it just used a ValueTree (and it could also work just like a string for less complex cases).

This is pure nonsense - you can’t use a ValueTree in the current implementation because who would dispose the object? Or did you mean that any upgrade to drag and drop should use a ValueTree as the object being passed? I would think that is not quite as good as a generic solution (although definitely better than what we have now, since we would have a real object with lifetime management instead of a string).

A ValueTree is not the most natural expression of user-defined objects. Sure, there’s DynamicObject but it’s not as convenient as a straight class (as in my original implementation).

your face is pure nonsense! Of course I’m talking about if it were changed to use ValueTrees instead of strings [notice the words of my post, and how they actually imply this!]. :wink:

It’s quite easy to make a DynamicObject wrapper that can hold other objects so you can avoid needing to subclass anything at all. Besides, I would say that your implementation of a ‘straight class’ is not as convenient as a ValueTree because you would always need to (1) create a class, and (2) cast to the correct type on reception to transfer ANY kind of data more complex than a String; with a ValueTree, you would automatically be able to transmit complex information consisting of multiple values. For the even-more-complicated-cases like you have, you can still do that. I think making it easier for the general case is more useful.

Anyway, as I said before, I don’t actually care very much, it just makes sense to use an existing flexible structure.

Ah okay. For some reason I thought you meant converting a ValueTree into string via Xml and storing that. Which has an obvious object lifetime problem.

  1. You would still have to create the ValueTree, and 2) there is no real difference between calling a function with a string to retrieve a value (getPropertyAsValue() or getChildWithName()) versus using dynamic_cast with an identifier. But anyway, a ValueTree would still be better than what we have now because like you said, it would be trivial to stuff the ValueTree with a DynamicObject and work with that instead.

So really the only requirements for an update to Drag and Drop are:

  1. the originator (ListBox, TableListBox, or anyone who calls startDragging()) of the drop creates the reference counted object
  2. the drag and drop code passes the object around during a drag operation
  3. the drag and drop code releases its reference when the drag is complete

ValueTree fulfills these requirements

sorry, when I said “create a class” I meant actually implementing one for use. And the casting thing was related to that; knowing the specific class and casting, etc… whereas with the ValueTree you just take what you need from it - only needing to actually have to manually cast anything when dealing with the more complex cases using DynamicObject based data. For all other cases, the data would just be there for the taking.

[Not saying that casting is necessarily bad, but if you don’t need to bother doing it it’s always nicer.]

Don’t worry chaps, I’m implementing something right now. Doesn’t really resemble any of your suggestions, but should keep everyone happy…

I’m really confused…how was this possible without changing ListBox and TableListBox??

I can’t use anything that you implemented because ListBox and TableListBox still use strings:

class ListBoxModel
{
    //...
    virtual const String getDragSourceDescription (const SparseSet<int>& currentlySelectedRows);

I was just addressing the basic drag-and-drop mechanism first. Adding an extra parameter to allow ListBox to pass more complicated objects would be the next step.

phew !!! That is great news! It makes sense…I was thinking the worst, that you were just torturing me, or trying to make me insanely jealous towards users of the TreeView.

Y’know, I’ve just had a bit of an idea.

I’ve never used a ReferenceCountedObjectPtr before, but it’s actually a great base class for allowing user objects to be passed around… Now, if I was to change the ‘var’ class so that instead of a DynamicObject, it actually held a ReferenceCountedObject (which is the base class for DynamicObject anyway, so existing code could all still be made to work), then a var could be used to hold all kinds of custom objects.

That’d be a pretty useful thing in general, but in this case I could just replace the string drag descriptor with a var, and not complicate it by having a string + custom data object.

I certainly won’t complain, and it does make sense but if you do that then the entire installed base of Juce users, who up until now have been mostly satisfied with being able to use a simple String for drag and drop, will suddenly find it necessary to create objects and downcast them - when before their code was quite simple.

In my private patch to drag and drop I preserved compatibility with old code by having two sets of routines, and calling the old one with the string:

class JUCE_API  DragAndDropTarget
{
public:
    virtual bool isInterestedInDragSource (DragAndDropData::Ptr dragData,
                                                             Component* sourceComponent)
    {
       return isInterestedInDragSource (dragData->getSourceDescription(), sourceComponent);
    }

    // LEGACY
    virtual bool isInterestedInDragSource (const String& sourceDescription,
                                           Component* sourceComponent) { }
};

DragAndDropData is a ReferenceCountedObject that contains the String for the old API and also that array of abstracted templates that you are not fond of.

So basically the same as my last proposal:

    class JUCE_API  DragAndDropData : public ReferenceCountedObject
    {
    public:
        typedef ReferenceCountedObjectPtr<DragAndDropData> Ptr;
        DragAndDropData (const String& sourceDescription_=String::empty);
        virtual ~DragAndDropData();
        const String getSourceDescription ();
    private:
        String sourceDescription;
    };

Hmm…or maybe I am not fully knowledgable on a ‘var’ type…is changing from String to var a trivial task?

…no, I think you’ve missed the point. If I replace a String with a var, then it’ll still function as a string, and people’s existing code will still work. It’ll just mean that you have also the option to use it as an object if you want to.