Control (View) with multiple parameters?

Let’s say I want to create a ADSR visualization control which controls (read&write) 4 parameters - kAttack, kDecay etc.
How to do it as clean as possible since default CControls supports one tag?

You cannot use a CControl for a this kind of view. You have to use a CView as base and write the code which handles the multiple parameters yourself.

Cheers,
Arne

I am stuck on the same thing: a custom element that can control two parameters.

I derive a custom view class MyView from CView. In the construtor I pass a pointer to the VST3Editor:

class MyView : public CView
{
public:
     AAView (const CRect& size, Steinberg::Vst::EditController* listener = nullptr);
     // ...
private:
     Steinberg::Vst::EditController* editController;
     // ...
}

In the UiDescriptionEditor I place a CView and set the custom-view-name to “MyView”.
This triggers a call of Controller::createCustomView() and I can construct MyView:

VSTGUI::CView* Controller::createCustomView (UTF8StringPtr name,
                                              const UIAttributes& attributes,
                                               const IUIDescription* description,
                                               VST3Editor* editor)
{
     if (UTF8StringView (name) == "MyView")
    {
          myView = new MyView(CRect(), this);
          return myView;
    }
    return nullptr;
}

MyView can then inform the Editor about value changes for different parameters:

// in MyView::onMouseDown():
editController->beginEdit(tag);

// in MyView::onMouseMoved():
editController->performEdit (tag, value);
editController->setParamNormalized (tag, value);

// in MyView::onMouseUp:
editController->endEdit(tag);

Do I understand it right: performEdit() informs the host and setParamNormalized() updates the editor?

For the other way round (MyView receives parameter changes) I guess I have to add MyView as listener to the two parameters.
In the sdk I see only code that is related Parameter Listener with CControls.

How can this be done? Can someone point me to the right direction?

That’s all correct. To get informed about parameter changes from the edit controller you can have a look at the PadController class at vstgui/plugin-bindings/vst3padcontroller.cpp how it is done there.

Cheers,
Arne

Arne, thanks to your tip I got it working now!

Here’s the code with some comments:

MyView.h

namespace VSTGUI {

class MyView : public CView
             , public Steinberg::FObject
             , public DelegationController
{
public:
    MyView (Steinberg::Vst::EditController* editController = nullptr);
    ~MyView ();

    // --- CView methods ---
    
    void draw (CDrawContext *pContext) override;
    
    CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override;
    CMouseEventResult onMouseUp (CPoint& where, const CButtonState& buttons) override;
    CMouseEventResult onMouseMoved (CPoint& where, const CButtonState& buttons) override;
    
    CLASS_METHODS (MyView, CView)

private:
    
    // --- IDependent (FObject) methods ---
    
    void PLUGIN_API update (Steinberg::FUnknown* changedUnknown, Steinberg::int32 message) override;

    // --- attributes ---
    
    Steinberg::Vst::EditController* editController;
     
    Steinberg::Vst::Parameter* parameter1;
    Steinberg::Vst::Parameter* parameter2;
    
    float mValue1;
    float mValue2;
};
}

MyView.cpp

namespace VSTGUI {

MyView::MyView (Steinberg::Vst::EditController* editController)
: DelegationController (nullptr)
, CView (CRect(0,0,0,0))
, editController(editController)
, mValue1(0)
, mValue2(0)
{
    // retrieve the parameter that we are interested in from controller
    parameter1 = editController->getParameterObject (kParamId1);
    parameter2 = editController->getParameterObject (kParamId2);
    
    // listen to these parameters (parameter changes will trigger update() )
    if (parameter1)
        parameter1->addDependent (this);
    if (parameter2)
         parameter2->addDependent (this);
}

MyView::~MyView ()
{
    if (parameter1)
        parameter1->removeDependent (this);
    if (parameter2)
        parameter2->removeDependent (this);
}

// ...

CMouseEventResult MyView::onMouseDown (CPoint& where, const CButtonState& buttons)
{
    // ...
    // editController->beginEdit(tag);
    // ...
}


CMouseEventResult MyView::onMouseMoved (CPoint& where, const CButtonState& buttons)
{
    // ...
    // editController->performEdit (tag, value);
    // editController->setParamNormalized (tag, value);
    // ...
}

CMouseEventResult MyView::onMouseUp (CPoint& where, const CButtonState& buttons)
{
     // ...
    // editController->endEdit(tag);
    // ...
    
}

void PLUGIN_API MyView::update (Steinberg::FUnknown* changedUnknown,
                                      Steinberg::int32 message)
{
    // if a parameter value is changed (by host or by another control) this
    // method is called
    
    auto* p = Steinberg::FCast<Steinberg::Vst::Parameter> (changedUnknown);
    if (p)
    {
        if (message == kChanged)
        {
            // update local parameter value and draw()
            switch (p->getInfo().id) {
                 case kParamId1:
                     mValue1 = p->getNormalized ();
                     setDirty (true);
                    break;
                 case kParamId2:
                     mValue2 = p->getNormalized ();
                     setDirty (true);
                    break;
                 default:
                     break;
             }
         } else if (message == kWillDestroy)
        {
            // stop listening to parameter changes
            if (parameter1)
                parameter1->removeDependent (this);
            if (parameter2)
                parameter2->removeDependent (this);
            parameter1 = nullptr;
            parameter2 = nullptr;
        }
    }
}
}