Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Custom Property Drawer in List layout messed up

Discussion in 'Immediate Mode GUI (IMGUI)' started by NDSno1, Sep 30, 2018.

  1. NDSno1

    NDSno1

    Joined:
    Dec 20, 2014
    Posts:
    223
    Hi all,
    I'm having a weird trouble with custom Property Drawer. My current setup is like this (fairly complicated)
    A Custom Editor Window that has:
    - A list that contains:
    - Class WheelInfo. That contains:
    - Struct SuspensionData.
    For SuspensionData drawer, I just followed the Unity Live Training, because I just want to replace the float's with sliders, and foldouts to clean things up.
    For wheelInfo, same, Just to contain SuspensionData and some sliders.
    But things turns out like this:
    2018-09-30.png
    As you can see, the foldout of each element in the list is gone. The foldout for each suspensionData struct inside each WheelInfo is also gone, and the layout is messed up. I have no idea why this is happening, eventhough I'm following 100% the Live Training and the Documentation example.
    Here are the codes:
    Code (CSharp):
    1.  
    2. [CustomPropertyDrawer(typeof(WheelInfo))]
    3. public class WheelInfoInspector : PropertyDrawer {
    4.  
    5.     public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    6.     {
    7.         //base.OnGUI(position, property, label);
    8.         EditorGUI.BeginProperty(position, label, property);
    9.         position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);
    10. EditorGUILayout.PropertyField(property.FindPropertyRelative("suspension"), true);
    11.         EditorGUI.EndProperty();
    12.  
    13. }
    Code (CSharp):
    1. [CustomPropertyDrawer(typeof(SuspensionData))]
    2. public class SuspensionInspector : PropertyDrawer
    3. {
    4. public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    5.     {
    6.         //base.OnGUI(position, property, label);
    7.         EditorGUI.BeginProperty(position, label, property);
    8.         position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);
    9.  
    10.    
    11.             springFold = EditorGUILayout.Foldout(springFold, new GUIContent("Spring", "Spring Settings"), true);
    12.  
    13.             if (springFold)
    14.             {
    15.                 EditorGUILayout.Slider(property.FindPropertyRelative("suspensionTravel"), 0f, 10f);
    16.                }
    17.  
    18.         EditorGUI.EndProperty();
    19.     }
    20. }
    By the way, is there a way to render the default property drawer, and then draw extra things along with the default layout?
    Thank you very much.
     

    Attached Files:

  2. PsyKaw

    PsyKaw

    Joined:
    Aug 16, 2012
    Posts:
    102
    Hey,

    Don't use EditorGUILayout in property drawers but instead EditorGUI or GUI. Because Property Drawer has a Rect in params of OnGUI which are reserved for drawer, if you put EditorGUILayout/GUILayout, it will be drawed after the reserved rect.

    You can't use PropertyDrawer if you want tot do that. You can do it with code in your Custom Editor Window.
     
    andreiagmu and NDSno1 like this.
  3. NDSno1

    NDSno1

    Joined:
    Dec 20, 2014
    Posts:
    223
    Thank you very much for the reply.
    So I cannot get lazy and has to calculate rect on my own now, no more auto layout :)
     
  4. NDSno1

    NDSno1

    Joined:
    Dec 20, 2014
    Posts:
    223
    Update. Now I'm having trouble with the layout of list:
    2018-10-01.png
    Here is the code for a little head-up:
    Code (CSharp):
    1. EditorGUI.PropertyField(new Rect(position.x, position.y, position.size.x / 3, EditorGUI.GetPropertyHeight(property.FindPropertyRelative("wheelController"))),
    2.             property.FindPropertyRelative("wheelController"),
    3.             new GUIContent("Wheel Controller", "The wheel Controller script of this wheel"));
    4.         EditorGUI.PropertyField(new Rect(position.x + position.size.x / 3, position.y, position.size.x / 3, EditorGUI.GetPropertyHeight(property.FindPropertyRelative("wheelMesh"))),
    5.             property.FindPropertyRelative("wheelMesh"),
    6.             new GUIContent("Wheel Model", "The Mesh of this Wheel"));
    7.         EditorGUI.PropertyField(new Rect(position.x + ((position.size.x / 3) * 2), position.y, position.size.x / 3, EditorGUI.GetPropertyHeight(property.FindPropertyRelative("wheelPos"))),
    8.             property.FindPropertyRelative("wheelPos"),
    9.             new GUIContent("Wheel Position", "The position of the current wheel on the rigidbody"));
    10.         EditorGUI.Slider(new Rect(position.x, position.y + EditorGUI.GetPropertyHeight(property.FindPropertyRelative("wheelPos")), position.size.x / 2, EditorGUI.GetPropertyHeight(property.FindPropertyRelative("sprungMass"))),
    11.             property.FindPropertyRelative("sprungMass"), 0, 2000,
    12.             new GUIContent("Sprung Mass", "The mass this wheel is supporting"));
    And inside the editor window class, the list containing the wheel info class is like this:
    Code (CSharp):
    1. EditorGUILayout.PropertyField(wheelInfoListProperty, true);
    So basically, the wheel info property drawer, I'm putting 3 property fields on one line, and then the next line is a slider. I got the position of the slider within the local property rect of current wheel info class right, but there is something wrong with the layout of the list.

    I have no idea why the property fields are clogged up like this. Should the next element in the list automatically gets spaced out, and should be inside a foldout?
    I would really appriciate any help, any reply, because I cannot find any documentation related to what I'm trying to do.
     
  5. PsyKaw

    PsyKaw

    Joined:
    Aug 16, 2012
    Posts:
    102
    You have to specify the height of the drawer like this:
    Code (CSharp):
    1.  
    2. public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    3. {
    4.     int lineCount = 2;
    5.     return EditorGUIUtility.singleLineHeight * lineCount + EditorGUIUtility.standardVerticalSpacing * (lineCount-1);
    6. }
    7.  
     
    Trixxy, Nefisto and NDSno1 like this.
  6. NDSno1

    NDSno1

    Joined:
    Dec 20, 2014
    Posts:
    223
    Oh man thank you so much for your help. The wheel info drawer is mostly done now, but I'm having trouble with the foldouts of suspension drawer
    2018-10-03.png
    As you can see, the "spring" foldout opens but the height of the suspension drawer doesn't change, and all the foldouts of the other elements also opens. The foldout thingy, I honestly have no idea why.
    Here are the codes for suspension drawer:
    Code (CSharp):
    1.  static bool springFold;
    2.     static bool damperFold;
    3.     static bool geoFold;
    4.     static bool totalFold;
    5.     static bool infoFold;
    6.  
    7. public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    8.     {
    9.         //base.OnGUI(position, property, label);
    10.         EditorGUI.BeginProperty(position, label, property);
    11.         Rect pos = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive) , label);
    12.         EditorGUIUtility.labelWidth = 128;
    13.        
    14.         springFold = EditorGUI.Foldout(new Rect(pos.x, pos.y, 50, EditorGUIUtility.singleLineHeight), springFold,
    15.             new GUIContent("Spring", "Spring Settings"), true);
    16.         if (springFold)
    17.         {
    18.             springExtraLine = 2; //add lines if this foldout expands
    19.             EditorGUI.Slider(new Rect(pos.x, pos.y + EditorGUIUtility.singleLineHeight, pos.size.x / 2, EditorGUIUtility.singleLineHeight),
    20.                 property.FindPropertyRelative("suspensionTravel"), 0, 20, new GUIContent("Travel", "The Maximum Length the spring can travel in m"));
    21.             EditorGUI.Slider(new Rect(pos.x + pos.size.x / 2, pos.y + EditorGUIUtility.singleLineHeight, pos.size.x / 2, EditorGUIUtility.singleLineHeight),
    22.                 property.FindPropertyRelative("springRate"), 0, 100000, new GUIContent("Rate", "The Rate/Stiffness of the Spring in N/m"));
    23.         }
    24.         else { springExtraLine = 1; }
    25. }
    26.  public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    27.     {
    28.         int totalLine = 3 + springExtraLine + damperExtraLine + geoExtraLine;
    29.  
    30.         return EditorGUIUtility.singleLineHeight * totalLine ;
    31.  
    32.     }
    33.  
    And here is wheel info drawer that contains suspension drawer:
    Code (CSharp):
    1.  
    2.  public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    3.     {
    4. EditorGUI.PropertyField(new Rect(pos.x, pos.y + EditorGUIUtility.singleLineHeight * 2, pos.size.x, EditorGUI.GetPropertyHeight(property.FindPropertyRelative("suspension"))),
    5. EditorGUI.BeginProperty(position, label, property);
    6. Rect pos = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);
    7.             property.FindPropertyRelative("suspension"), true);
    8. EditorGUI.EndProperty();
    9. }
    10. public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    11.     {
    12.         //return base.GetPropertyHeight(property, label);
    13.         int lineCount = 4;
    14.         return EditorGUIUtility.singleLineHeight * lineCount + EditorGUIUtility.standardVerticalSpacing * (lineCount - 1);
    15.     }
    16.  
    Would you mind helping me again? Thank you. I would post the full code if you need. I'm only posting snippets now because the full code is very long.
    P.s: Unity really need to step up the documentation for editor scripting. There are so many "black magic' stuffs.
     
  7. PsyKaw

    PsyKaw

    Joined:
    Aug 16, 2012
    Posts:
    102
    You are welcome. :)

    SerializedProperty has a property isExpanded, you must use it for fold your property (so others elements foldout will not be expanded).
    Don't compute lineCount in OnGUI() because GetPropertyHeight() is called before it.
    Do something like that:
    Code (CSharp):
    1. public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    2. {
    3.     //base.OnGUI(position, property, label);
    4.     EditorGUI.BeginProperty(position, label, property);
    5.     Rect pos = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive) , label);
    6.     EditorGUIUtility.labelWidth = 128;
    7.  
    8.     SerializedProperty travelProperty = property.FindPropertyRelative("suspensionTravel");
    9.     travelProperty.isExpanded = EditorGUI.Foldout(new Rect(pos.x, pos.y, 50, EditorGUIUtility.singleLineHeight), travelProperty.isExpanded, new GUIContent("Spring", "Spring Settings"), true);
    10.     if (travelProperty.isExpanded)
    11.     {
    12.         EditorGUI.Slider(new Rect(pos.x, pos.y + EditorGUIUtility.singleLineHeight, pos.size.x / 2, EditorGUIUtility.singleLineHeight), travelProperty, 0, 20, new GUIContent("Travel", "The Maximum Length the spring can travel in m"));
    13.         EditorGUI.Slider(new Rect(pos.x + pos.size.x / 2, pos.y + EditorGUIUtility.singleLineHeight, pos.size.x / 2, EditorGUIUtility.singleLineHeight), property.FindPropertyRelative("springRate"), 0, 100000, new GUIContent("Rate", "The Rate/Stiffness of the Spring in N/m"));
    14.     }
    15. }
    16. public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    17. {
    18.     int totalLine = 3;
    19.  
    20.     SerializedProperty travelProperty = property.FindPropertyRelative("suspensionTravel");
    21.     if(travelProperty.isExpanded)
    22.         totalLine ++;
    23.  
    24.     return EditorGUIUtility.singleLineHeight * totalLine + EditorGUIUtility.standardVerticalSpacing * (totalLine - 1);
    25. }
    It took me a couple of years to understand very well editor scripting o_O
     
  8. NDSno1

    NDSno1

    Joined:
    Dec 20, 2014
    Posts:
    223
    You sir saved me from countless hours of headaches and sky-rocketed my progress :)
    Now just one more step and everything will be perfect. Is there a way to collapse/fold list elements just like how the original editor gui did? Right now what I'm having is this:
    2018-10-04.png
    The property drawer of each element is perfect. However an ability to fold element would be better when the list gets long.

    Again, thank you so much :)
     
  9. rakkarage

    rakkarage

    Joined:
    Feb 3, 2014
    Posts:
    683
  10. Kaldrax

    Kaldrax

    Joined:
    Sep 14, 2017
    Posts:
    44
    PsyKaw you saved my butt with this post! I finally understand what GetPropertyHeight is doing thanks to you.
     
    PsyKaw likes this.
  11. Trixxy

    Trixxy

    Joined:
    Apr 9, 2015
    Posts:
    32
    WOW!!! I have to say, even on version 2022.2 this works a treat, words cannot explain how happy I was seeing what I wanted to happen actually happen :D . Thank you for your wisdom and consider me taught!