Can AttachedControlBase become derivable?

It would be great if this class was a derivable class in juce_AudioProcessorValueTreeState.h so we could provide our own implementation of derived classes to attach to our custom widgets that can’t really be represented as sliders, buttons, or comboBoxes.

At present, I’m using my 2D widget to modify a pair of hidden Sliders that are attached accordingly. I’d prefer to avoid doing this, but that’s what must be done to make this work.

4 Likes

You can just copy it imo, its a small class.

This keeps coming up…

2 Likes

that’s not very D.R.Y. though…

Yeah, there’s been a few requests for it. I’ll bump its priority up.

2 Likes

Can we move this or one of the other threads with same request to the Feature-Request category, so we can vote for it?

I’ve moved this thread, since you’ve helpfully linked in the others.

1 Like

Bump!!
This would be very very useful!!

Indeed, recently had to copy it to make my own RadioButtonGroupAttachment.

Let’s all share our implementations so we can see how everyone solves the problem. I’m sure they’ll all be similar but uniquely different :slight_smile: I’ll go first:

#pragma once

#include "../JuceLibraryCode/JuceHeader.h"

struct GroupButton : public Button
{
    GroupButton(const String& name, int index) : Button(name), groupIndex(index) { }

    const int groupIndex;
};

struct GroupButtonParamListener : public AudioProcessorParameter::Listener, public AsyncUpdater, public Timer
{
    /*
     a group of buttons that are connected to a AudioParameterChoice
     */
    GroupButtonParamListener(Array<GroupButton*>&& bs,
                             AudioParameterChoice& choices);
    ~GroupButtonParamListener();
    void timerCallback() override;

    void handleAsyncUpdate() override;

    void parameterValueChanged(int i, float v) override;
    void parameterGestureChanged(int, bool) override { }

    juce::Atomic<int> parameterIndex;
    Array<GroupButton*> buttons;
    AudioParameterChoice& param;
    std::function<void(int,float)> parameterChanged;
};
#include "GroupButtonParamListener.h"

GroupButtonParamListener::GroupButtonParamListener(Array<GroupButton*>&& bs,
                                                   AudioParameterChoice& choices) :
buttons( std::move(bs) ),
param(choices)
{
    jassert( buttons.size() == param.choices.size() );
    for( auto* button : buttons )
    {
        button->onClick = [this, button]()
        {
            //all group indexes start at 1
            this->param.beginChangeGesture();
            this->param.setValueNotifyingHost( ((float)button->groupIndex-1) / float(this->param.choices.size()-1) );
            this->param.endChangeGesture();
        };
    }
    startTimer(50);
}

GroupButtonParamListener::~GroupButtonParamListener()
{
    param.removeListener(this);
}

void GroupButtonParamListener::timerCallback()
{
    stopTimer();
    param.addListener(this);
    buttons[param.getIndex()]->triggerClick();
}

void GroupButtonParamListener::handleAsyncUpdate()
{
    jassert( buttons.size() == param.choices.size() );
    auto idx = param.getIndex();
    buttons[idx]->setToggleState(true, dontSendNotification);
    //button is updated in PluginVal, but not in Logic..  hmmmm

    if( parameterChanged )
        parameterChanged(parameterIndex.get(),
                         (float)idx / float(param.choices.size() - 1) );

}
void GroupButtonParamListener::parameterValueChanged(int i, float v)
{
    parameterIndex.set(i);
    triggerAsyncUpdate();
}

Not the most reusable nor the highest quality solution, but here’s how I was able to do a XY(Z)pad component for a plugin that uses AudioProcessorValueTreeState :

1 Like

Just a quick bump on this :slight_smile:

And another bump!

2 Likes

I’m in :wink:

We’re working on it :slight_smile:

Well, if you need any user-feedback … I have a good supply of it right now!

What else is involved besides moving it to the APVTS header file?

It’s not the nicest interface. I’m sure we can do better.

I don’t think anyone here would really care how nice the interface is, as long as it becomes derivable. I requested this almost a year ago. There are only a couple of other feature requests that have as many votes…

I don’t think that’s true; we get a lot of feedback from the JUCE community letting us know they value our thoughtful approach to API design. We’re not going to suddenly lower our standards.