How to get parameterChanged() to be called for push button?


#1

I have an ImageButton that the user can change the behavior of, from toggle-style to push-button style. When they set it to toggled style, then I get the expected parameterChanged() callbacks both on the mouse down and mouse up events. But when they change it to be push-button style, those callbacks no longer occur.

When the user changes the button style, I execute this code:

    int mode = triggerModeComboBox->getSelectedItemIndex();
    playPatternButton->setClickingTogglesState( (mode == kTriggerMode_Latch) );
    playPatternButton->setTriggeredOnMouseDown( (mode == kTriggerMode_Manual) );

I added that last line in an attempt to fix the problem, but my processor still doesn’t get notified when the button is pressed.

When set to push-button style (mode == kTriggerMode_Manual), I need the parameter to change to 1 and then 0, and the processor to receive both the mouse down and mouse up notifications (which come via the ButtonAttachment in the component that contains that button).

How can I do that? I can see the mouse down and mouse up events in the component’s buttonClicked() callback, but responding to these button up and down changes requires my setting a value in my DSP code as soon as possible, so that function doesn’t seem like where the proper response code should be.

Is there any way to get my button to send me parameter change notifications for both mouse down (1) and mouse up (0), while acting as a push-button (in other words, returning back to 0 when I let go of the button)?


#2

Looks like I need to derive a class from ImageButton that notifies its listeners even when it is not “latching” its state, on both mouse down and mouse up.


#3

Since every Button is a Component, you will always be able to override mouseDown() and mouseUp().
To your initial problem please note, that the Button has a on/off state (when you used it with setClickingTogglesState (true)). This is different from button pressed in or released.

One more thing, you can use the MouseListener and connect to the Button using Component::addMouseListener(). But if your class that listens is already a Component, don’t get confused, when you receive the mouseDown from the Button you listen to and the Component itself.
For clarity I would avoid that scenario, since it is hard to read and maintain.


#4

Indeed. It’s a bit unusual to change a button from being toggle-style to being push-style, I imagine. The issue I have is simply that when used as a push-style button, the processor only sees the parameter change when the button is returned to un-pressed (0). I simply need it to change the parameter both when the button gets pressed and when the button gets released. I need the attached parameter to change on both press (to 1) and release (to 0), and the processor that is listening for that parameter to be notified, regardless of whether setClickingTogglesState() was passed true or false.


#5

Have you looked at Button::setCommandToTrigger ()?

It’s designed for buttons that should trigger an action.

Also in order to avoid changing the button, have you considered to use two buttons, sitting in the same position with the same size etc. and then using Component::setVisibility () to switch between them?


#6

I thought of using two buttons (and still might), but that doesn’t fix the issue with a push-button-style button. I just need a button that changes the attached parameter to 1 when pressed and 0 when released, but does not latch that state (because my DSP code need to know right away when the button is pressed and when it is released… similar to what you’d want with a MIDI keyboard key).

I don’t think I want a command target. That doesn’t seem like the correct model for what I need.


#7

OK. Maybe you can leave it a toggle-button, but simply set the toggleState back to “0” once you’ve processed the parameter change?


#8

I also don’t think, that CommandTarget will do anything for you, since it is designed to issue a command and return.
To get progress I really would inherit the Button you want to use, and override mouseDown() and mouseUp():

void mouseDown() override
{
    if (! getClickingTogglesState ())
        setToggleState (true, sendNotification);
}
void mouseUp() override
{
    if (! getClickingTogglesState ())
        setToggleState (false, sendNotification);
}

I don’t know, if a change of the original Button behaviour could be considered by the juce team… it sounds sensible to me to have a toggleState change possible for this kind of usage.


#9

I can’t set the toggleState to 0 when it gets set to 1. I need to keep the parameter at 1 as long as the button is held down, then set it to 0 only when the button is released.

I just don’t know why the Button class doesn’t allow this already. Does nobody ever need to attach a push-style button to a parameter, and to know when it changes to 1 and back to 0?

I tried deriving a class to just remove the check for triggerOnMouseDown in mouseDown() and mouseUp(), so that it would always change state on mouseDown() and mouseUp(), but those functions reference a bunch of private stuff, making it impossible to simply remove those checks. If the class is meant to be derived from, then why does it contains private functions (rather than protected ones), and no access functions for the private member variables it uses? Member functions of classes that are intended to be derived from should either be intended to be called from the overriding function (which these obviously aren’t), or else they should reference only protected or otherwise accessible members. Otherwise it’s impossible to override them properly.

I’m stuck. :frowning:


#11

Danial, that’s closer! If I add an else condition to call the base class when those tests fail, then I’m almost to what I need for a button that I can use either as toggled or push-style. The only remaining issue I see is that the button now appears to still be pressed after I release the mouse, until I move the mouse out of the button bounds.


#12

Yes you are right, I missed, that there is already an implementation, that needs to be called.
So you probably only have to add the updateState() and you should be good to go:

void mouseDown() override
{
    if (getClickingTogglesState ())
    {
        Button::mouseDown();
    }
    else
    {
        updateState (true, true);
        setToggleState (true, sendNotification);
    }
}

void mouseUp() override
{
    if (getClickingTogglesState ())
    {
        Button::mouseUp();
    }
    else
    {
        updateState (true, false);
        setToggleState (false, sendNotification);
    }
}

#13

Unfortunately, I can’t call updateState(). It’s private in the Button class.


#14

Would have been too easy, wouldn’t it? Sorry for misguiding you.

I have a horrible solution, I should get the kludge award for that:

void mouseDown() override
{
    if (! getClickingTogglesState ())
    {
        setTriggeredOnMouseDown (false); // delays the click to mouseUp
        setToggleState (true, sendNotification);
    }
    Button::mouseDown();
}

void mouseUp() override
{
    if (! getClickingTogglesState ())
    {
        setTriggeredOnMouseDown (true); // pretends the click already happened
        setToggleState (false, sendNotification);
    }
    Button::mouseUp();
}

Bonus points for adding a member to set the setTriggeredOnMouseDown() back to the original value, left as an exercise… :wink:


#15

Nope. Still has the problem that releasing the mouse doesn’t cause the button to draw its “up” image until I move the mouse outside the button bounds. Plus, it adds that same problem to the case where I have it toggles state on click. This would be a whole lot easier if mouseDown() and mouseUp() were written to allow them to be overridden properly.


#16

That’s a shame. I’m officially out of ideas for now… sorry.


#17

Is the current issue just drawing? We use a momentary style button in our plugins with just:

void CustomButton::mouseDown(const MouseEvent &e)
{
    if (isMomentary)
    {
        setState(Button::buttonDown);

        if (!getToggleState())
            setToggleState(true, sendNotification);
    }
    ...
}

void CustomButton::mouseUp(const MouseEvent &e)
{
    if (isMomentary)
    {
        setState(Button::buttonOver);

        if (getToggleState())
            setToggleState(false, sendNotification);
    }
    ...
}

Ours isn’t an ImageButton but it should work in the same way, I think the setState() calls are what should fix it for you :slight_smile:


#18

That seems to be working much better. I need to do some more testing, but it does appear to accomplish the goal. (I assume I’d add an “else Button::mouseXXX();” in each case, for when it’s a toggle-type instead of momentary? That’s what the “…” represents?)


#19

Yep! We have some other modes for our buttons that I cut out of the example, but for normal buttons we just call the base juce::Button mouse methods