x


Undo functionality for Custom Editor

*Edit: Solution below in edited code. ////////////////////////////////////////////////////

I've been trying to incorporate undo functionality for my custom editor script for a few hours now, with no success.

I've tried all functions associated with the "undo" class, but I just cannot get it to work.

Can someone please explain the proper way to get a slider value tied into the undo system?

The (fixed) code I now have:

using UnityEditor;
using UnityEngine;

public class sliderTester2 : EditorWindow{

    [SerializeField]        float sliderValue = 1f;

    [MenuItem("Slider Tester/ Slider Test v2")]
    static void Init() {

        sliderTester2 myWindow = (sliderTester2)EditorWindow.GetWindow(typeof(sliderTester2));
        myWindow.autoRepaintOnSceneChange = true;

    }

    void OnGUI(){

        checkMouse();

     //   Undo.SetSnapshotTarget(this, "Slider Value Changed");

        GUILayout.BeginVertical("box");

        GUILayout.Label("Test Slider");
        sliderValue = EditorGUILayout.Slider(sliderValue, .1f, 5f);

        if (GUI.changed){

        //    Undo.CreateSnapshot();
        //    Undo.RegisterSnapshot();

        }

        GUILayout.EndVertical();

        Undo.ClearSnapshotTarget();

        this.Repaint();

    } // on GUI

    void checkMouse(){

        Event e = Event.current;

        if (e.button == 0 && e.isMouse){

            Debug.Log("Mouse down");
            Undo.SetSnapshotTarget(this, "Changed Slider");
            Undo.CreateSnapshot();
            Undo.RegisterSnapshot();

        }
    }


} // class definition
more ▼

asked Mar 12 '11 at 06:47 PM

reissgrant gravatar image

reissgrant
246 14 15 25

(comments are locked)
10|3000 characters needed characters left

1 answer: sort voted first

From the reference:

Certain operations, such as dragging, consist of many small incremental changes. Typically it is not desired to create an undo step for each of these small changes. For example, if the user performs an undo after a dragging operation, it is expected that the object is reverted back to the state it had before the dragging started. The functions SetSnapshotTarget, CreateSnapshot, and RegisterSnapshot are available to handle cases like this.

I followed the links to get to this code. I tested it and it works, however for some strange reason I get imprecision for the value when I try to undo. 0.1 might become 0.123 et.c. Also, if you select the text box with the value, then drag the slider, you will be creating snapshots for every single frame for some reason. I tried to make the code so it only create a snapshot when the mouse button is released but I couldn't get any input code working (Neither Input.GetMouseButtonXxx nor Event.current.button worked for me)! If you know a way to handle this case you might get a better solution.

Edit: You might get this to work if you set myWindow.wantsMouseMove = true; in init, although I leave that for you to play around with. Note that the user can change the value both by using the slider and editing the text so it might get complex.

Anyhow, there's 5 changes to your code. See comments in code, they are also numbered for ease.

  1. You need your variables to be serializable. It must not be static and it must be either public or have the SerializeField attribute.
  2. (Edit, see below) Hook up with key modifiers to repaint our scene.
  3. Set this as being the current snapshot target.
  4. Create and Register snapshot when the GUI was changed. If possible, this code could benefit from an input test to only perform these operations when the user releases the mouse button.
  5. Clear the snapshot target at end of call. I don't know if this is necessary but that's how I did it anyway.

Worth noting is that it seems there is a slight delay from when you press CTRL + Z (Undo shortcut) to the actual value is undone. This doesn't seem to be the case if the text box is selected when you do this. I don't know why this happens. Maybe I am overlooking something or maybe it's some mechanic of UnityEditor that does this.

Edit: After further investigation it seems that OnGUI isn't called continously. It is only called when the user interacts with the GUI. This causes the lag inside the editor window since it isn't repainted. I fixed this by adding Repaint to key modifiers although it might not be the best way to go about. It solved this particlular case however and I've updated the code.

And the code follows:

using UnityEditor;
using UnityEngine;

public class sliderTester : EditorWindow
{
    // 1.
    // Drop the static and make it serializable
    // since Undo snapshots serialize the object.
    [SerializeField]
    float sliderValue = 1f;

    [MenuItem("Slider Tester/ Slider Test v1")]
    static void Init()
    {
        sliderTester myWindow = (sliderTester)EditorWindow.GetWindow(typeof(sliderTester));
        myWindow.autoRepaintOnSceneChange = true;

        // 2.
        // Since shortcut undo doesn't seem to update the window, we
        // hook up to a delegate to do this ourselves.
        EditorApplication.modifierKeysChanged += myWindow.Repaint;
    }

    void OnGUI()
    {
        // 3.
        // Set this being the snapshot target.
        Undo.SetSnapshotTarget(this, "Changed Settings");

        GUILayout.BeginVertical("box");

        GUILayout.Label("Test Slider");
        sliderValue = EditorGUILayout.Slider(sliderValue, .1f, 5f);

        // 4.
        // Handle snapshots when gui changes.
        if (GUI.changed)
        {
            Undo.CreateSnapshot();
            Undo.RegisterSnapshot();
        }

        GUILayout.EndVertical();

        // 5. (Not sure if needed)
        // Clear the snapshot at end of call.
        Undo.ClearSnapshotTarget();

    } // on GUI
} // class definition
more ▼

answered Mar 15 '11 at 04:39 PM

Statement gravatar image

Statement ♦♦
20.1k 35 70 175

Thanks, it appears that the "static" and "serializable" changes were the reason I could never get results. The imprecision is not acceptable though, because these sliders are intended to adjust RGB values. It still gets me further than what I had so if nothing else comes along today I will mark your answer as accepted. Thanks again ;)

Mar 15 '11 at 05:12 PM reissgrant

It might be the imprecision can be solved using wantsMouseMove. I think it can be due the editor not picking up the "last" gui update. What you can do if you want to force gui updates is to call Repaint in Update() instead of using the delegate in init. Have a try with that and report back if it works so people can reap the benefits of collaborative wisdom :)

Mar 15 '11 at 05:21 PM Statement ♦♦

Ok I found a solution to the imprecision bug, and updated my code in my first post to reflect my solution. Thanks again Statement!

Mar 15 '11 at 05:22 PM reissgrant
(comments are locked)
10|3000 characters needed characters left
Your answer
toggle preview:

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this question

By Email:

Once you sign in you will be able to subscribe for any updates here

By RSS:

Answers

Answers and Comments

Topics:

x1670
x199
x28

asked: Mar 12 '11 at 06:47 PM

Seen: 2052 times

Last Updated: Mar 15 '11 at 05:41 PM