UndoManager Transactions + Treeview

I am implementing undo support for a treeview with drag and drop and multiselection and wonder how JUCE creators and users deal with undo transactions for that. What I’d like to do is start a new transaction on user interactions, especially mouseDown. However there appears to be no easy way to do that as the treeview has a private content component which handles mouseDown by itself. If I add a mouse listener, my code only gets called after the content component mouseDown, which is too late because I also would like to include item selection in the undo transaction.

In the JuceDemo there’s a treeview example that just runs a timer which starts a new transaction every 500ms. Is that good practice? I know the undomanager ignores empty transactions, but what happens if a dragging operation is longer than 500ms, or some length calculation happens when the timer is firing.

Overall I’d like to always start transactions based on user input code and I wonder whether there might be a general solution. If it was globally possible to get all user interface events before they trigger code in the components, maybe that could be used to auto-create transactions whenever something new starts.

Or the opposite could maybe be done… waiting for user inactivity to start a new transaction which gets filled later. Is there a way to globally hook into all events?

How do you all deal with undo transactions, esp in treeviews?

I’ve generally found a good policy to be to have a timer that runs e.g. every 1 second, but which only starts a new transaction if no mouse buttons are down (which you can find out globally)

1 Like

Thanks Jules! Maybe the JuceDemo could be upgraded to include the check (ValueTreesDemo.cpp). Currently it just has:

void timerCallback() override

However overall I would like to have a better solution than a timer. The timer + mouse down check still means sometimes undo will happen in the middle of typing words or using the mousewheel. And I’m scared things might break if my code performs a “lengthy” data transformation that needs to be undoable and the timer fires in just that moment. I also saw the Projucer does begin transactions in routines like mouseDown.

If you’re doing something lengthy on the message thread then
a) it’s impossible for the timer to interrupt it
b) you shouldn’t be blocking the message thread!

But if you’re doing the transformation on a background thread, then you might need to be smarter - perhaps stop the timer while it’s running and restart afterwards.