Buttons in VSTs

Hello, I’ve been trying to add a button to my plugin that increments a value in the processor. I have gotten it to work for just a few seconds using textbuttons (such as by saying tbutton.onClick = [this] {processor.val1 += 1;}; ), but it gets extremely laggy and stops working. I tried setting up a listener, but it refuses to compile even with 0 errors. I’m unable to add a regular button of any kind (“object of abstract class type is not allowed”). I have gotten it to work with a slider, however. All the tutorials seem to use sliders and no buttons, and the button tutorials are for non-plugin applications.
TLDR; Does anyone know how to properly set up buttons in a VST plugin?

There is no trick to using a button in a VST. I suggest you include some simple code that demonstrates the failure.

This is nothing specific to VST. But the error message tells you exactly what‘s wrong: Trying to directly use a Button won‘t work as button is an abstract base class that only describes the class interface common to all types of buttons but does not implement all behaviour. Therefore a Button does not describe anything with full functionality. But classes derived from Button like TextButton or DrawableButton are complete and do supply full functionality. If you are looking for a “normal“ button, textbutton is probably what you are looking for

OP mentioned they did try with a TextButton, so they are having an issue beyond just trying to create a button.

This is the most recent thing I’ve tried:

PluginEditor.h:
First, I add “public TextButton::Listener” to the class, just like for sliders
Then below, I create the button with “TextButton button1{ “+” };”
And then because this is what I do for sliders, I assume the same thing applies to buttons: “void buttonClicked(TextButton* button);” (I can’t override this, even though that’s what I do for sliders)

PluginEditor.cpp:
//in the audio processor editor
addAndMakeVisible(button1);
button1.setBounds(200, 200, 20, 20);

//then at the bottom, underneath everything else
void NewProjectAudioProcessorEditor::buttonClicked(TextButton* button)
{
if (button == &button1)
{
processor.channel += 1;
}
}

But it says “cannot instantiate abstract class”, and “void juce::Button::Listener::buttonClicked(juce::Button *) is abstract”, but it says textButtonClicked isn’t an existing function, so what should I be doing instead?

you have a coding error, the logic is correct:

View : public Component, 
       public Button::Listener
{

button = newButton()
button.addListener(this);

}

void buttonClicked(button* inButton)
{
 etc..
}

Hi @FrostMist, to make your code more readable, please indent every line with 4 spaces, or surround the whole code with three backticks, before and after, like this:

```
void your_code_here ()
{
}
``` 

The same is obtained by selecting the code in your post while editing, then pressing the button that has this symbol on it: </>

@Jake_Penn I tried implementing your advice, but I got some errors.
"button" has no storage class or type specifier
identifier "newButton" is undefined
Although I tried adding public TextButton::Listener to the class in PluginEditor.h, and I put button1.addListener(this); in PluginEditor.cpp, but I’m having one issue. At the bottom, I put this:

void buttonClicked(TextButton* inButton)
    {
        if (inButton == &button1)
        {
            processor.channel += 1;
        }
    }

But it says button1 and processor are undefined, even though I have them set in the .h file. But if I change it to this:

void NewProjectAudioProcessorEditor::buttonClicked(TextButton* button){
    if (button == &button1)
    {
        processor.channel += 1;
    }
}

It finds those variables, but says “inherited member not allowed” for the buttonClicked() function, and doesn’t compile.

Hey Frost, it seems you’re confused over some basics.

I’d recommend going back and reviewing inheritance & polymorphism in C++, as well as classes & namespaces.

newButton was just some pseudo code, I wish I could be of more assistance but I’m taking a moment to post a more complete snippet. Please note this isn’t compiled, this is just a general overview so perhaps you can find your issue.

.h example:

class ComponentClass
:   public Component,
    public Button::Listener
{
public:
    
    /** */
    ComponentClass();
    
    /** */
    ~ComponentClass();

    /** */
    void paint(Graphics& g) override;
    
    /** */
    void resized() override;
    
private:
    
    /** */
    void buttonClicked(Button* inButton) override;
    
    std::unique_ptr<TextButton> mButton;
};

.cpp example

ComponentClass::ComponentClass {
    mButton.reset(new TextButton());
    mButton->setButtonText("PWR");
    mButton->setClickingTogglesState(true);
    mButton->addListener(this);
    addAndMakeVisible(mButton.get());
}

void ComponentClass::paint(Graphics& g)
{

}

/** */
void ComponentClass::resized()
{

}
    
/** */
void ComponentClass::buttonClicked(Button* inButton)
{
DBG("BUTTON CLICK RECEIVED!")
}

Good luck!

@cpr was right, I read your post a bit too quick. However, one of your issues still is that you are mixing base & derived classes. This piece of code won’t work:

void buttonClicked(TextButton* inButton)
    {
        if (inButton == &button1)
        {
            processor.channel += 1;
        }
    }

If you look at the Button::Listener docs, you’ll see that the function signature for the virtual function buttonClicked you are overriding is slightly different:

void buttonClicked (Button*)

As I said, the Button class is supposed to manage behaviour for ALL KINDS of buttons. And so is the Button::Listener. Therefore, the argument is a pointer to the base class Type Button* and NOT the specific type TextButton*. As function signatures slightly differ, the compiler sees your version as a different function and misses your implementation of buttonClicked. Your PluginEditor class becomes malformed and all kinds of strange compiler errors may arise.

So you should change your function signature first. Inside the function body, you can still compare the Button* pointer passed in to the memory address of your TextButton member, as a TextButton is a subclass of a general Button. You could now theoretically also add a second button of the type DrawableButton, make your class a listener to this button too and you’ll get callbacks for both buttons here, although they are of a different type. This wouldn’t work if the buttonClicked function would take a TextButton* pointer.

I hope this got a bit clearer now. I’d recommend you as well to have a look at inheritance in C++.

Note: There might still be other errors with your code that trigger errors, so no guarantee that it solves all your problems, but at least this one is obvious from your code snippet

1 Like

Thank you for your help everyone, I got it producing the same results as the .onClick function by changing all my “TextButton” function pointers to regular Buttons, and now I realize what the real issue is. My GUI stops updating/responding after the Juce logo fades away, unless I open/close the VST wrapper or minimize/reopen the plugin, and then it registers all the previous button presses since it froze, and then freezes again. (These issues are occurring in FL Studio, I haven’t tested it in other DAWs yet).

Final update: I discovered the repaint(); function. Calling that every time a button is pressed fixes the issues I was having. I guess sliders do it automatically, but buttons don’t?

They do if you didn’t override the mouse functions like mouseDown, mouseUp etc.

I guess you tried a lot on that class, time to take the learned to a clean start.

Only override set your lambda to onClick or ButtonListener::buttonClicked() as described in the various answers. See if that works as expected and then add bit by bit.