- Home /
CustomPropertyDrawer for Array types in 4.3
I'm trying to implement a drawer for an array type. The closest I can get is by writing a property drawer for the elements it contains, but not for the array (parent).
[CustomPropertyDrawer (typeof (PutOnArraysAttribute))]
// I'll get OnGUI calls for the elements of the array but not for the array itself
This happens on 4.3+. Any ideas?
Answer by slippdouglas · Jan 14, 2015 at 11:25 PM
Create a small System.Serializable
nested struct/class within your MonoBehaviour
to contain your list:
public class SomeMonoBehaviour : MonoBehaviour
{
[Serializable] public struct ThingsWrapper {
public List<string> list;
// optional, for convenience (so you can use ThingsWrapper as if it's a List):
public static implicit operator List<string>(ThingsWrapper c) { return c.list; }
public static implicit operator ThingsWrapper(List<string> l) { return new PointsWrapper(){ list = l }; }
}
…
Add a field to your MonoBehaviour
of this nested ThingsWrapper
type adorned with your PutOnArrays
attribute:
…
[PutOnArrays]
public ThingsWrapper things;
…
(Those implicit operator
s allows your code to keep using the field as if it were an array/list:)
…
void Awake() {
List<string> things = this.things;
things.Add("hi");
Debug.Log(things);
}
…
} // end of SomeMonoBehaviour
Then, in your PutOnArraysDrawer
class, use FindPropertyRelative()
to work with the list
field:
[CustomPropertyDrawer(typeof(PutOnArraysAttribute))]
public class PutOnArraysDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
property = property.FindPropertyRelative("list");
// …
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
property = property.FindPropertyRelative("list");
// …
}
}
This approach both allows you specify custom attribute/drawers for arrays and for their elements, and keeps those attribute associations within your MonoBehaviour
(rather than requiring new class files for each use-case).
If you wanted, I'm sure you wrap up ThingsWrapper
into a reusable base class to simplify the code in your class— but it's really not that much code in the first place. I've set up my Attribute to take a listFieldName
constructor arg and store it in a property, then my Drawer passes ((PutOnArraysAttribute)this.attribute).listFieldName
into FindPropertyRelative()
(instead of assuming "list"
). Where you go with this is up to you.
Answer by sotirosn · Mar 17, 2016 at 07:00 AM
I think they should let you put array types into the CustomPropertyDrawer attribute:
[CustomPropertyDrawer(typeof(Hotkey[]))]
public class HotkeysDrawer : PropertyDrawer { ... }
Answer by Arkade · Jun 29, 2014 at 02:08 PM
I believe you cannot create a PropertyDrawer for an Array, only an Editor for the containing class and then PropertyDrawers for the members (like you mentioned).
See http://catlikecoding.com/unity/tutorials/editor/custom-list/.
To be a tad more precise, you used to be able to. This is the relevant entry in the 4.3 release change log:
Editor: PropertyDrawer attributes on members that are arrays are now applied to each element in the array rather than the array as a whole. This was always the intention since there is no other way to apply attributes to array elements, but it didn't work correctly before. Apologies for the inconvenience to anyone who relied on the unintended behavior for custom drawing of arrays.
It's annoying, as it would be really really useful, especially with 4.5's ReorderableList, you have to jump extra hoops to use it nicely now. I$$anonymous$$O, they should add a way to apply attributes to the arrays themselves and have both behaviours available to us.