Search Unity

Is it possible to mixed inherited custom inspector and default inspector for not inherited members ?

Discussion in 'Immediate Mode GUI (IMGUI)' started by Kiupe, Feb 9, 2018.

  1. Kiupe

    Kiupe

    Joined:
    Feb 1, 2013
    Posts:
    528
    Hello,

    Let's say I have a class A with a custom inspector and a class B that inherit from A. I'd like to be able when selecting a B component to display the class A custom inspector and display class B default inspector for all not inherited members.

    Is it possible ? Thanks.
     
    ItsaMeTuni likes this.
  2. Xarbrough

    Xarbrough

    Joined:
    Dec 11, 2014
    Posts:
    1,188
    Yes, here's the code:

    Code (CSharp):
    1. [CustomEditor(typeof(ClassA), true)]
    2. public class InspectorA : Editor {
    3.  
    4.     string[] propertiesInBaseClass = new string[]{"myStringA", "m_Script"};
    5.  
    6.     public override void OnInspectorGUI()
    7.     {
    8.         EditorGUILayout.LabelField("Special A Drawing");
    9.  
    10.         DrawPropertiesExcluding(serializedObject, propertiesInBaseClass);
    11.     }
    12. }
    The CustomEditor attribute has an option editorForChildClasses which you set to let Unity draw InspectorA for all components inheriting ClassA. Now you can handle your special ClassA drawing the regular way, and it shows up for both. However, at this point ClassB doesn't show its own properties, so we need to draw them with the DrawPropertiesExcluding, which draws all serialized properties except the ones defined in a string array. I couldn't find documentation on it, but its a protected method of the Editor class.

    If you need to programmatically find the fields in your base ClassA, you can use reflection:

    Code (CSharp):
    1. string[] propertiesInBaseClass = new string[]{"myStringA", "m_Script"};
    2.  
    3. private void OnEnable()
    4. {
    5.     FieldInfo[] fields = typeof(ClassA).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
    6.     propertiesInBaseClass = new string[fields.Length];
    7.     for (int i = 0; i < fields.Length; i++) {
    8.         propertiesInBaseClass[i] = fields[i].Name;
    9.     }
    10. }
    This finds all public and private fields and puts their names into the string array to be ignored by DrawPropertiesExcluding. If you you want to use reflection this way, you will probably want to handle more special cases, e.g. private fields with the SerializeField attribute or think about hiding the m_Script fields either by manually inserting it into the list or by searching for fields in the parent classes of ClassA (which might become slow etc).

    Potentially you can also use the SerializedProperty iterator to find the base class properties, but I don't have any experience with it.

    PS: If you need more control over special classes, you can of course create InspectorB for ClassB and let the inspector be a child of InspectorA, then simply calls base.OnInspectorGUI() to draw the base GUI. You can still leave editorForChildClasses set to true in the base editor, because more specific editors override anyway.
     
    Last edited: Feb 15, 2018
    SanyaBane, mayntos, adamgryu and 10 others like this.
  3. BusyRoots

    BusyRoots

    Joined:
    Jul 25, 2017
    Posts:
    41
    Thanks @Kiupe for asking this question and @Xarbrough for your excellent answer. I had a similar issue und just wanted to place two buttons in the middle of a larger amount of fields in the inspector.
    I was very very happy to find the thread because i already started to take each single field and build a custom inspector from the ground up. That would have been a nightmare because all the Tooltips and Headers would have gone lost as well. The DrawPropertiesExcluding method made it waaaaaay easier and that gives me after all a happy Tuesday after work evening :):):).
    Thanks a lot!
     
  4. Deleted User

    Deleted User

    Guest

    Thanks for your answer. It`s really helpful!