Skip to content

Commit

Permalink
Better way to handle objects instantiation when using the ReferencePi…
Browse files Browse the repository at this point in the history
…cker attribute; possibility to force uninitialized object creation
  • Loading branch information
arimger committed Jan 25, 2024
1 parent 71c855d commit 72eaa6f
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 16 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#if UNITY_2019_3_OR_NEWER
using System;
using System.Runtime.Serialization;

using UnityEditor;
using UnityEngine;
Expand All @@ -17,13 +16,12 @@ public class ReferencePickerAttributeDrawer : ToolboxSelfPropertyDrawer<Referenc
private static readonly TypeAppearanceContext sharedAppearance = new TypeAppearanceContext(sharedConstraint, TypeGrouping.None, true);
private static readonly TypeField typeField = new TypeField(sharedConstraint, sharedAppearance);


private void UpdateContexts(ReferencePickerAttribute attribute)
{
sharedAppearance.TypeGrouping = attribute.TypeGrouping;
}

private Type GetParentType(SerializedProperty property, ReferencePickerAttribute attribute)
private Type GetParentType(ReferencePickerAttribute attribute, SerializedProperty property)
{
var fieldInfo = property.GetFieldInfo(out _);
var fieldType = property.GetProperType(fieldInfo);
Expand All @@ -42,7 +40,7 @@ private Type GetParentType(SerializedProperty property, ReferencePickerAttribute
return fieldType;
}

private void CreateTypeProperty(Rect position, SerializedProperty property, Type parentType)
private void CreateTypeProperty(SerializedProperty property, Type parentType, ReferencePickerAttribute attribute, Rect position)
{
TypeUtilities.TryGetTypeFromManagedReferenceFullTypeName(property.managedReferenceFullTypename, out var currentType);
typeField.OnGui(position, true, (type) =>
Expand All @@ -51,7 +49,7 @@ private void CreateTypeProperty(Rect position, SerializedProperty property, Type
{
if (!property.serializedObject.isEditingMultipleObjects)
{
UpdateTypeProperty(property, type);
UpdateTypeProperty(property, type, attribute);
}
else
{
Expand All @@ -61,7 +59,7 @@ private void CreateTypeProperty(Rect position, SerializedProperty property, Type
using (var so = new SerializedObject(target))
{
SerializedProperty sp = so.FindProperty(property.propertyPath);
UpdateTypeProperty(sp, type);
UpdateTypeProperty(sp, type, attribute);
}
}
}
Expand All @@ -73,9 +71,10 @@ private void CreateTypeProperty(Rect position, SerializedProperty property, Type
}, currentType, parentType);
}

private void UpdateTypeProperty(SerializedProperty property, Type referenceType)
private void UpdateTypeProperty(SerializedProperty property, Type targetType, ReferencePickerAttribute attribute)
{
var obj = referenceType != null ? FormatterServices.GetUninitializedObject(referenceType) : null;
var forceUninitializedInstance = attribute.ForceUninitializedInstance;
var obj = ReflectionUtility.CreateInstance(targetType, forceUninitializedInstance);
property.serializedObject.Update();
property.managedReferenceValue = obj;
property.serializedObject.ApplyModifiedProperties();
Expand Down Expand Up @@ -106,7 +105,6 @@ private Rect PrepareTypePropertyPosition(bool hasLabel, in Rect labelPosition, i
return position;
}


protected override void OnGuiSafe(SerializedProperty property, GUIContent label, ReferencePickerAttribute attribute)
{
//NOTE: we want to close scope manually because ExitGUIException can interrupt drawing and SerializedProperties stack
Expand All @@ -122,8 +120,8 @@ protected override void OnGuiSafe(SerializedProperty property, GUIContent label,
var hasLabel = !string.IsNullOrEmpty(label.text);
var position = PrepareTypePropertyPosition(hasLabel, in labelRect, in inputRect, isPropertyExpanded);

var parentType = GetParentType(property, attribute);
CreateTypeProperty(position, property, parentType);
var parentType = GetParentType(attribute, property);
CreateTypeProperty(property, parentType, attribute, position);
if (isPropertyExpanded)
{
ToolboxEditorGui.DrawPropertyChildren(property);
Expand All @@ -134,7 +132,6 @@ protected override void OnGuiSafe(SerializedProperty property, GUIContent label,
}
}


public override bool IsPropertyValid(SerializedProperty property)
{
return property.propertyType == SerializedPropertyType.ManagedReference;
Expand Down
31 changes: 28 additions & 3 deletions Assets/Editor Toolbox/Editor/Utilities/ReflectionUtility.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
using System;
using System.Reflection;

using System.Runtime.Serialization;
using UnityEditor;
using Object = UnityEngine.Object;

namespace Toolbox.Editor
{
internal static class ReflectionUtility
{
private readonly static Assembly editorAssembly = typeof(UnityEditor.Editor).Assembly;
private static readonly Assembly editorAssembly = typeof(UnityEditor.Editor).Assembly;

public const BindingFlags allBindings = BindingFlags.Instance |
BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public;


/// <summary>
/// Returns <see cref="MethodInfo"/> of the searched method within the Editor <see cref="Assembly"/>.
/// </summary>
Expand Down Expand Up @@ -96,5 +95,31 @@ internal static bool TryInvokeMethod(string methodName, SerializedObject seriali

return true;
}

internal static object CreateInstance(Type targetType, bool forceUninitializedInstance)
{
if (targetType == null)
{
return null;
}

if (forceUninitializedInstance)
{
return FormatterServices.GetUninitializedObject(targetType);
}

if (targetType.IsValueType)
{
return Activator.CreateInstance(targetType);
}

var defaultConstructor = targetType.GetConstructor(Type.EmptyTypes);
if (defaultConstructor != null)
{
return Activator.CreateInstance(targetType);
}

return FormatterServices.GetUninitializedObject(targetType);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ public ReferencePickerAttribute(Type parentType, TypeGrouping typeGrouping)
/// Defaults to <see cref="TypeGrouping.None"/> unless explicitly specified.
/// </summary>
public TypeGrouping TypeGrouping { get; set; } = TypeGrouping.None;

public bool ForceUninitializedInstance { get; set; }
}
}
#endif
13 changes: 12 additions & 1 deletion Assets/Examples/Scripts/SampleBehaviour6.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public class SampleBehaviour6 : MonoBehaviour
#if UNITY_2019_3_OR_NEWER
[SerializeReference, ReferencePicker(TypeGrouping = TypeGrouping.ByFlatName)]
public Interface1 var1;
[SerializeReference, ReferencePicker]
[SerializeReference, ReferencePicker(ForceUninitializedInstance = true)]
public ClassWithInterfaceBase var2;
[SerializeReference, ReferencePicker(ParentType = typeof(ClassWithInterface2))]
public ClassWithInterfaceBase var3;
Expand All @@ -25,6 +25,12 @@ public struct Struct : Interface1
{
public bool var1;
public bool var2;

public Struct(bool var1, bool var2)
{
this.var1 = var1;
this.var2 = var2;
}
}

public abstract class ClassWithInterfaceBase : Interface1
Expand Down Expand Up @@ -54,6 +60,11 @@ public class ClassWithInterface2 : ClassWithInterfaceBase
public class ClassWithInterface3 : ClassWithInterfaceBase
{
public int var1;

public ClassWithInterface3(int var1)
{
this.var1 = var1;
}
}

[Serializable]
Expand Down

0 comments on commit 72eaa6f

Please sign in to comment.