Search Unity

Custom Property Drawer Height and Width (SOLVED)

Discussion in 'Editor & General Support' started by GamezAtWork, May 6, 2017.

  1. GamezAtWork

    GamezAtWork

    Joined:
    Jan 12, 2013
    Posts:
    3
    Hey guys!

    So... I've been trying to create a Custom Property Drawer for one of my custom classes, and... well... I have no idea how to fix this height and width issue...

    Long story short, it looks like this:
    upload_2017-5-6_18-27-37.png
    Like, Script is supposed to be a Monobehaviour (which is actually filled up by the way), UI Image is supposed to be a Texture2D (which is ALSO filled up), and when you click on Script it makes the dropdown list appear instead, and ARGH.

    I would really like to find out why the width of this property drawer, the height of the property drawer, and all the elements' positions are all messed up, along with the interactable areas, and how to fix it.

    (By the way, the elements actually DO work as intended, just that their click areas are almost all wrong)

    Here is my code:

    Code (CSharp):
    1.     public override float GetPropertyHeight (SerializedProperty property, GUIContent label) {
    2.  
    3.         SerializedObject childObj = new UnityEditor.SerializedObject(property.objectReferenceValue as Command);
    4.         SerializedProperty ite = childObj.GetIterator();
    5.  
    6.         float totalHeight = EditorGUI.GetPropertyHeight (property, label);
    7.  
    8.         while (ite.NextVisible(true))
    9.         {
    10.             totalHeight += EditorGUI.GetPropertyHeight(ite, label);
    11.         }
    12.  
    13.  
    14.         return totalHeight;
    15.     }
    Code (CSharp):
    1. public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
    2.  
    3.         allTypeStrings = new List<string>();
    4.         allTypes = Methods.GetSubclasses<Command>(true);
    5.         foreach(System.Type type in allTypes)
    6.         {
    7.             allTypeStrings.Add(type.Name);
    8.         }
    9.  
    10.         label = EditorGUI.BeginProperty(position, label, property);
    11.  
    12.         // Draw label
    13.         position = EditorGUI.PrefixLabel(position,  GUIUtility.GetControlID (FocusType.Passive), label);
    14.  
    15.         // Don't make child fields be indented
    16.         int indent = EditorGUI.indentLevel;
    17.         EditorGUI.indentLevel = 0;
    18.  
    19.         Rect classRect = new Rect(position.x, position.y, position.width, position.height);
    20.  
    21.         Command actualVal = property.objectReferenceValue as Command;
    22.         int currIndex = -1;
    23.         if (actualVal != null)
    24.         {
    25.             for (int j = 0; j < allTypeStrings.Count; ++j)
    26.             {
    27.                 if (allTypeStrings[j] == actualVal.GetType().Name)
    28.                     currIndex = j;
    29.             }
    30.         }
    31.  
    32.         EditorGUI.BeginChangeCheck ();
    33.  
    34.         int newIndex = EditorGUI.Popup(classRect, currIndex, allTypeStrings.ToArray());
    35.  
    36.         if (EditorGUI.EndChangeCheck())
    37.         {
    38.             property.objectReferenceValue = ScriptableObject.CreateInstance(allTypes[newIndex]);
    39.             //actualVal = ScriptableObject.CreateInstance(allTypes[newIndex]) as Command;
    40.             Debug.Log(property.objectReferenceValue);
    41.             Debug.Log("Changed to " + (property.objectReferenceValue as System.Object as Command).commandName);
    42.         }
    43.  
    44.         Command finalValue = property.objectReferenceValue as Command;
    45.  
    46.         if (finalValue != null)
    47.         {
    48.             EditorGUI.indentLevel = 1;
    49.  
    50.             SerializedObject childObj = new UnityEditor.SerializedObject(finalValue);
    51.  
    52.             Debug.Log("Child number is " + childObj.GetIterator().displayName);
    53.             SerializedProperty ite = childObj.GetIterator();
    54.             int i = 1;
    55.             while (ite.NextVisible(true))
    56.             {
    57.                 Debug.Log("Child is " + ite.displayName);
    58.                 Rect newRect = new Rect(position.x, position.y + i * 20, position.width, position.height);
    59.                 EditorGUI.PropertyField(newRect, ite);
    60.                 ++i;
    61.             }
    62.             childObj.ApplyModifiedProperties();
    63.         }
    64.  
    65.         // Set indent back to what it was
    66.         EditorGUI.indentLevel = indent;
    67.  
    68.         EditorGUI.EndProperty();
    69.  
    70.     }
     
    Oyedoyin1 and (deleted member) like this.
  2. GamezAtWork

    GamezAtWork

    Joined:
    Jan 12, 2013
    Posts:
    3
    Okay never mind I solved it!

    So apparently it was indeed a combination of multiple problems at once.

    Height Issue
    Firstly, I used position.height for the rects, which, i assume uses the value from GetPropertyHeight (the one I made). This kinda caused all of the properties to have the HUGE height of all the properties combined, which caused it to mess up.
    So I changed them from position.height to EditorGUI.GetPropertyHeight(property) and EditorGUI.GetPropertyHeight(ite), and that solved like almost all the problems.

    upload_2017-5-10_16-24-54.png

    Indentation Issue
    So the next problem was that everything was like SUPER INDENTED to the right (even when I remove the indent level thing), which was caused by this line:

    Code (CSharp):
    1. position = EditorGUI.PrefixLabel(position,  GUIUtility.GetControlID (FocusType.Passive), label);
    The rect which is outputted by the PrefixLabel actually was the area on the right of the Label, which means everything was squeezed into the right space. I mean, it could work in other cases, but I don't want that, so I fixed it by just removing the PrefixLabel and adding in the label into Popup instead, like so:

    Code (CSharp):
    1. int newIndex = EditorGUI.Popup(classRect, property.displayName, currIndex, allTypeStrings.ToArray());
    upload_2017-5-10_16-30-32.png

    Overlap with bottom
    For the last part, that was more a careless mistake than anything else.
    In my GetPropertyHeight function, I used while (ite.NextVisible(true)), but that meant that it will NOT execute on the first element, which means it will always be short of one element. Fixing it was trivial:

    Code (CSharp):
    1.         float totalHeight = EditorGUI.GetPropertyHeight (property, label, true);
    2.  
    3.         ite.Next(true);
    4.         totalHeight += EditorGUI.GetPropertyHeight(ite, label, true);
    5.  
    6.         while (ite.NextVisible(true))
    7.         {
    8.             totalHeight += EditorGUI.GetPropertyHeight(ite, label, true);
    9.         }
    10.  
    Final Result
    upload_2017-5-10_16-39-28.png

    Q.E.D (I hope)

    EDIT:

    That last part is wrong! It turns out that in the old case, it DOES add all the elements' heights, the REAL problem was that I forgot to take into account the vertical spacing between the elements when I placed them and when I calculated the height. These are the corrected bits:

    Code (CSharp):
    1.     public override float GetPropertyHeight (SerializedProperty property, GUIContent label) {
    2.  
    3.         SerializedObject childObj = new UnityEditor.SerializedObject(property.objectReferenceValue as Command);
    4.         SerializedProperty ite = childObj.GetIterator();
    5.  
    6.         float totalHeight = EditorGUI.GetPropertyHeight (property, label, true) + EditorGUIUtility.standardVerticalSpacing;
    7.  
    8.         while (ite.NextVisible(true))
    9.         {
    10.             totalHeight += EditorGUI.GetPropertyHeight(ite, label, true) + EditorGUIUtility.standardVerticalSpacing;
    11.         }
    12.  
    13.         return totalHeight;
    14.     }
    Code (CSharp):
    1.             SerializedObject childObj = new UnityEditor.SerializedObject(finalValue);
    2.  
    3.             SerializedProperty ite = childObj.GetIterator();
    4.             float prevHeight = EditorGUI.GetPropertyHeight(property, label, true);
    5.  
    6.             while (ite.NextVisible(true))
    7.             {
    8.                 Debug.Log("Child is " + ite.displayName);
    9.                 Rect newRect = new Rect(position.x, position.y + prevHeight + EditorGUIUtility.standardVerticalSpacing, position.width, EditorGUI.GetPropertyHeight(ite, label, true));
    10.                 prevHeight += newRect.height + EditorGUIUtility.standardVerticalSpacing;
    11.                 EditorGUI.PropertyField(newRect, ite);
    12.             }
    13.             childObj.ApplyModifiedProperties();
     
    Last edited: May 10, 2017
  3. dsalisbury

    dsalisbury

    Joined:
    Mar 5, 2018
    Posts:
    4
    I just want to say thank you. The fact that you asked a question, didn't get an answer, figured it out on your own, and then came back to tell the rest of us makes you a gd hero. :)
     
  4. Bropoc

    Bropoc

    Joined:
    Sep 30, 2014
    Posts:
    8
    For real. It's a bad habit for this landscape where someone has a problem and we never get resolution. Woe be to us who suffer the same.
     
    azevedco likes this.
  5. WorkshopInProgress

    WorkshopInProgress

    Joined:
    Feb 21, 2019
    Posts:
    2
    This is honestly a more helpful answer than the Unity documentation on PropertyDrawers. I hadn't realized we could use EditorGUI functions in it.
     
  6. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,291
    If you have feedback about the docs then do please use the rating function at the bottom of the page. It does take us time but we do respond to this feedback. We actually resolved over 1500 of them last week ;)
     
    EZaca likes this.
  7. VengadoraVG

    VengadoraVG

    Joined:
    Nov 21, 2015
    Posts:
    13
    property drawers implementation is unecessarily complex :S
     
  8. telgo

    telgo

    Joined:
    Dec 10, 2015
    Posts:
    9
    What rating function? I am not sure what you are referring to.

    My issue is with the TextSwitcher PropertyDrawer. I just want the text field to be big enough for several lines of text, but can't see how to do this.
     
  9. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,291
    I'm referring to this on each page of the docs
    upload_2020-3-26_15-56-8.png

    You can file a bug report regarding the TextSwitcher https://unity3d.com/unity/qa/bug-reporting
     
  10. telgo

    telgo

    Joined:
    Dec 10, 2015
    Posts:
    9
    Thanks for the prompt response. This isn't really a bug though, just missing info about the PropertyDrawer. I'll try anyway.
     
  11. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,291
  12. Reahreic

    Reahreic

    Joined:
    Mar 23, 2011
    Posts:
    254
    @telgo
    It may not help you, but i use this to generate scroll-able 'description' text areas in my custom editors. (The braces just help me visualize the layout in the code, i use them with BeginHorizontal/Vertical too, epecially when i have several nested.)

    Code (CSharp):
    1. //Height values by number of lines
    2. float textAreaMinHeight = 3;
    3. float textAreaMaxHeight = 5;
    4. Vector2 descScrollPos = Vector2.zero;
    5.  
    6. //Stick this in your OnInspectorGUI
    7. descScrollPos = EditorGUILayout.BeginScrollView(descScrollPos, EditorStyles.textArea, GUILayout.MinHeight(textAreaMinHeight), GUILayout.MaxHeight(textAreaMaxHeight));
    8. {
    9.   EditorStyles.label.wordWrap = true;
    10.   propText.stringValue = EditorGUILayout.TextArea(propText.stringValue, EditorStyles.label, GUILayout.ExpandHeight(true));
    11. }
    12. EditorGUILayout.EndScrollView();
     
    Ash-Blue likes this.
  13. Rindramer

    Rindramer

    Joined:
    Jun 18, 2020
    Posts:
    1
    Hey, I have lil question,

    if I try to cast the objectReferenceValue as my custom class in the GetHeight function I get an error saying that I cant convert from type Object to type [my custom class].
    And I literally copied the code to make sure I didn't have any typos

    Can anyone help?
     
  14. ShervinM

    ShervinM

    Joined:
    Sep 16, 2017
    Posts:
    67
    I assume your custom class is not a UnityObject.

    objectReference value is of type UnityEngine.Object, not System.Object
     
  15. NotlawGD

    NotlawGD

    Joined:
    Oct 24, 2022
    Posts:
    3
    Thank you for pointing this out. I was trying to display a custom class on the inspector with a PropertyDrawer and I didn't know how to get the height of the property I was trying to draw, so everything was getting messy. But as soon as I saw you point the EditorGUI.GetPropertyHeight() method, you saved me lots of hours of research. For real, thanks a lot!
     
  16. highpockets

    highpockets

    Joined:
    Jan 17, 2013
    Posts:
    71
    I came across the height issue with elements overlapping etc. while creating some PropertyDrawers. My particular case is with ManagedReference types. And these properties are based off of abstract classes so Unity doesn't paint the children automatically and for good reason since an abstract class could have anything within it in the derived class, but I assumed that once the reference was set, the children would magically appear. Not the case.. The property drawer shows a popup of derived concrete classes and the selected class in that popup is what becomes the reference. I didn't initially think to override GetPropertyHeight, but that was the ticket along with making sure that includeChildren is passed and set to true which I originally overlooked and includeChildren defaults to false. So for those who are stuck:

    Code (CSharp):
    1. //PropertyField to show with children included
    2. EditorGUI.PropertyField(position, property, true);
    AND:


    Code (CSharp):
    1. //GetPropertyHeight override
    2. public override float GetPropertyHeight (SerializedProperty property, GUIContent label) =>
    3.     EditorGUI.GetPropertyHeight(property, label, true) + 10;