Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
239 views
in Technique[技术] by (71.8m points)

c# - How to select elements in nested ReorderableList in a CustomEditor?

I have a ReorderableList in my CustomEditor script. In the drawElementCallback I added a second nested ReorderableList. Everything works fine and I can add elements to both lists like here

enter image description here

BUT as you can see for some reason I can not select the elements of the inner ReorderableLists so I also can not remove items.

How can I select items in the inner list?


Here the classes broken down to the basic example

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

[Serializable]
public class SomeClass
{
    public string Name;
    public List<SomeClass> InnerList;
}

[CreateAssetMenu(menuName = "Example", fileName = "new Example Asset")]
public class Example : ScriptableObject
{
    public List<SomeClass> SomeClasses;

    [CustomEditor(typeof(Example))]
    private class ModuleDrawer : Editor
    {
        private SerializedProperty SomeClasses;
        private ReorderableList list;

        private void OnEnable()
        {
            SomeClasses = serializedObject.FindProperty("SomeClasses");

            // setupt the outer list
            list = new ReorderableList(serializedObject, SomeClasses)
            {
                displayAdd = true,
                displayRemove = true,
                draggable = true,

                drawHeaderCallback = rect =>
                {
                    EditorGUI.LabelField(rect, "Outer List");
                },

                drawElementCallback = (rect, index, a, h) =>
                {
                    // get outer element
                    var element = SomeClasses.GetArrayElementAtIndex(index);

                    var InnerList = element.FindPropertyRelative("InnerList");

                    // Setup the inner list
                    var innerReorderableList = new ReorderableList(element.serializedObject, InnerList)
                    {
                        displayAdd = true,
                        displayRemove = true,
                        draggable = true,

                        drawHeaderCallback = innerRect =>
                        {
                            EditorGUI.LabelField(innerRect, "Inner List");
                        },

                        drawElementCallback = (innerRect, innerIndex, innerA, innerH) =>
                        {
                            // Get element of inner list
                            var innerElement = InnerList.GetArrayElementAtIndex(innerIndex);

                            var name = innerElement.FindPropertyRelative("Name");

                            EditorGUI.PropertyField(innerRect, name);
                        }
                    };

                    var height = (InnerList.arraySize + 3) * EditorGUIUtility.singleLineHeight;
                    innerReorderableList.DoList(new Rect(rect.x, rect.y, rect.width, height));
                },

                elementHeightCallback = index =>
                {
                    var element = SomeClasses.GetArrayElementAtIndex(index);

                    var innerList = element.FindPropertyRelative("InnerList");

                    return (innerList.arraySize + 4) * EditorGUIUtility.singleLineHeight;
                }
            };
        }

        public override void OnInspectorGUI()
        {
            serializedObject.Update();

            list.DoLayoutList();

            serializedObject.ApplyModifiedProperties();
        }
    }
}

Update

The same apprantly also doesn't work if I have a ReorderableList within a CustomPropertyDrawer.

But to my suprise if I have a UnityEvent both works: Having it inside a CustomPropertyDrawer or within a ReorderableList. I can add and remove items as expected.

As you can see here I added a UnityEvent field by adding

[Serializable]
public class SomeClass
{
    public string Name;
    public List<SomeClass> InnerList;
}

and using

//...
innerReorderableList.DoList(new Rect(rect.x, rect.y, rect.width, height));

var InnerEvent = element.FindPropertyRelative("InnerEvent");
var pers = InnerEvent.FindPropertyRelative("m_PersistentCalls.m_Calls");
var evHeight = (Mathf.Max(1, pers.arraySize) * 2 + 3) * EditorGUIUtility.singleLineHeight;
EditorGUI.PropertyField(new Rect(rect.x, rect.y, rect.width, evHeight), InnerEvent);

and adjusting the elementHeightCallback to

elementHeightCallback = index =>
{
    var element = SomeClasses.GetArrayElementAtIndex(index);

    var innerList = element.FindPropertyRelative("InnerList");
    var InnerEvent = element.FindPropertyRelative("InnerEvent");
    var pers = InnerEvent.FindPropertyRelative("m_PersistentCalls.m_Calls");

    return (Mathf.Max(1, innerList.arraySize) + 4 + Mathf.Max(1, pers.arraySize) * 2 + 4) * EditorGUIUtility.singleLineHeight;
}

I can fully interact with it as expected and also select and remove entries.

enter image description here

So what are they doing different?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

It looks like you are creating the inner lists over and over without storing them anywhere. I modified your code to store the reorderable lists in a dictionary with the element.propertyPath as a key. Hope this helps.

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

[Serializable]
public class SomeClass
{
    public string Name;
    public List<SomeClass> InnerList;
}

[CreateAssetMenu(menuName = "Example", fileName = "new Example Asset")]
public class Example : ScriptableObject
{
    public List<SomeClass> SomeClasses;

    [CustomEditor(typeof(Example))]
    private class ModuleDrawer : Editor
    {
        private SerializedProperty SomeClasses;
        private ReorderableList list;

        private Dictionary<string, ReorderableList> innerListDict = new Dictionary<string, ReorderableList>();

        private void OnEnable()
        {
            SomeClasses = serializedObject.FindProperty("SomeClasses");

            // setupt the outer list
            list = new ReorderableList(serializedObject, SomeClasses)
            {
                displayAdd = true,
                displayRemove = true,
                draggable = true,

                drawHeaderCallback = rect =>
                {
                    EditorGUI.LabelField(rect, "Outer List");
                },

                drawElementCallback = (rect, index, a, h) =>
                {
                    // get outer element
                    var element = SomeClasses.GetArrayElementAtIndex(index);

                    var InnerList = element.FindPropertyRelative("InnerList");

                    string listKey = element.propertyPath;

                    ReorderableList innerReorderableList;

                    if (innerListDict.ContainsKey(listKey))
                    {
                        // fetch the reorderable list in dict
                        innerReorderableList = innerListDict[listKey];
                    }
                    else
                    {
                        // create reorderabl list and store it in dict
                        innerReorderableList = new ReorderableList(element.serializedObject, InnerList)
                        {
                            displayAdd = true,
                            displayRemove = true,
                            draggable = true,

                            drawHeaderCallback = innerRect =>
                            {
                                EditorGUI.LabelField(innerRect, "Inner List");
                            },

                            drawElementCallback = (innerRect, innerIndex, innerA, innerH) =>
                            {
                                // Get element of inner list
                                var innerElement = InnerList.GetArrayElementAtIndex(innerIndex);

                                var name = innerElement.FindPropertyRelative("Name");

                                EditorGUI.PropertyField(innerRect, name);
                            }
                        };
                        innerListDict[listKey] = innerReorderableList;
                    }

                    // Setup the inner list
                    var height = (InnerList.arraySize + 3) * EditorGUIUtility.singleLineHeight;
                    innerReorderableList.DoList(new Rect(rect.x, rect.y, rect.width, height));
                },

                elementHeightCallback = index =>
                {
                    var element = SomeClasses.GetArrayElementAtIndex(index);

                    var innerList = element.FindPropertyRelative("InnerList");

                    return (innerList.arraySize + 4) * EditorGUIUtility.singleLineHeight;
                }
            };
        }

        public override void OnInspectorGUI()
        {
            serializedObject.Update();

            list.DoLayoutList();

            serializedObject.ApplyModifiedProperties();
        }
    }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...