Toggle buttons when swiping over a row

Hi everybody,

I try to have a row of buttons, where I can click on one and swipe over a row of many buttons, so they are all toggled. I tried this subclass, but it doesn’t work:

class SwipeableButton : public TextButton
{
public:
    SwipeableButton (const String& name) : TextButton (name) {}

    void mouseEnter (const MouseEvent& e) override
    {
        if (e.mods.isLeftButtonDown())
            setToggleState (! getToggleState(), sendNotification);
    }
};

Anybody has an idea?

Cheers!

2 Likes

seems like it should work… try adding your buttons to a button group (parent), making the button group a mouse listener to all contained buttons, and running this code from the parent.

Thanks for your suggestion.

Unfortunately that can’t work. As soon as the mouse button is down, no more mouseEnter and mouseExit events are delivered, since that is considered a drag move. Hence also the addMouseListener wouldn’t get a mouseEnter. I guess I would have to use mouseMoved and keep track myself, when the mouse entered… But that sounds way too complicated.

I dug into handleMouseEvent in the ComponentPeer and the internalMouseEnter stuff, but couldn’t find a spot, where it would be possible to override that behaviour, even with patching JUCE. But maybe I would have to dig deeper…

I’m not sure I understand your UI… but would the LassoComponent class help?

Rail

Good idea, haven’t thought of that one. But not really what I need. That would mean I select the buttons and then…?

My case:
I have 27 toggle buttons, and often you have to select a whole row or a block. So clicking one after each other is not very effective. This is how it looks like:
56

The UX I was thinking of is like people type on their phones nowadays.
You probably have seen that, you don’t press letter by letter, but rather swipe over the buttons, crossing the letters, and AI does the rest.
Or in a host, when you want to mute a row of tracks, you just swipe over all bypass buttons.
It is actually a quite common scenario, especially in combination with touch screens…

Simplest solution… Either have Lasso to group a selection of buttons… or click on the header on the left to select all the buttons… then when you click on one in the group have them all toggle.

Otherwise you’ll probably be able to do what you describe using Button::hitTest to add and remove button objects from an OwnedArray of Button pointers.

Rail

with the button group solution --> track mouseMove for all child components, and when mouseMove is called, iterate through your buttons, check if the current mouse point (x,y) is contained within the bounds of the button you’d like to toggle AND check the isMouseButtonDown() function is true – if so, setToggleState(true).

of course, untested and off the top of my head – but should work.

Thanks, just to double check, that I didn’t miss anything:

  • I didn’t see a ButtonGroup, is that a class named slightly different?
  • You are not asking me to set a common setRadioGroupID, that would be counterproductive…

The solution to handle this in the parent can be done (and might be the only solution atm), it is just a lot to take care of:

  • keeping track, when I entered the button, so I trigger the state change only once (opposed to on every move inside the button)
  • keeping track, when I left the button, so when I re-enter, I can activate it again

It is all doable, but it feels like re-inventing the wheel. What I really need is a switch in the Component to change the behaviour, if mouseEnter and mouseExit are triggered regardless of the isMouseButtonDown state. I think I will create a new thread for that feature request.

Meanwhile I experimented with making the button drag and drop aware, which kinda works, but only for one button, seems like the drag is consumed for some reason:

class SwipeableButton : public TextButton, public DragAndDropContainer, public DragAndDropTarget
{
public:
    SwipeableButton (const String& name) : TextButton (name) {}

    void mouseDown (const MouseEvent& e) override
    {
        startDragging ("swipe", this);
    }

    void itemDragEnter (const SourceDetails &dragSourceDetails) override
    {
        DBG ("Enter: " << getComponentID());
        if (dragSourceDetails.description.toString() == "swipe")
            setToggleState (! getToggleState(), sendNotification);
    }

    bool isInterestedInDragSource (const SourceDetails &dragSourceDetails) override
    {
        return dragSourceDetails.description.toString() == "swipe";
    }

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