From 02fb8a98409afd296f1e430d6dc1988352e5262b Mon Sep 17 00:00:00 2001 From: Marc-Antoine Girard Date: Sat, 9 Jul 2022 14:59:12 -0400 Subject: [PATCH 1/7] Update SerializableInterfacePropertyDrawer.cs This fixes the issue #19 --- Editor/SerializableInterfacePropertyDrawer.cs | 50 +++++++++++++++++-- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/Editor/SerializableInterfacePropertyDrawer.cs b/Editor/SerializableInterfacePropertyDrawer.cs index 7ce4953..dd55a1c 100644 --- a/Editor/SerializableInterfacePropertyDrawer.cs +++ b/Editor/SerializableInterfacePropertyDrawer.cs @@ -10,7 +10,11 @@ namespace TNRD [CustomPropertyDrawer(typeof(SerializableInterface<>), true)] internal sealed class SerializableInterfacePropertyDrawer : PropertyDrawer { + private const string k_RawReference = "rawReference"; + private SerializedProperty serializedProperty; + private object previousReferenceValue; + private string lastPropertyPath; private Type genericType; private IReferenceDrawer activeDrawer; @@ -23,15 +27,47 @@ public override bool CanCacheInspectorGUI(SerializedProperty property) private void Initialize(SerializedProperty property) { - if (serializedProperty == property) - return; - - activeDrawer = null; serializedProperty = property; + activeDrawer = null; genericType = GetGenericArgument(); + Assert.IsNotNull(genericType, "Unable to find generic argument, are you doing some shady inheritance?"); } + /// + /// Avoids an unexpected behaviour + /// + private void AvoidDuplicateReferences(SerializedProperty property) + { + if (!IsTargetObjectArray(property)) return; // This function + if (lastPropertyPath == null) return; + if (lastPropertyPath == property.propertyPath) return; // only one element + + var rawReferenceProperty = property.FindPropertyRelative(k_RawReference); // Cache property + var currentReferenceValue = rawReferenceProperty.managedReferenceValue; + + if (currentReferenceValue == null) return; // Value is null. Probably new or not set yet + + if (previousReferenceValue == currentReferenceValue) + { + // The best behaviour would be to create a shallow copy. The extension method from SolidUtilities works perfectly. + // Using serialization or reflection might also work + var instance = Activator.CreateInstance(currentReferenceValue.GetType()); + rawReferenceProperty.managedReferenceValue = instance; + + // propertyRelative.managedReferenceValue = currentReferenceValue.ShallowCopy(); + } + + previousReferenceValue = currentReferenceValue; + } + + + private static bool IsTargetObjectArray(SerializedProperty prop) + { + return prop.propertyPath.Contains(".Array.data["); + } + + /// public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { @@ -44,8 +80,12 @@ public override float GetPropertyHeight(SerializedProperty property, GUIContent public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { Initialize(property); + AvoidDuplicateReferences(property); + activeDrawer = GetReferenceDrawer(activeDrawer, property, label); activeDrawer.OnGUI(position); + + lastPropertyPath = property.propertyPath; } private Type GetGenericArgument() @@ -108,4 +148,4 @@ GUIContent label } } } -} +} \ No newline at end of file From c69d2e1b2367e59095ae326b02ce00767be54682 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Girard Date: Sat, 9 Jul 2022 17:32:31 -0400 Subject: [PATCH 2/7] Clean up SerializableInterfacePropertyDrawer.cs --- Editor/SerializableInterfacePropertyDrawer.cs | 56 +++++++++---------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/Editor/SerializableInterfacePropertyDrawer.cs b/Editor/SerializableInterfacePropertyDrawer.cs index dd55a1c..68b82a8 100644 --- a/Editor/SerializableInterfacePropertyDrawer.cs +++ b/Editor/SerializableInterfacePropertyDrawer.cs @@ -10,24 +10,23 @@ namespace TNRD [CustomPropertyDrawer(typeof(SerializableInterface<>), true)] internal sealed class SerializableInterfacePropertyDrawer : PropertyDrawer { - private const string k_RawReference = "rawReference"; + private const string RAW_REFERENCE = "rawReference"; + private const string MODE = "mode"; - private SerializedProperty serializedProperty; - private object previousReferenceValue; - private string lastPropertyPath; private Type genericType; - private IReferenceDrawer activeDrawer; + private object previousReferenceValue; + private string previousPropertyPath; + /// public override bool CanCacheInspectorGUI(SerializedProperty property) { return false; } - private void Initialize(SerializedProperty property) + private void Initialize() { - serializedProperty = property; activeDrawer = null; genericType = GetGenericArgument(); @@ -35,34 +34,35 @@ private void Initialize(SerializedProperty property) } /// - /// Avoids an unexpected behaviour + /// When a new instance of is added in an array using the inspector's gizmo, + /// avoids having their field reference the same instance. /// - private void AvoidDuplicateReferences(SerializedProperty property) + /// The SerializedProperty to make the custom GUI for. + private void AvoidDuplicateReferencesInArray(SerializedProperty property) { - if (!IsTargetObjectArray(property)) return; // This function - if (lastPropertyPath == null) return; - if (lastPropertyPath == property.propertyPath) return; // only one element + if (!IsPropertyInArray(property)) return; + if (previousPropertyPath == null) return; + if (previousPropertyPath == property.propertyPath) return; - var rawReferenceProperty = property.FindPropertyRelative(k_RawReference); // Cache property + var rawReferenceProperty = property.FindPropertyRelative(RAW_REFERENCE); var currentReferenceValue = rawReferenceProperty.managedReferenceValue; - if (currentReferenceValue == null) return; // Value is null. Probably new or not set yet + if (currentReferenceValue == null) return; if (previousReferenceValue == currentReferenceValue) - { - // The best behaviour would be to create a shallow copy. The extension method from SolidUtilities works perfectly. - // Using serialization or reflection might also work - var instance = Activator.CreateInstance(currentReferenceValue.GetType()); - rawReferenceProperty.managedReferenceValue = instance; - - // propertyRelative.managedReferenceValue = currentReferenceValue.ShallowCopy(); - } + rawReferenceProperty.managedReferenceValue = CreateInstance(currentReferenceValue); previousReferenceValue = currentReferenceValue; } + private static object CreateInstance(object source) + { + var instance = Activator.CreateInstance(source.GetType()); + EditorUtility.CopySerializedManagedFieldsOnly(source, instance); + return instance; + } - private static bool IsTargetObjectArray(SerializedProperty prop) + private static bool IsPropertyInArray(SerializedProperty prop) { return prop.propertyPath.Contains(".Array.data["); } @@ -71,7 +71,7 @@ private static bool IsTargetObjectArray(SerializedProperty prop) /// public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { - Initialize(property); + Initialize(); activeDrawer = GetReferenceDrawer(activeDrawer, property, label); return activeDrawer.GetHeight(); } @@ -79,13 +79,13 @@ public override float GetPropertyHeight(SerializedProperty property, GUIContent /// public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { - Initialize(property); - AvoidDuplicateReferences(property); + Initialize(); + AvoidDuplicateReferencesInArray(property); activeDrawer = GetReferenceDrawer(activeDrawer, property, label); activeDrawer.OnGUI(position); - lastPropertyPath = property.propertyPath; + previousPropertyPath = property.propertyPath; } private Type GetGenericArgument() @@ -130,7 +130,7 @@ private IReferenceDrawer GetReferenceDrawer( GUIContent label ) { - SerializedProperty modeProperty = serializedProperty.FindPropertyRelative("mode"); + SerializedProperty modeProperty = property.FindPropertyRelative(MODE); ReferenceMode referenceMode = (ReferenceMode)modeProperty.enumValueIndex; switch (referenceMode) From ba10941d3a3fdc57e107592de57e70f39058568c Mon Sep 17 00:00:00 2001 From: Marc-Antoine Girard Date: Sat, 9 Jul 2022 17:53:02 -0400 Subject: [PATCH 3/7] Update SerializableInterfacePropertyDrawer.cs --- Editor/SerializableInterfacePropertyDrawer.cs | 66 ++++++++++--------- 1 file changed, 35 insertions(+), 31 deletions(-) diff --git a/Editor/SerializableInterfacePropertyDrawer.cs b/Editor/SerializableInterfacePropertyDrawer.cs index 68b82a8..8e37d3c 100644 --- a/Editor/SerializableInterfacePropertyDrawer.cs +++ b/Editor/SerializableInterfacePropertyDrawer.cs @@ -13,26 +13,48 @@ internal sealed class SerializableInterfacePropertyDrawer : PropertyDrawer private const string RAW_REFERENCE = "rawReference"; private const string MODE = "mode"; + private SerializedProperty serializedProperty; private Type genericType; + private IReferenceDrawer activeDrawer; private object previousReferenceValue; private string previousPropertyPath; /// - public override bool CanCacheInspectorGUI(SerializedProperty property) - { - return false; - } + public override bool CanCacheInspectorGUI(SerializedProperty property) => false; - private void Initialize() + private void Initialize(SerializedProperty property) { + if (serializedProperty == property) + return; + activeDrawer = null; + serializedProperty = property; genericType = GetGenericArgument(); Assert.IsNotNull(genericType, "Unable to find generic argument, are you doing some shady inheritance?"); } + /// + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) + { + Initialize(property); + activeDrawer = GetReferenceDrawer(activeDrawer, property, label); + return activeDrawer.GetHeight(); + } + + /// + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) + { + Initialize(property); + AvoidDuplicateReferencesInArray(property); + + activeDrawer = GetReferenceDrawer(activeDrawer, property, label); + activeDrawer.OnGUI(position); + previousPropertyPath = property.propertyPath; + } + /// /// When a new instance of is added in an array using the inspector's gizmo, /// avoids having their field reference the same instance. @@ -50,42 +72,24 @@ private void AvoidDuplicateReferencesInArray(SerializedProperty property) if (currentReferenceValue == null) return; if (previousReferenceValue == currentReferenceValue) + { rawReferenceProperty.managedReferenceValue = CreateInstance(currentReferenceValue); + rawReferenceProperty.serializedObject.ApplyModifiedProperties(); + } previousReferenceValue = currentReferenceValue; } - - private static object CreateInstance(object source) - { - var instance = Activator.CreateInstance(source.GetType()); - EditorUtility.CopySerializedManagedFieldsOnly(source, instance); - return instance; - } - + private static bool IsPropertyInArray(SerializedProperty prop) { return prop.propertyPath.Contains(".Array.data["); } - - /// - public override float GetPropertyHeight(SerializedProperty property, GUIContent label) - { - Initialize(); - activeDrawer = GetReferenceDrawer(activeDrawer, property, label); - return activeDrawer.GetHeight(); - } - - /// - public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) + private static object CreateInstance(object source) { - Initialize(); - AvoidDuplicateReferencesInArray(property); - - activeDrawer = GetReferenceDrawer(activeDrawer, property, label); - activeDrawer.OnGUI(position); - - previousPropertyPath = property.propertyPath; + var instance = Activator.CreateInstance(source.GetType()); + EditorUtility.CopySerializedManagedFieldsOnly(source, instance); + return instance; } private Type GetGenericArgument() From d6e8d0be6b30237a99748d8b3dd6b38cae10eeff Mon Sep 17 00:00:00 2001 From: Marc-Antoine Girard Date: Sat, 9 Jul 2022 17:57:46 -0400 Subject: [PATCH 4/7] Update SerializableInterfacePropertyDrawer.cs removed unnecessary change --- Editor/SerializableInterfacePropertyDrawer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Editor/SerializableInterfacePropertyDrawer.cs b/Editor/SerializableInterfacePropertyDrawer.cs index 8e37d3c..13502ff 100644 --- a/Editor/SerializableInterfacePropertyDrawer.cs +++ b/Editor/SerializableInterfacePropertyDrawer.cs @@ -134,7 +134,7 @@ private IReferenceDrawer GetReferenceDrawer( GUIContent label ) { - SerializedProperty modeProperty = property.FindPropertyRelative(MODE); + SerializedProperty modeProperty = serializedProperty.FindPropertyRelative(MODE); ReferenceMode referenceMode = (ReferenceMode)modeProperty.enumValueIndex; switch (referenceMode) From ae0f89febc17a440992be3b0eb8a1489c35215af Mon Sep 17 00:00:00 2001 From: Marc-Antoine Girard Date: Sat, 9 Jul 2022 19:10:52 -0400 Subject: [PATCH 5/7] Moved changes to RawReferenceDrawer Since it only applies to SerializableInterfaces in Raw mode, it made more sense to only do it in RawReferenceDrawer instead of SerializableInterfacePropertyDrawer. Tested the same way, no apparent diff. --- Editor/Drawers/RawReferenceDrawer.cs | 50 ++++++++++++++++++- Editor/SerializableInterfacePropertyDrawer.cs | 48 +----------------- 2 files changed, 50 insertions(+), 48 deletions(-) diff --git a/Editor/Drawers/RawReferenceDrawer.cs b/Editor/Drawers/RawReferenceDrawer.cs index 72fa6e7..a84752b 100644 --- a/Editor/Drawers/RawReferenceDrawer.cs +++ b/Editor/Drawers/RawReferenceDrawer.cs @@ -11,6 +11,9 @@ internal class RawReferenceDrawer : ReferenceDrawer, IReferenceDrawer private readonly GUIContent label; private readonly FieldInfo fieldInfo; + private static object previousReferenceValue; + private static string previousPropertyPath; + private object RawReferenceValue { get @@ -21,6 +24,15 @@ private object RawReferenceValue 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 } } @@ -47,6 +59,8 @@ public float GetHeight() /// public void OnGUI(Rect position) { + AvoidDuplicateReferencesInArray(); + Rect objectFieldRect = new Rect(position) { height = EditorGUIUtility.singleLineHeight @@ -71,6 +85,8 @@ public void OnGUI(Rect position) true); HandleDragAndDrop(objectFieldRect); + + previousPropertyPath = Property.propertyPath; } /// @@ -91,5 +107,37 @@ protected override void OnPropertiesClicked() } } } + + private void AvoidDuplicateReferencesInArray() + { + if (!IsPropertyInArray(Property)) return; + if (previousPropertyPath == null) return; + if (previousPropertyPath == Property.propertyPath) return; + + var rawReferenceProperty = Property.FindPropertyRelative("rawReference"); + var currentReferenceValue = RawReferenceValue; + + if (currentReferenceValue == null) return; + + if (previousReferenceValue == currentReferenceValue) + { + rawReferenceProperty.managedReferenceValue = CreateInstance(currentReferenceValue); + rawReferenceProperty.serializedObject.ApplyModifiedProperties(); + } + + previousReferenceValue = currentReferenceValue; + } + + private static bool IsPropertyInArray(SerializedProperty prop) + { + return prop.propertyPath.Contains(".Array.data["); + } + + private static object CreateInstance(object source) + { + var instance = Activator.CreateInstance(source.GetType()); + EditorUtility.CopySerializedManagedFieldsOnly(source, instance); + return instance; + } } -} +} \ No newline at end of file diff --git a/Editor/SerializableInterfacePropertyDrawer.cs b/Editor/SerializableInterfacePropertyDrawer.cs index 13502ff..1d23d42 100644 --- a/Editor/SerializableInterfacePropertyDrawer.cs +++ b/Editor/SerializableInterfacePropertyDrawer.cs @@ -10,17 +10,11 @@ namespace TNRD [CustomPropertyDrawer(typeof(SerializableInterface<>), true)] internal sealed class SerializableInterfacePropertyDrawer : PropertyDrawer { - private const string RAW_REFERENCE = "rawReference"; - private const string MODE = "mode"; - private SerializedProperty serializedProperty; private Type genericType; private IReferenceDrawer activeDrawer; - private object previousReferenceValue; - private string previousPropertyPath; - /// public override bool CanCacheInspectorGUI(SerializedProperty property) => false; @@ -48,48 +42,8 @@ public override float GetPropertyHeight(SerializedProperty property, GUIContent public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { Initialize(property); - AvoidDuplicateReferencesInArray(property); - activeDrawer = GetReferenceDrawer(activeDrawer, property, label); activeDrawer.OnGUI(position); - previousPropertyPath = property.propertyPath; - } - - /// - /// When a new instance of is added in an array using the inspector's gizmo, - /// avoids having their field reference the same instance. - /// - /// The SerializedProperty to make the custom GUI for. - private void AvoidDuplicateReferencesInArray(SerializedProperty property) - { - if (!IsPropertyInArray(property)) return; - if (previousPropertyPath == null) return; - if (previousPropertyPath == property.propertyPath) return; - - var rawReferenceProperty = property.FindPropertyRelative(RAW_REFERENCE); - var currentReferenceValue = rawReferenceProperty.managedReferenceValue; - - if (currentReferenceValue == null) return; - - if (previousReferenceValue == currentReferenceValue) - { - rawReferenceProperty.managedReferenceValue = CreateInstance(currentReferenceValue); - rawReferenceProperty.serializedObject.ApplyModifiedProperties(); - } - - previousReferenceValue = currentReferenceValue; - } - - private static bool IsPropertyInArray(SerializedProperty prop) - { - return prop.propertyPath.Contains(".Array.data["); - } - - private static object CreateInstance(object source) - { - var instance = Activator.CreateInstance(source.GetType()); - EditorUtility.CopySerializedManagedFieldsOnly(source, instance); - return instance; } private Type GetGenericArgument() @@ -134,7 +88,7 @@ private IReferenceDrawer GetReferenceDrawer( GUIContent label ) { - SerializedProperty modeProperty = serializedProperty.FindPropertyRelative(MODE); + SerializedProperty modeProperty = serializedProperty.FindPropertyRelative("mode"); ReferenceMode referenceMode = (ReferenceMode)modeProperty.enumValueIndex; switch (referenceMode) From 0a43f4e4ed76fa42421a7cdfbe22e069f091cc26 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Girard Date: Sat, 9 Jul 2022 19:19:54 -0400 Subject: [PATCH 6/7] Forgot a managedReferenceValue Would only work in UNITY_2021_1_OR_NEWER otherwise --- Editor/Drawers/RawReferenceDrawer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Editor/Drawers/RawReferenceDrawer.cs b/Editor/Drawers/RawReferenceDrawer.cs index a84752b..3f2ce5b 100644 --- a/Editor/Drawers/RawReferenceDrawer.cs +++ b/Editor/Drawers/RawReferenceDrawer.cs @@ -121,7 +121,7 @@ private void AvoidDuplicateReferencesInArray() if (previousReferenceValue == currentReferenceValue) { - rawReferenceProperty.managedReferenceValue = CreateInstance(currentReferenceValue); + RawReferenceValue = CreateInstance(currentReferenceValue); rawReferenceProperty.serializedObject.ApplyModifiedProperties(); } From 9fd9c1898f1522342aa3454882ee9936ef292cf4 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Girard Date: Sun, 10 Jul 2022 17:56:40 -0400 Subject: [PATCH 7/7] Added changes requested --- Editor/Drawers/RawReferenceDrawer.cs | 20 +++++++++++-------- Editor/SerializableInterfacePropertyDrawer.cs | 3 +-- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/Editor/Drawers/RawReferenceDrawer.cs b/Editor/Drawers/RawReferenceDrawer.cs index 3f2ce5b..c11b9bf 100644 --- a/Editor/Drawers/RawReferenceDrawer.cs +++ b/Editor/Drawers/RawReferenceDrawer.cs @@ -110,14 +110,18 @@ protected override void OnPropertiesClicked() private void AvoidDuplicateReferencesInArray() { - if (!IsPropertyInArray(Property)) return; - if (previousPropertyPath == null) return; - if (previousPropertyPath == Property.propertyPath) return; + if (!IsPropertyInArray(Property)) + return; + if (previousPropertyPath == null) + return; + if (previousPropertyPath == Property.propertyPath) + return; - var rawReferenceProperty = Property.FindPropertyRelative("rawReference"); - var currentReferenceValue = RawReferenceValue; + SerializedProperty rawReferenceProperty = Property.FindPropertyRelative("rawReference"); + object currentReferenceValue = RawReferenceValue; - if (currentReferenceValue == null) return; + if (currentReferenceValue == null) + return; if (previousReferenceValue == currentReferenceValue) { @@ -135,9 +139,9 @@ private static bool IsPropertyInArray(SerializedProperty prop) private static object CreateInstance(object source) { - var instance = Activator.CreateInstance(source.GetType()); + object instance = Activator.CreateInstance(source.GetType()); EditorUtility.CopySerializedManagedFieldsOnly(source, instance); return instance; } } -} \ No newline at end of file +} diff --git a/Editor/SerializableInterfacePropertyDrawer.cs b/Editor/SerializableInterfacePropertyDrawer.cs index 1d23d42..e71019a 100644 --- a/Editor/SerializableInterfacePropertyDrawer.cs +++ b/Editor/SerializableInterfacePropertyDrawer.cs @@ -26,7 +26,6 @@ private void Initialize(SerializedProperty property) activeDrawer = null; serializedProperty = property; genericType = GetGenericArgument(); - Assert.IsNotNull(genericType, "Unable to find generic argument, are you doing some shady inheritance?"); } @@ -106,4 +105,4 @@ GUIContent label } } } -} \ No newline at end of file +}