Need a little help understanding DragAndDrop

I’ve been struggling for a while trying to get some DragAndDrop functionality working. I’ve tried it two different ways: using DragAndDropContainer and ComponentDragger.

I got CompontDragger working but didn’t do what I wanted. It lets me move a container around the screen. The DragAndDropContainer seemed closer to the behavior I want. The original container is still visible, and a translucent copy appears and drags when you start dragging.

However, the translucent dragged container is only visible within the original objects bounds. In the image below, the red/blue box is a DragAndDropContainer. The dark green box in the bottom right is a DragAndDropTarget.

There is something about the limitations of DragAndDrop or I’m fundamentally not understanding how to use it to accomplish: dragging the red/blue container over and drop it in the green container.
image

Any help or ideas would be appreciated. I can post the code but I feel it’s my understanding of DragAndDrop that’s wrong, not bugs in the simple example.

1 Like

Your code would be helpful.

Here is the code, the example is as barebones as possible.

// MainComponent.h
#pragma once
#include <JuceHeader.h>
using namespace juce;

class TargetSource : public Component,
                     public DragAndDropContainer
{
public:
    void paint(Graphics &g) override
    {
        g.fillAll (Colours::blue);
        g.setColour(Colours::red);
        g.fillRect(0,0,25,25);
    }

    void mouseDown (const MouseEvent& e) override
    {
        std::cout << "TargetSource::mouseDown():" << std::endl;
        startDragging("", this);
    }

    void mouseDrag (const MouseEvent& e) override
    {
        std::cout << "TargetSource::mouseDrag():" << getX() << " " << getY() << " " << e.x << " " << e.y << std::endl;
    }
};

class TargetArea : public Component,
                   public DragAndDropTarget
{
public:
    void paint(Graphics &g) override
    {
        g.fillAll (Colours::green.withAlpha (0.2f));
    }

    bool isInterestedInDragSource (const SourceDetails& /*dragSourceDetails*/) override
    {
        return true;
    }

    void itemDragEnter (const SourceDetails& /*dragSourceDetails*/) override
    {
        std::cout << "DragAndDropCarrier():: itemDragEnter()" << std::endl;
        repaint();
    }

    void itemDragMove (const SourceDetails& /*dragSourceDetails*/) override
    {
        std::cout << "DragAndDropCarrier():: itemDragMove()" << std::endl;
    }

    void itemDragExit (const SourceDetails& /*dragSourceDetails*/) override
    {
        std::cout << "DragAndDropCarrier():: itemDragExit()" << std::endl;
        repaint();
    }

    void itemDropped (const SourceDetails& dragSourceDetails) override {}
};

class MainComponent  : public juce::Component
{
public:
    MainComponent();
    ~MainComponent() override;

    void paint (juce::Graphics&) override;
    void resized() override;
private:

    TargetArea targetArea;
    TargetSource targetSource;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainComponent)
};
// MainComponent.cpp
#include "MainComponent.h"
MainComponent::MainComponent()
{
    setSize (600, 400);

    targetArea.setBounds(450, 250, 150, 140);
    addAndMakeVisible(targetArea);

    targetSource.setBounds(0,0,50,50);
    addAndMakeVisible(targetSource);
}

MainComponent::~MainComponent() {}

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

    g.setFont (juce::Font (16.0f));
    g.setColour (juce::Colours::white);
    g.drawText ("Hello World!", getLocalBounds(), juce::Justification::centred, true);
}

void MainComponent::resized() {}

Your targetSource shouldn’t inherit from DragAndDropContainer. Use the following code in the mouseDrag () callback from your targetSource:

     juce::DragAndDropContainer* dragC =
        juce::DragAndDropContainer::findParentDragContainerFor(this);
if (!dragC->isDragAndDropActive())
{
    dragC->startDragging("TargetSouce", this);
}

Now you can use dragSourceDetails in the itemDropped() callback in the TargetArea for:

if (dragSourceDetails.description == "TargetSource")
and do, whatever you want to do with it.

You might check out the DemoRunner that is provided with JUCE. If you look at the Widgets Demo, there is a “Drag & Drop” tab that demonstrates what you’re looking for. All of the relevant code is in the DragAndDropDemo class.

@baramgb Thank you! It’s working now!

I removed the DragAndDropContainer inheritance on TargetSource and did have to move it to MainComponent at the top which I think would be obvious to the more experienced but I crashed the program on my first try to implement the proposed changes. I added a nullptr check to the assignment of “dragC” in order to catch the crash and figure out MainComponent needed to inherit Drag&DropContainer.

@Connorreviere Actually I’ve spent several days going through every dragging example in DemoRunner including the Widgets Demo trying to figure this out. That demo shows effectively how a DragAndDropTarget works, but the fact a ListBox was the only Target source meant it was missing the critical details for why my example didn’t work for a simple custom container. I needed a working example about exactly how to set this up for the mouseDrag() method on the source container. Because the demo uses a ListBox, there is no mouseDrag() in that file.

Thank you both for your prompt help. I’ll post the fixed code below in case someone finds this thread in a search.

// MainComponent.h
#pragma once
#include <JuceHeader.h>
using namespace juce;

class TargetSource : public Component
{
public:
    void paint(Graphics &g) override
    {
        g.fillAll (Colours::blue);
        g.setColour(Colours::red);
        g.fillRect(0,0,25,25);
    }

    void mouseDown (const MouseEvent& e) override
    {
        std::cout << "TargetSource::mouseDown():" << std::endl;
    }

    void mouseDrag (const MouseEvent& e) override
    {
        std::cout << "TargetSource::mouseDrag():" << getX() << " " << getY() << " " << e.x << " " << e.y << std::endl;
        juce::DragAndDropContainer* dragC =
        juce::DragAndDropContainer::findParentDragContainerFor(this);
        if (!dragC) {
            std::cout << "TargetSource::mouseDrag(): can't find parent drag container" << std::endl;
        } else {
            if (!dragC->isDragAndDropActive())
            {
              dragC->startDragging("TargetSource", this);
            }
        }
    }
};

class TargetArea : public Component,
                   public DragAndDropTarget
{
public:
    void paint(Graphics &g) override
    {
        g.fillAll (Colours::green.withAlpha (0.2f));
    }

    bool isInterestedInDragSource (const SourceDetails& /*dragSourceDetails*/) override
    {
        return true;
    }

    void itemDragEnter (const SourceDetails& /*dragSourceDetails*/) override
    {
        std::cout << "DragAndDropCarrier():: itemDragEnter()" << std::endl;
        repaint();
    }

    void itemDragMove (const SourceDetails& /*dragSourceDetails*/) override
    {
        std::cout << "DragAndDropCarrier():: itemDragMove()" << std::endl;
    }

    void itemDragExit (const SourceDetails& /*dragSourceDetails*/) override
    {
        std::cout << "DragAndDropCarrier():: itemDragExit()" << std::endl;
        repaint();
    }

    void itemDropped (const SourceDetails& dragSourceDetails) override {
        if (dragSourceDetails.description == "TargetSource") {
            std::cout << "DragAndDropCarrier():: itemDropped(): valid item dropped" << std::endl;
        } else {
            std::cout << "DragAndDropCarrier():: itemDropped(): unknown container" << std::endl;
        }
    }
};

class MainComponent  : public juce::Component,
                       public juce::DragAndDropContainer
{
public:
    MainComponent();
    ~MainComponent() override;

    void paint (juce::Graphics&) override;
    void resized() override;
private:

    TargetArea targetArea;
    TargetSource targetSource;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainComponent)
};
// MainComponent.cpp
#include "MainComponent.h"
MainComponent::MainComponent()
{
    setSize (600, 400);

    targetArea.setBounds(450, 250, 150, 140);
    addAndMakeVisible(targetArea);

    targetSource.setBounds(0,0,50,50);
    addAndMakeVisible(targetSource);
}

MainComponent::~MainComponent() {}

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

    g.setFont (juce::Font (16.0f));
    g.setColour (juce::Colours::white);
    g.drawText ("Hello World!", getLocalBounds(), juce::Justification::centred, true);
}

void MainComponent::resized() {}
5 Likes