1.4.0: Added ReadableScriptableObjectAttribute and fixed an GUI.enable isssue

* 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
This commit is contained in:
Anders Ejlersen 2021-12-19 13:45:23 +01:00
parent 05f04657c5
commit ee7326c33a
8 changed files with 198 additions and 31 deletions

View file

@ -15,7 +15,7 @@ namespace Module.Inspector.Editor
return;
bool prevEnabled = GUI.enabled;
GUI.enabled = accessType == EAccessType.Enabled;
GUI.enabled = prevEnabled && accessType == EAccessType.Enabled;
label.tooltip = result.tooltip;

View file

@ -0,0 +1,159 @@
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());
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 63160a5efc0643de9a3cb8058efb1038
timeCreated: 1639910668

View file

@ -114,32 +114,8 @@ namespace Module.Inspector.Editor
return false;
if (typeof(Object).IsAssignableFrom(type))
return false;
if (type.IsGenericType)
return false;
return true;
}
private static string ConvertUnityTypeFullnameToCSharpFullname(string unityNaming)
{
if (string.IsNullOrEmpty(unityNaming))
return string.Empty;
int index = unityNaming.LastIndexOf(' ');
string str;
if (index != -1)
{
str = unityNaming.Substring(0, index).Trim();
str += ", ";
str += unityNaming.Substring(index).Trim();
}
else
{
str = unityNaming;
}
return str.Replace('/', '+');
return !type.IsGenericType;
}
private static string ConvertUnityTypeFullnameToCSharpName(string unityNaming)

View file

@ -91,8 +91,12 @@ List of all available drawer attributes:
* Adds popup with all const fields of type string in provided type
* `QuaternionToEuler`
* Converts quaternion value to Euler angles and back again
* `ReadableScriptableObject`
* Creates an inline editor in the inspector window, so you can edit `ScriptableObjects` without finding the object first
* `SceneDropdown`
* Adds a popup with all scenes in EditorBuildSettings scenes
* `SerializeReferenceTo`
* Adds a popup for `SerializeReference` fields with types inheriting from assigned type or field type
* `Slider`
* Adds a min & max slider and clamps value (Requirement: MinValue and MaxValue)
* `StringToAnimatorParameter`
@ -105,8 +109,7 @@ List of all available drawer attributes:
* Adds popup with all tag values for field of type string
* `UrlGoTo`
* Adds a button to the field that calls Application.OpenUrl with string value
* `SerializeReferenceTo`
* Adds a popup for `SerializeReference` fields with types inheriting from assigned type or field type
## Value
@ -115,6 +118,8 @@ List of all value attributes:
* `ArrayIndex`
* Clamps value between other fields array size `[0;size[`
* `AssignIfNull`
* If value is null will either use field or custom type to get from self or children
* `LargerThanField`
* If value is greater than other fields value, then set value to other fields value
* `MaxValue`
@ -123,8 +128,7 @@ List of all value attributes:
* If value is less than min value, then set value to min value
* `SmallerThanField`
* If value is less than other fields value, then set value to other fields value
* `AssignIfNull`
* If value is null will either use field or custom type to get from self or children
## Validate

View file

@ -0,0 +1,22 @@
#if UNITY_2019_3_OR_NEWER
using System;
namespace Module.Inspector
{
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
public sealed class ReadableScriptableObjectAttribute : DrawerPropertyAttribute
{
public readonly bool editable;
public ReadableScriptableObjectAttribute()
{
editable = true;
}
public ReadableScriptableObjectAttribute(bool editable)
{
this.editable = editable;
}
}
}
#endif

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 319dfc0b017247d69fbe91575384ccf8
timeCreated: 1639910607

View file

@ -1,6 +1,6 @@
{
"name": "com.module.inspector",
"version": "1.3.2",
"version": "1.4.0",
"displayName": "Module.Inspector",
"description": "Custom inspector with various useful property drawers",
"unity": "2019.2",