For example, I have a property attribute which sets a red background color:-
[CustomPropertyDrawer(typeof(RedBGAttribute))]
public class RedBGDrawer : PropertyDrawer {
public override VisualElement CreatePropertyGUI(SerializedProperty property) {
var propertyField = new PropertyField(property);
propertyField.style.backgroundColor= new StyleColor(Color.red);
return propertyField;
}
}
this works fine for all cases, except if you attach the attribute to a type which has a custom property drawer. In which case, it will use the type’s default drawer (while still applying the red bg color).
Here’s the full example, if anyone wants to test it
//serializable class
[Serializable]
public class Class {
public int whatever;
}
//custom class drawer
[CustomPropertyDrawer(typeof(Class))]
public class CustomClassDrawer : PropertyDrawer {
public override VisualElement CreatePropertyGUI(SerializedProperty property) =>
new Label("Custom class drawer");
}
//red bg attribute
public class RedBGAttribute : PropertyAttribute { }
//red bg attribute drawer
[CustomPropertyDrawer(typeof(RedBGAttribute))]
public class RedBGDrawer : PropertyDrawer {
public override VisualElement CreatePropertyGUI(SerializedProperty property) {
var propertyField = new PropertyField(property);
propertyField.style.backgroundColor= new StyleColor(Color.red);
return propertyField;
}
}
public class MB : MonoBehaviour {
//will use CustomClassDrawer
public Class f1;
//will not use CustomClassDrawer
[RedBG] public Class f2;
}
I suppose it’s expected. It worked that way in IMGUI too.
A hypothetical problem with support for nested drawers is that, currently, there is no way for Unity to know which drawer should be called first. You could add some sort of priority heuristics, but then it could break other property drawers that expect the current behavior. That’s my guess for why it hasn’t been implemented yet.
Now, something you can do with UITK that you couldn’t do with IMGUI is to use a Decorator for this. It doesn’t work before 2022.2, and it’s not very pretty, but it’s easy. It could look something like this:
[CustomPropertyDrawer(typeof(RedBGAttribute))]
public class RedBGDrawer : DecoratorDrawer
{
public override VisualElement CreatePropertyGUI(SerializedProperty property)
{
var dummyElement = new VisualElement();
dummyElement.RegisterCallback<AttachToPanelEvent>(OnAttachToPanel);
return dummyElement;
void OnAttachToPanel(AttachToPanelEvent e)
{
// We only need to do this once, so we unregister this callback.
dummyElement.UnregisterCallback<AttachToPanelEvent>(OnAttachToPanel);
var propertyField = dummyElement.GetFirstAncestorOfType<PropertyField>();
propertyField.style.backgroundColor = Color.red;
}
}
}
The decorator creates a dummyElement that obtains its parent PropertyField when it’s attached to panel. Then you can do whatever you need with the parent PropertyField.
I could’ve sworn that IMGUI would’ve used the custom drawer in this case, but it’s been a while.
For me, it seems sensical that it would nest the class drawer under the attributes drawer, since it does so for the attributes drawers (in case there were multiple attributes). But for backward compatibility’s sake, maybe they can add an argument to the PropertyField, to force using a custom drawer.
Regarding your solution, It’s interesting, but my initial thought is, I don’t think it would work for all my use cases. For example, if I had a SerializeReference field, and I wanted an attribute to add a drop down of allowed types. A decorator drawer will not have a reference to the serialized property, so I wouldn’t be able to determine the type of the field. I suppose I could read the parent property field’s binding path, but I would still need at least a reference to the serialized object.
As a final backup solution, I guess I would have to do what the default inspector does, and find the custom drawer type, instantiate it, and call the needed methods. I believe it would be straight forward if the drawer is using UITK.
You could access PropertyField.m_SerializedProperty with reflection… It’s even uglier, but it could be cleaner than other alternatives…
I wish they passed the serialized property to decorators. I realize it isn’t their original intended use, but I see no possible harm in supporting it.