module-inspector/Editor/Drawers/ReadableScriptableObjectAttributeDrawer.cs

157 lines
6.1 KiB
C#

using Module.Inspector.Editor.Utilities;
using UnityEditor;
using UnityEngine;
namespace Module.Inspector.Editor
{
[CustomPropertyDrawer(typeof(ReadableScriptableObjectAttribute))]
internal sealed class ReadableScriptableObjectAttributeDrawer : DrawerPropertyDrawer
{
public override bool Draw(Rect position, DrawerPropertyAttribute attribute, SerializedProperty property, GUIContent label, EditorPropertyUtility.Result result)
{
if (!IsSupported(property))
return false;
var att = (ReadableScriptableObjectAttribute)attribute;
EditorGUI.BeginChangeCheck();
EditorGUI.BeginProperty(position, label, property);
{
if (property.objectReferenceValue != null)
{
property.isExpanded = DrawFoldout(position, property, label);
if (property.isExpanded)
DrawContent(position, property.objectReferenceValue, att.editable);
}
else
{
// Note: Why not use EditorGUI.PropertyField?
// The reason is that the foldout uses Toggle and ObjectField and if we don't use
// Label and ObjectField here, then ObjectField will be given a new control id when changing
// objectReferenceValue from an existing object to null and back again. The new object will
// not be set, due to null giving a new control id.
var rectObj = new Rect
{
x = position.x + EditorGUIUtility.labelWidth + 2.0f,
y = position.y,
width = position.width - EditorGUIUtility.labelWidth - 2.0f,
height = position.height
};
GUI.Label(position, label);
property.objectReferenceValue = EditorGUI.ObjectField(rectObj, property.objectReferenceValue, property.GetValueType(), false);
}
}
EditorGUI.EndProperty();
bool hasChanged = EditorGUI.EndChangeCheck();
if (hasChanged)
property.serializedObject.ApplyModifiedProperties();
return true;
}
private static bool DrawFoldout(Rect position, SerializedProperty property, GUIContent label)
{
// Note: Why not use EditorGUI.BeginFoldoutHeaderGroup?
// FoldoutHeaderGroup doesn't support nesting, so any ScriptableObject with a field that uses
// a FoldoutHeaderGroup will not be drawn, e.g. arrays.
const float FOLDOUT_OFFSET = 14.0f;
const float FIELD_OFFSET = 16.0f;
position.x -= FOLDOUT_OFFSET;
position.width += FOLDOUT_OFFSET;
position.height = EditorGUIUtility.singleLineHeight;
var rectObj = new Rect
{
x = position.x + EditorGUIUtility.labelWidth + FIELD_OFFSET,
y = position.y,
width = position.width - EditorGUIUtility.labelWidth - FIELD_OFFSET,
height = position.height
};
GUIStyle style = property.objectReferenceValue != null ? EditorStyles.foldoutHeader : EditorStyles.label;
bool foldout = property.isExpanded;
if (Event.current.type == EventType.Repaint)
{
GUI.Toggle(position, foldout, label, style);
EditorGUI.ObjectField(rectObj, property.objectReferenceValue, property.GetValueType(), false);
}
else
{
property.objectReferenceValue = EditorGUI.ObjectField(rectObj, property.objectReferenceValue, property.GetValueType(), false);
foldout = GUI.Toggle(position, foldout, label, style);
}
return foldout;
}
private static void DrawContent(Rect rect, Object obj, bool editable)
{
var serializedObject = new SerializedObject(obj);
SerializedProperty it = serializedObject.GetIterator();
it.NextVisible(true);
rect.y += EditorGUIUtility.singleLineHeight;
EditorGUI.indentLevel++;
EditorGUI.BeginChangeCheck();
{
bool prevEnabled = GUI.enabled;
GUI.enabled = prevEnabled && editable;
while (it.NextVisible(false))
{
rect.height = EditorGUI.GetPropertyHeight(it);
EditorGUI.PropertyField(rect, it, new GUIContent(it.displayName), true);
rect.y += rect.height;
}
GUI.enabled = prevEnabled;
}
bool hasChanged = EditorGUI.EndChangeCheck();
EditorGUI.indentLevel--;
if (hasChanged)
serializedObject.ApplyModifiedProperties();
}
public override string GetErrorMessage(SerializedProperty property)
{
return IsSupported(property)
? "Unknown error"
: "Only supports types which inherits from ScriptableObject type";
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
if (property.objectReferenceValue == null || !property.isExpanded)
return base.GetPropertyHeight(property, label);
var so = new SerializedObject(property.objectReferenceValue);
SerializedProperty it = so.GetIterator();
var height = 0.0f;
it.NextVisible(true);
height += EditorGUI.GetPropertyHeight(it);
while (it.NextVisible(false))
{
height += EditorGUI.GetPropertyHeight(it);
}
return height;
}
private static bool IsSupported(SerializedProperty property)
{
return property.propertyType == SerializedPropertyType.ObjectReference &&
typeof(ScriptableObject).IsAssignableFrom(property.GetValueType());
}
}
}