diff --git a/Editor/Drawers/CustomObjectDrawer.cs b/Editor/Drawers/CustomObjectDrawer.cs index b1931f2..5b6b27e 100644 --- a/Editor/Drawers/CustomObjectDrawer.cs +++ b/Editor/Drawers/CustomObjectDrawer.cs @@ -94,6 +94,7 @@ private void HandleMouseDown(Rect position, Rect positionWithoutThumb) else if (Event.button == 1 && positionWithoutThumb.Contains(Event.mousePosition)) { GenericMenu menu = new GenericMenu(); + menu.AddItem(new GUIContent("Clear"), false, () => { DeletePressed?.Invoke(); }); menu.AddItem(new GUIContent("Properties..."), false, () => { PropertiesClicked?.Invoke(); }); menu.DropDown(position); Event.Use(); diff --git a/Editor/Drawers/RawReferenceDrawer.cs b/Editor/Drawers/RawReferenceDrawer.cs index 72fa6e7..1c42b30 100644 --- a/Editor/Drawers/RawReferenceDrawer.cs +++ b/Editor/Drawers/RawReferenceDrawer.cs @@ -9,28 +9,15 @@ namespace TNRD.Drawers internal class RawReferenceDrawer : ReferenceDrawer, IReferenceDrawer { private readonly GUIContent label; - private readonly FieldInfo fieldInfo; - private object RawReferenceValue - { - get - { -#if UNITY_2021_1_OR_NEWER - return RawReferenceProperty.managedReferenceValue; -#else - ISerializableInterface instance = - (ISerializableInterface)fieldInfo.GetValue(Property.serializedObject.targetObject); - return instance.GetRawReference(); -#endif - } - } + private static object previousReferenceValue; + private static string previousPropertyPath; /// public RawReferenceDrawer(SerializedProperty property, GUIContent label, Type genericType, FieldInfo fieldInfo) - : base(property, genericType) + : base(property, genericType, fieldInfo) { this.label = label; - this.fieldInfo = fieldInfo; } /// @@ -47,6 +34,8 @@ public float GetHeight() /// public void OnGUI(Rect position) { + AvoidDuplicateReferencesInArray(); + Rect objectFieldRect = new Rect(position) { height = EditorGUIUtility.singleLineHeight @@ -59,6 +48,9 @@ public void OnGUI(Rect position) : new GUIContent(rawReferenceValue.GetType().Name, IconUtility.ScriptIcon); CustomObjectDrawer.OnGUI(objectFieldRect, label, content); + + HandleDragAndDrop(objectFieldRect); + if (rawReferenceValue == null) return; @@ -70,12 +62,20 @@ public void OnGUI(Rect position) new GUIContent(rawReferenceValue.GetType().Name), true); - HandleDragAndDrop(objectFieldRect); + previousPropertyPath = Property.propertyPath; + } + + protected override void PingObject() + { + // No support for pinging raw objects for now (I guess this would ping the MonoScript?) } /// protected override void OnPropertiesClicked() { + if (RawReferenceValue == null) + return; + Type type = RawReferenceValue.GetType(); string typeName = type.Name; @@ -91,5 +91,37 @@ protected override void OnPropertiesClicked() } } } + + private void AvoidDuplicateReferencesInArray() + { + if (!IsPropertyInArray(Property)) + return; + if (previousPropertyPath == null) + return; + if (previousPropertyPath == Property.propertyPath) + return; + + object currentReferenceValue = RawReferenceValue; + + if (currentReferenceValue == null) + return; + + if (previousReferenceValue == currentReferenceValue) + PropertyValue = CreateInstance(currentReferenceValue); + + previousReferenceValue = currentReferenceValue; + } + + private static bool IsPropertyInArray(SerializedProperty prop) + { + return prop.propertyPath.Contains(".Array.data["); + } + + private static object CreateInstance(object source) + { + object instance = Activator.CreateInstance(source.GetType()); + EditorUtility.CopySerializedManagedFieldsOnly(source, instance); + return instance; + } } } diff --git a/Editor/Drawers/ReferenceDrawer.cs b/Editor/Drawers/ReferenceDrawer.cs index 984dc05..a64eb99 100644 --- a/Editor/Drawers/ReferenceDrawer.cs +++ b/Editor/Drawers/ReferenceDrawer.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Reflection; using TNRD.Utilities; using UnityEditor; using UnityEditor.IMGUI.Controls; @@ -29,10 +30,72 @@ private enum DragAndDropMode protected SerializedProperty RawReferenceProperty => Property.FindPropertyRelative("rawReference"); protected SerializedProperty UnityReferenceProperty => Property.FindPropertyRelative("unityReference"); - protected ReferenceDrawer(SerializedProperty property, Type genericType) + protected readonly FieldInfo fieldInfo; + + protected ReferenceMode ModeValue + { + get => (ReferenceMode)ReferenceModeProperty.enumValueIndex; + set => ReferenceModeProperty.enumValueIndex = (int)value; + } + + protected object RawReferenceValue + { + get + { +#if UNITY_2021_1_OR_NEWER + return RawReferenceProperty.managedReferenceValue; +#else + ISerializableInterface instance = + (ISerializableInterface)fieldInfo.GetValue(Property.serializedObject.targetObject); + return instance.GetRawReference(); +#endif + } + set + { +#if UNITY_2021_1_OR_NEWER + RawReferenceProperty.managedReferenceValue = value; +#else + fieldInfo.SetValue(Property.serializedObject.targetObject, value); +#endif + } + } + + protected object PropertyValue + { + get + { + return ModeValue switch + { + ReferenceMode.Raw => RawReferenceValue, + ReferenceMode.Unity => UnityReferenceProperty.objectReferenceValue, + _ => throw new ArgumentOutOfRangeException() + }; + } + set + { + switch (ModeValue) + { + case ReferenceMode.Raw: + RawReferenceValue = value; + UnityReferenceProperty.objectReferenceValue = null; + break; + case ReferenceMode.Unity: + UnityReferenceProperty.objectReferenceValue = GetUnityObject((Object)value); + RawReferenceValue = null; + break; + default: + throw new ArgumentOutOfRangeException(); + } + + Property.serializedObject.ApplyModifiedProperties(); + } + } + + protected ReferenceDrawer(SerializedProperty property, Type genericType, FieldInfo fieldInfo) { Property = property; GenericType = genericType; + this.fieldInfo = fieldInfo; CustomObjectDrawer = new CustomObjectDrawer(); CustomObjectDrawer.ButtonClicked += OnButtonClicked; @@ -66,56 +129,35 @@ private void OnButtonClicked(Rect position) private void OnClicked() { - switch ((ReferenceMode)ReferenceModeProperty.enumValueIndex) - { - case ReferenceMode.Raw: - // No support for pinging raw objects for now (I guess this would ping the MonoScript?) - break; - case ReferenceMode.Unity: - EditorGUIUtility.PingObject(UnityReferenceProperty.objectReferenceValue); - break; - default: - throw new ArgumentOutOfRangeException(); - } + PingObject(); } private void OnDeletePressed() { - RawReferenceProperty.managedReferenceValue = null; - UnityReferenceProperty.objectReferenceValue = null; - Property.serializedObject.ApplyModifiedProperties(); + PropertyValue = null; } private void OnItemSelected(ReferenceMode mode, object reference) { - ReferenceModeProperty.enumValueIndex = (int)mode; - - switch (mode) - { - case ReferenceMode.Raw: - RawReferenceProperty.managedReferenceValue = reference; - break; - case ReferenceMode.Unity: - UnityReferenceProperty.objectReferenceValue = (Object)reference; - break; - default: - throw new ArgumentOutOfRangeException(nameof(mode), mode, null); - } - - Property.serializedObject.ApplyModifiedProperties(); + ModeValue = mode; + PropertyValue = reference; } protected abstract void OnPropertiesClicked(); protected void HandleDragAndDrop(Rect position) { + if (!position.Contains(Event.current.mousePosition)) + return; + if (Event.current.type == EventType.DragPerform) { + HandleDragUpdated(); HandleDragPerform(); } else if (Event.current.type == EventType.DragUpdated) { - HandleDragUpdated(position); + HandleDragUpdated(); } } @@ -134,11 +176,8 @@ private void SetDragAndDropMode(bool success, DragAndDropMode? successMode = nul } } - private void HandleDragUpdated(Rect position) + private void HandleDragUpdated() { - if (!position.Contains(Event.current.mousePosition)) - return; - if (DragAndDrop.objectReferences.Length > 1) { SetDragAndDropMode(false); @@ -156,12 +195,12 @@ private void HandleDragUpdated(Rect position) { Type scriptType = monoScript.GetClass(); - if (scriptType.IsSubclassOf(typeof(UnityEngine.Object))) + if (scriptType.IsSubclassOf(typeof(Object))) { SetDragAndDropMode(false); return; } - + if (!GenericType.IsAssignableFrom(scriptType)) { SetDragAndDropMode(false); @@ -182,18 +221,26 @@ private void HandleDragPerform() switch (dragAndDropMode) { case DragAndDropMode.Raw: - RawReferenceProperty.managedReferenceValue = - Activator.CreateInstance(((MonoScript)DragAndDrop.objectReferences[0]).GetClass()); - ReferenceModeProperty.enumValueIndex = (int)ReferenceMode.Raw; + ModeValue = ReferenceMode.Raw; + PropertyValue = Activator.CreateInstance(((MonoScript)DragAndDrop.objectReferences[0]).GetClass()); break; case DragAndDropMode.Unity: - UnityReferenceProperty.objectReferenceValue = DragAndDrop.objectReferences[0]; - ReferenceModeProperty.enumValueIndex = (int)ReferenceMode.Unity; + ModeValue = ReferenceMode.Unity; + PropertyValue = DragAndDrop.objectReferences[0]; break; case DragAndDropMode.None: default: throw new ArgumentOutOfRangeException(); } } + + private Object GetUnityObject(Object objectReference) + { + if(objectReference is GameObject gameObject) + return gameObject.GetComponent(GenericType); + return objectReference; + } + + protected abstract void PingObject(); } } diff --git a/Editor/Drawers/UnityReferenceDrawer.cs b/Editor/Drawers/UnityReferenceDrawer.cs index b409799..8592c49 100644 --- a/Editor/Drawers/UnityReferenceDrawer.cs +++ b/Editor/Drawers/UnityReferenceDrawer.cs @@ -1,4 +1,5 @@ using System; +using System.Reflection; using TNRD.Utilities; using UnityEditor; using UnityEngine; @@ -10,8 +11,8 @@ internal class UnityReferenceDrawer : ReferenceDrawer, IReferenceDrawer { private readonly GUIContent label; - public UnityReferenceDrawer(SerializedProperty property, GUIContent label, Type genericType) - : base(property, genericType) + public UnityReferenceDrawer(SerializedProperty property, GUIContent label, Type genericType, FieldInfo fieldInfo) + : base(property, genericType, fieldInfo) { this.label = label; } @@ -30,6 +31,11 @@ public void OnGUI(Rect position) HandleDragAndDrop(position); } + protected override void PingObject() + { + EditorGUIUtility.PingObject((Object)PropertyValue); + } + protected override void OnPropertiesClicked() { PropertyEditorUtility.Show(UnityReferenceProperty.objectReferenceValue); diff --git a/Editor/Items/NoneDropdownItem.cs b/Editor/Items/NoneDropdownItem.cs new file mode 100644 index 0000000..ca05cdc --- /dev/null +++ b/Editor/Items/NoneDropdownItem.cs @@ -0,0 +1,17 @@ +using UnityEditor.IMGUI.Controls; + +namespace TNRD.Items +{ + public class NoneDropdownItem : AdvancedDropdownItem, IDropdownItem + { + private ReferenceMode mode => ReferenceMode.Raw; + public NoneDropdownItem() : base("None") { } + + ReferenceMode IDropdownItem.Mode => mode; + + public object GetValue() + { + return null; + } + } +} diff --git a/Editor/Items/NoneDropdownItem.cs.meta b/Editor/Items/NoneDropdownItem.cs.meta new file mode 100644 index 0000000..2813776 --- /dev/null +++ b/Editor/Items/NoneDropdownItem.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a7015062205d46cbb9cb01b0b88fff55 +timeCreated: 1657572673 \ No newline at end of file diff --git a/Editor/SerializableInterfacePropertyDrawer.cs b/Editor/SerializableInterfacePropertyDrawer.cs index 7ce4953..72d84cc 100644 --- a/Editor/SerializableInterfacePropertyDrawer.cs +++ b/Editor/SerializableInterfacePropertyDrawer.cs @@ -16,10 +16,7 @@ internal sealed class SerializableInterfacePropertyDrawer : PropertyDrawer private IReferenceDrawer activeDrawer; /// - public override bool CanCacheInspectorGUI(SerializedProperty property) - { - return false; - } + public override bool CanCacheInspectorGUI(SerializedProperty property) => false; private void Initialize(SerializedProperty property) { @@ -102,7 +99,7 @@ GUIContent label case ReferenceMode.Unity: return original is UnityReferenceDrawer ? original - : new UnityReferenceDrawer(property, label, genericType); + : new UnityReferenceDrawer(property, label, genericType, fieldInfo); default: throw new ArgumentOutOfRangeException(); } diff --git a/Editor/Utilities/SerializableInterfaceAdvancedDropdown.cs b/Editor/Utilities/SerializableInterfaceAdvancedDropdown.cs index af82ecd..a485c08 100644 --- a/Editor/Utilities/SerializableInterfaceAdvancedDropdown.cs +++ b/Editor/Utilities/SerializableInterfaceAdvancedDropdown.cs @@ -48,6 +48,11 @@ protected override AdvancedDropdownItem BuildRoot() .AddChild(new ClassesItemBuilder(interfaceType).Build()) .AddChild(new SceneItemBuilder(interfaceType, relevantScene).Build()); + foreach (var dropdownItem in item.children) + { + dropdownItem.AddChild(new NoneDropdownItem()); + } + if (canSort) { sortChildrenMethod.Invoke(item, @@ -62,6 +67,12 @@ protected override AdvancedDropdownItem BuildRoot() private int Sort(AdvancedDropdownItem a, AdvancedDropdownItem b) { + // For aesthetic reasons. Always puts the None first + if (a is NoneDropdownItem) + return -1; + if (b is NoneDropdownItem) + return 1; + int childrenA = a.children.Count(); int childrenB = b.children.Count();