See my posting on the "Strange undo/redo behaviour" thread for the rationale behind this technique.
The example code uses a module with a user interface consisting of a digital counter object called "channelCounter" along with a couple of buttons called "channelUp" and "channelDown" to increment and decrement the counter, but the principle can be applied to any kind of objects.
Create a knob and place it on the panel next to the counter. Resize it to 10 x 10 pixels so that it doesn't get in the way. Set its variable name to "channelKnob" and importantly set its default value to 1. You might as well also set its minimal value, maximum value and number of discrete steps to 1, 16 and 16 respectively.
This special invisible knob is used to make the internal state of the counter automatically managed by VM so that you no longer have to worry about handling undo/redo and persistence for the counter.
An internal variable called "channel" is visible for your code to read the setting of the counter.
Here's the code made as simple as possible...
***Edited to fix subtle bug in OnUndoRedo()***
Code: Select all
// In Initialize()...
// make the knob invisible...
channelKnob.SetVisible( false );
// set initial value to match the default value of the invisible knob...
setChannel( INITIAL_VALUE );
// In Notify( ... )
case Knob_Changed: // doubleValue is the new VoltageKnob value
if( component == channelKnob )
{
setChannel( (int) doubleValue ); // (int) cast because channel is an integer rather than a double
}
break;
case Button_Changed: // doubleValue is the new button/toggle button value
if( doubleValue != OFF_VALUE )
{
// button pressed
if( component == channelUp )
{
// increment channel...
if( channel < MAX_CHANNEL )
updateChannel( channel + 1 );
else
updateChannel( MIN_CHANNEL ); // wrap around
}
else if( component == channelDown )
{
// decrement channel...
if( channel > MIN_CHANNEL )
updateChannel( channel - 1 );
else
updateChannel( MAX_CHANNEL ); // wrap around
}
}
break;
// In ProcessSample()...
// *** insert some code using value of channel ***
// In OnUndoRedo()...
if( undoType == "changeChannel" )
{
// update the invisible knob value
// this also causes an Knob_Changed event to be handled by Notify()
channelKnob.SetValue( newValue );
}
// In User variables and Functions area...
// some constants...
private static final double OFF_VALUE = 0;
private static final int MIN_CHANNEL = 1;
private static final int MAX_CHANNEL = 16;
private static final int INITIAL_VALUE= 1; // same as the invisible knob's default value
// ***** the actual variable whose value is being manipulated by the user interface...
private int channel = INITIAL_VALUE; // set to same initial value as the invisible knob's default value
// *****
// helper functions...
private void updateChannel( int newChannel )
{
// save change for undo...
CreateUndoNode( "changeChannel", "Change Channel", channel, newChannel );
// update the invisible knob...
channelKnob.SetValue( newChannel );
// set the value...
setChannel( newChannel );
}
private void setChannel( int newChannel )
{
// set the variable's value...
channel = newChannel;
// update the digital counter...
channelCounter.SetValue( channel );
}