Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Bug with Undo functionality while binding.

Discussion in 'UI Toolkit' started by Yamin-Nather, Oct 23, 2020.

  1. Yamin-Nather

    Yamin-Nather

    Joined:
    Dec 31, 2014
    Posts:
    5
    TLDR Version- I want to know whats the best way to display Propertyfield's of SerializedFields of an EditorWindow class without creating ghost Undo's that are still present once the EditorWindow is deleted.

    Detailed Version:
    The way I first tried to display fields for a serialized variable of the editor window object was to use a Property Field bound(or binded?? I dont know lol) to the variable because EditorWindows inherit from Scriptable Object and everything seemed to work fine. I make changes in the Editor Window and it updates the variables and vice versa, and Undo works just as expected too.

    But the problem starts to show up once the Editor Window is deleted. An Undo is registered when I change a property field, and the undo persists even after closing the Editor Window. So if I open an Editor Window and make like around 20 changes and then close the window, now if I want to revert the editor back to how it was before opening the Editor Window, I would have to Undo 20 times for no reason, because the Editor Window doesnt reopen too when you Undo.

    The Undo's name is "Undo Inspector", so basically I would have 20 blank 'Undo Inspector' undos because the Editor Window is no longer open.

    So can some tell me a better way to do this? Removing the Undos for an object once the concerned object is deleted should be a built in thing unless explicitly told to not happen with Undo.DeleteObject or whatever that function is cant remember it correctly atm.

    Steps to recreate this issue:

    Step 1: Opening the Editor Window for the first time

    upload_2020-10-23_9-0-0.png


    Step 2: Changing a value

    upload_2020-10-23_9-1-41.png

    Step 3: Checking out the undo created for the change
    upload_2020-10-23_9-3-10.png


    Step 4: Doing the change for like 10 times(Repeat of Step 2)

    Step 5: Closing the Window
    upload_2020-10-23_9-6-11.png



    Step 6: Check that the undo still persists as a ghost Undo(not one but ten of these because we changed value like ten times), but we dont want that!!
    upload_2020-10-23_9-9-16.png




    My Editor Window Script:


    Code (CSharp):
    1.  
    2. using UnityEditor;
    3. using UnityEditor.UIElements;
    4. using UnityEngine;
    5.  
    6. public class TestEditorWindow : EditorWindow
    7. {
    8.     #region Variables
    9.     private static TestEditorWindow s_Window;
    10.     [SerializeField] private float m_TestFloat;
    11.     #endregion
    12.  
    13.     [MenuItem("Test Editor Window/Open")]
    14.     public static void OpenWindow_F()
    15.     {
    16.         if (s_Window != null) s_Window.Close();
    17.         s_Window = CreateWindow<TestEditorWindow>();
    18.     }
    19.  
    20.     private void OnEnable()
    21.     {
    22.         UICreate_F();
    23.     }
    24.  
    25.     private void UICreate_F()
    26.     {
    27.         PropertyField propertyField0 = new PropertyField();
    28.         propertyField0.BindProperty(new SerializedObject(this).FindProperty("m_TestFloat"));
    29.         rootVisualElement.Add(propertyField0);
    30.     }
    31. }
    32.  
    There's another... i guess its a bug im not really sure but creating a property field and binding it to a serializedProperty through the constructor parameters dont work, the property field is not displayed at all.

    Code (CSharp):
    1. PropertyField propertyField0 = new PropertyField(new SerializedObject(this).FindProperty("m_TestFloat"));
    2.  
    3. rootVisualElement.Add(propertyField0);
    4.  
    But it works if I bind it using the BindProperty method of the PropertyField.

    Code (CSharp):
    1. PropertyField propertyField0 = new PropertyField();
    2. propertyField0.BindProperty(new SerializedObject(this).FindProperty("m_TestFloat"));
    3. rootVisualElement.Add(propertyField0);

    Nothing major but a one liner becomes a three liner because of this :D.
     
  2. sebastienp_unity

    sebastienp_unity

    Unity Technologies

    Joined:
    Feb 16, 2018
    Posts:
    165
    Unfortunately the current Unity API doesn't allow you do discard undos on object after the object is deleted.

    You can observe this behavior with the following repro steps:
    1- Create material
    2- Select the material
    3- Do a bunch of edit in the Inspector
    4- Delete the material.
    5- See that a bunch of undos still exist and relates to the deleted material.

    There are no public API to crawl the undo queue and remove specific command.

    As for the Binding system in the PropertyField: you are doing the right thing. If you used a UXML and use the loadTree function I think the binding is done automatically.

     
  3. uDamian

    uDamian

    Unity Technologies

    Joined:
    Dec 11, 2017
    Posts:
    1,203
    We partially solved this in the UI Builder (which also wants to have it's own "undo stack" bound to its lifetime) using:

    https://docs.unity3d.com/ScriptReference/Undo.RegisterCompleteObjectUndo.html

    to register our own Undo actions on a separate self-managed ScriptableObject (not the EditorWindow itself). Then use:

    https://docs.unity3d.com/ScriptReference/Undo.ClearUndo.html

    on Window close to clear all undos related to our state object. This does also mean you have to do you own custom bindings where you modify the object directly and not via PropertyFields and SerializedObject bindings.

    Though, it's worth pushing the limits I listed above since it might work in more cases than what I tested.
     
  4. Yamin-Nather

    Yamin-Nather

    Joined:
    Dec 31, 2014
    Posts:
    5

    Sorry for the late reply, but yeah, I was using Undo.RegiserCompleteObjectUndo like u said before, but it would have been better if it happened automatically and I felt it wasnt worth it to do it like that. But if Undo works that way, then I guess i'll just have to deal with it.

    Thanks you for clearing my doubts!