Skip to content

Commit

Permalink
Merge pull request #102 from arimger/develop
Browse files Browse the repository at this point in the history
Develop - 0.12.9
  • Loading branch information
arimger authored Jan 27, 2024
2 parents 71fb80d + 8c73437 commit 4a72fd0
Show file tree
Hide file tree
Showing 11 changed files with 129 additions and 35 deletions.
7 changes: 7 additions & 0 deletions Assets/Editor Toolbox/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## 0.12.9 [27.01.2024]

### Changed:
- Fix rare invalid SerializedProperty iterator when editing child properties
- Extend objects creation behaviour while using the ReferencePickerAttribute (possibility to create uninitialized objects)
- Minor UX improvements in the ScriptableObjectCreationWizard

## 0.12.8 [29.12.2023]

### Changed:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,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 @@ -41,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 @@ -50,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 @@ -60,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 @@ -72,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 ? Activator.CreateInstance(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 @@ -105,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 @@ -121,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 @@ -133,7 +132,6 @@ protected override void OnGuiSafe(SerializedProperty property, GUIContent label,
}
}


public override bool IsPropertyValid(SerializedProperty property)
{
return property.propertyType == SerializedPropertyType.ManagedReference;
Expand Down
5 changes: 5 additions & 0 deletions Assets/Editor Toolbox/Editor/ToolboxEditorGui.cs
Original file line number Diff line number Diff line change
Expand Up @@ -531,7 +531,12 @@ public static void DrawPropertyChildren(SerializedProperty property, Action<Seri
enterChildren = false;
var childProperty = targetProperty.Copy();
//handle current property using Toolbox features
EditorGUI.BeginChangeCheck();
drawElementAction(childProperty);
if (EditorGUI.EndChangeCheck())
{
break;
}
}
}

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 @@ -9,6 +9,7 @@
namespace Toolbox.Editor.Wizards
{
using Toolbox.Editor.Internal;
using Editor = UnityEditor.Editor;

/// <summary>
/// Utility window responsible for creation of <see cref="ScriptableObject"/>s.
Expand All @@ -34,7 +35,7 @@ private class CreationData
{
private bool IsDefaultObjectValid()
{
return DefaultObject != null && DefaultObject.GetType() == InstanceType;
return BlueprintObject != null && BlueprintObject.GetType() == InstanceType;
}

public void Validate()
Expand All @@ -47,7 +48,7 @@ public void Validate()
InstancesCount = Mathf.Max(InstancesCount, 1);
if (!IsDefaultObjectValid())
{
DefaultObject = null;
BlueprintObject = null;
}
}

Expand All @@ -59,7 +60,7 @@ public void Validate()
public int InstancesCount { get; set; } = 1;
[field: SerializeField, InLineEditor]
[field: Tooltip("Will be used as a blueprint for all created ScriptableObjects.")]
public Object DefaultObject { get; set; }
public Object BlueprintObject { get; set; }
}

private static readonly TypeConstraintContext sharedConstraint = new TypeConstraintScriptableObject();
Expand All @@ -69,7 +70,13 @@ public void Validate()
private readonly CreationData data = new CreationData();

private bool inspectDefaultObject;
private bool useSearchField = true;
private Editor blueprintObjectEditor;

protected override void OnDestroy()
{
base.OnDestroy();
DestroyImmediate(blueprintObjectEditor);
}

[MenuItem("Assets/Create/Editor Toolbox/Wizards/ScriptableObject Creation Wizard", priority = 5)]
internal static void Initialize()
Expand All @@ -83,10 +90,8 @@ private void DrawSettingsPanel()
{
EditorGUILayout.LabelField("Settings", EditorStyles.boldLabel);

useSearchField = EditorGUILayout.ToggleLeft("Use Search Field", useSearchField);

var rect = EditorGUILayout.GetControlRect(true);
typeField.OnGui(rect, useSearchField, OnTypeSelected, data.InstanceType);
typeField.OnGui(rect, true, OnTypeSelected, data.InstanceType);
if (data.InstanceType == null)
{
return;
Expand All @@ -97,7 +102,15 @@ private void DrawSettingsPanel()
EditorGUI.BeginChangeCheck();
data.InstanceName = EditorGUILayout.TextField(Style.nameContent, data.InstanceName);
data.InstancesCount = EditorGUILayout.IntField(Style.countContent, data.InstancesCount);
var assignedInstance = EditorGUILayout.ObjectField(Style.objectContent, data.DefaultObject, data.InstanceType, false);

EditorGUI.BeginChangeCheck();
var assignedInstance = EditorGUILayout.ObjectField(Style.objectContent, data.BlueprintObject, data.InstanceType, false);
data.BlueprintObject = assignedInstance;
if (EditorGUI.EndChangeCheck())
{
UpdateBlueprintObjectEditor();
}

if (assignedInstance != null)
{
inspectDefaultObject = GUILayout.Toggle(inspectDefaultObject,
Expand All @@ -112,11 +125,11 @@ private void DrawSettingsPanel()
{
using (new EditorGUILayout.VerticalScope(Style.backgroundStyle))
{
ToolboxEditorGui.DrawObjectProperties(assignedInstance);
blueprintObjectEditor.OnInspectorGUI();
}
}

data.DefaultObject = assignedInstance;

if (EditorGUI.EndChangeCheck())
{
OnWizardUpdate();
Expand Down Expand Up @@ -147,7 +160,7 @@ private void CreateObjects(CreationData data)
var instancesCount = data.InstancesCount;
for (var i = 0; i < instancesCount; i++)
{
var instance = CreateObject(data.InstanceType, data.DefaultObject);
var instance = CreateObject(data.InstanceType, data.BlueprintObject);
CreateAsset(instance, data.InstanceName, assetPath, i);
}

Expand Down Expand Up @@ -181,6 +194,25 @@ private void OnTypeSelected(Type type)
}
}

private void UpdateBlueprintObjectEditor()
{
DestroyImmediate(blueprintObjectEditor);
blueprintObjectEditor = null;

var targetObject = data.BlueprintObject;
if (targetObject == null)
{
return;
}

blueprintObjectEditor = Editor.CreateEditor(targetObject);
blueprintObjectEditor.hideFlags = HideFlags.HideAndDontSave;
if (blueprintObjectEditor is ToolboxEditor toolboxEditor)
{
toolboxEditor.IgnoreProperty(PropertyUtility.Defaults.scriptPropertyName);
}
}

private static string GetActiveFolderPath()
{
var projectWindowUtilType = typeof(ProjectWindowUtil);
Expand Down Expand Up @@ -219,8 +251,8 @@ private static class Style
internal static readonly GUIStyle foldoutStyle;

internal static readonly GUIContent nameContent = new GUIContent("Instance Name");
internal static readonly GUIContent countContent = new GUIContent("Instances To Create", "Indicates how many instances will be created.");
internal static readonly GUIContent objectContent = new GUIContent("Default Object", "Will be used as a blueprint for all created ScriptableObjects.");
internal static readonly GUIContent countContent = new GUIContent("Instances Count", "Indicates how many instances will be created.");
internal static readonly GUIContent objectContent = new GUIContent("Blueprint Object", "Will be used as a blueprint for all created ScriptableObjects.");
internal static readonly GUIContent foldoutContent = new GUIContent("Inspect", "Show/Hide Properties");

internal static readonly GUILayoutOption[] foldoutOptions = new GUILayoutOption[]
Expand Down
4 changes: 2 additions & 2 deletions Assets/Editor Toolbox/Editor/Wizards/ToolboxWizard.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ public class ToolboxWizard : EditorWindow

private Vector2 scrollPosition;

private void OnEnable()
protected virtual void OnEnable()
{
if (targetEditor != null)
{
ReinitEditor(targetEditor);
}
}

private void OnDestroy()
protected virtual void OnDestroy()
{
DestroyImmediate(targetEditor);
}
Expand Down
11 changes: 9 additions & 2 deletions Assets/Editor Toolbox/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -631,13 +631,20 @@ public int var1;
#### SerializeReference (ReferencePicker) <a name="toolboxreference"></a>

You can draw properties marked with the **[SerializeReference]** attribute with an additional type picker that allows you to manipulate what managed type will be serialized.

Depending on the picked type we have different object creation strategies:
- `Activator.CreateInstance(targetType)` (default constructor will be called and all readonly members will be initialized)
- Target type has default constructor
- Target type is a value type
- `FormatterServices.GetUninitializedObject(targetType)` (object will be uninitialized)
- Target type has one or more constructors with arguments
- `ForceUninitializedInstance` property is set to true

To prevent issues after renaming types use `UnityEngine.Scripting.APIUpdating.MovedFromAttribute`.

```csharp
[SerializeReference, ReferencePicker(TypeGrouping = TypeGrouping.ByFlatName)]
public Interface1 var1;
[SerializeReference, ReferencePicker]
[SerializeReference, ReferencePicker(ForceUninitializedInstance = true)]
public Interface1 var1;
[SerializeReference, ReferencePicker(ParentType = typeof(ClassWithInterface2)]
public ClassWithInterfaceBase var2;
Expand Down
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
2 changes: 1 addition & 1 deletion Assets/Editor Toolbox/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "com.browar.editor-toolbox",
"displayName": "Editor Toolbox",
"version": "0.12.8",
"version": "0.12.9",
"unity": "2018.1",
"description": "Tools, custom attributes, drawers, hierarchy overlay, and other extensions for the Unity Editor.",
"keywords": [
Expand Down
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
Loading

0 comments on commit 4a72fd0

Please sign in to comment.