Enable buttons by dragging


#1

Hi!

I want to implement one feature in my program. For example, I have a component with 10 ImageButtons (in a row) and I want enable multiple buttons by dragging mouse (clicking each button is annoying). I've tried some ways to solve this problem, but without any good results.

For example, I tried something like that:

void MyComponent::mouseEnter (const MouseEvent& e)
{
    for (int i = 0; i < mButtons.size(); ++i)
    {
        if (e.originalComponent == mButtons[i] && mButtons[i]->isDown() == false)
        {
            mButtons[i]->triggerClick();
            break;
        }
    }
}

But it's still don't work.

 

Can anyone help me to solve that problem, please?

Thanks in advance!


#2

Sounds like an odd User Interface, but you’d use a LassoComponent

https://www.juce.com/api/classLassoComponent.html

Rail


#3

No, I just want to toggle on buttons when we drag mouse on buttons.


#4

Then subclass the Button class and override the mouseEnter method

Rail


#5

It won't be called during a drag - mouse events are either redirected to the original (initial) mouseDown recipient or muted. Looking at the OS-specific implementation I guess a generic scrub isn't possible without some complicated timer logic like the midi keyboard component uses?

-- p


#6

I've tried this before creating that post...The problem is, when we press left mouse button on the button, other buttons don't get events mouseEnter or mouseDrag events :(


#7

Is it sensible to just pop a transparent component over the top and send button events from that transparent overlay instead?


#8

So you offer to overlay all buttons by transparent component? In this case we should handle all clicks on this component, check if their position in bounds of buttons. And so we should do by dragging, just by hardcode mouseDrag and activate buttons by position? I think it's not so convenient.


#9

mmm, agreed.  Try this instead - it toggles the state when you drag over the buttons. But it'd be easy to induce some other behaviours. 



class MainContentComponent

:

public Component

{

public:

    MainContentComponent()

    {

        for (int i = 0; i < 10; ++i)

        {

            auto b = new TextButton(String(i));

            addAndMakeVisible(b);

            b->setClickingTogglesState(true);

            b->setInterceptsMouseClicks(false, false);

            buttons.add(b);

        }

        

        setSize (600, 400);

    }

    

    ~MainContentComponent()

    {}

    

    Button * getButtonAtLocation(const Point<int> & location)

    {

        for (auto b: buttons)

        {

            auto bounds = b->getBounds();


            if (bounds.contains(location))

                return b;

        }

        

        return nullptr;

    }


    void mouseDown(const MouseEvent & e)

    {


        lastButtonOver = getButtonAtLocation(e.getPosition());

        

        if (lastButtonOver)

            lastButtonOver->triggerClick();

    }

    

    void mouseDrag(const MouseEvent & e)

    {

        auto b = getButtonAtLocation(e.getPosition());

        

        if (b != lastButtonOver && b != nullptr)

            b->triggerClick();


        lastButtonOver = b;

    }

    

    void mouseUp(const MouseEvent &)

    {

        lastButtonOver = nullptr;

    }

    

    void paint (Graphics & g)

    {

        g.fillAll (Colours::black);

    }

    

    void resized()

    {

        const int h = 20;

        int ypos = 0;

        for (auto b: buttons)

        {

            b->setBounds(10, ypos, 100, h);

            ypos += h + 2;

        }

    }



private:

    OwnedArray<TextButton> buttons;

    Button * lastButtonOver;


    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainContentComponent)

};

#10

thanks a lot! I like yours solution!

 

But I have one issue: if I don't add that line (right after creating ImageButton),

btn->addMouseListener (this, false);

 the MainContentComponent didn't get any events like mouseDown, mouseDrag, etc.

But if I add that line, by pressing button it gets incorrect position: it gets position relatively to the left top corner of the button where click happened, not the position of click in MainContentComponent. What can I do to solve that problem?

 

Thanks again, your code is nice :)


#11

I thought I'd put a call into button->setInterceptsMouseClicks(false, false); in there for each button. That'll stop it accepting mouse clicks. 

Otherwise you can use a method in MouseEvent which translates the click location between component co-ordinate spaces. 

https://www.juce.com/api/classMouseEvent.html#a83759659994ba50b9b857a44ba51cff8

 


#12

Thank you! I really appreciate your help! Your solution was the most convenient and proper I think. It helped me to implement the feature I wanted to add.

 

Thanks a lot again!


#13

That works great! for a midi keyboard-like scrub you just need to add the trigger off events (or re-trigger for a toggle) and it's worth noting that if you use a reverse iterator in getButtonAtLocation() it'll mimic the UI collision detection, i.e. buttons added later will have a z-depth above earlier ones, f.ex. on a midi keyboard you'd want to test black keys before white keys since the latter may overlap the former.

I still /guess/ that Juce's midi scrub logic is better / thread-safe.

-- p