Repaint on Undo

I need to repaint an EditorWindow when the user performs undo/redo.

But I don't see any callbacks or events associated with undo/redo...

How can I do this?

This seems to work:

if (Event.current.type == EventType.ValidateCommand)
{
    switch (Event.current.commandName)
    {
        case "UndoRedoPerformed":

            // repaint etc.
            break;
    }
}

Add this to OnGUI(). I have it in a switch since I'm reacting to other events as well... You can debug log the commandName to get the name of the command you're interested in (that's how I found UndoRedoPerformed).

EDIT: This method turns out to be unreliable since only the active window gets the event. IOW, if the user performs undo/redo with another window active, my EditorWindow never gets the event.

It's sort of okay(-ish) for repaint since the editor window will repaint anyway when it gets focus again, but unfortunately I need to do some other stuff too (re-initialize some data). So if you need to intercept undo/redo reliably, this doesn't work.

Internally it seems that unity uses an Undo callback, but it's not exposed for tool developers :(

I'll see what I can do about making EditorApplication.undoRedoPerformed public. If you want to go cowboy here, you can try it out by registering your own callback on that event trough reflection.

I would never suggest you do this for runtime code, but for editorcode there is less risk. Please note that the reason we keep certain things internal are usually:

  • it might not work
  • we are planning to change how it works (read: your code has a high change of breaking in future unity updates, even dotreleases)

If you can live with those two constraints, give this a shot.

See http://stackoverflow.com/questions/3120422/how-to-attach-event-handler-to-an-event-using-reflection for inspiration on how to register for events using reflection.

PPS2: on every domain reload (when you press play), you will need to register your eventhandler again, events are not serialized by Unity.

This has now been replaced by Undo.undoRedoPerformed as of Unity 4.3

Usage:

Undo.undoRedoPerformed += OnUndoRedo; // subscribe to the event

void OnUndoRedo() {
    // some code here
}

According to Lucas Meijer in a message elsewhere on this page:

PPS2: on every domain reload (when you press play), you will need to register your eventhandler again, events are not serialized by Unity.

I didn’t manage to grab the events from EditorApplication but I found out the undoRedoPerformed field is still there only it is private. So I set it using reflection and now it’s working even when the window is not focused. I call this in the editor constructor:

FieldInfo undoCallback = typeof(EditorApplication).GetField("undoRedoPerformed", BindingFlags.NonPublic | BindingFlags.Static);
undoCallback.SetValue(null, (EditorApplication.CallbackFunction)OnUndoRedo);

Note that I’m working with Editor inside the inspector instead of a separate EditorWindow but I think it should work there as well.

You’ll get a validation event when an undo is performed, you can hook into that event:

public void OnSceneGUI() {
	if (Event.current.commandName == "UndoRedoPerformed") {
		OnUndoRedo();
		return;
	}
	//...
}

I just call EditorWindow.Repaint() during my Undo.undoRedoPerformed callback.

// During EditorWindow initialization...
Undo.undoRedoPerformed += OnUndoRedo;

// If your initialization code is in a static method, you need to use this instead:
var editorWindow = GetWindow<YourEditorWindowType>();
Undo.undoRedoPerformed += editorWindow.OnUndoRedo;

void OnUndoRedo()
{
    Repaint();
}