Custom Property Drawer Height and Width (SOLVED)

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:
3059600--229835--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:

    public override float GetPropertyHeight (SerializedProperty property, GUIContent label) {

        SerializedObject childObj = new UnityEditor.SerializedObject(property.objectReferenceValue as Command);
        SerializedProperty ite = childObj.GetIterator();

        float totalHeight = EditorGUI.GetPropertyHeight (property, label);

        while (ite.NextVisible(true))
        {
            totalHeight += EditorGUI.GetPropertyHeight(ite, label);
        }


        return totalHeight;
    }
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {

        allTypeStrings = new List<string>();
        allTypes = Methods.GetSubclasses<Command>(true);
        foreach(System.Type type in allTypes)
        {
            allTypeStrings.Add(type.Name);
        }

        label = EditorGUI.BeginProperty(position, label, property);

        // Draw label
        position = EditorGUI.PrefixLabel(position,  GUIUtility.GetControlID (FocusType.Passive), label);

        // Don't make child fields be indented
        int indent = EditorGUI.indentLevel;
        EditorGUI.indentLevel = 0;

        Rect classRect = new Rect(position.x, position.y, position.width, position.height);

        Command actualVal = property.objectReferenceValue as Command;
        int currIndex = -1;
        if (actualVal != null)
        {
            for (int j = 0; j < allTypeStrings.Count; ++j)
            {
                if (allTypeStrings[j] == actualVal.GetType().Name)
                    currIndex = j;
            }
        }

        EditorGUI.BeginChangeCheck ();

        int newIndex = EditorGUI.Popup(classRect, currIndex, allTypeStrings.ToArray());

        if (EditorGUI.EndChangeCheck())
        {
            property.objectReferenceValue = ScriptableObject.CreateInstance(allTypes[newIndex]);
            //actualVal = ScriptableObject.CreateInstance(allTypes[newIndex]) as Command;
            Debug.Log(property.objectReferenceValue);
            Debug.Log("Changed to " + (property.objectReferenceValue as System.Object as Command).commandName);
        }

        Command finalValue = property.objectReferenceValue as Command;

        if (finalValue != null)
        {
            EditorGUI.indentLevel = 1;

            SerializedObject childObj = new UnityEditor.SerializedObject(finalValue);

            Debug.Log("Child number is " + childObj.GetIterator().displayName);
            SerializedProperty ite = childObj.GetIterator();
            int i = 1;
            while (ite.NextVisible(true))
            {
                Debug.Log("Child is " + ite.displayName);
                Rect newRect = new Rect(position.x, position.y + i * 20, position.width, position.height);
                EditorGUI.PropertyField(newRect, ite);
                ++i;
            }
            childObj.ApplyModifiedProperties();
        }

        // Set indent back to what it was
        EditorGUI.indentLevel = indent;

        EditorGUI.EndProperty();

    }
2 Likes

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.

3064341--230313--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:

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:

int newIndex = EditorGUI.Popup(classRect, property.displayName, currIndex, allTypeStrings.ToArray());

3064341--230317--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:

        float totalHeight = EditorGUI.GetPropertyHeight (property, label, true);

        ite.Next(true);
        totalHeight += EditorGUI.GetPropertyHeight(ite, label, true);

        while (ite.NextVisible(true))
        {
            totalHeight += EditorGUI.GetPropertyHeight(ite, label, true);
        }

Final Result
3064341--230318--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:

    public override float GetPropertyHeight (SerializedProperty property, GUIContent label) {

        SerializedObject childObj = new UnityEditor.SerializedObject(property.objectReferenceValue as Command);
        SerializedProperty ite = childObj.GetIterator();

        float totalHeight = EditorGUI.GetPropertyHeight (property, label, true) + EditorGUIUtility.standardVerticalSpacing;

        while (ite.NextVisible(true))
        {
            totalHeight += EditorGUI.GetPropertyHeight(ite, label, true) + EditorGUIUtility.standardVerticalSpacing;
        }

        return totalHeight;
    }
            SerializedObject childObj = new UnityEditor.SerializedObject(finalValue);

            SerializedProperty ite = childObj.GetIterator();
            float prevHeight = EditorGUI.GetPropertyHeight(property, label, true);

            while (ite.NextVisible(true))
            {
                Debug.Log("Child is " + ite.displayName);
                Rect newRect = new Rect(position.x, position.y + prevHeight + EditorGUIUtility.standardVerticalSpacing, position.width, EditorGUI.GetPropertyHeight(ite, label, true));
                prevHeight += newRect.height + EditorGUIUtility.standardVerticalSpacing;
                EditorGUI.PropertyField(newRect, ite);
            }
            childObj.ApplyModifiedProperties();
21 Likes

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. :slight_smile:

12 Likes

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.

1 Like

This is honestly a more helpful answer than the Unity documentation on PropertyDrawers. I hadn’t realized we could use EditorGUI functions in it.

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 :wink:

1 Like

property drawers implementation is unecessarily complex :S

5 Likes

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.

I’m referring to this on each page of the docs
5633089--585028--upload_2020-3-26_15-56-8.png

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

Thanks for the prompt response. This isn’t really a bug though, just missing info about the PropertyDrawer. I’ll try anyway.

Ok. You want to make a property drawer taller or is this specific to TextSwitcher, I’m not at all familiar with TextSwitcher however I think you want https://docs.unity3d.com/ScriptReference/PropertyDrawer.GetPropertyHeight.html

@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.)

//Height values by number of lines
float textAreaMinHeight = 3;
float textAreaMaxHeight = 5;
Vector2 descScrollPos = Vector2.zero;

//Stick this in your OnInspectorGUI
descScrollPos = EditorGUILayout.BeginScrollView(descScrollPos, EditorStyles.textArea, GUILayout.MinHeight(textAreaMinHeight), GUILayout.MaxHeight(textAreaMaxHeight));
{
  EditorStyles.label.wordWrap = true;
  propText.stringValue = EditorGUILayout.TextArea(propText.stringValue, EditorStyles.label, GUILayout.ExpandHeight(true));
}
EditorGUILayout.EndScrollView();
1 Like

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?

I assume your custom class is not a UnityObject.

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

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!

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:

//PropertyField to show with children included
EditorGUI.PropertyField(position, property, true);

AND:

//GetPropertyHeight override
public override float GetPropertyHeight (SerializedProperty property, GUIContent label) =>
    EditorGUI.GetPropertyHeight(property, label, true) + 10;