Search Unity

  1. Good news ✨ We have more Unite Now videos available for you to watch on-demand! Come check them out and ask our experts any questions!
    Dismiss Notice
  2. Ever participated in one our Game Jams? Want pointers on your project? Our Evangelists will be available on Friday to give feedback. Come share your games with us!
    Dismiss Notice

Bug Can't create bindings for an enum - "not compatible"

Discussion in 'UI Toolkit' started by Baste, Aug 14, 2019.

  1. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    4,755
    Code (csharp):
    1. public enum MyEnum
    2. {
    3.     A,
    4.     B,
    5.     C
    6. }
    7.  
    8. public class Test : MonoBehaviour
    9. {
    10.     public MyEnum myEnum;
    11. }
    12.  
    13. [CustomEditor(typeof(Test))]
    14. public class TestEditor : Editor
    15. {
    16.     private VisualElement rootElement;
    17.  
    18.     private void OnEnable()
    19.     {
    20.         rootElement = new VisualElement();
    21.     }
    22.  
    23.     public override VisualElement CreateInspectorGUI()
    24.     {
    25.         rootElement.Clear();
    26.  
    27.         var enumField = new EnumField("My Enum");
    28.         enumField.bindingPath = "myEnum";
    29.  
    30.         rootElement.Add(enumField);
    31.         rootElement.Bind(serializedObject);
    32.  
    33.         return rootElement;
    34.     }
    35. }
    Gives:
    Code (csharp):
    1. Field type UnityEditor.UIElements.EnumField is not compatible with Enum property "myEnum"
    2. UnityEditor.UIElements.BindingExtensions:Bind(VisualElement, SerializedObject)
    3. TestEditor:CreateInspectorGUI() (at Assets/Editor/TestEditor.cs:23)
    4. UnityEditor.InspectorWindow:RedrawFromNative()
    I tried making a PopupField<MyEnum>, that gave the same result.

    This should work, right?
     
  2. antoine-unity

    antoine-unity

    Unity Technologies

    Joined:
    Sep 10, 2015
    Posts:
    158
    Hello,

    This is an oversight that is supposed to be fixed in 2019.3, but as it turned out I have encountered a different issue while trying out your script in both 2019.3 and 2019.2. We've added that to our list of issues.

    Which version of Unity were you trying this with ?
     
  3. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    4,755
    2019.2.0f1
     
  4. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    4,755
    I worked around it by using a PropertyField for the property, but that runs into a different issue; I'm not able to get
    RegisterCallback to do anything for a PropertyField that targets an enum:

    Code (csharp):
    1.  
    2. using System;
    3. using UnityEditor;
    4. using UnityEditor.UIElements;
    5. using UnityEngine;
    6. using UnityEngine.UIElements;
    7.  
    8. [CustomEditor(typeof(Test))]
    9. public class TestEditor : Editor
    10. {
    11.     private VisualElement rootElement;
    12.  
    13.     private void OnEnable()
    14.     {
    15.         rootElement = new VisualElement();
    16.     }
    17.  
    18.     public override VisualElement CreateInspectorGUI()
    19.     {
    20.         rootElement.Clear();
    21.  
    22.         var enumProp = new PropertyField(serializedObject.FindProperty("myEnum"));
    23.         var intProp  = new PropertyField(serializedObject.FindProperty("val"));
    24.  
    25.         // none of these gets called
    26.         enumProp.RegisterCallback<ChangeEvent<MyEnum>>(Callback_MyEnum);
    27.         enumProp.RegisterCallback<ChangeEvent<Enum  >>(Callback_Enum);
    28.         enumProp.RegisterCallback<ChangeEvent<int   >>(Callback_Int);
    29.         enumProp.RegisterCallback<ChangeEvent<object>>(Callback_Object);
    30.  
    31.         // this gets called
    32.         intProp.RegisterCallback<ChangeEvent<int>>(Callback_Int);
    33.  
    34.         rootElement.Add(enumProp);
    35.         rootElement.Add(intProp);
    36.         rootElement.Bind(serializedObject);
    37.         return rootElement;
    38.     }
    39.  
    40.     private void Callback_MyEnum(ChangeEvent<MyEnum> evt) => Debug.Log("My enum");
    41.     private void Callback_Int   (ChangeEvent<int>    evt) => Debug.Log("int");
    42.     private void Callback_Enum  (ChangeEvent<Enum>   evt) => Debug.Log("enum");
    43.     private void Callback_Object(ChangeEvent<object> evt) => Debug.Log("object");
    44. }
     
  5. unity_IpxdANggCs1roQ

    unity_IpxdANggCs1roQ

    Joined:
    Feb 19, 2018
    Posts:
    28
    Maybe, this could be helpful. But, I'm on 2019.3.0a12
    Code (CSharp):
    1.  
    2. [CustomEditor(typeof(Test))]
    3. public class TestEditor : Editor
    4. {
    5.   Test _boundData;
    6.  
    7.   public override VisualElement CreateInspectorGUI()
    8.   {
    9.     /*
    10.     ...
    11.     after setting up your inspector
    12.     ...
    13.      */
    14.  
    15.     // get object which is bound with this custom editor
    16.     _boundData = (Test)target;
    17.     // set visualelement reference which you want to send event to from your Test object.
    18.     _boundData.event_send_target = yourVisualElement;
    19.     // register event callback
    20.     yourVisualElement.RegisterCallback<ChangeEvent<Test>>(your_callback_function);
    21.  
    22.   }
    23. }
    24.  
    25. public class Test : MonoBehaviour
    26. {
    27.   public MyEnum myEnum;
    28.   public VisualElement event_send_target;
    29.  
    30. #if UNITY_EDITOR
    31.   // detect change and send event
    32.   void OnValidate()
    33.   {
    34.     // send event
    35.     if (event_send_target != null)
    36.     {
    37.       using (var changeEvent = ChangeEvent<Test>.GetPooled())
    38.       {
    39.         changeEvent.target = event_send_target;
    40.         event_send_target.SendEvent(changeEvent);
    41.       }
    42.     }
    43.   }
    44. #endif
    45. }
    46.   public enum MyEnum
    47.   {
    48.     A,
    49.     B,
    50.     C
    51.   }
     
    Last edited: Aug 17, 2019
    Baste likes this.
  6. unity_IpxdANggCs1roQ

    unity_IpxdANggCs1roQ

    Joined:
    Feb 19, 2018
    Posts:
    28
  7. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    4,755
    Thanks!

    I'd really prefer not to leak details about the editor into the runtime code, but I can hide those details behind #if's, so I'll look into if it's viable..
     
  8. antoine-unity

    antoine-unity

    Unity Technologies

    Joined:
    Sep 10, 2015
    Posts:
    158
    Hello!

    So here's the whole picture for this issue. The message
    Field type UnityEditor.UIElements.EnumField is not compatible with Enum property "myEnum"
    is unfortunately accurate for 2019.2. The work for making EnumField truly on par with IMGUI was done only for 2019.3 and backporting seems unlikely as it required quite a lot of refactoring. This was required to make binding work correctly.

    In the mean time it's possible to bind a PopupField<string> to an enum property. You can register for
    ChangeEvent<string>
    to detect changes. If you need to make sense of the value, the
    index
    property of the
    PopupField
    object can be used in conjunction with
    SerializedProperty.enumValueIndex
    .

    Right in both 2019.2 and 2019.3 PopupField suffers the issue I've described earlier about a NRE due to the specific order of operations in the script you've shared (EnumField has this issue in 2019.3 as well).

    We'll fix this of course but the workaround is to remove the call to
    Bind()
    inside
    CreateInspectorGUI()
    as this already done by the system when calling this method.

    So here is something that should work in 2019.2 :

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEditor;
    3. using UnityEditor.UIElements;
    4. using UnityEngine.UIElements;
    5.  
    6. public enum MyEnum
    7. {
    8.     A,
    9.     B,
    10.     C
    11. }
    12. public class Test : MonoBehaviour
    13. {
    14.     public MyEnum myEnum;
    15. }
    16. [CustomEditor(typeof(Test))]
    17. public class TestEditor : Editor
    18. {
    19.     private VisualElement rootElement;
    20.     private void OnEnable()
    21.     {
    22.         rootElement = new VisualElement();
    23.     }
    24.     public override VisualElement CreateInspectorGUI()
    25.     {
    26.         rootElement.Clear();
    27.         var enumField = new PopupField<string>("My Enum");
    28.         enumField.bindingPath = "myEnum";
    29.         rootElement.Add(enumField);
    30.         // This hits a bug in Unity but an explicit Bind() is not necessary anyways      
    31.         // rootElement.Bind(serializedObject);
    32.  
    33.         rootElement.RegisterCallback<ChangeEvent<string>>(OnChangeEvt);
    34.         return rootElement;
    35.     }
    36.  
    37.     private void OnChangeEvt(ChangeEvent<string> evt)
    38.     {
    39.         Debug.Log(evt.newValue);
    40.     }
    41. }
    Hope this helps
     
  9. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    4,755
    Thanks!

    Backports are not an issue, this project won't have any issues updating to a newer Unity version. You could consider adding a better error message to 2019.2 - "enum isn't supported yet" or something along those lines.

    I think I called bind explicitly because that was done in some example? Not sure. If we don't need to do that - when does the binding happen? There's nothing in that code that relates the SerializedObject to the VisualElement tree.
     
  10. antoine-unity

    antoine-unity

    Unity Technologies

    Joined:
    Sep 10, 2015
    Posts:
    158
    The binding happens automatically after the system calls
    CreateInspectorGUI()
    .
    I'll look out for a mistake in our examples.
     
    Baste likes this.
  11. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    4,755
    It's probably my bad - I got it from the UI Elements Examples repo, and there the SerializedObjects are edited in an EditorWindow instead of an Editor in the Inspector Comparer Window.

    It's not super-intuitive that the binding happens automatically, but I'm super in favour of it. I've pretty much always wondered why the hell the default IMGUI Editor didn't automatically do serializedObject.ApplyModifiedProperties at the end of OnInspectorGUI.
     
  12. ayellowpaper

    ayellowpaper

    Joined:
    Dec 8, 2013
    Posts:
    36
    Glad I found this. Incredibly frustrating issue but at least there is a workaround.
     
unityunity