gasgiant / Markup-Attributes

A Unity Editor extension for customizing inspector layout with attributes.
MIT License
289 stars 11 forks source link

Any idea how to make this work with Serializable class as opposed to only ScriptableObject and MonoBehaviours? #14

Open perrauo opened 1 year ago

perrauo commented 1 year ago

Unity Editors do not work with Serializable class. I assume we would need a PropertyDrawer. Also a custom base class of some kind in order to make it work with arrays.

using UnityEngine;
using UnityEditor;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
//using Cirrus.Objects;
//using Cirrus.Unity.Objects;

using UnityEngine;
using UnityEditor;
using System;
using Object = UnityEngine.Object;

namespace MarkupAttributes.Editor
{
    [Serializable]
    public class MarkupSerializableBase
    { 

    }

#if UNITY_EDITOR
    [CustomPropertyDrawer(typeof(MarkupSerializableBase), true)]
    public class MarkupPropertyDrawer : PropertyDrawer
    {
        private SerializedProperty[] allProps;
        private SerializedProperty[] firstLevelProps;
        private List<PropertyLayoutData> layoutData;

        private InspectorLayoutController layoutController;
        private CallbackManager callbackManager;
        private Dictionary<SerializedProperty, InlineEditorData> inlineEditors = new Dictionary<SerializedProperty, InlineEditorData>();
        private List<TargetObjectWrapper> targetsRequireUpdate = new List<TargetObjectWrapper>();

        protected virtual void OnInitialize() { }
        protected virtual void OnCleanup() { }
        protected void AddCallback(SerializedProperty property, CallbackEvent type, Action<SerializedProperty> callback)
        {
            callbackManager.AddCallback(property, type, callback);
        }

        protected void OnEnable()
        {           
            InitializeMarkedUpEditor();
        }

        protected void OnDisable()
        {
            CleanupMarkedUpEditor();
        }

        private SerializedObject serializedObject;
        //private Object target;

        public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
        {
            //if(property == null) return;
            serializedObject = property.serializedObject;
            //target = property.objectReferenceValue;
            //property.tar

            DrawMarkedUpInspector();
        }

        protected void InitializeMarkedUpEditor()
        {
            EditorLayoutDataBuilder.BuildLayoutData(serializedObject, out allProps,
                out firstLevelProps, out layoutData, out inlineEditors, out targetsRequireUpdate);
            layoutController = new InspectorLayoutController(serializedObject.GetType().FullName,
                layoutData.ToArray());
            callbackManager = new CallbackManager(firstLevelProps);
            OnInitialize();
        }

        protected void CleanupMarkedUpEditor()
        {
            OnCleanup();
            foreach(var item in inlineEditors)
            {
                //DestroyImmediate(item.Value.editor);
            }
        }

        protected bool DrawMarkedUpInspector()
        {
            EditorGUI.BeginChangeCheck();
            serializedObject.UpdateIfRequiredOrScript();

            CreateInlineEditors();
            UpdateTargets();
            int topLevelIndex = 1;
            layoutController.Begin();

            if(!MarkupGUI.IsInsideInlineEditor)
            {
                using(new EditorGUI.DisabledScope(true))
                {
                    EditorGUILayout.PropertyField(allProps[0]);
                }
            }

            for(int i = 1; i < allProps.Length; i++)
            {
                layoutController.BeforeProperty(i);
                if(layoutController.ScopeVisible)
                {
                    using(new EditorGUI.DisabledScope(!layoutController.ScopeEnabled))
                    {
                        DrawProperty(i, topLevelIndex);
                    }
                }

                if(layoutController.IsTopLevel(i))
                    topLevelIndex += 1;
            }
            layoutController.Finish();

            serializedObject.ApplyModifiedProperties();
            return EditorGUI.EndChangeCheck();
        }

        private void DrawProperty(int index, int topLevelIndex)
        {
            var prop = allProps[index];
            bool topLevel = layoutController.IsTopLevel(index);

            if(topLevel) callbackManager.InvokeCallback(topLevelIndex, CallbackEvent.BeforeProperty);

            using(new EditorGUI.DisabledScope(!layoutController.IsPropertyEnabled(index)))
            {
                if(layoutController.IsPropertyVisible(index))
                {
                    if(!topLevel || !callbackManager.InvokeCallback(index, CallbackEvent.ReplaceProperty))
                    {
                        if(inlineEditors.ContainsKey(prop))
                        {
                            InlineEditorData data = inlineEditors[prop];
                            MarkupGUI.DrawEditorInline(prop, data.editor, data.mode, data.enabled);
                        }
                        else
                        {
                            EditorGUILayout.PropertyField(prop, layoutController.IncludeChildren(index));
                        }
                    }
                }
            }
            if(topLevel) callbackManager.InvokeCallback(topLevelIndex, CallbackEvent.AfterProperty);
        }

        private void CreateInlineEditors()
        {
            var props = new List<SerializedProperty>(inlineEditors.Keys);
            foreach(var prop in props)
            {
                var editor = inlineEditors[prop].editor;

                if(prop.objectReferenceValue != serializedObject.targetObject)
                {
                    Material material = prop.objectReferenceValue as Material;
                    if(material != null)
                    {
                        //CreateCachedEditor(material, typeof(HeaderlessMaterialEditor), ref editor);
                        inlineEditors[prop].enabled = AssetDatabase.GetAssetPath(material).StartsWith("Assets");
                    }
                    else
                    {
                        //CreateCachedEditor(prop.objectReferenceValue, null, ref editor);
                    }
                }
                else
                {
                    editor = null;
                    prop.objectReferenceValue = null;
                    Debug.LogError("Self reference in the InlinedEditor property is not allowed.");
                }

                inlineEditors[prop].editor = editor;
            }
        }

        private void UpdateTargets()
        {
            foreach(var wrapper in targetsRequireUpdate)
            {
                wrapper.Update();
            }
        }
    }
#endif
}
Simply-Cods commented 1 year ago

I know this is stale, but there are 2 built-in ways to make this work with serializable classes (and structs) without creating a custom property drawer. This package also works with arrays and lists without doing anything

MyComponent.cs

using UnityEngine;
using MarkupAttributes;
using System.Collections.Generic;

public class MyComponent : MonoBehaviour
{
    [Box("Group")]
    public MyClass1 _myClass1;

    [MarkedUpField(indentChildren:false, showControl: false)]
    public MyClass2 _myClass2;

    public float[] _myArr;

    public List<float> _myList = new List<float>();
}

[MarkedUpType(indentChildren: false, showControl: false)]
[System.Serializable]
public class MyClass1
{
    public float _myFloat1 = 1f;
}

[System.Serializable]
public class MyClass2
{
    public float _myFloat2 = 1f;
}

MyEditor.cs

using UnityEditor;
using MarkupAttributes.Editor;

[CustomEditor(typeof(MyComponent))]
public class MyEditor : MarkedUpEditor
{
}

This gives the following result screen

You can also modify the way serializable types are shown in the inspector using the attribute parameters (both of them default to true)

Hope this answers your question