Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice

Disable "size"-Attribute for PropertyDrawer

Discussion in 'Immediate Mode GUI (IMGUI)' started by HoloGuy, Mar 24, 2017.

  1. HoloGuy

    HoloGuy

    Joined:
    Oct 4, 2016
    Posts:
    11
    Hi everyone,

    I'm looking for a way to disable the standard size attribute on top of my custom PropertyDrawer.

    size.PNG

    This is the code for my PropertyDrawer:

    Code (CSharp):
    1. using UnityEditor;
    2. using UnityEngine;
    3.  
    4. [CustomPropertyDrawer(typeof(TrainingGestureData))]
    5. public class TrainingGestureDataDrawer : PropertyDrawer
    6. {
    7.     public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    8.     {
    9.         SerializedProperty gestureId = property.FindPropertyRelative("GestureID");
    10.         SerializedProperty gestureName = property.FindPropertyRelative("GestureName");
    11.         SerializedProperty inputData = property.FindPropertyRelative("InputData");
    12.  
    13.         EditorGUI.BeginProperty(position, label, property);
    14.  
    15.         int ident = EditorGUI.indentLevel;
    16.         EditorGUI.indentLevel = 0;
    17.  
    18.         // Calculate rects
    19.         float frw = position.width / 20;
    20.  
    21.         Rect labelRect = new Rect(position.x, position.y, 1 * frw, 16);
    22.         Rect nameRect = new Rect(position.x + 1 * frw, position.y, 18 * frw, 16);
    23.         Rect buttonClearRect = new Rect(position.x + 19 * frw, position.y, 1 * frw, 16);
    24.  
    25.         Rect numberRect = new Rect(position.x + 1 * frw, position.y + 18, 5 * frw, 16);
    26.         Rect buttonTrainRect = new Rect(position.x + 5 * frw, position.y + 18, frw * 3, 16);
    27.         Rect buttonTrainDelayedRect = new Rect(position.x + 8 * frw, position.y + 18, frw * 5, 16);
    28.         Rect buttonRemoveRect = new Rect(position.x + 13 * frw, position.y + 18, frw * 6, 16);
    29.  
    30.         // Draw fields (use GUIContent.none to disable label)
    31.         EditorGUI.LabelField(labelRect, gestureId.intValue.ToString());
    32.         EditorGUI.PropertyField(nameRect, gestureName, GUIContent.none);
    33.         EditorGUI.LabelField(numberRect, "Inputs: " + inputData.arraySize);
    34.  
    35.         // Get object
    36.         Training trainingObj = (Training)property.serializedObject.targetObject;
    37.  
    38.         if (GUI.Button(buttonClearRect, "-"))
    39.         {
    40.             trainingObj.DeleteGesture(gestureId.intValue);
    41.         }
    42.  
    43.         if (GUI.Button(buttonTrainRect, "Record"))
    44.         {
    45.             trainingObj.StartCoroutine(trainingObj.Record(gestureId.intValue));
    46.         }
    47.  
    48.         if (GUI.Button(buttonTrainDelayedRect, "Record Delayed"))
    49.         {
    50.             trainingObj.StartCoroutine(trainingObj.RecordDelayed(gestureId.intValue));
    51.         }
    52.  
    53.         if (GUI.Button(buttonRemoveRect, "Remove Last Input"))
    54.         {
    55.             trainingObj.RemoveLast(gestureId.intValue);
    56.         }
    57.  
    58.         EditorGUI.indentLevel = ident;
    59.  
    60.         EditorGUI.EndProperty();
    61.     }
    62.  
    63.     public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    64.     {
    65.         return 45.0f;
    66.     }
    67. }
    68.  
    Greetings
     
  2. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,686
    It looks like GestureData is an array or list. Is that correct? For example, something like:

    Code (csharp):
    1. public TrainingGestureData[] m_GestureData;
    If so, there's no way to specify a custom property drawer for an array or list. You could wrap it in a class and define a custom property drawer for the class:

    Code (csharp):
    1. [Serializable]
    2. public class GestureDataArray {
    3.     public TrainingGestureData[] m_GestureData;
    4. }
    Code (csharp):
    1. [CustomPropertyDrawer(typeof(GestureDataArray))]
    2. public class GestureDataArrayDrawer : PropertyDrawer {...
    Or you could write a custom editor for the encompassing class.
     
  3. HoloGuy

    HoloGuy

    Joined:
    Oct 4, 2016
    Posts:
    11
    Hi TonyLi!

    Thank you for your answer! You are correct, TrainingGestureData is a list of wrapped and serialized arrays of doubles:

    Code (CSharp):
    1. [System.Serializable]
    2. public class TrainingGestureData
    3. {
    4.     [SerializeField]
    5.     private static int _counter = 0;
    6.  
    7.     public int GestureID;
    8.     public string GestureName = "(Gesture Name)";
    9.     public List<TrainingInputData> InputData;
    10.  
    11.     public TrainingGestureData()
    12.     {
    13.         GestureID = _counter;
    14.         InputData = new List<TrainingInputData>();
    15.  
    16.         _counter++;
    17.     }
    18. }
    Code (CSharp):
    1.  
    2. [System.Serializable]
    3. public class TrainingInputData
    4. {
    5.     public double[] Inputs;
    6.  
    7.     public TrainingInputData(double[] inputs)
    8.     {
    9.         Inputs = inputs;
    10.     }
    11. }
    12.  
    But writing a custom property drawer is what I've already done, isn't it?
     
  4. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,686
    You've written a custom property drawer for a single TrainingGestureData object.

    But is Gesture Data an array/list of TrainingGestureData objects? If so, it's not possible to define a custom property drawer for an array or list. You'll have to wrap it into another class and define a custom property drawer for that class.
     
  5. HoloGuy

    HoloGuy

    Joined:
    Oct 4, 2016
    Posts:
    11
    Ah, got it! Thank you!
     
  6. LeEHil

    LeEHil

    Joined:
    Mar 23, 2020
    Posts:
    2
    I know this is an old post, but still, there is not really a satisfying answer to me. So first of all, if I understood your question right there are several ways to hide/disable the "size" field. Just in case anyone else will need this in the future:


    Solution 1:

    Code (CSharp):
    1.          private void DisplayArray(SerializedProperty array, string caption=null)
    2.          {
    3.              EditorGUILayout.LabelField((caption == null) ? array.name : caption);
    4.              int size = array.arraySize;
    5.              array.Next(true);       //generic field (list)
    6.              array.Next(true);       //arrays size field
    7.              {
    8.                  GUI.enabled = false;
    9.                  EditorGUILayout.PropertyField(array);
    10.              }
    11.              for (int i = 0; i < size; i++)
    12.              {
    13.                  array.Next(false);       //first array element        
    14.                  EditorGUILayout.PropertyField(array);
    15.              }
    16.          }

    Solution 2:

    Code (CSharp):
    1.          private void DisplayArray(SerializedProperty array, string caption=null)
    2.          {
    3.              EditorGUILayout.LabelField((caption == null) ? array.name : caption);
    4.              for (int i = 0; i < array.arraySize; i++)
    5.              {
    6.                  EditorGUILayout.PropertyField(array.GetArrayElementAtIndex(i));
    7.              }
    8.              //other methods:
    9.              //array.InsertArrayElementAtIndex;
    10.              //array.DeleteArrayElementAtIndex;
    11.              //array.MoveArrayElement;
    12.          }
    13.      }
    In the inspector this will look like this:




    Now all you'd have to do is:

    Code (CSharp):
    1.      [CustomEditor(typeof(blablabla))]
    2.      public class blablablaEdior : Editor
    3.      {
    4.          public override void OnInspectorGUI()
    5.          {
    6.              serializedObject.Update();
    7.              SerializedProperty listProp = serializedObject.FindProperty("list");
    8.              DisplayArray(listProp);        
    9.              serializedObject.ApplyModifiedProperties();
    10.          }
    11.     }
    see also my answer here: https://answers.unity.com/questions...e-box.html?childToView=1832407#answer-1832407
     
    marcospgp likes this.
  7. marcospgp

    marcospgp

    Joined:
    Jun 11, 2018
    Posts:
    194
    With the new UI Toolkit workflow (https://docs.unity3d.com/2021.2/Documentation/Manual/UIE-HowTo-CreateCustomInspector.html) I imagine that this would break when the array is modified, right? Because the GUI wouldn't be created from scratch and thus the array size would not reflect the lastest value.
     
  8. marcospgp

    marcospgp

    Joined:
    Jun 11, 2018
    Posts:
    194
    Aaand here's the code for disabling the size field in a custom inspector with UI Toolkit:

    Code (CSharp):
    1.  
    2. using UnityEditor;
    3. using UnityEditor.UIElements;
    4. using UnityEngine.UIElements;
    5.  
    6. [CustomEditor(typeof(MyComponent))]
    7. public class MyComponentInspector : Editor {
    8.     public override VisualElement CreateInspectorGUI() {
    9.         var inspector = new VisualElement();
    10.  
    11.         var myArray = new PropertyField(
    12.             this.serializedObject.FindProperty("myArray")
    13.         );
    14.  
    15.         inspector.Add(myArray);
    16.  
    17.         // Wait for property field to be populated before modifying it.
    18.         // Source: https://forum.unity.com/threads/solved-how-to-force-update-visual-element-on-the-current-frame.727040/#post-4984787
    19.         _ = myArray.schedule.Execute(() => {
    20.             // Get size field of array
    21.             IntegerField sizeField = myArray.Q<IntegerField>();
    22.  
    23.             // Disallow changing array size in inspector
    24.             sizeField.SetEnabled(false);
    25.         });
    26.  
    27.         return inspector;
    28.     }
    29. }
    Note that custom inspectors should be defined in an Editor folder, or be surrounded by equivalent directives:

    Code (CSharp):
    1.  
    2. #if UNITY_EDITOR
    3.     // Editor-only code here
    4. #endif