Using Custom Attribute Processors

Attribute Processors allows for a ton of flexibility in customisation of how Odin draws your inspectors. You can dynamically add, edit and remove attributes and even completely change the layout of the inspector.

Let us imagine that we have the following; a class named MyProcessedClass and a MonoBehaviour that has that class as a field:

public class MyMonoBehaviour : MonoBehaviour
{
	public MyProcessedClass Processed;
}

[Serializable]
public class MyProcessedClass
{
	public ScaleMode Mode;
	public float Size;
}

Now we will make a custom OdinAttributeProcessor. Odin features both generic and non-generic versions of the OdinAttributeProcessor class, and you can use whatever fits your needs the best. You can expect to find the same generic constraint rules as you would on an OdinDrawer class.

public class MyProcessedClassAttributeProcessor : OdinAttributeProcessor<MyProcessedClass>

First, we'll override the ProcessSelfAttributes method. Odin calls this method for any fields or properties of the MyProcessedClass type.

Here we will add just two attributes to the property's attribute list. We have created the AttributeListExtension class with a few utility methods for this to make life simpler.

public override void ProcessSelfAttributes(InspectorProperty property, List<Attribute> attributes)
{
    attributes.Add(new InfoBoxAttribute("Dynamically added attributes!"));
    attributes.Add(new InlinePropertyAttribute());
}

Let us also override and implement the ProcessChildMemberAttributes method. Odin calls this method for all direct child properties of the MyProcessedClass property.

public override void ProcessChildMemberAttributes(
	InspectorProperty parentProperty,
	MemberInfo member,
	List<Attribute> attributes)
{
    // These attributes will be added to all of the child elements.
    attributes.Add(new HideLabelAttribute());
    attributes.Add(new BoxGroupAttribute("Box", showLabel: false));

    // Here we add attributes to child properties respectively.
    if (member.Name == "Mode")
    {
        attributes.Add(new EnumToggleButtonsAttribute());
    }
    else if (member.Name == "Size")
    {
        attributes.Add(new RangeAttribute(0, 5));
    }
}

It's worth mentioning that OdinAttributeProcessors only work with inspector attributes. This system does not affect serialization, and adding or removing serialization attributes will result in nothing. Odin will even ignore attributes such as SerializeField and OdinSerialize in the attribute list when determining whether it should draw a member. These attributes will be fetched directly from the inspected members in question, completely bypassing the attribute processor system.


Attribute processor:

using UnityEngine;
using Sirenix.OdinInspector.Editor;
using Sirenix.OdinInspector;
using System.Collections.Generic;
using System;
using System.Reflection;

public class MyProcessedClassAttributeProcessor : OdinAttributeProcessor<MyProcessedClass>
{
    public override void ProcessSelfAttributes(InspectorProperty property, List<Attribute> attributes)
    {
        attributes.Add(new InfoBoxAttribute("Dynamically added attributes!"));
        attributes.Add(new InlinePropertyAttribute());
    }

    public override void ProcessChildMemberAttributes(
        InspectorProperty parentProperty,
        MemberInfo member,
        List<Attribute> attributes)
    {
        // These attributes will be added to all of the child elements.
        attributes.Add(new HideLabelAttribute());
        attributes.Add(new BoxGroupAttribute("Box", showLabel: false));

        // Here we add attributes to child properties respectively.
        if (member.Name == "Mode")
        {
            attributes.Add(new EnumToggleButtonsAttribute());
        }
        else if (member.Name == "Size")
        {
            attributes.Add(new RangeAttribute(0, 5));
        }
    }
}