How to programmatically toggle ToggleButtons?

What is the right way to toggle a ToggleButton in software?
Here is what I tried unsuccessfully:

std::unique_ptr muteToggleButton;

muteToggleButton.reset (new ToggleButton (“MuteToggleButton”));
addAndMakeVisible (muteToggleButton.get());


bool getMute() {
return muteToggleButton.get()->getToggleState();
}

void setMute(bool b) {
if (b != getMute()) {
muteToggleButton.get()->triggerClick();
}
}

Basically I want to automatically mute in situations likely to have acoustic feedback.

Thanks for any tips!

  • Julius

It occurs to me that perhaps the problem is that the click is triggered asynchronously. Maybe this is the Right Thing?:

void setMute(bool b) {
if (b != getMute()) {
muteToggleButton.get()->setToggleState(b,sendNotificationSync);
}
}

It seems to be working…

Hi Julius!

Yes, that’s right, just call setToggleState().

triggerClick() is handy when you want the GUI to flash a button to show it being clicked, but it will be async. So it’s not really very useful for toggle buttons, more for single-push ones.

You probably don’t actually need to add the boilerplate -

void setMute(bool b) {
    muteToggleButton->setToggleState(b,sendNotificationSync);
}

would do the same thing.

…however, it does worry me that it looks like you’re approaching things the wrong way round. You really want your GUI to follow any changes to your underlying data model, not to be relying on a callback from a GUI class to make something happen. Hopefully that’s what you’re really doing, but from your example it looks suspicious!

The robust way to implement a setMute() function would be for it to modify whatever your actual data model’s mute flag is, but it shouldn’t have any knowledge of GUI components (which may or may not even exist). Instead, any GUI objects would listen for changes to the model and do something in response like e.g. muteButton->setToggleState (isCurrentlyMuted(), dontSendNotification); to keep themselves up to date when the model changes.

Loads of people get this wrong, which is one reason we recommend the AudioProcessorValueTreeState - it forces you to keep the data model in a ValueTree, so you can just connect a value to e.g. a toggle button and the two will magically keep themselves in sync.

1 Like

Hi Jules!

Thanks for your orientation.

I thought I should follow the principle of having each state variable in only one place, and since the ToggleButton holds its own bit of state, I thought that the “model” had to be in there. The Faust world uses stateless GUI objects, so that all GUI and DSP state is held in the dsp class, and any number of UI classes can be “built” to obtain pointers directly to the state variables in the dsp class (corresponding to slider values and such). I will now read up on AudioProcessorValueTreeState . . .

Sounds like you know what you’re doing then! If you already have a lot of code then converting to AudioProcessorValueTreeState is probably more hassle than it’s worth, but we like to recommend it for new projects.

Usually I know what I’m trying to do. :slight_smile:
Thanks again!

AudioProcessorValueTreeState looks very nice!
Can it be used with AudioAppComponent as well as with an AudioProcessor? While I’ve already implemented my GUI wiring, this could be a shortcut to saving/restoring XML preset files. It looks like ValueTree alone might be enough for this purpose.

Well, yes, I guess you could use it like that. Certainly that’s the kind of task that ValueTree is really good for, whether or not you use the AudioProcessorValueTreeState around it.

Has something changed for toggle buttons, or especially setToggleState() since 2018?

Because this is not working for me. I got an array of 8 toggle buttons in a group declared with; ToggleButton tgCloneDestinations[8];

And then when I present the group to the user I want to clear the check boxes so they are all blank/unchecked, as each time user is getting to this, his/her choice will most likely be different.

for (int module = 0; module < maxModules; module++)
    {
        tgCloneDestinations[module].setToggleState (false, sendNotificationSync);
    }

All the above does, is lock, the toggle buttons, so they can’t be turned/checked on or off. I see that one of the overloads on that functions is deprecated.

I see you are using sendNotificationSync. I assume with 99% certainty that you are locking something up in one of your button callbacks… So what happens if your button callbacks and why do you want a synchronous notification instead of an async?

I just used Jules example. Actually I don’t want a notification at all, and I did the below with exact same result, it locks/freezes the functionality of all the button’s check boxes so they won’t work (turn off or on) when clicked on;

for (int module = 0; module < maxModules; module++)
    {
        tgCloneDestinations[module].setToggleState (false, dontSendNotification);
    }

Let me explain what I need this for. The synthesizer I am making has 8 tone generators (oscillators), each exactly with the same 40 settings (knobs) that can be tweaked to change the sound. So I made a clone function, that can copy some or all of one tone generator’s parameters, to one or more (other) tone generators, and each time, although not often, the user want to do this, the likelihood is that choices (destinations) are going to be different than previous, so therefore I would like to clear all the check boxes after each use, or before they again are presented to the user.

Just to rule out the obvious:

  • are the buttons vanilla buttons, e.g. TextButtons or did you inherit and override some crucial functions like mouseDown, paintButton or any other?
  • do you have ButtonAttachments, that contradict what you are doing?
  • any other Button::Listener functionality that works against?
  • any logic that recreates the buttons as a result of the state change, using the old information?
  • are you using Button::setRadioGroupId()?

This is how I declare the buttons;

ToggleButton tgCloneDestinations[8];

I do have a custom lookandfeel, however it only has special processing how to show knobs and button text. Either way I just commented out that part, recompiled, and still the same problem.

As far as I know I have no button attachments.

I actually don’t have a button listener on this group. I have a CloneComponent with a “Clone” and “Cancel” button, plus off course the check boxes. Then in PluginEditor I am doing this;

addChildComponent (cloneTG);
cloneTG.clone.addListener (this);
cloneTG.cancel.addListener (this);
cloneTG.setAlwaysOnTop (true);

and this in buttonClicked in PluginEditor;

if (id == "CloneTG")
	{
		for (int module = 0; module < maxModules; module++)
		{
			if (cloneTG.tgCloneDestinations[module].getToggleState () == true) cloneToneGenerator (module);
		}

		cloneTG.setVisible (false);
	}

I don’t believe I have any logic that recreates buttons.

Nor am I using using Button::setRadioGroupId().

Problem solved - Fix came to me when I was trying to sleep last night :slight_smile:

I made the mistake to try and set the status of the toggle buttons inside a paint() function of the same component that contained the toggle buttons, so apparently whenever I clicked a toggle button it activated paint() and cleared them again - ouch!

Thank you very much for forcing me to squeeze a bit more out of my noodle!