* Attribute: Added ReadableScriptableObjectAttribute to allow for inline editing of ScriptableObjects in the inspector * Attribute: Fixed issue, where root drawer wouldn't use existing value of GUI.enabled, when setting GUI.enabled state with new access value
159 lines
6.1 KiB
C#
159 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 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());
|
|
}
|
|
}
|
|
} |