Community Made Tools

Have you made any useful utilities with Odin?

Login and submit your creations here

Page Slider Attribute

Authored by Bjarke
Shared 22-05-2019

An attribute that makes big editors with a lot of data more easy to navigate.

The root/first PageSlider property will act as the slide container, and all PageSliders inside of that will be drawn as a buttons which will slide to that object when pressed.

Usage

[PageSlider]
public SomeTestClass test;

// Or place it above the class to enable it globally.
[PageSlider]
public class SomeOtherTestClass
{
}

SomeScriptableObject.cs Some random data for you to try it out with.

// 
// Test data - you can delete this part or name the file SomeScriptableObject.cs and try it out
// 
[CreateAssetMenu]
public class SomeScriptableObject : SerializedScriptableObject
{
    [ShowInInspector]
    [LabelText("$testName")]
    public static SomeData test = new SomeData();

    public string testName()
    {
        return test.Name;
    }
}

[PageSlider, HideReferenceObjectPicker]
public class SomeData
{
    public string Name;
    public int b, c, d;
    public SomeData b1, c1, d2;
    [ListDrawerSettings(Expanded = true)]
    public SomeData[] a = new SomeData[3];

    [ListDrawerSettings(Expanded = true), InlineEditor(ObjectFieldMode = InlineEditorObjectFieldModes.Foldout, Expanded = true)]
    [PageSlider]
    public Material[] e = new Material[0];
    public int f, g, h;


    public override string ToString()
    {
        return this.Name ?? "Some Data";
    }
}

PageSliderAttribute.cs

public class PageSliderAttribute : System.Attribute
{
}

PageSliderAttributeDrawer.cs

[DrawerPriority(0, 200, 0)]
public class PageSliderAttributeDrawer : OdinAttributeDrawer<PageSliderAttribute>
{
    private static GUIStyle titleStyle;
    private static SlidePageNavigationHelper<InspectorProperty> currentSlider;
    private static InspectorProperty currentDrawingPageProperty;
    private SlidePageNavigationHelper<InspectorProperty> slider;
    private SlidePageNavigationHelper<InspectorProperty>.Page page;
    private GUIContent pageLabel;

    protected override bool CanDrawAttributeProperty(InspectorProperty property)
    {
        return !(property.ChildResolver is ICollectionResolver);
    }

    protected override void Initialize()
    {
        titleStyle = titleStyle ?? new GUIStyle("ShurikenModuleTitle");
    }

    protected override void DrawPropertyLayout(GUIContent label)
    {
        if (this.Property.ValueEntry.WeakSmartValue == null)
        {
            this.CallNextDrawer(label);
            return;
        }

        this.UpdateBreadcrumbLabel(label);

        if (currentSlider == null)
        {
            this.DrawPageSlider(label);
        }
        else if (currentDrawingPageProperty == this.Property)
        {
            this.CallNextDrawer(null);
        }
        else
        {
            if (GUILayout.Button(new GUIContent(this.GetLabelText(label)), titleStyle))
            {
                currentSlider.PushPage(this.Property, Guid.NewGuid().ToString());
                currentSlider.EnumeratePages.Last();
                this.page = currentSlider.EnumeratePages.Last();
                this.page.Name = this.GetLabelText(label);
                this.pageLabel = label;
            }
        }
    }

    private void UpdateBreadcrumbLabel(GUIContent label)
    {
        if (Event.current.type != EventType.Layout) return;
        if (this.page == null) return;
        if (this.pageLabel != null && this.pageLabel != this.Property.Label) return;

        var newLabel = this.GetLabelText(label ?? this.pageLabel);

        if (newLabel != this.page.Name)
        {
            this.page.Name = newLabel;
            this.page.GetType().GetField("TitleWidth", Flags.AllMembers).SetValue(this.page, null);
        }
    }

    private void DrawPageSlider(GUIContent label)
    {
        try
        {
            if (this.slider == null)
            {
                this.slider = new SlidePageNavigationHelper<InspectorProperty>();
                this.slider.PushPage(this.Property, Guid.NewGuid().ToString());
                this.page = this.slider.EnumeratePages.Last();
                this.page.Name = this.GetLabelText(label);
            }

            currentSlider = this.slider;

            SirenixEditorGUI.BeginBox();
            SirenixEditorGUI.BeginToolbarBoxHeader();
            {
                var rect = GUILayoutUtility.GetRect(0, 20);
                rect.x -= 5;
                this.slider.DrawPageNavigation(rect);
            }
            SirenixEditorGUI.EndToolbarBoxHeader();
            {
                this.slider.BeginGroup();
                foreach (var p in this.slider.EnumeratePages)
                {
                    if (p.BeginPage())
                    {
                        if (p.Value == this.Property)
                        {
                            this.CallNextDrawer(null);
                        }
                        else
                        {
                            currentDrawingPageProperty = p.Value;
                            if (p.Value.Tree != this.Property.Tree)
                            {
                                InspectorUtilities.BeginDrawPropertyTree(p.Value.Tree, true);
                            }
                            p.Value.Draw(null);

                            if (p.Value.Tree != this.Property.Tree)
                            {
                                InspectorUtilities.EndDrawPropertyTree(p.Value.Tree);
                            }
                            currentDrawingPageProperty = null;
                        }
                    }
                    p.EndPage();
                }
                this.slider.EndGroup();
            }
            SirenixEditorGUI.EndBox();

        }
        finally
        {
            currentSlider = null;
        }
    }

    private string GetLabelText(GUIContent label)
    {
        if (label != null)
        {
            return label.text;
        }

        var val = this.Property.ValueEntry.WeakSmartValue;
        if (val == null)
        {
            return "Null";
        }
        var uObj = val as UnityEngine.Object;
        if (uObj)
        {
            if (string.IsNullOrEmpty(uObj.name))
            {
                return uObj.ToString();
            }
            else
            {
                return uObj.name;
            }
        }

        return val.ToString();
    }
}