UndoManager is very mature. I’m explicitly mentioning AudioProcessorValueTreeState usage of it.
I’ve made now another test on my personal machine with clean JUCE 5 (master 7e959).
Plain “plug-in” with only gain slider and undo redo buttons.
For the sake of keeping it short I’m only showing the important editor parts.
That’s my constructor:
addAndMakeVisible(undoBtn);
addAndMakeVisible(redoBtn);
addAndMakeVisible(gainSlider);
undoBtn.addListener(this);
redoBtn.addListener(this);
gainAttach = new AudioProcessorValueTreeState::SliderAttachment(p.params,"gain",gainSlider);
setSize (400, 200);
That’s my a simple listener for the buttons:
void AudioProcessorValueTreeUndoAudioProcessorEditor::buttonClicked (Button* btn)
{
UndoManager* undoMgt = processor.params.undoManager;
if (btn == &undoBtn)
if (undoMgt->canUndo()) undoMgt->undo();
if (btn == &redoBtn)
if (undoMgt->canRedo()) undoMgt->redo();
}
On the processor side,
Constructor inits:
,params(*this, &undoManager)
{
params.createAndAddParameter("gain", "Gain", "gain", NormalisableRange<float>(0.0,1.0), 1.0, nullptr, nullptr);
params.state = ValueTree( Identifier("undoTest"));
}
And you get those behaviors:
-
Click Undo just when running it first time will assert (UndoManager::perform:126) as:
jassertfalse; // don't call perform() recursively from the UndoableAction::perform() // or undo() methods, or else these actions will be discarded!
I can “overcome” those assertions by adding on our constructor:
undoManager.clearUndoHistory();
- Now it pretty much “works”. but there are more undoable/redoable actions than what I’d expect from the plug-in…
- start the plugin
- move slider
- undo
- Try to redo, it’ll fail.
- start the plugin
- move slider
- move slider
- Try undo, it’ll consider both moves as a single transaction…
I didn’t try equator but if you compare this to other plug-ins undo/redo. this isn’t a common workflow. or am I’m implementing it wrong?