- Home /
Change only PrefixLabel with Property drawer, use default drawing for rest of property.
I have a class with its first field an enum not a string. I would like an list of these classes to appear in the inspect with the the enum value as the PrefixLabel similar to the this would happen if it was a string. I can make this happen with the following ProperyDrawer code.
[CustomPropertyDrawer (typeof (TempleIdleGenerator.SlotItemFreq))]
public class SlotItemFreq : PropertyDrawer {
public override void OnGUI (Rect position, SerializedProperty property, GUIContent label) {
// Using BeginProperty / EndProperty on the parent property means that
// prefab override logic works on the entire property.
EditorGUI.BeginProperty (position, label, property);
// Draw label
int index = property.FindPropertyRelative ("slotID").enumValueIndex;
label.text = property.FindPropertyRelative ("slotID").enumDisplayNames [index];
position = EditorGUI.PrefixLabel (position, GUIUtility.GetControlID (FocusType.Passive), label);
//What goes here?
EditorGUI.EndProperty ();
// or maybe here?
}
}
the class code is
[System.Serializable]
public class SlotItemFreq{
public SlotID slotID;
public List<ItemFreq> itemFreqs;
}
II would like everything after the initial field to display in the inspector the same as would if I did not have a custom ProperyDrawer. What code do I need where the "//what goes here " or the "//maybe here?" comments are?
Cheers, Grant
Answer by Adam-Mechtley · Jan 18, 2017 at 05:17 PM
@Bunny83 wrote:
Unfortunately you can't use EditorGUI.PropertyField as it would call your property drawer again so you would crash due to an endless recursion.
Actually this is not correct. Unity has a PropertyDrawer stack internally, so it is perfectly valid to simply do something like this:
public override void OnGUI (Rect position, SerializedProperty property, GUIContent label) {
int index = property.FindPropertyRelative ("slotID").enumValueIndex;
label.text = property.FindPropertyRelative ("slotID").enumDisplayNames [index];
EditorGUI.PropertyField(position, property, label, true);
}
You can see this done in the new scripting example for SerializedProperty.isExpanded in the 5.6.0 documentation.
This almost worked but there is some height field that is not being set. When I expand the property it shows the children but does change the height so each element remains one line apart and displays over the element below it.
You need to also override PropertyDrawer.GetPropertyHeight(). Something like this:
public override void GetPropertyHeight(SerializedProperty property, GUIContent label)
{
return property.isExpanded ?
EditorGUIUtility.singleLineHeight * 3f + EditorGUIUtility.standardVerticalSpacing * 2f :
EditorGUIUtility.singleLineHeight;
}
Copy directly from the example in SerializedProperty.isExpanded in the 5.6.0 documentation. You mentioned solves the height problem.
public override float GetPropertyHeight(SerializedProperty property, GUIContent label) {
// use the default property height, which takes into account the expanded state
return EditorGUI.GetPropertyHeight(property);
}
Answer by Bunny83 · Jan 18, 2017 at 02:53 PM
Well, that's a problem. You would need to use EditorGUI.DefaultPropertyField
which however is an internal method. If you want to replicate it manually, good luck ^^. Unfortunately you can't use EditorGUI.PropertyField
as it would call your property drawer again so you would crash due to an endless recursion.
Actually the PropertyDrawer default implementation does call EditorGUI.DefaultPropertyField
automatically, however they made that horrible decision to implement it like this:
public virtual void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
EditorGUI.DefaultPropertyField(position, property, label);
EditorGUI.LabelField(position, label, EditorGUIUtility.TempContent("No GUI Implemented"));
}
So it displays this additional "warning". So your solution could have been as easy as:
public override void OnGUI (Rect position, SerializedProperty property, GUIContent label) {
int index = property.FindPropertyRelative ("slotID").enumValueIndex;
label.text = property.FindPropertyRelative ("slotID").enumDisplayNames [index];
base.OnGUI(position, property, label);
}
But it's not possible because they "think" it would be convenient to tell the user that he implemented a PropertyDrawer and did not override the OnGUI method ...
So one option is to actually replicate what DefaultPropertyField does or to use reflection to call that internal method yourself ^^. Replicating is difficult since most methods DefaultPropertyField uses are internal or private as well. So the easiest option is to use reflection.
I quickly wrote this extension:
namespace B83.EditorExtensions
{
using System.Reflection;
public static class RefEditorGUI
{
public delegate bool DefaultPropertyFieldDelegate(Rect position, SerializedProperty property, GUIContent label);
public static DefaultPropertyFieldDelegate DefaultPropertyField;
static RefEditorGUI()
{
var t = typeof(EditorGUI);
var delegateType = typeof(DefaultPropertyFieldDelegate);
var m = t.GetMethod("DefaultPropertyField", BindingFlags.Static | BindingFlags.NonPublic);
DefaultPropertyField = (DefaultPropertyFieldDelegate)System.Delegate.CreateDelegate(delegateType, m);
}
}
}
Simply place it in an editor script somewhere. Instead of EditorGUI.DefaultPropertyField
you should be able to simply use RefEditorGUI.DefaultPropertyField
. Of course you need to put a using on top of your script where you use it or remove the namespace.
Note: I haven't tested the class. So if you use it would be nice to get feedback. If it doesn't work i will look into it.
edit
I had a bad feeling something could be wrong so i did a quick check and yes i messed something up ^^. I've fixed the code above. Now it works just fine.
Thanks I tried this but I did not realise I needed the include children so I tried the next answer.