Problem with drag and drop from a ListBox

I have a listbox which uses drag and drop. If I have multiple instances of my plugin, it is possible to drag from one instance to the other. There are two problems.

  1. If instance A is covering instance B, and instance A is top most, juce will pick instance B as the drop destination depending on the order of windows. It should pick the instance that is on top, but it starts from the back of the list, apparently. See findDesktopComponentBelow() in juce_DragAndDropContainer.cpp

  2. There is no way to turn off external drag and drop in the listbox, as it defaults to true. See line 129 of juce_Listbox.cpp.

  3. There does not appear to be a way to add this functionality in a sub-class, so for now, I have just edited the Juce code with an optional flag to allow external dragging from list boxes.

-Joseph

I’ve just pushed a fix for 1.

https://github.com/WeAreROLI/JUCE/commit/9dde83e662ded2bf7c727e30886efc0d7b6a343b

Will have a look at the others tomorrow.

Awesome. Thank you t0m. (=

In your DragAndDropTarget can you just do

bool isInterestedInDragSource (const SourceDetails& details) override
{
    return details.sourceComponent.get() == &yourListBox;
}

?

That was the first thing I tried. Unfortunately, in that case, if they are overlapping, then it won’t allow you to drop on the area where the two instances intersect.

If the plug-in windows are overlapping? I can’t reproduce that in a simple test case.

I wasn’t able to at first too. My plugin window has a component that covers the entire plugin window and is a draganddroptarget and also a draganddropcontainer. The listbox is inside this window.

I was using this code to see if its the same instance.

bool isInterestedInDragSource(const SourceDetails &dragSourceDetails)
{
Component *topForSource = dragSourceDetails.sourceComponent->getTopLevelComponent();
Component *topForCur = getTopLevelComponent();
if ( topForCur == topForSource )
{
return true;
}
}

  1. I place the first instance on the top left and then the second instance on the bottom right (overlapping)
  2. Drag from the 1st instance on the left and drag to the right to the intersecting area and attempt to drop

I can shoot a video if you still can’t repro. Thanks again!

A video would be helpful and/or some source code. I’m using the following which must be pretty similar to what you’re describing:

#pragma once

#include "../JuceLibraryCode/JuceHeader.h"
#include "PluginProcessor.h"

class DragAndDropTestAudioProcessorEditor  : public AudioProcessorEditor,
                                             public DragAndDropContainer,
                                             public DragAndDropTarget
{
public:
    DragAndDropTestAudioProcessorEditor (DragAndDropTestAudioProcessor& p)
        : AudioProcessorEditor (&p), processor (p)
    {
        lb.setRowHeight (30);
        addAndMakeVisible (lb);
        addChildComponent (&text);

        setSize (400, 300);
    }

    ~DragAndDropTestAudioProcessorEditor() {}

    bool isInterestedInDragSource (const SourceDetails& dragSourceDetails) override
    {
        if (auto* source = dragSourceDetails.sourceComponent.get())
            return getTopLevelComponent() == source->getTopLevelComponent();

        return false;
    }

    void itemDropped (const SourceDetails& dragSourceDetails) override
    {
        text.setVisible (true);
    }

    void paint (Graphics& g) override
    {
        g.fillAll (getLookAndFeel().findColour (ResizableWindow::backgroundColourId));
    }

    void resized() override
    {
        text.setBounds (0, 0, 200, 200);
        lb.centreWithSize (90, 90);
    }

private:
    struct MyListBoxModel : public ListBoxModel
    {
        int getNumRows() override { return 3; }

        void paintListBoxItem (int rowNumber, Graphics& g, int width, int height, bool) override
        {
            g.setColour (Colours::cyan);
            Rectangle<int> bounds (0, 0, width, height);
            g.fillRect (bounds.reduced (1));
        }

        var getDragSourceDescription (const SparseSet<int>& rowsToDescribe) override
        {
            return var ("LisBox rows");
        }
    };

    MyListBoxModel model;
    ListBox lb { "Listbox", &model };
    Label text { "Dropped", "Dropped" };

    DragAndDropTestAudioProcessor& processor;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DragAndDropTestAudioProcessorEditor)
};

ListBoxDragDrop

And here’s it working as expected in REAPER (it also successfully prevents a drag to the other window).

1 Like

I think I have a use case related to point 2 in the original post. I have a ListBox in a CallOutBox floating window that I want to allow drag and drop only within that ListBox. As can be seen in the image below the drag and drop image is displayed under the CallOutBox. When I force allowDraggingToOtherWindows to false in juce_ListBox.cpp on now line 171 (version 7.0.4) I get the proper behavior. Would it be possible to add a set/get method “setAllowDraggingToOtherWindows(bool)” to ListBox?

Thanks!
Brent

Screenshot 2023-01-25 082059