Skip to content

Commit 4a72fd0

Browse files
authored
Merge pull request #102 from arimger/develop
Develop - 0.12.9
2 parents 71fb80d + 8c73437 commit 4a72fd0

File tree

11 files changed

+129
-35
lines changed

11 files changed

+129
-35
lines changed

Assets/Editor Toolbox/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
## 0.12.9 [27.01.2024]
2+
3+
### Changed:
4+
- Fix rare invalid SerializedProperty iterator when editing child properties
5+
- Extend objects creation behaviour while using the ReferencePickerAttribute (possibility to create uninitialized objects)
6+
- Minor UX improvements in the ScriptableObjectCreationWizard
7+
18
## 0.12.8 [29.12.2023]
29

310
### Changed:

Assets/Editor Toolbox/Editor/Drawers/Toolbox/PropertySelf/ReferencePickerAttributeDrawer.cs

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,12 @@ public class ReferencePickerAttributeDrawer : ToolboxSelfPropertyDrawer<Referenc
1616
private static readonly TypeAppearanceContext sharedAppearance = new TypeAppearanceContext(sharedConstraint, TypeGrouping.None, true);
1717
private static readonly TypeField typeField = new TypeField(sharedConstraint, sharedAppearance);
1818

19-
2019
private void UpdateContexts(ReferencePickerAttribute attribute)
2120
{
2221
sharedAppearance.TypeGrouping = attribute.TypeGrouping;
2322
}
2423

25-
private Type GetParentType(SerializedProperty property, ReferencePickerAttribute attribute)
24+
private Type GetParentType(ReferencePickerAttribute attribute, SerializedProperty property)
2625
{
2726
var fieldInfo = property.GetFieldInfo(out _);
2827
var fieldType = property.GetProperType(fieldInfo);
@@ -41,7 +40,7 @@ private Type GetParentType(SerializedProperty property, ReferencePickerAttribute
4140
return fieldType;
4241
}
4342

44-
private void CreateTypeProperty(Rect position, SerializedProperty property, Type parentType)
43+
private void CreateTypeProperty(SerializedProperty property, Type parentType, ReferencePickerAttribute attribute, Rect position)
4544
{
4645
TypeUtilities.TryGetTypeFromManagedReferenceFullTypeName(property.managedReferenceFullTypename, out var currentType);
4746
typeField.OnGui(position, true, (type) =>
@@ -50,7 +49,7 @@ private void CreateTypeProperty(Rect position, SerializedProperty property, Type
5049
{
5150
if (!property.serializedObject.isEditingMultipleObjects)
5251
{
53-
UpdateTypeProperty(property, type);
52+
UpdateTypeProperty(property, type, attribute);
5453
}
5554
else
5655
{
@@ -60,7 +59,7 @@ private void CreateTypeProperty(Rect position, SerializedProperty property, Type
6059
using (var so = new SerializedObject(target))
6160
{
6261
SerializedProperty sp = so.FindProperty(property.propertyPath);
63-
UpdateTypeProperty(sp, type);
62+
UpdateTypeProperty(sp, type, attribute);
6463
}
6564
}
6665
}
@@ -72,9 +71,10 @@ private void CreateTypeProperty(Rect position, SerializedProperty property, Type
7271
}, currentType, parentType);
7372
}
7473

75-
private void UpdateTypeProperty(SerializedProperty property, Type referenceType)
74+
private void UpdateTypeProperty(SerializedProperty property, Type targetType, ReferencePickerAttribute attribute)
7675
{
77-
var obj = referenceType != null ? Activator.CreateInstance(referenceType) : null;
76+
var forceUninitializedInstance = attribute.ForceUninitializedInstance;
77+
var obj = ReflectionUtility.CreateInstance(targetType, forceUninitializedInstance);
7878
property.serializedObject.Update();
7979
property.managedReferenceValue = obj;
8080
property.serializedObject.ApplyModifiedProperties();
@@ -105,7 +105,6 @@ private Rect PrepareTypePropertyPosition(bool hasLabel, in Rect labelPosition, i
105105
return position;
106106
}
107107

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

124-
var parentType = GetParentType(property, attribute);
125-
CreateTypeProperty(position, property, parentType);
123+
var parentType = GetParentType(attribute, property);
124+
CreateTypeProperty(property, parentType, attribute, position);
126125
if (isPropertyExpanded)
127126
{
128127
ToolboxEditorGui.DrawPropertyChildren(property);
@@ -133,7 +132,6 @@ protected override void OnGuiSafe(SerializedProperty property, GUIContent label,
133132
}
134133
}
135134

136-
137135
public override bool IsPropertyValid(SerializedProperty property)
138136
{
139137
return property.propertyType == SerializedPropertyType.ManagedReference;

Assets/Editor Toolbox/Editor/ToolboxEditorGui.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,7 +531,12 @@ public static void DrawPropertyChildren(SerializedProperty property, Action<Seri
531531
enterChildren = false;
532532
var childProperty = targetProperty.Copy();
533533
//handle current property using Toolbox features
534+
EditorGUI.BeginChangeCheck();
534535
drawElementAction(childProperty);
536+
if (EditorGUI.EndChangeCheck())
537+
{
538+
break;
539+
}
535540
}
536541
}
537542

Assets/Editor Toolbox/Editor/Utilities/ReflectionUtility.cs

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
11
using System;
22
using System.Reflection;
3-
3+
using System.Runtime.Serialization;
44
using UnityEditor;
55
using Object = UnityEngine.Object;
66

77
namespace Toolbox.Editor
88
{
99
internal static class ReflectionUtility
1010
{
11-
private readonly static Assembly editorAssembly = typeof(UnityEditor.Editor).Assembly;
11+
private static readonly Assembly editorAssembly = typeof(UnityEditor.Editor).Assembly;
1212

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

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

9796
return true;
9897
}
98+
99+
internal static object CreateInstance(Type targetType, bool forceUninitializedInstance)
100+
{
101+
if (targetType == null)
102+
{
103+
return null;
104+
}
105+
106+
if (forceUninitializedInstance)
107+
{
108+
return FormatterServices.GetUninitializedObject(targetType);
109+
}
110+
111+
if (targetType.IsValueType)
112+
{
113+
return Activator.CreateInstance(targetType);
114+
}
115+
116+
var defaultConstructor = targetType.GetConstructor(Type.EmptyTypes);
117+
if (defaultConstructor != null)
118+
{
119+
return Activator.CreateInstance(targetType);
120+
}
121+
122+
return FormatterServices.GetUninitializedObject(targetType);
123+
}
99124
}
100125
}

Assets/Editor Toolbox/Editor/Wizards/ScriptableObjectCreationWizard.cs

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
namespace Toolbox.Editor.Wizards
1010
{
1111
using Toolbox.Editor.Internal;
12+
using Editor = UnityEditor.Editor;
1213

1314
/// <summary>
1415
/// Utility window responsible for creation of <see cref="ScriptableObject"/>s.
@@ -34,7 +35,7 @@ private class CreationData
3435
{
3536
private bool IsDefaultObjectValid()
3637
{
37-
return DefaultObject != null && DefaultObject.GetType() == InstanceType;
38+
return BlueprintObject != null && BlueprintObject.GetType() == InstanceType;
3839
}
3940

4041
public void Validate()
@@ -47,7 +48,7 @@ public void Validate()
4748
InstancesCount = Mathf.Max(InstancesCount, 1);
4849
if (!IsDefaultObjectValid())
4950
{
50-
DefaultObject = null;
51+
BlueprintObject = null;
5152
}
5253
}
5354

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

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

7172
private bool inspectDefaultObject;
72-
private bool useSearchField = true;
73+
private Editor blueprintObjectEditor;
74+
75+
protected override void OnDestroy()
76+
{
77+
base.OnDestroy();
78+
DestroyImmediate(blueprintObjectEditor);
79+
}
7380

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

86-
useSearchField = EditorGUILayout.ToggleLeft("Use Search Field", useSearchField);
87-
8893
var rect = EditorGUILayout.GetControlRect(true);
89-
typeField.OnGui(rect, useSearchField, OnTypeSelected, data.InstanceType);
94+
typeField.OnGui(rect, true, OnTypeSelected, data.InstanceType);
9095
if (data.InstanceType == null)
9196
{
9297
return;
@@ -97,7 +102,15 @@ private void DrawSettingsPanel()
97102
EditorGUI.BeginChangeCheck();
98103
data.InstanceName = EditorGUILayout.TextField(Style.nameContent, data.InstanceName);
99104
data.InstancesCount = EditorGUILayout.IntField(Style.countContent, data.InstancesCount);
100-
var assignedInstance = EditorGUILayout.ObjectField(Style.objectContent, data.DefaultObject, data.InstanceType, false);
105+
106+
EditorGUI.BeginChangeCheck();
107+
var assignedInstance = EditorGUILayout.ObjectField(Style.objectContent, data.BlueprintObject, data.InstanceType, false);
108+
data.BlueprintObject = assignedInstance;
109+
if (EditorGUI.EndChangeCheck())
110+
{
111+
UpdateBlueprintObjectEditor();
112+
}
113+
101114
if (assignedInstance != null)
102115
{
103116
inspectDefaultObject = GUILayout.Toggle(inspectDefaultObject,
@@ -112,11 +125,11 @@ private void DrawSettingsPanel()
112125
{
113126
using (new EditorGUILayout.VerticalScope(Style.backgroundStyle))
114127
{
115-
ToolboxEditorGui.DrawObjectProperties(assignedInstance);
128+
blueprintObjectEditor.OnInspectorGUI();
116129
}
117130
}
118131

119-
data.DefaultObject = assignedInstance;
132+
120133
if (EditorGUI.EndChangeCheck())
121134
{
122135
OnWizardUpdate();
@@ -147,7 +160,7 @@ private void CreateObjects(CreationData data)
147160
var instancesCount = data.InstancesCount;
148161
for (var i = 0; i < instancesCount; i++)
149162
{
150-
var instance = CreateObject(data.InstanceType, data.DefaultObject);
163+
var instance = CreateObject(data.InstanceType, data.BlueprintObject);
151164
CreateAsset(instance, data.InstanceName, assetPath, i);
152165
}
153166

@@ -181,6 +194,25 @@ private void OnTypeSelected(Type type)
181194
}
182195
}
183196

197+
private void UpdateBlueprintObjectEditor()
198+
{
199+
DestroyImmediate(blueprintObjectEditor);
200+
blueprintObjectEditor = null;
201+
202+
var targetObject = data.BlueprintObject;
203+
if (targetObject == null)
204+
{
205+
return;
206+
}
207+
208+
blueprintObjectEditor = Editor.CreateEditor(targetObject);
209+
blueprintObjectEditor.hideFlags = HideFlags.HideAndDontSave;
210+
if (blueprintObjectEditor is ToolboxEditor toolboxEditor)
211+
{
212+
toolboxEditor.IgnoreProperty(PropertyUtility.Defaults.scriptPropertyName);
213+
}
214+
}
215+
184216
private static string GetActiveFolderPath()
185217
{
186218
var projectWindowUtilType = typeof(ProjectWindowUtil);
@@ -219,8 +251,8 @@ private static class Style
219251
internal static readonly GUIStyle foldoutStyle;
220252

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

226258
internal static readonly GUILayoutOption[] foldoutOptions = new GUILayoutOption[]

Assets/Editor Toolbox/Editor/Wizards/ToolboxWizard.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@ public class ToolboxWizard : EditorWindow
1111

1212
private Vector2 scrollPosition;
1313

14-
private void OnEnable()
14+
protected virtual void OnEnable()
1515
{
1616
if (targetEditor != null)
1717
{
1818
ReinitEditor(targetEditor);
1919
}
2020
}
2121

22-
private void OnDestroy()
22+
protected virtual void OnDestroy()
2323
{
2424
DestroyImmediate(targetEditor);
2525
}

Assets/Editor Toolbox/README.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -631,13 +631,20 @@ public int var1;
631631
#### SerializeReference (ReferencePicker) <a name="toolboxreference"></a>
632632

633633
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.
634-
634+
Depending on the picked type we have different object creation strategies:
635+
- `Activator.CreateInstance(targetType)` (default constructor will be called and all readonly members will be initialized)
636+
- Target type has default constructor
637+
- Target type is a value type
638+
- `FormatterServices.GetUninitializedObject(targetType)` (object will be uninitialized)
639+
- Target type has one or more constructors with arguments
640+
- `ForceUninitializedInstance` property is set to true
641+
635642
To prevent issues after renaming types use `UnityEngine.Scripting.APIUpdating.MovedFromAttribute`.
636643

637644
```csharp
638645
[SerializeReference, ReferencePicker(TypeGrouping = TypeGrouping.ByFlatName)]
639646
public Interface1 var1;
640-
[SerializeReference, ReferencePicker]
647+
[SerializeReference, ReferencePicker(ForceUninitializedInstance = true)]
641648
public Interface1 var1;
642649
[SerializeReference, ReferencePicker(ParentType = typeof(ClassWithInterface2)]
643650
public ClassWithInterfaceBase var2;

Assets/Editor Toolbox/Runtime/Attributes/Toolbox/PropertySelfAttributes/ReferencePickerAttribute.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ public ReferencePickerAttribute(Type parentType, TypeGrouping typeGrouping)
3131
/// Defaults to <see cref="TypeGrouping.None"/> unless explicitly specified.
3232
/// </summary>
3333
public TypeGrouping TypeGrouping { get; set; } = TypeGrouping.None;
34+
35+
public bool ForceUninitializedInstance { get; set; }
3436
}
3537
}
3638
#endif

Assets/Editor Toolbox/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "com.browar.editor-toolbox",
33
"displayName": "Editor Toolbox",
4-
"version": "0.12.8",
4+
"version": "0.12.9",
55
"unity": "2018.1",
66
"description": "Tools, custom attributes, drawers, hierarchy overlay, and other extensions for the Unity Editor.",
77
"keywords": [

Assets/Examples/Scripts/SampleBehaviour6.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ public class SampleBehaviour6 : MonoBehaviour
99
#if UNITY_2019_3_OR_NEWER
1010
[SerializeReference, ReferencePicker(TypeGrouping = TypeGrouping.ByFlatName)]
1111
public Interface1 var1;
12-
[SerializeReference, ReferencePicker]
12+
[SerializeReference, ReferencePicker(ForceUninitializedInstance = true)]
1313
public ClassWithInterfaceBase var2;
1414
[SerializeReference, ReferencePicker(ParentType = typeof(ClassWithInterface2))]
1515
public ClassWithInterfaceBase var3;
@@ -25,6 +25,12 @@ public struct Struct : Interface1
2525
{
2626
public bool var1;
2727
public bool var2;
28+
29+
public Struct(bool var1, bool var2)
30+
{
31+
this.var1 = var1;
32+
this.var2 = var2;
33+
}
2834
}
2935

3036
public abstract class ClassWithInterfaceBase : Interface1
@@ -54,6 +60,11 @@ public class ClassWithInterface2 : ClassWithInterfaceBase
5460
public class ClassWithInterface3 : ClassWithInterfaceBase
5561
{
5662
public int var1;
63+
64+
public ClassWithInterface3(int var1)
65+
{
66+
this.var1 = var1;
67+
}
5768
}
5869

5970
[Serializable]

0 commit comments

Comments
 (0)