Search Unity

Drawing default Property Drawer in a Custom Property Drawer

Discussion in 'Scripting' started by Wahooney, Aug 24, 2013.

  1. Wahooney

    Wahooney

    Joined:
    Mar 8, 2010
    Posts:
    281
    Hi all!

    Is there a way to access a property's original drawer inside a custom drawer?

    I'm trying to add a drawer that adds separators around a property, ie:

    [Separator()]public float spacing = 10.0f;

    but that only works if you either hide the current property or if you make special provisions for all the different types that property can be (which doesn't work for objectReferences and layerMasks).

    Using base.OnGUI (pos, prop, label); gives a "No GUI Implemented" note in the inspector and an EditorGUI.PropertyField (property); gives an infinite loop (understandably).

    Any help would be appreciated!

    Thanks.
     
  2. karljj1

    karljj1

    Joined:
    Feb 17, 2011
    Posts:
    440
    Hi,

    The only way I have been able to do this is to remove your custom PropertyDrawer from the list, draw it and then add yourself back in.

    Do this in the OnGUI function of your property drawer.

    Code (csharp):
    1.  
    2.             // Use default property drawer.
    3.             Dictionary<string, PropertyDrawer> s_dictionary = typeof( PropertyDrawer ).GetField( "s_PropertyDrawers", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic ).GetValue( null ) as Dictionary<string, PropertyDrawer>;                
    4.             foreach( KeyValuePair<string, PropertyDrawer> entry in s_dictionary )
    5.             {                    
    6.                 if( entry.Value == this )
    7.                 {
    8.                     // Remove this property drawer.
    9.                     s_dictionary[entry.Key] = null;
    10.                     EditorGUI.PropertyField( position, property, label, true );
    11.                     s_dictionary[entry.Key] = this;
    12.                     return;
    13.                 }
    14.             }  
    15.  
     
  3. huulong

    huulong

    Joined:
    Jul 1, 2013
    Posts:
    224
    Note for those trying to implement the method with new versions of Unity: looks like the field has moved, it may correspond to either ScriptAttributeUtility.s_DrawerStack (you should pop then push this back) or the custom drawer's PropertyHandler.m_DecoratorDrawers (a list of decorator drawers).

    I don't get the difference between decorator and property drawers for now, but I will leave this for now in my project as it's not too important, plus I may go with a Custom Editor for the object containing those properties instead. But with some Reflection you'll probably get what you need.

    I also tried

    Code (CSharp):
    1. MethodInfo defaultPropertyFieldMethodInfo = typeof(EditorGUI).GetMethod("DefaultPropertyField", BindingFlags.Static | BindingFlags.NonPublic);          
    2.         defaultPropertyFieldMethodInfo.Invoke(null, new object[] { position, property, label } );
    but it's not enough, it just draws the actual field not its children which contain the meat of the beast. You'd need to iterate on children similarly to what's done in PropertyHandler.OnGUI to get what you need. And this OnGUI seems to call the property drawer's own OnGUI, hence the infinite recursion, and the need to pop out the drawer of the stack.
     
  4. Errorsatz

    Errorsatz

    Joined:
    Aug 8, 2012
    Posts:
    555
    Not sure if it's exactly the same situation, but I recently wanted to draw a list in a custom way, while still drawing the individual elements the standard way. This seemed to work fine (excluding the script field isn't required, it's just the visual I wanted for this usage):
    Code (csharp):
    1. public class BasicElementEditor : Editor
    2. {
    3.     private static readonly string[] ExcludedFields = new string[] { "m_Script" };
    4.  
    5.     public override void OnInspectorGUI()
    6.     {
    7.         serializedObject.Update();
    8.         DrawPropertiesExcluding(serializedObject, ExcludedFields);
    9.         serializedObject.ApplyModifiedProperties();
    10.     }
    11. }
    12.  
    13. ...
    14.  
    15. // At the point I want to draw the element in question:
    16. Editor innerEditor = Editor.CreateEditor(listItem, typeof(BasicElementEditor));
    17. innerEditor.OnInspectorGUI();
    Also, I found http://catlikecoding.com/unity/tutorials/editor/custom-list/ to be a very handy tutorial for this kind of thing.
     
  5. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,334
    Decorator drawers draw over a property (like a header). A property drawer decides how to draw a property.
     
  6. Glurth

    Glurth

    Joined:
    Dec 29, 2014
    Posts:
    109
    Couple years late... but I found this guy works to display the default property drawer. Even works from INSIDE a CustomPropertyDrawer for the same type:

    Code (CSharp):
    1.             MethodInfo defaultDraw = typeof(EditorGUI).GetMethod("DefaultPropertyField", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
    2.             defaultDraw.Invoke(null, new object[3] { position, property, label });
     
    electric_jesus, JasonC_ and mikaelK like this.
  7. Trisibo

    Trisibo

    Joined:
    Nov 1, 2010
    Posts:
    245
    If instead of getting the default property drawer you want to get an unknown custom property drawer (other than the one you are inside of), this works as of Unity 2019.4.4f1:

    Code (CSharp):
    1. // Getting the field type this way assumes that the property instance is not a managed reference (with a SerializeReference attribute); if it was, it should be retrieved in a different way:
    2. Type fieldType = fieldInfo.FieldType;
    3.  
    4. Type propertyDrawerType = (Type)Type.GetType("UnityEditor.ScriptAttributeUtility,UnityEditor")
    5.     .GetMethod("GetDrawerTypeForType", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public)
    6.     .Invoke(null, new object[] { fieldType });
    7.  
    8. PropertyDrawer propertyDrawer = null;
    9. if (typeof(PropertyDrawer).IsAssignableFrom(propertyDrawerType))
    10.     propertyDrawer = (PropertyDrawer)Activator.CreateInstance(propertyDrawerType);
    11.  
    12. if (propertyDrawer != null)
    13. {
    14.     typeof(PropertyDrawer)
    15.         .GetField("m_FieldInfo", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
    16.         .SetValue(propertyDrawer, fieldInfo);
    17. }
    Once you get the propertyDrawer, you can draw with it, etc. If it's null (meaning no custom property drawers have been found), just drawing with EditorGUI.PropertyField works fine for me to draw with the default property drawer.

    Note that it's some rough and quick code, you should cache the found property drawer and other stuff somewhere for better performance.
     
    JasonC_ likes this.
  8. forestrf

    forestrf

    Joined:
    Aug 28, 2010
    Posts:
    230
    Viniterra and grom27 like this.
  9. killergoat72

    killergoat72

    Joined:
    Dec 30, 2015
    Posts:
    2
    You just made my day. Thank you.
     
    forestrf likes this.