Custom editor: display array within ReorderableList

I have a CustomEditor that displays my level builder information. It uses the UnityEditorInternal.ReorderableList to draw a list of levels with their properties in the Inspector.

private void OnEnable()
{
    //Get top-level list property
    list = new ReorderableList(serializedObject,
                                serializedObject.FindProperty("levels"),
                                true, true, true, true);
      
    GUIStyle rightAlign = new GUIStyle();
    rightAlign.alignment = TextAnchor.UpperRight;

    //Draw and style list elements
    list.drawElementCallback =
    (Rect rect, int index, bool isActive, bool isFocused) => {
        var element = list.serializedProperty.GetArrayElementAtIndex(index);
        rect.y += 2;

        var third = rect.width / 3f;

        EditorGUI.LabelField(
            new Rect(rect.x, rect.y, 18f, EditorGUIUtility.singleLineHeight),
            element.FindPropertyRelative("id").intValue.ToString(), rightAlign);

        EditorGUI.PropertyField(
            new Rect(rect.x + 25f, rect.y, third - 30f, EditorGUIUtility.singleLineHeight),
            element.FindPropertyRelative("name"), GUIContent.none);
    }
}

So far everything works fine, but now I would like to add a property for an array within my reorderable list. When I try to use element.FindPropertyRelative like in the example above, the inspector draws an empty drop down arrow.

How can I draw array elements like the default Inspector does within my ReorderableList? If possible, I would like to stick to UnityEditorInternal.ReorderableList because it already has all the neat callbacks and most of what I need my custom Inspector to do.

Thanks for any suggestions!

Reviving this thread half a decade later. Did you ever figure this out? I can’t get any foldout (like an array) in a ReorderableList to not draw as a blank area when expanded.

I probably did not back then, but future-me to the rescue, here is a working example:

5350815--540447--upload_2020-1-8_22-7-19.png

using System.Collections.Generic;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;

public class Test : MonoBehaviour
{
    public List<LevelData> levels;

    [System.Serializable]
    public class LevelData
    {
        public string[] names;
    }
}

[CustomEditor(typeof(Test))]
public class TestEditor : Editor
{
    private ReorderableList list;

    private void OnEnable()
    {
        list = new ReorderableList(
            serializedObject,
            serializedObject.FindProperty("levels"),
            true, true, true, true)
        {
            elementHeightCallback = ElementHeightCallback,
            drawElementCallback = DrawListElement
        };
    }

    private float ElementHeightCallback(int index)
    {
        // Set the height of each row dynamically depending on the height of the names entries.
        SerializedProperty namesProp = GetNamesProp(index);
        return EditorGUI.GetPropertyHeight(namesProp) + EditorGUIUtility.standardVerticalSpacing;
    }

    private void DrawListElement(Rect rect, int index, bool isActive, bool isFocused)
    {
        SerializedProperty namesProp = GetNamesProp(index);

        // By default, the array dropdown is offset to the left which intersects with
        // the drag handle, so we can either indent the array property or inset the rect.
        EditorGUI.indentLevel++;

        // Take note of the last argument, since this is an array,
        // we want to draw it with all of its children.
        EditorGUI.PropertyField(rect, namesProp, includeChildren: true);
        EditorGUI.indentLevel--;
    }

    private SerializedProperty GetNamesProp(int index)
    {
        var element = list.serializedProperty.GetArrayElementAtIndex(index);
        return element.FindPropertyRelative("names");
    }

    public override void OnInspectorGUI()
    {
        serializedObject.Update();
        list.DoLayoutList();
        serializedObject.ApplyModifiedProperties();
    }
}
1 Like