ASPiK SDK
Loading...
Searching...
No Matches
Design the Sub-Controller

The sub-controller is implemented as a C++ object that is derived from IController, the VSTGUI4 interface that "listens" to the controls in our group (two knobs and one button in total). In addition, at creation time, any controls that share the same sub-controller object will be registered with the sub-controller during a function called verifyView that will be called once for each control in the group. It is in this function that we will pick up and store pointers to all of the CAnimKnobs in the group. We will also be looking for, and will store the pointer to the CTextButton that will be the linking control. Before going on, you need to understand how sub-controllers get connected to GUI controls.

Single Controls
If you want to add a sub-controller to a single control (and there are valid reasons for doing this), then you just set the "sub-controller" attribute string in the GUI Designer (it is usually just below the "custom-view-name" entry field). At creation time, the sub-controller will be registered (by the VSTGUI core) and then verifyView will get called for the object. The sub-controller can then save a pointer to the object for use during operation.

Multiple Controls
If you want a sub-controller to operate on a set of controls that are all connected to it, then the trick in VSTGUI4 is to create a CViewContainer object to hold all of the controls and set the CViewContainer's sub-controller string in the GUI designer. The CViewContainer will be created first and will then trigger the creation of the sub-controller. After that, each GUI control inside of this container that will be visible will be registered with the sub-controller object via the verifyView method. In this way, all of the objects can share a common sub-controller that will cache pointers to the items in the group and operate on them. This idea can be extended to nested sets of CViewContainer objects as well!

Sub-Controller Members
• We will need a way to store pointers to all of the CAnimKnobs in the group; we can use a std::vector for that
• We know that there will be one and only one CTextButton for the link control, so we can just declare a single pointer for it
• We are going to also cache a pointer to the parent object that creates the sub-controller, which is the PluginGUI - it is also a IController subclassed object! We'll handle the stuff we need to make our knobs link together, and then we'll forward our unused calls back to the parent IController for further processing. You can approach this design from several vantage points, and this strategy will allow us to only minimally affect the GUI operation, with all other chores handled on the parent object.

The sub-controller is defined in the customviews.h file near the bottom so be sure to follow along in that code:

class KnobLinkController : public IController
{
// snipped code
protected:
// --- the parent controller; we can issue IController commands to it!
IController* parentController = nullptr;
// --- a CTextButton is the switcher (linkControl)
CTextButton* linkControl = nullptr;
// --- when linked, all of these controls move when one of them moves,
// regardless of their control tags
typedef std::vector<CAnimKnob*> KnobList;
KnobList linkedKnobs;
// --- flag for linking knobs
bool linkControls = false;
};
//

Constructor In the sub-controller constructor, we will cache the parent pointer and reset the boolean linking flag:

KnobLinkController(IController* _parentController)
{
// --- save the parent listener
parentController = _parentController;
// --- INITIALIZE LINK STATE
linkControls = false;
}
//

IController Overrides
There are multiple ways to handle the sub-controller functionality and in our case we will have a fairly simple setup. You should first read the documentation for the IController in the VSTGUI4 SDK (it uses Doxygen as well for documenting the library). You can find the IController interface in the ..\vstgui4\vstgui\uidescription\icontroller.h file.

Here are a list of the functions we'll override and implement with a brief note on their operation:

virtual CView* verifyView(CView* view, const UIAttributes& attributes, const IUIDescription* description)
• we will check the incoming CView* to test if it is a CAnimKnob or a CTextButton
• if it is a knob we will add it to our std::vector list
• if it is the button, we will set it as the linking device
• we will check the button to see if it is already pressed at creation time (it could be a saved state or preset) and we will set our boolean flag if so

The fundamental code is here:

CAnimKnob* knob = dynamic_cast<CAnimKnob*>(view);
CTextButton* button = dynamic_cast<CTextButton*>(view);
// --- save button, push back knob onto list
if (button)
{
linkControl = button;
if (button->getValueNormalized() != 0)
linkControls = true;
else
linkControls = false;
}
else if (knob)
linkedKnobs.push_back(knob);
//

virtual void valueChanged(CControl* control)
• we will check the incoming CControl* - if it is the link button, we'll set our flag
• if it is one of the knobs, we will check its value and if the linking flag is set, we will set all the other knobs in the list to match
• after any changes are applied, we will forward the call to the parent's valueChanged( ) function for further processing.

virtual void valueChanged(CControl* control)
{
// --- snipped code
if (control == linkControl)
{
if (control->getValueNormalized() != 0)
linkControls = true;
else
linkControls = false;
return parentController->valueChanged(control);
}
//

To set all the knobs together, we just iterate the list of controls:

// --- iterate list
for (std::vector<CAnimKnob*>::iterator itr = linkedKnobs.begin(); itr != linkedKnobs.end(); ++itr)
{
// --- set the control value for all knobs except the one generating this message
CControl* ctrl = *</i>i</i>tr;
if (ctrl && control != ctrl)
{
// --- set the control visually
ctrl->setValueNormalized(control->getValueNormalized());
// --- do the value change at parent level, to set on plugin
parentController->valueChanged(ctrl);
}
}
//

For the rest of these functions, we will forward the function call to the parent (see the code - it is straightforward)

// --- -just forward these to parent object
virtual CView* createView(const UIAttributes& attributes, const IUIDescription* description)
virtual void controlBeginEdit(CControl* pControl)
virtual void controlEndEdit(CControl* pControl)
virtual void controlTagWillChange(CControl* pControl)
virtual void controlTagDidChange(CControl* pControl)
//

Notice how the sub-controller intelligently handles the connections between the GUI objects and that it is coded in the GUI portion of the ASPiK project - the PluginCore does not know of the sub-controller's existence (in this case, but that is possible - see below). This is the proper way to handle this kind of arrangement of control functions and you should use the sub-controller paradigm for this kind of GUI customization.

With the sub-controller link in place, we need to modify the PluginGUI::createSubController( ) function to decode the incoming sub-controller name string and create the device:

IController* PluginGUI::createSubController(UTF8StringPtr name, const IUIDescription* description)
{
std::string strName(name);
int findIt = strName.find("KnobLinkController");
if (findIt >= 0)
{
// --- create the sub-controller
KnobLinkController* knobLinker = new KnobLinkController(this);
return KnobLinkController;
}
//