ComponentDragger Tutorial

Introduction

In this tutorial, I will explain the basic usage of the ComponentDragger class.

The source code is available on github.

GitHub - szkkng/CompDragger: This is an example of using the ComponentDragger class(JUCE)
This is an example of using the ComponentDragger class(JUCE) - GitHub - szkkng/CompDragger: This is an example of using the ComponentDragger class(JUCE)

Prerequisites

Before we start the tutorial, let’s create a new project.

Then, prepare the following header file under Source directory. we will implement ComponentDragger in this file.

Okay, let’s get started!

ComponentDragger

GreenRect.h

First, the implementation of GreenRect is as follows:

class GreenRect : public juce::Component
{
public:
    GreenRect()
    {
        blueRect.setColour (juce::Label::outlineColourId, blue);
        blueRect.setColour (juce::Label::backgroundColourId, blue.withAlpha (0.2f));

        yellowRect.setColour (juce::Label::outlineColourId, yellow);
        yellowRect.setColour (juce::Label::backgroundColourId, yellow.withAlpha (0.2f));
        
        addAndMakeVisible (blueRect);
        addAndMakeVisible (yellowRect, 0);
    };
    
    void paint (juce::Graphics& g) override
    {
        g.fillAll (green.withAlpha (0.1f));
        
        g.setColour (green);
        g.drawRect (getLocalBounds());
    }
    
    void resized () override
    {
        blueRect.setBounds (getLocalBounds().withSizeKeepingCentre (25, 25));
        yellowRect.setBounds (getLocalBounds().withSizeKeepingCentre (50, 50));
    }
    
private:
    struct  DraggableComp : public juce::Label
    {
        juce::ComponentDragger dragger;
                        
        void mouseEnter (const juce::MouseEvent& event) override
        {
            auto lightColour = findColour (juce::Label::backgroundColourId).withAlpha (1.0f);
            
            setColour (juce::Label::backgroundColourId, lightColour);
            repaint();
        }
        
        void mouseExit (const juce::MouseEvent& event) override
        {
            auto darkColour = findColour (juce::Label::backgroundColourId).withAlpha (0.2f);
            
            setColour (juce::Label::backgroundColourId, darkColour);
            repaint();
        }
            
        void mouseDown (const juce::MouseEvent& event) override
        {
            dragger.startDraggingComponent (this, event);            
            event.source.enableUnboundedMouseMovement (true);
        }

        void mouseDrag (const juce::MouseEvent& event) override
        {
            dragger.dragComponent (this, event, nullptr);
        }
    };
    
    DraggableComp blueRect;
    DraggableComp yellowRect;

    juce::Colour blue   = juce::Colour::fromFloatRGBA (0.42f, 0.83f, 1.0f,  1.0f);
    juce::Colour green  = juce::Colour::fromFloatRGBA (0.34f, 0.74f, 0.65f, 1.0f);
    juce::Colour yellow = juce::Colour::fromFloatRGBA (1.0f,  0.71f, 0.19f, 1.0f);
};

GreenRect is a parent component that has two child components, blueRect and yellowRect, which can be dragged. There are various member functions called, but most of them are related to the appearance, and the most important ones are startDraggingComponent() and dragComponent().

startDraggingComponent()

startDraggingComponent() needs to be added to mouseDown() as shown below:

・・・
        void mouseDown (const juce::MouseEvent& event) override
        {
            dragger.startDraggingComponent (this, event);            
            event.source.enableUnboundedMouseMovement (true);
        }

dragComponent()

Next, dragComponent needs to be added to mouseDrag().

・・・        
        void mouseDrag (const juce::MouseEvent& event) override
        {
            dragger.dragComponent (this, event, nullptr);
        }

Creating the GreenRect object

Now, let’s include this GreenRect.h in main.h and declare this object.

#pragma once

#include <JuceHeader.h>
#include "GreenRect.h"

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

    //==============================================================================
    void paint (juce::Graphics&) override;
    void resized() override;

private:
    //==============================================================================
    GreenRect greenRect;
    
    juce::Colour black = juce::Colour::fromFloatRGBA (0.08f, 0.08f, 0.08f, 1.0f);

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainComponent)
};

main.cpp is very simple. All we need to implement is the following:

#include "MainComponent.h"

//==============================================================================
MainComponent::MainComponent()
{
    setSize (500, 300);
    
    addAndMakeVisible (greenRect);
}

MainComponent::~MainComponent()
{
}

//==============================================================================
void MainComponent::paint (juce::Graphics& g)
{
    g.fillAll (black);
}

void MainComponent::resized()
{
    greenRect.setBounds (getLocalBounds().reduced (100, 50));
}

Now let’s build it!

You should be able to drag the blueRect and yellowRect as shown below.

ComponentBoundsConstrainer

There is only one big problem with the current implementation. If you drag the blueRect or yellowRect outside the range of the greenRect, they will disappear as shown below:

Therefore we need to implement it so that we can only drag these components within the GreenRect.

Using the ComponentBoundsConstrainer class, we can implement this!

Creating the object

First, let’s create an object of this class and change the third argument of dragComponent() from nullptr to a reference to this object.

struct DraggableComp : public juce::Label
{
    juce::ComponentDragger dragger;
    juce::ComponentBoundsConstrainer constrainer;
・・・
    void mouseDrag(const juce::MouseEvent& event) override
    {
        dragger.dragComponent (this, event, &constrainer);
    }
};

setMinimumOnscreenAmounts()

As it is, we have not yet limited the range of the drag, so we need to set that range for this object. So, by calling setMinimumOnscreenAmounts(), we can set how many pixels of the component we are dragging can be out of the range.

struct  DraggableComp : public juce::Label
{
    DraggableComp()
    {
        constrainer.setMinimumOnscreenAmounts (5, 15, 25, 50);
    }
・・・

The arguments we are passing to this member function represent, in order from the beginning, top, left, bottom, and right.

As you can see from the image above (minimumWhenOffTheRight), if the value is greater than or equal to the size of the yellowRect, you can limit the drag to within the greenRect. So, let’s implement it as follows:

struct  DraggableComp : public juce::Label
{
    DraggableComp()
    {
        constrainer.setMinimumOnscreenAmounts (50, 50, 50, 50);
    }
・・・                

Now build it and drag it! You should now only be able to drag within the greenRect as shown below!

Conclusion

In this tutorial, I have explained the basic usage of the ComponentDragger class. The first time I tried to use this class, I was very confused and wasted a lot of time. I hope this article will be useful for someone else.

Thank you for reading to the end. If you find something wrong or a better way to implement it, please let me know in the comments or DM. Happy coding!

References

Comments

Copied title and URL