Button Value Toggle UndoManager

Hello JUCE family.

I have now hopped onto the ValueTree and UndoManager train to make data manipulation, Undo/Redo, and serialization a bit easier.

Right now I have a Button and I want it to toggle a boolean value in my ValueTree. To do this, I setup the Button to toggle it’s internal Value on click, and I make sure the internal Value of the Button references the corresponding Value in my ValueTree. Also, I have an UndoManager that is connected to the ValueTree properties via .setProperty() calls.

To connect the Button with my ValueTree & UndoManager, I’ve used the following lines:

// Clicking will toggle the boolean of the internal `Value` object
myButton.setClickingTogglesState (true);

// The `Button's` internal toggle `Value` should reference a property in my `ValueTree`
myButton.getToggleStateValue().referTo (myValueTree.getPropertyAsValue (myPropertyIdentifier, myUndoManager));

My problem is: if I make multiple clicks on my Button, all clicks get grouped into a single transaction in my UndoManager. So, if my Button started in a true toggle state, then I clicked the button four times in a row, reaching a true toggle state again, and then I called undoManager.undo(), all of the clicks would be undone, bringing the toggle state back to true instead of false. I want each click to be an independent transaction. Is there a connection I am missing?

I know I could just get a callback with Button::Listener every time the button is clicked and add it to the UndoManager history with myUndoManager.beginNewTransaction(), but I thought there might be a more automatic solution.


1 Like

Is the attachment not working for you?
(Set button as toggle)

From my understanding I do not need to use the AudioProcessorValueTreeState::ButtonAttachment since I’m not using an AudioProcessorValueTreeState but rather just a plain ValueTree. The connection between the Button and the ValueTree property is initiated by the myButton.getToggleStateValue().referTo(..) call written in my last post.

From what I can tell, the call to getPropertyAsValue (myPropertyIdentifier, myUndoManager) correctly connects my UndoManager to the Button toggle. After diving into the source code, I see that when the Button is toggled, the Value change does indeed use my UndoManager to .perform() an action. BUT this action is added to the current UndoManager transaction instead of being added as its own independent new transaction.

Feature Request:
It would be nice if there was a boolean parameter called changesCreateNewTransactions in the getPropertyAsValue() function of ValueTree. So this way, I could choose to create a new Transaction in my UndoManager every time the button is toggled.

No, that wouldn’t work at all - you can’t have transactions created in response to the valuetree changing, because you’ll also get change events when you’re undoing/redoing, and if you try to inject a transaction at that point, it’ll mess up the undo history.

The only safe place to trigger a new transaction is from an actual GUI event, so you know that it’s in response to the user actually doing something. So you’d add it to the actual mouse-click event or something. I think in tracktion we have a timer that triggers a new transaction every couple of seconds as long as the user’s not in the middle of a mouse-drag operation, and that works out about right.


Oh wow I didn’t think about that. Thank you for the insight and tips Jules! That’s exactly what I needed to direct me forward.

I’m currently experiencing the same problem with sliders. I understand the reason why it works like this but I haven’t understood how I can work around this design to obtain slider position update with values on the valuetree and vice versa. Would anyone mind to explain a solution to this problem?

I’ve tried using this library from @daniel but the behavior is the same as described above. Am I missing something?