From b9467a4de6ab349b250cd732f528292fc069f0e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=82osz=20Matkowski?= Date: Mon, 16 Dec 2024 20:25:34 +0100 Subject: [PATCH 01/46] Move Toolbox assembly to the Runtime directory; fix InspectorUtility.SetIsEditorExpanded method --- .../Editor/Utilities/InspectorUtility.cs | 4 ++-- Assets/Editor Toolbox/README.md | 4 ++-- Assets/Editor Toolbox/{ => Runtime}/Toolbox.asmdef | 0 .../Editor Toolbox/{ => Runtime}/Toolbox.asmdef.meta | 0 Assets/Examples/Scenes/SampleScene.unity | 10 +++++----- 5 files changed, 9 insertions(+), 9 deletions(-) rename Assets/Editor Toolbox/{ => Runtime}/Toolbox.asmdef (100%) rename Assets/Editor Toolbox/{ => Runtime}/Toolbox.asmdef.meta (100%) diff --git a/Assets/Editor Toolbox/Editor/Utilities/InspectorUtility.cs b/Assets/Editor Toolbox/Editor/Utilities/InspectorUtility.cs index 4e36831a..e3a92a1f 100644 --- a/Assets/Editor Toolbox/Editor/Utilities/InspectorUtility.cs +++ b/Assets/Editor Toolbox/Editor/Utilities/InspectorUtility.cs @@ -52,7 +52,7 @@ internal static bool GetIsEditorExpanded(Editor editor) /// internal static void SetIsEditorExpanded(Editor editor, bool value) { - InternalEditorUtility.SetIsInspectorExpanded(editor.target, true); + InternalEditorUtility.SetIsInspectorExpanded(editor.target, value); //NOTE: in older versions Editor's foldouts are based on the m_IsVisible field and the Awake() method #if !UNITY_2019_1_OR_NEWER const string isVisibleFieldName = "m_IsVisible"; @@ -60,7 +60,7 @@ internal static void SetIsEditorExpanded(Editor editor, bool value) BindingFlags.Instance | BindingFlags.NonPublic); if (isVisible != null) { - isVisible.SetValue(editor, true); + isVisible.SetValue(editor, value); } #endif } diff --git a/Assets/Editor Toolbox/README.md b/Assets/Editor Toolbox/README.md index c5d629df..c9ce2ee5 100644 --- a/Assets/Editor Toolbox/README.md +++ b/Assets/Editor Toolbox/README.md @@ -971,11 +971,11 @@ public void Usage() Allows to serialize folders in form of assets and retrieve direct paths in Runtime. ```csharp -public SerializedDirectory serializeDirectory; +public SerializedDirectory serializedDirectory; public void Usage() { - string path = serializeDirectory.DirectoryPath; + string path = serializedDirectory.DirectoryPath; } ``` diff --git a/Assets/Editor Toolbox/Toolbox.asmdef b/Assets/Editor Toolbox/Runtime/Toolbox.asmdef similarity index 100% rename from Assets/Editor Toolbox/Toolbox.asmdef rename to Assets/Editor Toolbox/Runtime/Toolbox.asmdef diff --git a/Assets/Editor Toolbox/Toolbox.asmdef.meta b/Assets/Editor Toolbox/Runtime/Toolbox.asmdef.meta similarity index 100% rename from Assets/Editor Toolbox/Toolbox.asmdef.meta rename to Assets/Editor Toolbox/Runtime/Toolbox.asmdef.meta diff --git a/Assets/Examples/Scenes/SampleScene.unity b/Assets/Examples/Scenes/SampleScene.unity index afd0d538..78ad7593 100644 --- a/Assets/Examples/Scenes/SampleScene.unity +++ b/Assets/Examples/Scenes/SampleScene.unity @@ -1828,7 +1828,7 @@ Transform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} - m_RootOrder: 5 + m_RootOrder: 7 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!114 &1220714192 MonoBehaviour: @@ -2207,7 +2207,7 @@ GameObject: - component: {fileID: 1438743619} - component: {fileID: 1438743618} m_Layer: 0 - m_Name: DecoratorDrawers [Sample] + m_Name: ToolboxDecoratorDrawers [Sample] m_TagString: Untagged m_Icon: {fileID: 2800000, guid: b105bf1fb7fe62e4baba0ffd7b433e7b, type: 3} m_NavMeshLayer: 0 @@ -2285,7 +2285,7 @@ GameObject: - component: {fileID: 1583836799} - component: {fileID: 1583836798} m_Layer: 0 - m_Name: Special & Others [Sample] + m_Name: Toolbox Special & Others [Sample] m_TagString: Untagged m_Icon: {fileID: 2800000, guid: b105bf1fb7fe62e4baba0ffd7b433e7b, type: 3} m_NavMeshLayer: 0 @@ -2336,7 +2336,7 @@ Transform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} - m_RootOrder: 7 + m_RootOrder: 5 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1584091011 GameObject: @@ -2456,7 +2456,7 @@ GameObject: - component: {fileID: 1670253092} - component: {fileID: 1670253093} m_Layer: 0 - m_Name: ConditionDrawers [Sample] + m_Name: ToolboxConditionDrawers [Sample] m_TagString: Untagged m_Icon: {fileID: 2800000, guid: b105bf1fb7fe62e4baba0ffd7b433e7b, type: 3} m_NavMeshLayer: 0 From 253d08bb92d53bcd1d4728cf1d72095d77b61477 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=82osz=20Matkowski?= Date: Wed, 19 Feb 2025 17:25:04 +0100 Subject: [PATCH 02/46] Fix searching for private members in the base types --- .../Helpers/Extraction/FieldValueExtractor.cs | 2 +- .../Extraction/MethodValueExtractor.cs | 7 +- .../Extraction/PropertyValueExtractor.cs | 2 +- .../Decorator/EditorButtonAttributeDrawer.cs | 2 +- .../ReorderableListExposedAttributeDrawer.cs | 2 +- .../Editor/Utilities/ReflectionUtility.cs | 79 ++++++++++++++++--- 6 files changed, 75 insertions(+), 19 deletions(-) diff --git a/Assets/Editor Toolbox/Editor/Drawers/Helpers/Extraction/FieldValueExtractor.cs b/Assets/Editor Toolbox/Editor/Drawers/Helpers/Extraction/FieldValueExtractor.cs index 53d59da4..589c9764 100644 --- a/Assets/Editor Toolbox/Editor/Drawers/Helpers/Extraction/FieldValueExtractor.cs +++ b/Assets/Editor Toolbox/Editor/Drawers/Helpers/Extraction/FieldValueExtractor.cs @@ -11,7 +11,7 @@ public bool TryGetValue(string source, object declaringObject, out object value) } var type = declaringObject.GetType(); - var info = type.GetField(source, ReflectionUtility.allBindings); + var info = ReflectionUtility.GetField(type, source); if (info == null) { return false; diff --git a/Assets/Editor Toolbox/Editor/Drawers/Helpers/Extraction/MethodValueExtractor.cs b/Assets/Editor Toolbox/Editor/Drawers/Helpers/Extraction/MethodValueExtractor.cs index 652f67e2..166b001b 100644 --- a/Assets/Editor Toolbox/Editor/Drawers/Helpers/Extraction/MethodValueExtractor.cs +++ b/Assets/Editor Toolbox/Editor/Drawers/Helpers/Extraction/MethodValueExtractor.cs @@ -1,7 +1,4 @@ -using System; -using System.Reflection; - -namespace Toolbox.Editor.Drawers +namespace Toolbox.Editor.Drawers { public class MethodValueExtractor : IValueExtractor { @@ -14,7 +11,7 @@ public bool TryGetValue(string source, object declaringObject, out object value) } var type = declaringObject.GetType(); - var info = type.GetMethod(source, ReflectionUtility.allBindings, null, CallingConventions.Any, new Type[0], null); + var info = ReflectionUtility.GetMethod(type, source); if (info == null) { return false; diff --git a/Assets/Editor Toolbox/Editor/Drawers/Helpers/Extraction/PropertyValueExtractor.cs b/Assets/Editor Toolbox/Editor/Drawers/Helpers/Extraction/PropertyValueExtractor.cs index 933bda56..691a18e3 100644 --- a/Assets/Editor Toolbox/Editor/Drawers/Helpers/Extraction/PropertyValueExtractor.cs +++ b/Assets/Editor Toolbox/Editor/Drawers/Helpers/Extraction/PropertyValueExtractor.cs @@ -11,7 +11,7 @@ public bool TryGetValue(string source, object declaringObject, out object value) } var type = declaringObject.GetType(); - var info = type.GetProperty(source, ReflectionUtility.allBindings); + var info = ReflectionUtility.GetProperty(type, source); if (info == null) { return false; diff --git a/Assets/Editor Toolbox/Editor/Drawers/Toolbox/Decorator/EditorButtonAttributeDrawer.cs b/Assets/Editor Toolbox/Editor/Drawers/Toolbox/Decorator/EditorButtonAttributeDrawer.cs index fdc0d496..15eab5cd 100644 --- a/Assets/Editor Toolbox/Editor/Drawers/Toolbox/Decorator/EditorButtonAttributeDrawer.cs +++ b/Assets/Editor Toolbox/Editor/Drawers/Toolbox/Decorator/EditorButtonAttributeDrawer.cs @@ -10,7 +10,7 @@ public class EditorButtonAttributeDrawer : ToolboxDecoratorDrawer /// Returns of the searched method within the Editor . /// @@ -21,17 +48,17 @@ internal static MethodInfo GetEditorMethod(string classType, string methodName, return editorAssembly.GetType(classType).GetMethod(methodName, falgs); } - internal static MethodInfo GetObjectMethod(string methodName, SerializedObject serializedObject) + internal static MethodInfo GetMethod(string methodName, SerializedObject serializedObject) { - return GetObjectMethod(methodName, serializedObject.targetObjects); + return GetMethod(methodName, serializedObject.targetObjects); } - internal static MethodInfo GetObjectMethod(string methodName, params object[] targetObjects) + internal static MethodInfo GetMethod(string methodName, params object[] targetObjects) { - return GetObjectMethod(methodName, allBindings, targetObjects); + return GetMethod(methodName, allBindings, targetObjects); } - internal static MethodInfo GetObjectMethod(string methodName, BindingFlags bindingFlags, params object[] targetObjects) + internal static MethodInfo GetMethod(string methodName, BindingFlags bindingFlags, params object[] targetObjects) { if (targetObjects == null || targetObjects.Length == 0) { @@ -39,14 +66,24 @@ internal static MethodInfo GetObjectMethod(string methodName, BindingFlags bindi } var targetType = targetObjects[0].GetType(); - var methodInfo = GetObjectMethod(targetType, methodName, bindingFlags); + return GetMethod(targetType, methodName, bindingFlags); + } + + internal static MethodInfo GetMethod(Type targetType, string methodName) + { + return GetMethod(targetType, methodName, allBindings); + } + + internal static MethodInfo GetMethod(Type targetType, string methodName, BindingFlags bindingFlags) + { + var methodInfo = targetType.GetMethod(methodName, bindingFlags, null, CallingConventions.Any, new Type[0], null); if (methodInfo == null && bindingFlags.HasFlag(BindingFlags.NonPublic)) { //NOTE: if a method is not found and we searching for a private method we should look into parent classes var baseType = targetType.BaseType; while (baseType != null) { - methodInfo = GetObjectMethod(baseType, methodName, bindingFlags); + methodInfo = baseType.GetMethod(methodName, bindingFlags, null, CallingConventions.Any, new Type[0], null); if (methodInfo != null) { break; @@ -59,9 +96,31 @@ internal static MethodInfo GetObjectMethod(string methodName, BindingFlags bindi return methodInfo; } - internal static MethodInfo GetObjectMethod(Type targetType, string methodName, BindingFlags bindingFlags) + internal static PropertyInfo GetProperty(Type targetType, string propertyName) + { + return GetProperty(targetType, propertyName, allBindings); + } + + internal static PropertyInfo GetProperty(Type targetType, string propertyName, BindingFlags bindingFlags) { - return targetType.GetMethod(methodName, bindingFlags, null, CallingConventions.Any, new Type[0], null); + var property = targetType.GetProperty(propertyName, bindingFlags); + if (property == null && bindingFlags.HasFlag(BindingFlags.NonPublic)) + { + //NOTE: if a method is not found and we searching for a private method we should look into parent classes + Type baseType = targetType.BaseType; + while (baseType != null) + { + property = baseType.GetProperty(propertyName, bindingFlags); + if (property != null) + { + break; + } + + baseType = baseType.BaseType; + } + } + + return property; } /// @@ -70,7 +129,7 @@ internal static MethodInfo GetObjectMethod(Type targetType, string methodName, B internal static bool TryInvokeMethod(string methodName, SerializedObject serializedObject) { var targetObjects = serializedObject.targetObjects; - var method = GetObjectMethod(methodName, targetObjects); + var method = GetMethod(methodName, targetObjects); if (method == null) { return false; From 105bdb78f12c98134fe8ccc8ce0e5b14f3f23d68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=82osz=20Matkowski?= Date: Wed, 19 Feb 2025 17:30:02 +0100 Subject: [PATCH 03/46] Move FolderData type to the runtime assembly --- .../Drawers/Internal/FolderDataDrawer.cs | 2 +- .../Editor/ToolboxEditorProject.cs | 2 +- .../Editor/ToolboxEditorSettings.cs | 2 +- .../{Editor => Runtime}/Folders.meta | 2 +- .../{Editor => Runtime}/Folders/FolderData.cs | 14 +---------- .../Folders/FolderData.cs.meta | 2 +- .../Runtime/Folders/FolderDataType.cs | 8 +++++++ .../Runtime/Folders/FolderDataType.cs.meta | 11 +++++++++ .../Runtime/Folders/FolderIconType.cs | 8 +++++++ .../Runtime/Folders/FolderIconType.cs.meta | 11 +++++++++ Packages/packages-lock.json | 24 +++++++------------ 11 files changed, 52 insertions(+), 34 deletions(-) rename Assets/Editor Toolbox/{Editor => Runtime}/Folders.meta (77%) rename Assets/Editor Toolbox/{Editor => Runtime}/Folders/FolderData.cs (92%) rename Assets/Editor Toolbox/{Editor => Runtime}/Folders/FolderData.cs.meta (83%) create mode 100644 Assets/Editor Toolbox/Runtime/Folders/FolderDataType.cs create mode 100644 Assets/Editor Toolbox/Runtime/Folders/FolderDataType.cs.meta create mode 100644 Assets/Editor Toolbox/Runtime/Folders/FolderIconType.cs create mode 100644 Assets/Editor Toolbox/Runtime/Folders/FolderIconType.cs.meta diff --git a/Assets/Editor Toolbox/Editor/Drawers/Internal/FolderDataDrawer.cs b/Assets/Editor Toolbox/Editor/Drawers/Internal/FolderDataDrawer.cs index 1634c939..920b80e3 100644 --- a/Assets/Editor Toolbox/Editor/Drawers/Internal/FolderDataDrawer.cs +++ b/Assets/Editor Toolbox/Editor/Drawers/Internal/FolderDataDrawer.cs @@ -3,7 +3,7 @@ namespace Toolbox.Editor.Drawers { - using Toolbox.Editor.Folders; + using Toolbox.Folders; [CustomPropertyDrawer(typeof(FolderData))] internal class FolderDataDrawer : PropertyDrawer diff --git a/Assets/Editor Toolbox/Editor/ToolboxEditorProject.cs b/Assets/Editor Toolbox/Editor/ToolboxEditorProject.cs index a4a9d5b8..6336879b 100644 --- a/Assets/Editor Toolbox/Editor/ToolboxEditorProject.cs +++ b/Assets/Editor Toolbox/Editor/ToolboxEditorProject.cs @@ -5,7 +5,7 @@ namespace Toolbox.Editor { - using Toolbox.Editor.Folders; + using Toolbox.Folders; /// /// Static GUI representation for the Project Overlay. diff --git a/Assets/Editor Toolbox/Editor/ToolboxEditorSettings.cs b/Assets/Editor Toolbox/Editor/ToolboxEditorSettings.cs index 7f623c03..28197497 100644 --- a/Assets/Editor Toolbox/Editor/ToolboxEditorSettings.cs +++ b/Assets/Editor Toolbox/Editor/ToolboxEditorSettings.cs @@ -6,8 +6,8 @@ namespace Toolbox.Editor { using Toolbox.Editor.Drawers; - using Toolbox.Editor.Folders; using Toolbox.Editor.Hierarchy; + using Toolbox.Folders; internal interface IToolboxGeneralSettings { } diff --git a/Assets/Editor Toolbox/Editor/Folders.meta b/Assets/Editor Toolbox/Runtime/Folders.meta similarity index 77% rename from Assets/Editor Toolbox/Editor/Folders.meta rename to Assets/Editor Toolbox/Runtime/Folders.meta index 81033450..aebcfcd3 100644 --- a/Assets/Editor Toolbox/Editor/Folders.meta +++ b/Assets/Editor Toolbox/Runtime/Folders.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: be549d804ac1aff4689632341de6835f +guid: c422f5efbb0f4d94297a48755fbe306c folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Assets/Editor Toolbox/Editor/Folders/FolderData.cs b/Assets/Editor Toolbox/Runtime/Folders/FolderData.cs similarity index 92% rename from Assets/Editor Toolbox/Editor/Folders/FolderData.cs rename to Assets/Editor Toolbox/Runtime/Folders/FolderData.cs index 221e1311..e5325bef 100644 --- a/Assets/Editor Toolbox/Editor/Folders/FolderData.cs +++ b/Assets/Editor Toolbox/Runtime/Folders/FolderData.cs @@ -3,20 +3,8 @@ using UnityEngine; using UnityEngine.Serialization; -namespace Toolbox.Editor.Folders +namespace Toolbox.Folders { - public enum FolderDataType - { - Path, - Name - } - - public enum FolderIconType - { - Custom, - Editor - } - [Serializable] public struct FolderData { diff --git a/Assets/Editor Toolbox/Editor/Folders/FolderData.cs.meta b/Assets/Editor Toolbox/Runtime/Folders/FolderData.cs.meta similarity index 83% rename from Assets/Editor Toolbox/Editor/Folders/FolderData.cs.meta rename to Assets/Editor Toolbox/Runtime/Folders/FolderData.cs.meta index 3bf8b975..67810eff 100644 --- a/Assets/Editor Toolbox/Editor/Folders/FolderData.cs.meta +++ b/Assets/Editor Toolbox/Runtime/Folders/FolderData.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 4680b935459476d4d8ebb3425add6b9a +guid: 571656423e7c7024ea709ce9009cfe15 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Assets/Editor Toolbox/Runtime/Folders/FolderDataType.cs b/Assets/Editor Toolbox/Runtime/Folders/FolderDataType.cs new file mode 100644 index 00000000..501b092b --- /dev/null +++ b/Assets/Editor Toolbox/Runtime/Folders/FolderDataType.cs @@ -0,0 +1,8 @@ +namespace Toolbox.Folders +{ + public enum FolderDataType + { + Path, + Name + } +} \ No newline at end of file diff --git a/Assets/Editor Toolbox/Runtime/Folders/FolderDataType.cs.meta b/Assets/Editor Toolbox/Runtime/Folders/FolderDataType.cs.meta new file mode 100644 index 00000000..fc82e171 --- /dev/null +++ b/Assets/Editor Toolbox/Runtime/Folders/FolderDataType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d87c1403eec547b4f80d466b4c2dfa5b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Editor Toolbox/Runtime/Folders/FolderIconType.cs b/Assets/Editor Toolbox/Runtime/Folders/FolderIconType.cs new file mode 100644 index 00000000..b935fcdc --- /dev/null +++ b/Assets/Editor Toolbox/Runtime/Folders/FolderIconType.cs @@ -0,0 +1,8 @@ +namespace Toolbox.Folders +{ + public enum FolderIconType + { + Custom, + Editor + } +} \ No newline at end of file diff --git a/Assets/Editor Toolbox/Runtime/Folders/FolderIconType.cs.meta b/Assets/Editor Toolbox/Runtime/Folders/FolderIconType.cs.meta new file mode 100644 index 00000000..1dc979be --- /dev/null +++ b/Assets/Editor Toolbox/Runtime/Folders/FolderIconType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ac05f3425c2d59b43a1161838d0f463a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/packages-lock.json b/Packages/packages-lock.json index 4ae6aadb..dcf65abf 100644 --- a/Packages/packages-lock.json +++ b/Packages/packages-lock.json @@ -10,7 +10,10 @@ "version": "1.0.0", "depth": 0, "source": "builtin", - "dependencies": {} + "dependencies": { + "com.unity.modules.tilemap": "1.0.0", + "com.unity.modules.uielements": "1.0.0" + } }, "com.unity.analytics": { "version": "3.6.12", @@ -68,18 +71,18 @@ "url": "https://packages.unity.com" }, "com.unity.sysroot": { - "version": "1.0.0", + "version": "2.0.5", "depth": 1, "source": "registry", "dependencies": {}, "url": "https://packages.unity.com" }, "com.unity.sysroot.linux-x86_64": { - "version": "1.0.0", + "version": "2.0.4", "depth": 1, "source": "registry", "dependencies": { - "com.unity.sysroot": "1.0.0" + "com.unity.sysroot": "2.0.5" }, "url": "https://packages.unity.com" }, @@ -108,9 +111,9 @@ "depth": 0, "source": "registry", "dependencies": { + "com.unity.modules.audio": "1.0.0", "com.unity.modules.director": "1.0.0", "com.unity.modules.animation": "1.0.0", - "com.unity.modules.audio": "1.0.0", "com.unity.modules.particlesystem": "1.0.0" }, "url": "https://packages.unity.com" @@ -276,17 +279,6 @@ "version": "1.0.0", "depth": 0, "source": "builtin", - "dependencies": { - "com.unity.modules.ui": "1.0.0", - "com.unity.modules.imgui": "1.0.0", - "com.unity.modules.jsonserialize": "1.0.0", - "com.unity.modules.uielementsnative": "1.0.0" - } - }, - "com.unity.modules.uielementsnative": { - "version": "1.0.0", - "depth": 1, - "source": "builtin", "dependencies": { "com.unity.modules.ui": "1.0.0", "com.unity.modules.imgui": "1.0.0", From 7ac87ffb91187ffce82bcd69dc161e9e8072fa5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=82osz=20Matkowski?= Date: Wed, 19 Feb 2025 17:34:10 +0100 Subject: [PATCH 04/46] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c5d629df..c9ce2ee5 100644 --- a/README.md +++ b/README.md @@ -971,11 +971,11 @@ public void Usage() Allows to serialize folders in form of assets and retrieve direct paths in Runtime. ```csharp -public SerializedDirectory serializeDirectory; +public SerializedDirectory serializedDirectory; public void Usage() { - string path = serializeDirectory.DirectoryPath; + string path = serializedDirectory.DirectoryPath; } ``` From 5ee484fca7c954192c63e35433cefcaf2a30e69c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=82osz=20Matkowski?= Date: Wed, 19 Feb 2025 22:11:04 +0100 Subject: [PATCH 05/46] Clear InLinedEditor after folding properties; improve displaying mixed values while using the ReferencePickerAttribute; improve footer position in the ReorderableList class --- .../InLineEditorAttributeDrawer.cs | 8 ++--- .../ReferencePickerAttributeDrawer.cs | 32 ++++++++++++++++++- .../Editor/Internal/ReorderableListBase.cs | 4 +++ Packages/manifest.json | 1 - Packages/packages-lock.json | 29 +++++++++-------- ProjectSettings/TimelineSettings.asset | 16 ++++++++++ 6 files changed, 70 insertions(+), 20 deletions(-) create mode 100644 ProjectSettings/TimelineSettings.asset diff --git a/Assets/Editor Toolbox/Editor/Drawers/Toolbox/PropertySelf/InLineEditorAttributeDrawer.cs b/Assets/Editor Toolbox/Editor/Drawers/Toolbox/PropertySelf/InLineEditorAttributeDrawer.cs index 125e077b..93b8cf8e 100644 --- a/Assets/Editor Toolbox/Editor/Drawers/Toolbox/PropertySelf/InLineEditorAttributeDrawer.cs +++ b/Assets/Editor Toolbox/Editor/Drawers/Toolbox/PropertySelf/InLineEditorAttributeDrawer.cs @@ -46,7 +46,6 @@ static InLineEditorAttributeDrawer() private static readonly PropertyDataStorage storage; - private Editor GetTargetsEditor(SerializedProperty property, InLineEditorAttribute attribute) { var editor = storage.ReturnItem(property, attribute); @@ -108,7 +107,6 @@ private void DrawEditor(Editor editor, bool disableEditor, bool drawPreview, boo } } - /// /// Handles the property drawing process and tries to create a inlined version of the . /// @@ -151,15 +149,17 @@ protected override void OnGuiSafe(SerializedProperty property, GUIContent label, DrawEditor(editor, attribute); } } + else + { + storage.ClearItem(property); + } } - public override bool IsPropertyValid(SerializedProperty property) { return property.propertyType == SerializedPropertyType.ObjectReference; } - private static class Style { internal static readonly GUIStyle backgroundStyle; diff --git a/Assets/Editor Toolbox/Editor/Drawers/Toolbox/PropertySelf/ReferencePickerAttributeDrawer.cs b/Assets/Editor Toolbox/Editor/Drawers/Toolbox/PropertySelf/ReferencePickerAttributeDrawer.cs index a854feb5..ab71df12 100644 --- a/Assets/Editor Toolbox/Editor/Drawers/Toolbox/PropertySelf/ReferencePickerAttributeDrawer.cs +++ b/Assets/Editor Toolbox/Editor/Drawers/Toolbox/PropertySelf/ReferencePickerAttributeDrawer.cs @@ -41,9 +41,38 @@ private Type GetParentType(ReferencePickerAttribute attribute, SerializedPropert return fieldType; } + private static Type GetCurrentManagedReferenceType(SerializedProperty property, out bool hasMixedValues) + { + var fullTypeName = property.managedReferenceFullTypename; + hasMixedValues = false; + TypeUtility.TryGetTypeFromManagedReferenceFullTypeName(fullTypeName, out var targetType); + + var currentSerializedObject = property.serializedObject; + if (currentSerializedObject.isEditingMultipleObjects) + { + var targets = currentSerializedObject.targetObjects; + foreach (var target in targets) + { + using (var tempSerializedObject = new SerializedObject(target)) + { + var tempProperty = tempSerializedObject.FindProperty(property.propertyPath); + if (tempProperty.managedReferenceFullTypename != fullTypeName) + { + hasMixedValues = true; + break; + } + } + } + } + + return targetType; + } + private void CreateTypeProperty(SerializedProperty property, Type parentType, ReferencePickerAttribute attribute, Rect position) { - TypeUtility.TryGetTypeFromManagedReferenceFullTypeName(property.managedReferenceFullTypename, out var currentType); + var currentType = GetCurrentManagedReferenceType(property, out var hasMixedValues); + var hadMixedValues = EditorGUI.showMixedValue; + EditorGUI.showMixedValue = hasMixedValues; typeField.OnGui(position, attribute.AddTextSearchField, (type) => { try @@ -70,6 +99,7 @@ private void CreateTypeProperty(SerializedProperty property, Type parentType, Re ToolboxEditorLog.LogWarning("Invalid attempt to update disposed property."); } }, currentType, parentType); + EditorGUI.showMixedValue = false; } private void UpdateTypeProperty(SerializedProperty property, Type targetType, ReferencePickerAttribute attribute) diff --git a/Assets/Editor Toolbox/Editor/Internal/ReorderableListBase.cs b/Assets/Editor Toolbox/Editor/Internal/ReorderableListBase.cs index b4c3e9bc..2c8f829b 100644 --- a/Assets/Editor Toolbox/Editor/Internal/ReorderableListBase.cs +++ b/Assets/Editor Toolbox/Editor/Internal/ReorderableListBase.cs @@ -334,6 +334,8 @@ protected virtual bool DoListFooter(Rect footerRect) return false; } + footerRect.yMax += Style.footerXOffset; + footerRect.yMin += Style.footerYOffset; //draw the background on repaint event if (Event.current.type == EventType.Repaint) { @@ -870,6 +872,8 @@ protected static class Style #else internal static readonly float lineHeight = 16.0f; #endif + internal static readonly float footerXOffset = 0.0f; + internal static readonly float footerYOffset = -1.0f; internal static readonly float footerWidth = 60.0f; internal static readonly float footerButtonWidth = 25.0f; internal static readonly float footerButtonHeight = 13.0f; diff --git a/Packages/manifest.json b/Packages/manifest.json index 9e4d1c2d..794f1ad1 100644 --- a/Packages/manifest.json +++ b/Packages/manifest.json @@ -7,7 +7,6 @@ "com.unity.editorcoroutines": "1.0.0", "com.unity.ide.rider": "3.0.18", "com.unity.ide.visualstudio": "2.0.17", - "com.unity.ide.vscode": "1.2.5", "com.unity.test-framework": "1.1.31", "com.unity.textmeshpro": "3.0.6", "com.unity.timeline": "1.6.4", diff --git a/Packages/packages-lock.json b/Packages/packages-lock.json index dcf65abf..12bc5cc1 100644 --- a/Packages/packages-lock.json +++ b/Packages/packages-lock.json @@ -10,10 +10,7 @@ "version": "1.0.0", "depth": 0, "source": "builtin", - "dependencies": { - "com.unity.modules.tilemap": "1.0.0", - "com.unity.modules.uielements": "1.0.0" - } + "dependencies": {} }, "com.unity.analytics": { "version": "3.6.12", @@ -63,26 +60,19 @@ }, "url": "https://packages.unity.com" }, - "com.unity.ide.vscode": { - "version": "1.2.5", - "depth": 0, - "source": "registry", - "dependencies": {}, - "url": "https://packages.unity.com" - }, "com.unity.sysroot": { - "version": "2.0.5", + "version": "1.0.0", "depth": 1, "source": "registry", "dependencies": {}, "url": "https://packages.unity.com" }, "com.unity.sysroot.linux-x86_64": { - "version": "2.0.4", + "version": "1.0.0", "depth": 1, "source": "registry", "dependencies": { - "com.unity.sysroot": "2.0.5" + "com.unity.sysroot": "1.0.0" }, "url": "https://packages.unity.com" }, @@ -279,6 +269,17 @@ "version": "1.0.0", "depth": 0, "source": "builtin", + "dependencies": { + "com.unity.modules.ui": "1.0.0", + "com.unity.modules.imgui": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0", + "com.unity.modules.uielementsnative": "1.0.0" + } + }, + "com.unity.modules.uielementsnative": { + "version": "1.0.0", + "depth": 1, + "source": "builtin", "dependencies": { "com.unity.modules.ui": "1.0.0", "com.unity.modules.imgui": "1.0.0", diff --git a/ProjectSettings/TimelineSettings.asset b/ProjectSettings/TimelineSettings.asset new file mode 100644 index 00000000..cfaebd7a --- /dev/null +++ b/ProjectSettings/TimelineSettings.asset @@ -0,0 +1,16 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &1 +MonoBehaviour: + m_ObjectHideFlags: 61 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a287be6c49135cd4f9b2b8666c39d999, type: 3} + m_Name: + m_EditorClassIdentifier: + assetDefaultFramerate: 60 + m_DefaultFrameRate: 60 From 92d5047436227867811a5af29174c2118328479b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=82osz=20Matkowski?= Date: Sat, 22 Feb 2025 14:09:18 +0100 Subject: [PATCH 06/46] Fix AmbiguousMatchException while searching for overrided fields & properties; expose OnToolbarGuiRight callback --- .../Editor/ToolboxEditorToolbar.cs | 64 ++++++++++++++----- .../Editor/Utilities/ReflectionUtility.cs | 6 +- Assets/Examples/Editor/SampleToolbar.cs | 36 +++++++++-- 3 files changed, 82 insertions(+), 24 deletions(-) diff --git a/Assets/Editor Toolbox/Editor/ToolboxEditorToolbar.cs b/Assets/Editor Toolbox/Editor/ToolboxEditorToolbar.cs index bf5285f7..fff824c2 100644 --- a/Assets/Editor Toolbox/Editor/ToolboxEditorToolbar.cs +++ b/Assets/Editor Toolbox/Editor/ToolboxEditorToolbar.cs @@ -53,7 +53,7 @@ static ToolboxEditorToolbar() private static IEnumerator Initialize() { - while (ToolboxEditorToolbar.toolbar == null) + while (toolbar == null) { var toolbars = Resources.FindObjectsOfTypeAll(toolbarType); if (toolbars == null || toolbars.Length == 0) @@ -63,15 +63,15 @@ private static IEnumerator Initialize() } else { - ToolboxEditorToolbar.toolbar = toolbars[0]; + toolbar = toolbars[0]; } } #if UNITY_2021_1_OR_NEWER - var rootField = ToolboxEditorToolbar.toolbar.GetType().GetField("m_Root", BindingFlags.NonPublic | BindingFlags.Instance); - var root = rootField.GetValue(ToolboxEditorToolbar.toolbar) as VisualElement; - var toolbar = root.Q("ToolbarZoneLeftAlign"); + var rootField = toolbar.GetType().GetField("m_Root", BindingFlags.NonPublic | BindingFlags.Instance); + var root = rootField.GetValue(toolbar) as VisualElement; + var toolbarLeftZone = root.Q("ToolbarZoneLeftAlign"); var element = new VisualElement() { style = @@ -83,10 +83,25 @@ private static IEnumerator Initialize() var container = new IMGUIContainer(); container.style.flexGrow = 1; - container.onGUIHandler += OnGui; - + container.onGUIHandler += OnGuiLeft; element.Add(container); - toolbar.Add(element); + toolbarLeftZone.Add(element); + + var toolbarRightZone = root.Q("ToolbarZoneRightAlign"); + var rightElement = new VisualElement() + { + style = + { + flexGrow = 1, + flexDirection = FlexDirection.Row, + } + }; + + var rightContainer = new IMGUIContainer(); + rightContainer.style.flexGrow = 1; + rightContainer.onGUIHandler += OnGuiRight; + rightElement.Add(rightContainer); + toolbarRightZone.Add(rightElement); #else #if UNITY_2020_1_OR_NEWER var backend = guiBackend.GetValue(toolbar); @@ -101,15 +116,15 @@ private static IEnumerator Initialize() var container = elements[0] as IMGUIContainer; #endif var handler = onGuiHandler.GetValue(container) as Action; - handler -= OnGui; - handler += OnGui; + handler -= OnGuiLeft; + handler += OnGuiLeft; onGuiHandler.SetValue(container, handler); #endif } - private static void OnGui() + private static void OnGuiLeft() { - if (!IsToolbarAllowed || !IsToolbarValid) + if (!IsToolbarAllowed || !IsLeftToolbarValid) { return; } @@ -117,7 +132,7 @@ private static void OnGui() #if UNITY_2021_1_OR_NEWER using (new GUILayout.HorizontalScope()) { - OnToolbarGui.Invoke(); + OnToolbarGuiLeft(); } #else var screenWidth = EditorGUIUtility.currentViewWidth; @@ -139,12 +154,25 @@ private static void OnGui() { using (new GUILayout.HorizontalScope()) { - OnToolbarGui?.Invoke(); + OnToolbarGuiLeft(); } } #endif } + private static void OnGuiRight() + { + if (!IsToolbarAllowed || !IsRightToolbarValid) + { + return; + } + + using (new EditorGUILayout.HorizontalScope()) + { + OnToolbarGuiRight(); + } + } + public static void Repaint() { if (toolbar == null) @@ -156,11 +184,17 @@ public static void Repaint() } public static bool IsToolbarAllowed { get; set; } = true; - public static bool IsToolbarValid => toolbar != null && OnToolbarGui != null; + public static bool IsLeftToolbarValid => toolbar != null && OnToolbarGuiLeft != null; + public static bool IsRightToolbarValid => toolbar != null && OnToolbarGuiRight != null; public static float FromToolsOffset { get; set; } = 400.0f; public static float FromStripOffset { get; set; } = 150.0f; +#pragma warning disable 0067 + [Obsolete("Use OnToolbarGuiLeft instead")] public static event Action OnToolbarGui; +#pragma warning restore 0067 + public static event Action OnToolbarGuiLeft; + public static event Action OnToolbarGuiRight; private static class Style { diff --git a/Assets/Editor Toolbox/Editor/Utilities/ReflectionUtility.cs b/Assets/Editor Toolbox/Editor/Utilities/ReflectionUtility.cs index bab68128..4a52c74e 100644 --- a/Assets/Editor Toolbox/Editor/Utilities/ReflectionUtility.cs +++ b/Assets/Editor Toolbox/Editor/Utilities/ReflectionUtility.cs @@ -20,8 +20,9 @@ internal static FieldInfo GetField(Type targetType, string fieldName) internal static FieldInfo GetField(Type targetType, string fieldName, BindingFlags bindingFlags) { + bindingFlags |= BindingFlags.DeclaredOnly; var field = targetType.GetField(fieldName, bindingFlags); - if (field == null && bindingFlags.HasFlag(BindingFlags.NonPublic)) + if (field == null) { //NOTE: if a method is not found and we searching for a private method we should look into parent classes Type baseType = targetType.BaseType; @@ -103,8 +104,9 @@ internal static PropertyInfo GetProperty(Type targetType, string propertyName) internal static PropertyInfo GetProperty(Type targetType, string propertyName, BindingFlags bindingFlags) { + bindingFlags |= BindingFlags.DeclaredOnly; var property = targetType.GetProperty(propertyName, bindingFlags); - if (property == null && bindingFlags.HasFlag(BindingFlags.NonPublic)) + if (property == null) { //NOTE: if a method is not found and we searching for a private method we should look into parent classes Type baseType = targetType.BaseType; diff --git a/Assets/Examples/Editor/SampleToolbar.cs b/Assets/Examples/Editor/SampleToolbar.cs index dba275b9..d43e926c 100644 --- a/Assets/Examples/Editor/SampleToolbar.cs +++ b/Assets/Examples/Editor/SampleToolbar.cs @@ -10,7 +10,7 @@ public static class SampleToolbar /// /// This field will be used to exclude toolbar buttons for all scenes except this one. /// - private readonly static string mySampleSceneName = "SampleScene"; + private static readonly string mySampleSceneName = "SampleScene"; static SampleToolbar() { @@ -43,19 +43,18 @@ private static void ValidateFirstScene() /// private static void SceneOpenedCallback(Scene scene, OpenSceneMode mode) { - ToolboxEditorToolbar.OnToolbarGui -= OnToolbarGui; + ToolboxEditorToolbar.OnToolbarGuiLeft -= OnToolbarGuiLeft; + ToolboxEditorToolbar.OnToolbarGuiRight -= OnToolbarGuiRight; if (scene.name != mySampleSceneName) { return; } - ToolboxEditorToolbar.OnToolbarGui += OnToolbarGui; + ToolboxEditorToolbar.OnToolbarGuiLeft += OnToolbarGuiLeft; + ToolboxEditorToolbar.OnToolbarGuiRight += OnToolbarGuiRight; } - /// - /// Layout-based GUI call. - /// - private static void OnToolbarGui() + private static void OnToolbarGuiLeft() { GUILayout.FlexibleSpace(); if (GUILayout.Button("1", Style.commandLeftStyle)) @@ -79,6 +78,29 @@ private static void OnToolbarGui() } } + private static void OnToolbarGuiRight() + { + if (GUILayout.Button("1")) + { + Debug.Log("1"); + } + + if (GUILayout.Button("2")) + { + Debug.Log("2"); + } + + if (GUILayout.Button("3")) + { + Debug.Log("3"); + } + + if (GUILayout.Button("4")) + { + Debug.Log("4"); + } + } + private static class Style { internal static readonly GUIStyle commandMidStyle = new GUIStyle("CommandMid") From 43bace23964b67a1ba7fa22dd62f0aa7f1586678 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=82osz=20Matkowski?= Date: Sat, 22 Feb 2025 14:23:19 +0100 Subject: [PATCH 07/46] Update: README.md, package.json, CHANGELOG.md --- Assets/Editor Toolbox/CHANGELOG.md | 13 ++++++++----- Assets/Editor Toolbox/README.md | 20 +++++++++++++++----- Assets/Editor Toolbox/package.json | 2 +- README.md | 20 +++++++++++++++----- 4 files changed, 39 insertions(+), 16 deletions(-) diff --git a/Assets/Editor Toolbox/CHANGELOG.md b/Assets/Editor Toolbox/CHANGELOG.md index 035bcd06..fcdec6f8 100644 --- a/Assets/Editor Toolbox/CHANGELOG.md +++ b/Assets/Editor Toolbox/CHANGELOG.md @@ -1,13 +1,16 @@ +## 0.14.0 [23.02.2025] + ## 0.13.2 [29.11.2024] ### Added: -- AnimationCurveSettingsAttribute +- OnToolbarGuiRight callback (ability to draw GUI elements on the right side of the toolbar container); OnToolbarGui replaced with the OnToolbarGuiLeft callback ### Changed: -- Possibility to use [EditorButton] and [DynamicHelp] in nested types -- For now SerializeReference properties without children will always be folded -- Fix exception while building labels for generic types without arguments -- Fix drawing SerializedDictionary if value or key types cannot be serialized +- Fix fetching private members from base classes in various cases (e.g. [EditorButton] or conditionals) +- Move FolderData to the Runtime assembly to fix issues caused by the Visual Scripting package +- Fix minor rendering issues caused by the ReoerdableList's footer position +- Fix clearing cached Editor instances in the [InLineEditor] (fix for the AudioClip still playing) +- Improve displaying [SerializeReference]-based properties in the multi-editing mode ## 0.13.1 [30.08.2024] diff --git a/Assets/Editor Toolbox/README.md b/Assets/Editor Toolbox/README.md index c9ce2ee5..2f92290f 100644 --- a/Assets/Editor Toolbox/README.md +++ b/Assets/Editor Toolbox/README.md @@ -738,11 +738,12 @@ To prevent issues after renaming types use `UnityEngine.Scripting.APIUpdating.Mo ```csharp [SerializeReference, ReferencePicker(TypeGrouping = TypeGrouping.ByFlatName)] public ISampleInterface var1; -[SerializeReference, ReferencePicker(ForceUninitializedInstance = true)] -public ISampleInterface var1; [SerializeReference, ReferencePicker(ParentType = typeof(ClassWithInterface2)] public ClassWithInterfaceBase var2; - +[SerializeReference, ReferencePicker(ForceUninitializedInstance = true)] +public ISampleInterface var3; +``` +```csharp public interface ISampleInterface { } [Serializable] @@ -1029,10 +1030,11 @@ public static class MyEditorUtility { static MyEditorUtility() { - ToolboxEditorToolbar.OnToolbarGui += OnToolbarGui; + ToolboxEditorToolbar.OnToolbarGuiLeft += OnToolbarGuiLeft; + ToolboxEditorToolbar.OnToolbarGuiRight += OnToolbarGuiRight; } - private static void OnToolbarGui() + private static void OnToolbarGuiLeft() { GUILayout.FlexibleSpace(); if (GUILayout.Button("1", Style.commandLeftStyle)) @@ -1056,6 +1058,14 @@ public static class MyEditorUtility Debug.Log("5"); } } + + private static void OnToolbarGuiRight() + { + if (GUILayout.Button("1")) + { + Debug.Log("1"); + } + } } ``` diff --git a/Assets/Editor Toolbox/package.json b/Assets/Editor Toolbox/package.json index 4c6cdc76..2d7ea422 100644 --- a/Assets/Editor Toolbox/package.json +++ b/Assets/Editor Toolbox/package.json @@ -1,7 +1,7 @@ { "name": "com.browar.editor-toolbox", "displayName": "Editor Toolbox", - "version": "0.13.2", + "version": "0.14.0", "unity": "2018.1", "description": "Tools, custom attributes, drawers, hierarchy overlay, and other extensions for the Unity Editor.", "keywords": [ diff --git a/README.md b/README.md index c9ce2ee5..2f92290f 100644 --- a/README.md +++ b/README.md @@ -738,11 +738,12 @@ To prevent issues after renaming types use `UnityEngine.Scripting.APIUpdating.Mo ```csharp [SerializeReference, ReferencePicker(TypeGrouping = TypeGrouping.ByFlatName)] public ISampleInterface var1; -[SerializeReference, ReferencePicker(ForceUninitializedInstance = true)] -public ISampleInterface var1; [SerializeReference, ReferencePicker(ParentType = typeof(ClassWithInterface2)] public ClassWithInterfaceBase var2; - +[SerializeReference, ReferencePicker(ForceUninitializedInstance = true)] +public ISampleInterface var3; +``` +```csharp public interface ISampleInterface { } [Serializable] @@ -1029,10 +1030,11 @@ public static class MyEditorUtility { static MyEditorUtility() { - ToolboxEditorToolbar.OnToolbarGui += OnToolbarGui; + ToolboxEditorToolbar.OnToolbarGuiLeft += OnToolbarGuiLeft; + ToolboxEditorToolbar.OnToolbarGuiRight += OnToolbarGuiRight; } - private static void OnToolbarGui() + private static void OnToolbarGuiLeft() { GUILayout.FlexibleSpace(); if (GUILayout.Button("1", Style.commandLeftStyle)) @@ -1056,6 +1058,14 @@ public static class MyEditorUtility Debug.Log("5"); } } + + private static void OnToolbarGuiRight() + { + if (GUILayout.Button("1")) + { + Debug.Log("1"); + } + } } ``` From 69e34d8ce1be1e11f57c2a61065a66e1b4ab745e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=82osz=20Matkowski?= Date: Sat, 22 Feb 2025 14:26:53 +0100 Subject: [PATCH 08/46] Update: CHANGELOG.md --- Assets/Editor Toolbox/CHANGELOG.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Assets/Editor Toolbox/CHANGELOG.md b/Assets/Editor Toolbox/CHANGELOG.md index fcdec6f8..07b14ecc 100644 --- a/Assets/Editor Toolbox/CHANGELOG.md +++ b/Assets/Editor Toolbox/CHANGELOG.md @@ -1,7 +1,5 @@ ## 0.14.0 [23.02.2025] -## 0.13.2 [29.11.2024] - ### Added: - OnToolbarGuiRight callback (ability to draw GUI elements on the right side of the toolbar container); OnToolbarGui replaced with the OnToolbarGuiLeft callback @@ -12,6 +10,17 @@ - Fix clearing cached Editor instances in the [InLineEditor] (fix for the AudioClip still playing) - Improve displaying [SerializeReference]-based properties in the multi-editing mode +## 0.13.2 [29.11.2024] + +### Added: +- AnimationCurveSettingsAttribute + +### Changed: +- Possibility to use [EditorButton] and [DynamicHelp] in nested types +- For now SerializeReference properties without children will always be folded +- Fix exception while building labels for generic types without arguments +- Fix drawing SerializedDictionary if value or key types cannot be serialized + ## 0.13.1 [30.08.2024] ### Changed: From 7e850c5c2f8346da5c5921991a5e8d4a5d3e218e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=82osz=20Matkowski?= Date: Sat, 22 Feb 2025 14:34:11 +0100 Subject: [PATCH 09/46] Update workflows --- .github/workflows/activation.yml | 2 +- .github/workflows/test.yml | 2 +- .github/workflows/upm-dev.yml | 2 +- .github/workflows/upm.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/activation.yml b/.github/workflows/activation.yml index 1d856e59..439e7092 100644 --- a/.github/workflows/activation.yml +++ b/.github/workflows/activation.yml @@ -12,7 +12,7 @@ jobs: uses: game-ci/unity-request-activation-file@v2 # Upload artifact (Unity_v20XX.X.XXXX.alf) - name: Expose as artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: ${{ steps.getManualLicenseFile.outputs.filePath }} path: ${{ steps.getManualLicenseFile.outputs.filePath }} \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 66cda1f3..b408643d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -37,7 +37,7 @@ jobs: testMode: ${{ matrix.testMode }} artifactsPath: ${{ matrix.testMode }}-artifacts checkName: ${{ matrix.testMode }} Test Results - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 if: always() with: name: Test results for ${{ matrix.testMode }} diff --git a/.github/workflows/upm-dev.yml b/.github/workflows/upm-dev.yml index 8e3b9997..e25a96f7 100644 --- a/.github/workflows/upm-dev.yml +++ b/.github/workflows/upm-dev.yml @@ -8,7 +8,7 @@ jobs: name: split upm branch runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: fetch-depth: 0 - name: split upm branch diff --git a/.github/workflows/upm.yml b/.github/workflows/upm.yml index 286d4a45..59f3f282 100644 --- a/.github/workflows/upm.yml +++ b/.github/workflows/upm.yml @@ -8,7 +8,7 @@ jobs: name: split upm branch runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: fetch-depth: 0 - name: split upm branch From 4392c10b731854562b2c9c42cb2bafdada2c351b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=82osz=20Matkowski?= Date: Tue, 25 Mar 2025 00:51:10 +0100 Subject: [PATCH 10/46] Ability to decide if EditorButton should be rendered below or above the target property; minor SerializedScene API improvements; minor refactor changes --- .../Decorator/EditorButtonAttributeDrawer.cs | 53 +++++++++++++------ .../Editor Toolbox/Editor/ToolboxEditorGui.cs | 5 -- .../EditorButtonAttribute.cs | 10 +++- .../Runtime/Serialization/SerializedScene.cs | 32 +++++++++-- Assets/Examples/Scripts/SampleBehaviour4.cs | 2 +- 5 files changed, 75 insertions(+), 27 deletions(-) diff --git a/Assets/Editor Toolbox/Editor/Drawers/Toolbox/Decorator/EditorButtonAttributeDrawer.cs b/Assets/Editor Toolbox/Editor/Drawers/Toolbox/Decorator/EditorButtonAttributeDrawer.cs index 15eab5cd..f5ea2b60 100644 --- a/Assets/Editor Toolbox/Editor/Drawers/Toolbox/Decorator/EditorButtonAttributeDrawer.cs +++ b/Assets/Editor Toolbox/Editor/Drawers/Toolbox/Decorator/EditorButtonAttributeDrawer.cs @@ -8,6 +8,31 @@ namespace Toolbox.Editor.Drawers { public class EditorButtonAttributeDrawer : ToolboxDecoratorDrawer { + private void DrawButton(EditorButtonAttribute attribute) + { + var declaringObjects = GetDeclaringObjects(); + if (declaringObjects == null || declaringObjects.Length == 0) + { + //NOTE: something went really wrong, internal bug or OnGuiBeginSafe was called out of the Toolbox scope + return; + } + + var disable = !IsClickable(attribute, declaringObjects); + using (new EditorGUI.DisabledScope(disable)) + { + var label = string.IsNullOrEmpty(attribute.ExtraLabel) + ? ObjectNames.NicifyVariableName(attribute.MethodName) + : attribute.ExtraLabel; + var tooltip = attribute.Tooltip; + var content = new GUIContent(label, tooltip); + + if (GUILayout.Button(content, Style.buttonStyle)) + { + CallMethods(attribute, declaringObjects); + } + } + } + private MethodInfo GetMethod(EditorButtonAttribute attribute, object[] targetObjects, string methodName) { var methodInfo = ReflectionUtility.GetMethod(methodName, targetObjects); @@ -119,29 +144,25 @@ private void CallMethods(EditorButtonAttribute attribute, object[] targetObjects } } - protected override void OnGuiCloseSafe(EditorButtonAttribute attribute) + protected override void OnGuiBeginSafe(EditorButtonAttribute attribute) { - var declaringObjects = GetDeclaringObjects(); - if (declaringObjects == null || declaringObjects.Length == 0) + if (attribute.PositionType != ButtonPositionType.Above) { - //NOTE: something went really wrong, internal bug or OnGuiBeginSafe was called out of the Toolbox scope return; } - var disable = !IsClickable(attribute, declaringObjects); - using (new EditorGUI.DisabledScope(disable)) - { - var label = string.IsNullOrEmpty(attribute.ExtraLabel) - ? ObjectNames.NicifyVariableName(attribute.MethodName) - : attribute.ExtraLabel; - var tooltip = attribute.Tooltip; - var content = new GUIContent(label, tooltip); + DrawButton(attribute); + } - if (GUILayout.Button(content, Style.buttonStyle)) - { - CallMethods(attribute, declaringObjects); - } + protected override void OnGuiCloseSafe(EditorButtonAttribute attribute) + { + if (attribute.PositionType != ButtonPositionType.Default && + attribute.PositionType != ButtonPositionType.Below) + { + return; } + + DrawButton(attribute); } private static class Style diff --git a/Assets/Editor Toolbox/Editor/ToolboxEditorGui.cs b/Assets/Editor Toolbox/Editor/ToolboxEditorGui.cs index a7287573..d8977f25 100644 --- a/Assets/Editor Toolbox/Editor/ToolboxEditorGui.cs +++ b/Assets/Editor Toolbox/Editor/ToolboxEditorGui.cs @@ -528,12 +528,7 @@ public static void DrawPropertyChildren(SerializedProperty property, ActionTest Coroutine", activityType: ButtonActivityType.OnPlayMode)] [EditorButton(nameof(TestStaticMethod), activityType: ButtonActivityType.OnEditMode)] public int var1; From 795c87e452b11a36a64146c7b49bd4e8accd7768 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=82osz=20Matkowski?= Date: Thu, 27 Mar 2025 22:24:09 +0100 Subject: [PATCH 11/46] Improvements to the SerializedScene drawing logic --- .../Drawers/Regular/PropertyDrawerBase.cs | 3 +- .../Drawers/Regular/SerializedSceneDrawer.cs | 62 ++++++++++++++----- 2 files changed, 47 insertions(+), 18 deletions(-) diff --git a/Assets/Editor Toolbox/Editor/Drawers/Regular/PropertyDrawerBase.cs b/Assets/Editor Toolbox/Editor/Drawers/Regular/PropertyDrawerBase.cs index 02dad0d9..5472ef65 100644 --- a/Assets/Editor Toolbox/Editor/Drawers/Regular/PropertyDrawerBase.cs +++ b/Assets/Editor Toolbox/Editor/Drawers/Regular/PropertyDrawerBase.cs @@ -48,8 +48,7 @@ public sealed override void OnGUI(Rect position, SerializedProperty property, GU } ToolboxEditorLog.WrongAttributeUsageWarning(attribute, property); - //create additional warning label based on the property name - var warningContent = new GUIContent(property.displayName + " has invalid property drawer"); + var warningContent = new GUIContent($"{property.displayName} has invalid property drawer"); ToolboxEditorGui.DrawEmptyProperty(position, property, warningContent); } diff --git a/Assets/Editor Toolbox/Editor/Drawers/Regular/SerializedSceneDrawer.cs b/Assets/Editor Toolbox/Editor/Drawers/Regular/SerializedSceneDrawer.cs index b555f02b..ad7946be 100644 --- a/Assets/Editor Toolbox/Editor/Drawers/Regular/SerializedSceneDrawer.cs +++ b/Assets/Editor Toolbox/Editor/Drawers/Regular/SerializedSceneDrawer.cs @@ -1,4 +1,5 @@ -using UnityEditor; +using Toolbox.Editor.Internal; +using UnityEditor; using UnityEngine; namespace Toolbox.Editor.Drawers @@ -28,29 +29,39 @@ private void OpenBuildSettings() EditorWindow.GetWindow(typeof(BuildPlayerWindow)); } - - protected override float GetPropertyHeightSafe(SerializedProperty property, GUIContent label) - { - var lineHeight = EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; - return HasSceneDetails(property) - ? base.GetPropertyHeightSafe(property, label) + lineHeight * 2 - : base.GetPropertyHeightSafe(property, label); - } - protected override void OnGUISafe(Rect position, SerializedProperty property, GUIContent label) { + var hasDetails = HasSceneDetails(property); EditorGUI.BeginProperty(position, label, property); position.height = EditorGUIUtility.singleLineHeight; - position = EditorGUI.PrefixLabel(position, label); + if (hasDetails) + { + position.xMax -= Style.foldoutWidth; + } + var sceneProperty = property.FindPropertyRelative("sceneReference"); - EditorGUI.ObjectField(position, sceneProperty, GUIContent.none); + EditorGUI.ObjectField(position, sceneProperty, label); EditorGUI.EndProperty(); - if (!HasSceneDetails(property)) + if (hasDetails) + { + var prevXMin = position.xMin; + position.xMin = position.xMax; + position.xMax += Style.foldoutWidth; + using (new DisabledScope(true)) + { + property.isExpanded = GUI.Toggle(position, property.isExpanded, Style.foldoutContent, Style.foldoutStyle); + } + + position.xMin = prevXMin; + } + + if (!hasDetails || !property.isExpanded) { return; } + EditorGUI.indentLevel++; var sceneData = SceneData.GetSceneDataFromIndex(property); var spacing = EditorGUIUtility.standardVerticalSpacing; position.y += EditorGUIUtility.singleLineHeight + spacing; @@ -64,20 +75,21 @@ protected override void OnGUISafe(Rect position, SerializedProperty property, GU EditorGUI.LabelField(position, Style.notInBuildContent); position.y += EditorGUIUtility.singleLineHeight + spacing; EditorGUI.EndDisabledGroup(); - if (GUI.Button(position, Style.showDetailsContent)) + var buttonRect = EditorGUI.IndentedRect(position); + if (GUI.Button(buttonRect, Style.showDetailsContent)) { OpenBuildSettings(); } } - } + EditorGUI.indentLevel--; + } public override bool IsPropertyValid(SerializedProperty property) { return property.type == nameof(SerializedScene); } - private struct SceneData { public int index; @@ -134,10 +146,28 @@ public static SceneData GetSceneDataFromScene(SerializedProperty property) private static class Style { + internal const float foldoutWidth = 50.0f; + + internal static readonly GUIContent foldoutContent = new GUIContent("Details", "Show/Hide Scene Details"); internal static readonly GUIContent buildIndexContent = new GUIContent("Build Index"); internal static readonly GUIContent isEnabledContent = new GUIContent("Is Enabled"); internal static readonly GUIContent notInBuildContent = new GUIContent("Not in Build"); internal static readonly GUIContent showDetailsContent = new GUIContent("Open Build Settings"); + + internal static readonly GUIStyle foldoutStyle; + + static Style() + { + foldoutStyle = new GUIStyle(EditorStyles.miniButton) + { +#if UNITY_2019_3_OR_NEWER + fontSize = 10, +#else + fontSize = 9, +#endif + alignment = TextAnchor.MiddleCenter + }; + } } } } \ No newline at end of file From b432dcdbd61f38ee8aaa19a4881b61c1b5384442 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=82osz=20Matkowski?= Date: Tue, 22 Apr 2025 17:50:53 +0200 Subject: [PATCH 12/46] More validation checks while creating new settings assets --- Assets/Editor Toolbox/Editor/Management/ToolboxManager.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Assets/Editor Toolbox/Editor/Management/ToolboxManager.cs b/Assets/Editor Toolbox/Editor/Management/ToolboxManager.cs index e66e1f14..7471f00e 100644 --- a/Assets/Editor Toolbox/Editor/Management/ToolboxManager.cs +++ b/Assets/Editor Toolbox/Editor/Management/ToolboxManager.cs @@ -213,7 +213,12 @@ void ReintializeProvider() if (GUILayout.Button("Create a new settings file")) { - var settingsInstance = ScriptableObject.CreateInstance(settingsType); + var settingsInstance = ScriptableObject.CreateInstance(); + if (settingsInstance == null) + { + ToolboxEditorLog.LogError($"Something went wrong. Cannot create a new Settings file."); + return; + } var locationPath = EditorUtility.OpenFolderPanel("New Settings file location", "Assets", ""); //validate returned path and create relative one if possible From 3926a61efd23d631e55f2ec029a88e301b5330a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=82osz=20Matkowski?= Date: Tue, 22 Apr 2025 17:56:02 +0200 Subject: [PATCH 13/46] Update: README.md, package.json, CHANGELOG.md --- Assets/Editor Toolbox/CHANGELOG.md | 7 +++++++ Assets/Editor Toolbox/README.md | 2 +- Assets/Editor Toolbox/package.json | 2 +- README.md | 2 +- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Assets/Editor Toolbox/CHANGELOG.md b/Assets/Editor Toolbox/CHANGELOG.md index 07b14ecc..5be8a09a 100644 --- a/Assets/Editor Toolbox/CHANGELOG.md +++ b/Assets/Editor Toolbox/CHANGELOG.md @@ -1,3 +1,10 @@ +## 0.14.1 [22.04.2025] + +### Changed: +- Ability to define [EditorButton] position (below or above the target property) +- Improvements to the SerializedScene class & linked drawer +- Fix rare issue with nested layouts + ## 0.14.0 [23.02.2025] ### Added: diff --git a/Assets/Editor Toolbox/README.md b/Assets/Editor Toolbox/README.md index 2f92290f..bb41036d 100644 --- a/Assets/Editor Toolbox/README.md +++ b/Assets/Editor Toolbox/README.md @@ -417,7 +417,7 @@ public int var1; ![inspector](https://github.com/arimger/Unity-Editor-Toolbox/blob/develop/Docs/horizontal.png) ```csharp -[EditorButton(nameof(MyMethod), "My Custom Label", activityType: ButtonActivityType.OnPlayMode, ValidateMethodName = nameof(ValidationMethod))] +[EditorButton(nameof(MyMethod), "My Custom Label", activityType: ButtonActivityType.OnPlayMode, ValidateMethodName = nameof(ValidationMethod), PositionType = ButtonPositionType.Above)] public int var1; private void MyMethod() diff --git a/Assets/Editor Toolbox/package.json b/Assets/Editor Toolbox/package.json index 2d7ea422..2b692407 100644 --- a/Assets/Editor Toolbox/package.json +++ b/Assets/Editor Toolbox/package.json @@ -1,7 +1,7 @@ { "name": "com.browar.editor-toolbox", "displayName": "Editor Toolbox", - "version": "0.14.0", + "version": "0.14.1", "unity": "2018.1", "description": "Tools, custom attributes, drawers, hierarchy overlay, and other extensions for the Unity Editor.", "keywords": [ diff --git a/README.md b/README.md index 2f92290f..bb41036d 100644 --- a/README.md +++ b/README.md @@ -417,7 +417,7 @@ public int var1; ![inspector](https://github.com/arimger/Unity-Editor-Toolbox/blob/develop/Docs/horizontal.png) ```csharp -[EditorButton(nameof(MyMethod), "My Custom Label", activityType: ButtonActivityType.OnPlayMode, ValidateMethodName = nameof(ValidationMethod))] +[EditorButton(nameof(MyMethod), "My Custom Label", activityType: ButtonActivityType.OnPlayMode, ValidateMethodName = nameof(ValidationMethod), PositionType = ButtonPositionType.Above)] public int var1; private void MyMethod() From 688f4217835fad47b97f881092cd588267c259d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=82osz=20Matkowski?= Date: Tue, 22 Apr 2025 22:09:38 +0200 Subject: [PATCH 14/46] Update docs --- Docs/scenedetails.png | Bin 3895 -> 4586 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Docs/scenedetails.png b/Docs/scenedetails.png index 9e6cec7d679d47c6058d0c2fd743d0c22fb9c2d9..17ed73087f1ab21123b13f8974771c456512c03b 100644 GIT binary patch delta 4557 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 SbpxJlsPwdrv}!e+qW=rxe%i+X delta 3860 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 poRBbvruNAg$p347 Date: Sat, 31 May 2025 01:09:13 +0200 Subject: [PATCH 15/46] Fix drawing FormattedNumber for multiple different values; minor refactor changes --- .../Drawers/Regular/FormattedNumberAttributeDrawer.cs | 8 +++++--- Assets/Editor Toolbox/Editor/Utilities/PropertyUtility.cs | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Assets/Editor Toolbox/Editor/Drawers/Regular/FormattedNumberAttributeDrawer.cs b/Assets/Editor Toolbox/Editor/Drawers/Regular/FormattedNumberAttributeDrawer.cs index 64bbecae..66e34d4d 100644 --- a/Assets/Editor Toolbox/Editor/Drawers/Regular/FormattedNumberAttributeDrawer.cs +++ b/Assets/Editor Toolbox/Editor/Drawers/Regular/FormattedNumberAttributeDrawer.cs @@ -18,7 +18,6 @@ public class FormattedNumberAttributeDrawer : PropertyDrawerBase CurrencyDecimalSeparator = "." }; - private void ApplyControlName(string propertyKey) { GUI.SetNextControlName(propertyKey); @@ -42,7 +41,6 @@ private string GetFormat(SerializedProperty property, FormattedNumberAttribute a return string.Format("{0}{1}", attribute.Format, isInt ? 0 : attribute.DecimalsToShow); } - protected override void OnGUISafe(Rect position, SerializedProperty property, GUIContent label) { var key = property.GetPropertyHashKey(); @@ -62,6 +60,11 @@ protected override void OnGUISafe(Rect position, SerializedProperty property, GU #endif } + if (property.hasMultipleDifferentValues) + { + return; + } + var targetAttribute = attribute as FormattedNumberAttribute; var single = GetSingle(property); var format = GetFormat(property, targetAttribute); @@ -79,7 +82,6 @@ protected override void OnGUISafe(Rect position, SerializedProperty property, GU } } - public override bool IsPropertyValid(SerializedProperty property) { return property.propertyType == SerializedPropertyType.Integer || diff --git a/Assets/Editor Toolbox/Editor/Utilities/PropertyUtility.cs b/Assets/Editor Toolbox/Editor/Utilities/PropertyUtility.cs index e3b85044..eb44aeec 100644 --- a/Assets/Editor Toolbox/Editor/Utilities/PropertyUtility.cs +++ b/Assets/Editor Toolbox/Editor/Utilities/PropertyUtility.cs @@ -421,7 +421,7 @@ public static void OverrideLabelByValue(GUIContent label, SerializedProperty pro label.text = property.colorValue.ToString(); break; case SerializedPropertyType.ObjectReference: - label.text = property.objectReferenceValue ? property.objectReferenceValue.name : "null"; + label.text = property.objectReferenceValue ? property.objectReferenceValue.ToString() : "null"; break; case SerializedPropertyType.LayerMask: switch (property.intValue) From b43ce820defe5f041ac3a4f74f81d20e836ddbd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=82osz=20Matkowski?= Date: Thu, 3 Jul 2025 23:06:19 +0200 Subject: [PATCH 16/46] Add CustomSelectableEditor in the examples --- Assets/Examples/Editor/UI.meta | 8 +++ .../Editor/UI/CustomSelectableEditor.cs | 53 +++++++++++++++++++ .../Editor/UI/CustomSelectableEditor.cs.meta | 11 ++++ Assets/Examples/Scripts/UI.meta | 8 +++ .../Examples/Scripts/UI/CustomSelectable.cs | 11 ++++ .../Scripts/UI/CustomSelectable.cs.meta | 11 ++++ 6 files changed, 102 insertions(+) create mode 100644 Assets/Examples/Editor/UI.meta create mode 100644 Assets/Examples/Editor/UI/CustomSelectableEditor.cs create mode 100644 Assets/Examples/Editor/UI/CustomSelectableEditor.cs.meta create mode 100644 Assets/Examples/Scripts/UI.meta create mode 100644 Assets/Examples/Scripts/UI/CustomSelectable.cs create mode 100644 Assets/Examples/Scripts/UI/CustomSelectable.cs.meta diff --git a/Assets/Examples/Editor/UI.meta b/Assets/Examples/Editor/UI.meta new file mode 100644 index 00000000..ed8e199e --- /dev/null +++ b/Assets/Examples/Editor/UI.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 885e23d794b7f814da83e77483560126 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Examples/Editor/UI/CustomSelectableEditor.cs b/Assets/Examples/Editor/UI/CustomSelectableEditor.cs new file mode 100644 index 00000000..b3ec85d6 --- /dev/null +++ b/Assets/Examples/Editor/UI/CustomSelectableEditor.cs @@ -0,0 +1,53 @@ +using Toolbox.Editor; +using Toolbox.Editor.Drawers; +using UnityEditor; +using UnityEditor.UI; + +[CustomEditor(typeof(CustomSelectable))] +public class CustomSelectableEditor : SelectableEditor, IToolboxEditor +{ + private static readonly string[] selectableProperties = new string[] + { + "m_Script", + "m_Interactable", + "m_TargetGraphic", + "m_Transition", + "m_Colors", + "m_SpriteState", + "m_AnimationTriggers", + "m_Navigation" + }; + + protected override void OnEnable() + { + base.OnEnable(); + foreach (var property in selectableProperties) + { + IgnoreProperty(property); + } + } + + public sealed override void OnInspectorGUI() + { + base.OnInspectorGUI(); + ToolboxEditorHandler.HandleToolboxEditor(this); + } + + public void DrawCustomInspector() + { + Drawer.DrawEditor(serializedObject); + } + + public void IgnoreProperty(SerializedProperty property) + { + Drawer.IgnoreProperty(property); + } + + public void IgnoreProperty(string propertyPath) + { + Drawer.IgnoreProperty(propertyPath); + } + + Editor IToolboxEditor.ContextEditor => this; + public IToolboxEditorDrawer Drawer { get; } = new ToolboxEditorDrawer(); +} \ No newline at end of file diff --git a/Assets/Examples/Editor/UI/CustomSelectableEditor.cs.meta b/Assets/Examples/Editor/UI/CustomSelectableEditor.cs.meta new file mode 100644 index 00000000..5da85772 --- /dev/null +++ b/Assets/Examples/Editor/UI/CustomSelectableEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: defe90321736145448a9124a4d6afb19 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Examples/Scripts/UI.meta b/Assets/Examples/Scripts/UI.meta new file mode 100644 index 00000000..66376f2f --- /dev/null +++ b/Assets/Examples/Scripts/UI.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4886eeed85fe8be4790ea10fde578a2d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Examples/Scripts/UI/CustomSelectable.cs b/Assets/Examples/Scripts/UI/CustomSelectable.cs new file mode 100644 index 00000000..1c6dd0d1 --- /dev/null +++ b/Assets/Examples/Scripts/UI/CustomSelectable.cs @@ -0,0 +1,11 @@ +using UnityEngine; +using UnityEngine.UI; + +public class CustomSelectable : Selectable +{ + private interface ICustomLogic + { } + + [SerializeReference] + private ICustomLogic customLogic; +} \ No newline at end of file diff --git a/Assets/Examples/Scripts/UI/CustomSelectable.cs.meta b/Assets/Examples/Scripts/UI/CustomSelectable.cs.meta new file mode 100644 index 00000000..604e73dd --- /dev/null +++ b/Assets/Examples/Scripts/UI/CustomSelectable.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8b8cd6b8c1b344f4382ef956b38c1c4e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From 7cf6a7a0ae5004fc070d791852ef8c57fdd1ac34 Mon Sep 17 00:00:00 2001 From: PJ Legendre Date: Mon, 7 Jul 2025 13:22:34 -0400 Subject: [PATCH 17/46] Updated SerializedDictionary to implement IReadOnlyDictionary. --- .../Runtime/Serialization/SerializedDictionary.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Assets/Editor Toolbox/Runtime/Serialization/SerializedDictionary.cs b/Assets/Editor Toolbox/Runtime/Serialization/SerializedDictionary.cs index 0cb63422..28cd41db 100644 --- a/Assets/Editor Toolbox/Runtime/Serialization/SerializedDictionary.cs +++ b/Assets/Editor Toolbox/Runtime/Serialization/SerializedDictionary.cs @@ -10,7 +10,7 @@ namespace UnityEngine /// Highly suggested to cast it to a when used in runtime. /// [Serializable] - public sealed class SerializedDictionary : IDictionary, ISerializationCallbackReceiver + public sealed class SerializedDictionary : IDictionary, IReadOnlyDictionary, ISerializationCallbackReceiver { [Serializable] private struct KeyValuePair @@ -224,6 +224,10 @@ public TV this[TK key] public bool IsReadOnly => false; + IEnumerable IReadOnlyDictionary.Keys => Keys; + + IEnumerable IReadOnlyDictionary.Values => Values; + public static implicit operator Dictionary(SerializedDictionary serializedDictionary) { return serializedDictionary.dictionary; @@ -235,4 +239,4 @@ public static implicit operator SerializedDictionary(Dictionary } } } -#endif \ No newline at end of file +#endif From be4f1041323be60744921a1a4a99da6cd57528f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=82osz=20Matkowski?= Date: Wed, 6 Aug 2025 17:28:46 +0200 Subject: [PATCH 18/46] Include global indentation while drawing ReorderableLists --- .../Editor/Internal/ToolboxEditorList.cs | 18 +++++++++++++++++- .../Editor/Utilities/EditorGuiUtility.cs | 2 +- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/Assets/Editor Toolbox/Editor/Internal/ToolboxEditorList.cs b/Assets/Editor Toolbox/Editor/Internal/ToolboxEditorList.cs index a8fc07fc..83836097 100644 --- a/Assets/Editor Toolbox/Editor/Internal/ToolboxEditorList.cs +++ b/Assets/Editor Toolbox/Editor/Internal/ToolboxEditorList.cs @@ -12,6 +12,14 @@ namespace Toolbox.Editor.Internal /// public class ToolboxEditorList : ReorderableListBase { + /// + /// Style used for the vertical layout built around all list controls. + /// + private static readonly GUIStyle listGroupStyle = new GUIStyle() + { + margin = new RectOffset() + }; + private int lastCoveredIndex = -1; private Rect[] elementsRects; @@ -207,6 +215,13 @@ private void DrawTargetGap(int targetIndex, int draggingIndex) EditorGUI.DrawRect(rect, Style.selectionColor); } + private GUIStyle RequestBodyStyle() + { + var indent = EditorGuiUtility.IndentSize; + listGroupStyle.margin.left = (int)indent; + return listGroupStyle; + } + protected override void DoListMiddle() { using (var middleGroup = new EditorGUILayout.VerticalScope()) @@ -357,9 +372,10 @@ protected override void HandleHeaderEvents(Rect rect) /// public override void DoList(GUIContent label) { + var style = RequestBodyStyle(); //pack eveything in one, vertical scope //it will keep sections always in order - using (new EditorGUILayout.VerticalScope()) + using (new EditorGUILayout.VerticalScope(style)) { base.DoList(label); } diff --git a/Assets/Editor Toolbox/Editor/Utilities/EditorGuiUtility.cs b/Assets/Editor Toolbox/Editor/Utilities/EditorGuiUtility.cs index bf0e8e33..12cc3786 100644 --- a/Assets/Editor Toolbox/Editor/Utilities/EditorGuiUtility.cs +++ b/Assets/Editor Toolbox/Editor/Utilities/EditorGuiUtility.cs @@ -12,7 +12,7 @@ namespace Toolbox.Editor /// internal static class EditorGuiUtility { - private const float indentPerLevel = 15.0f; + internal const float indentPerLevel = 15.0f; private static readonly Dictionary loadedTextures = new Dictionary(); From 89f30546644b9d4a6a3c3c632a1e18c37eafd79c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=82osz=20Matkowski?= Date: Wed, 6 Aug 2025 23:05:35 +0200 Subject: [PATCH 19/46] Fix Append/Remove List operations if multi-selection is active --- .../ReferencePickerAttributeDrawer.cs | 5 +++++ .../Editor/Internal/ReorderableListBase.cs | 19 ++++++++++++++++--- .../Sample Scriptable Object.asset | 8 ++++++-- Assets/Examples/Scripts/SampleBehaviour3.cs | 4 +++- .../Scripts/SampleScriptableObject.cs | 18 +++++++++++++++--- 5 files changed, 45 insertions(+), 9 deletions(-) diff --git a/Assets/Editor Toolbox/Editor/Drawers/Toolbox/PropertySelf/ReferencePickerAttributeDrawer.cs b/Assets/Editor Toolbox/Editor/Drawers/Toolbox/PropertySelf/ReferencePickerAttributeDrawer.cs index ab71df12..c152d051 100644 --- a/Assets/Editor Toolbox/Editor/Drawers/Toolbox/PropertySelf/ReferencePickerAttributeDrawer.cs +++ b/Assets/Editor Toolbox/Editor/Drawers/Toolbox/PropertySelf/ReferencePickerAttributeDrawer.cs @@ -56,6 +56,11 @@ private static Type GetCurrentManagedReferenceType(SerializedProperty property, using (var tempSerializedObject = new SerializedObject(target)) { var tempProperty = tempSerializedObject.FindProperty(property.propertyPath); + if (tempProperty == null) + { + break; + } + if (tempProperty.managedReferenceFullTypename != fullTypeName) { hasMixedValues = true; diff --git a/Assets/Editor Toolbox/Editor/Internal/ReorderableListBase.cs b/Assets/Editor Toolbox/Editor/Internal/ReorderableListBase.cs index 2c8f829b..56bf8d08 100644 --- a/Assets/Editor Toolbox/Editor/Internal/ReorderableListBase.cs +++ b/Assets/Editor Toolbox/Editor/Internal/ReorderableListBase.cs @@ -457,7 +457,10 @@ public void CutKeyboardFocus() public void AppendElement() { - Index = (List.arraySize += 1) - 1; + var newSize = Size.intValue + 1; + Size.intValue = newSize; + Index = newSize - 1; + if (overrideNewElementCallback == null) { return; @@ -522,6 +525,7 @@ public virtual void DrawStandardFooter(Rect rect) //set button area rect rect = new Rect(rect.xMax - Style.footerWidth, rect.y, Style.footerWidth, rect.height); + var actionPerformed = false; //set rect properties from style var buttonWidth = Style.footerButtonWidth; var buttonHeight = Style.footerButtonHeight; @@ -531,7 +535,7 @@ public virtual void DrawStandardFooter(Rect rect) //set proper rect for each button var appendButtonRect = new Rect(rect.xMin + margin, rect.y - padding, buttonWidth, buttonHeight); - EditorGUI.BeginDisabledGroup(List.hasMultipleDifferentValues); + EditorGUI.BeginDisabledGroup(Size.hasMultipleDifferentValues); EditorGUI.BeginDisabledGroup(onCanAppendCallback != null && !onCanAppendCallback(this)); if (GUI.Button(appendButtonRect, onAppendDropdownCallback != null ? Style.iconToolbarDropContent @@ -551,9 +555,10 @@ public virtual void DrawStandardFooter(Rect rect) } onChangedCallback?.Invoke(this); + actionPerformed = true; } - EditorGUI.EndDisabledGroup(); + EditorGUI.EndDisabledGroup(); var removeButtonRect = new Rect(rect.xMax - buttonWidth - margin, rect.y - padding, buttonWidth, buttonHeight); EditorGUI.BeginDisabledGroup((onCanRemoveCallback != null && !onCanRemoveCallback(this) || Index < 0 || Index >= Count)); @@ -569,9 +574,17 @@ public virtual void DrawStandardFooter(Rect rect) } onChangedCallback?.Invoke(this); + actionPerformed = true; } + EditorGUI.EndDisabledGroup(); EditorGUI.EndDisabledGroup(); + + if (actionPerformed) + { + SerializedObject.ApplyModifiedProperties(); + GUIUtility.ExitGUI(); + } } /// diff --git a/Assets/Examples/Scriptables/Sample Scriptable Object.asset b/Assets/Examples/Scriptables/Sample Scriptable Object.asset index 96cdd21a..1f6d8c05 100644 --- a/Assets/Examples/Scriptables/Sample Scriptable Object.asset +++ b/Assets/Examples/Scriptables/Sample Scriptable Object.asset @@ -14,5 +14,9 @@ MonoBehaviour: m_EditorClassIdentifier: var1: 1 var2: 32 - vars: - - abc + vars1: [] + vars2: 00000000 + var3: + var1: 0 + vars: 0000000000000000 + var2: 0 diff --git a/Assets/Examples/Scripts/SampleBehaviour3.cs b/Assets/Examples/Scripts/SampleBehaviour3.cs index df1b8f65..e35df632 100644 --- a/Assets/Examples/Scripts/SampleBehaviour3.cs +++ b/Assets/Examples/Scripts/SampleBehaviour3.cs @@ -1,4 +1,6 @@ -using UnityEngine; +using Toolbox.Editor.Drawers; +using UnityEditor; +using UnityEngine; [ExecuteAlways] [AddComponentMenu("Editor Toolbox/Cheat Sheet 3 (Toolbox Conditions)")] diff --git a/Assets/Examples/Scripts/SampleScriptableObject.cs b/Assets/Examples/Scripts/SampleScriptableObject.cs index 218ff2ca..59bdb560 100644 --- a/Assets/Examples/Scripts/SampleScriptableObject.cs +++ b/Assets/Examples/Scripts/SampleScriptableObject.cs @@ -1,12 +1,24 @@ -using UnityEngine; - +using System; using Toolbox.Attributes; +using UnityEngine; [CreateInWizard] public class SampleScriptableObject : ScriptableObject { + [Serializable] + public class NestedClass + { + public bool var1; + [ReorderableList] + public int[] vars; + public bool var2; + } + public bool var1; [EnableIf(nameof(var1), true)] public int var2; - public string[] vars; + public string[] vars1; + [ReorderableList] + public int[] vars2; + public NestedClass var3; } \ No newline at end of file From 1631b60547f67bd9eb8699c13d629e731de5d209 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=82osz=20Matkowski?= Date: Sat, 9 Aug 2025 00:52:21 +0200 Subject: [PATCH 20/46] Ability to copy & paste SerializeReference-based arrays --- .../Management/ToolboxContextMenuManager.cs | 2 + .../CopySerializeReferenceCache.cs | 15 +- .../CopySerializeReferenceEntry.cs | 16 + .../CopySerializeReferenceEntry.cs.meta | 11 + .../CopySerializeReferenceOperation.cs | 66 +- ...SerializeReferenceArrayElementOperation.cs | 12 +- .../PasteSerializeReferenceOperation.cs | 109 +++- .../Editor/Utilities/PropertyUtility.cs | 58 +- Assets/Examples/Scenes/SampleScene.unity | 578 ++++++++++++++++++ 9 files changed, 806 insertions(+), 61 deletions(-) create mode 100644 Assets/Editor Toolbox/Editor/ContextMenu/Operations/SerializeReference/CopySerializeReferenceEntry.cs create mode 100644 Assets/Editor Toolbox/Editor/ContextMenu/Operations/SerializeReference/CopySerializeReferenceEntry.cs.meta diff --git a/Assets/Editor Toolbox/Editor/ContextMenu/Management/ToolboxContextMenuManager.cs b/Assets/Editor Toolbox/Editor/ContextMenu/Management/ToolboxContextMenuManager.cs index 69004448..733500f8 100644 --- a/Assets/Editor Toolbox/Editor/ContextMenu/Management/ToolboxContextMenuManager.cs +++ b/Assets/Editor Toolbox/Editor/ContextMenu/Management/ToolboxContextMenuManager.cs @@ -15,9 +15,11 @@ static ToolboxContextMenuManager() { registeredOperations = new List() { +#if UNITY_2021_3_OR_NEWER new CopySerializeReferenceOperation(), new PasteSerializeReferenceOperation(), new DuplicateSerializeReferenceArrayElementOperation() +#endif }; EditorApplication.contextualPropertyMenu -= OnContextMenuOpening; diff --git a/Assets/Editor Toolbox/Editor/ContextMenu/Operations/SerializeReference/CopySerializeReferenceCache.cs b/Assets/Editor Toolbox/Editor/ContextMenu/Operations/SerializeReference/CopySerializeReferenceCache.cs index b88da35e..f8e4ae71 100644 --- a/Assets/Editor Toolbox/Editor/ContextMenu/Operations/SerializeReference/CopySerializeReferenceCache.cs +++ b/Assets/Editor Toolbox/Editor/ContextMenu/Operations/SerializeReference/CopySerializeReferenceCache.cs @@ -1,16 +1,25 @@ using System; +using System.Collections.Generic; namespace Toolbox.Editor.ContextMenu.Operations { + /// + /// Cache created by the copy operation. + /// All properties are based on data from the source . + /// internal class CopySerializeReferenceCache { - public CopySerializeReferenceCache(Type referenceType, string data) + public CopySerializeReferenceCache(Type referenceType, IReadOnlyList entires) { ReferenceType = referenceType; - Data = data; + Entries = entires; } + /// + /// Base managed reference type of the source . + /// public Type ReferenceType { get; } - public string Data { get; } + public IReadOnlyList Entries { get; } + public bool IsArrayCopy => Entries != null && Entries.Count > 1; } } \ No newline at end of file diff --git a/Assets/Editor Toolbox/Editor/ContextMenu/Operations/SerializeReference/CopySerializeReferenceEntry.cs b/Assets/Editor Toolbox/Editor/ContextMenu/Operations/SerializeReference/CopySerializeReferenceEntry.cs new file mode 100644 index 00000000..0eb7097c --- /dev/null +++ b/Assets/Editor Toolbox/Editor/ContextMenu/Operations/SerializeReference/CopySerializeReferenceEntry.cs @@ -0,0 +1,16 @@ +using System; + +namespace Toolbox.Editor.ContextMenu.Operations +{ + internal class CopySerializeReferenceEntry + { + public CopySerializeReferenceEntry(Type referenceType, string referenceData) + { + ReferenceType = referenceType; + ReferenceData = referenceData; + } + + public Type ReferenceType { get; } + public string ReferenceData { get; } + } +} \ No newline at end of file diff --git a/Assets/Editor Toolbox/Editor/ContextMenu/Operations/SerializeReference/CopySerializeReferenceEntry.cs.meta b/Assets/Editor Toolbox/Editor/ContextMenu/Operations/SerializeReference/CopySerializeReferenceEntry.cs.meta new file mode 100644 index 00000000..931a758d --- /dev/null +++ b/Assets/Editor Toolbox/Editor/ContextMenu/Operations/SerializeReference/CopySerializeReferenceEntry.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b98e16e462cb4b941af6d616402c8e78 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Editor Toolbox/Editor/ContextMenu/Operations/SerializeReference/CopySerializeReferenceOperation.cs b/Assets/Editor Toolbox/Editor/ContextMenu/Operations/SerializeReference/CopySerializeReferenceOperation.cs index c25eb218..6d2293c8 100644 --- a/Assets/Editor Toolbox/Editor/ContextMenu/Operations/SerializeReference/CopySerializeReferenceOperation.cs +++ b/Assets/Editor Toolbox/Editor/ContextMenu/Operations/SerializeReference/CopySerializeReferenceOperation.cs @@ -1,4 +1,7 @@ -using UnityEditor; +#if UNITY_2021_3_OR_NEWER +using System.Collections.Generic; + +using UnityEditor; using UnityEngine; namespace Toolbox.Editor.ContextMenu.Operations @@ -9,40 +12,67 @@ internal class CopySerializeReferenceOperation : IContextMenuOperation [InitializeOnLoadMethod] private static void Initialize() + { + Reset(); + } + + private CopySerializeReferenceEntry CreateEntry(SerializedProperty property) + { + if (property == null) + { + return new CopySerializeReferenceEntry(null, null); + } + + var value = property.managedReferenceValue; + if (value == null) + { + return new CopySerializeReferenceEntry(null, null); + } + + var referenceType = value.GetType(); + var data = JsonUtility.ToJson(value); + return new CopySerializeReferenceEntry(referenceType, data); + } + + internal static void Reset() { Cache = null; } public bool IsVisible(SerializedProperty property) { -#if UNITY_2021_3_OR_NEWER - return property != null && property.propertyType == SerializedPropertyType.ManagedReference; -#else - return false; -#endif + return PropertyUtility.IsSerializeReferenceProperty(property); } public bool IsEnabled(SerializedProperty property) { - return true; + return property.isArray ? property.arraySize > 0 : true; } public void Perform(SerializedProperty property) { -#if UNITY_2021_3_OR_NEWER - var value = property.managedReferenceValue; - if (value != null) + var entries = new List(); + if (property.propertyType == SerializedPropertyType.ManagedReference) + { + var entry = CreateEntry(property); + entries.Add(entry); + } + else if (property.isArray) { - var referenceType = value.GetType(); - var data = JsonUtility.ToJson(value); - Cache = new CopySerializeReferenceCache(referenceType, data); - return; + var propertiesCount = property.arraySize; + for (var i = 0; i < propertiesCount; i++) + { + var childProperty = property.GetArrayElementAtIndex(i); + var entry = CreateEntry(childProperty); + entries.Add(entry); + } } - Cache = new CopySerializeReferenceCache(null, null); -#endif + PropertyUtility.TryGetSerializeReferenceType(property, out var referenceType); + Cache = new CopySerializeReferenceCache(referenceType, entries); } - public GUIContent Label => new GUIContent("Copy Serialize Reference"); + public GUIContent Label => new GUIContent("Copy Serialized References"); } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/Assets/Editor Toolbox/Editor/ContextMenu/Operations/SerializeReference/DuplicateSerializeReferenceArrayElementOperation.cs b/Assets/Editor Toolbox/Editor/ContextMenu/Operations/SerializeReference/DuplicateSerializeReferenceArrayElementOperation.cs index e8b70b29..06a2d992 100644 --- a/Assets/Editor Toolbox/Editor/ContextMenu/Operations/SerializeReference/DuplicateSerializeReferenceArrayElementOperation.cs +++ b/Assets/Editor Toolbox/Editor/ContextMenu/Operations/SerializeReference/DuplicateSerializeReferenceArrayElementOperation.cs @@ -1,4 +1,5 @@ -using UnityEditor; +#if UNITY_2021_3_OR_NEWER +using UnityEditor; using UnityEngine; namespace Toolbox.Editor.ContextMenu.Operations @@ -7,12 +8,8 @@ internal class DuplicateSerializeReferenceArrayElementOperation : IContextMenuOp { public bool IsVisible(SerializedProperty property) { -#if UNITY_2021_3_OR_NEWER return property != null && property.propertyType == SerializedPropertyType.ManagedReference && PropertyUtility.IsSerializableArrayElement(property); -#else - return false; -#endif } public bool IsEnabled(SerializedProperty property) @@ -22,7 +19,6 @@ public bool IsEnabled(SerializedProperty property) public void Perform(SerializedProperty property) { -#if UNITY_2021_3_OR_NEWER var sourceProperty = property.Copy(); sourceProperty.serializedObject.Update(); var sourceValue = sourceProperty.managedReferenceValue; @@ -40,9 +36,9 @@ public void Perform(SerializedProperty property) } sourceProperty.serializedObject.ApplyModifiedProperties(); -#endif } public GUIContent Label => new GUIContent("Duplicate Serialize Reference Array Element"); } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/Assets/Editor Toolbox/Editor/ContextMenu/Operations/SerializeReference/PasteSerializeReferenceOperation.cs b/Assets/Editor Toolbox/Editor/ContextMenu/Operations/SerializeReference/PasteSerializeReferenceOperation.cs index 09f0d342..c8376054 100644 --- a/Assets/Editor Toolbox/Editor/ContextMenu/Operations/SerializeReference/PasteSerializeReferenceOperation.cs +++ b/Assets/Editor Toolbox/Editor/ContextMenu/Operations/SerializeReference/PasteSerializeReferenceOperation.cs @@ -1,4 +1,6 @@ -using System; +#if UNITY_2021_3_OR_NEWER +using System; +using System.Collections.Generic; using UnityEditor; using UnityEngine; @@ -7,34 +9,67 @@ namespace Toolbox.Editor.ContextMenu.Operations { internal class PasteSerializeReferenceOperation : IContextMenuOperation { - private bool IsAssignmentValid(SerializedProperty property, object newValue) + private bool IsAssignmentValid(SerializedProperty targetProperty, IReadOnlyList entires) { -#if UNITY_2021_3_OR_NEWER - if (newValue == null) + if (!PropertyUtility.TryGetSerializeReferenceType(targetProperty, out var targetType)) + { + return false; + } + + for (var i = 0; i < entires.Count; i++) + { + var entry = entires[i]; + if (!IsAssignmentValid(targetType, entry)) + { + return false; + } + } + + return true; + } + + private bool IsAssignmentValid(Type targetType, CopySerializeReferenceEntry entry) + { + var entryType = entry.ReferenceType; + if (entryType == null) { return true; } - if (!TypeUtility.TryGetTypeFromManagedReferenceFullTypeName(property.managedReferenceFieldTypename, out var referenceType)) + return TypeUtility.IsTypeAssignableFrom(targetType, entry.ReferenceType); + } + + private bool IsOperationSupported(SerializedProperty targetProperty, CopySerializeReferenceCache cache) + { + if (cache == null) + { + return false; + } + + var entries = cache.Entries; + if (entries == null || entries.Count == 0) + { + return false; + } + + if (cache.IsArrayCopy && targetProperty.isArray) { return true; } - var newValueType = newValue.GetType(); - if (TypeUtility.IsTypeAssignableFrom(referenceType, newValueType)) + if (!cache.IsArrayCopy && !targetProperty.isArray) { return true; } -#endif + return false; } - private object GetCachedManagedReferenceValue() + private object GetManagedReferenceValue(CopySerializeReferenceEntry entry) { - var cachedData = CopySerializeReferenceOperation.Cache; - if (cachedData.ReferenceType != null) + if (entry.ReferenceType != null) { - var newValue = JsonUtility.FromJson(cachedData.Data, cachedData.ReferenceType); + var newValue = JsonUtility.FromJson(entry.ReferenceData, entry.ReferenceType); return newValue; } else @@ -43,42 +78,60 @@ private object GetCachedManagedReferenceValue() } } + private void PasteEntry(SerializedProperty targetProperty, CopySerializeReferenceEntry entry) + { + var newValue = GetManagedReferenceValue(entry); + targetProperty.managedReferenceValue = newValue; + } + public bool IsVisible(SerializedProperty property) { -#if UNITY_2021_3_OR_NEWER - return property != null && property.propertyType == SerializedPropertyType.ManagedReference; -#else - return false; -#endif + return PropertyUtility.IsSerializeReferenceProperty(property); } public bool IsEnabled(SerializedProperty property) { - return CopySerializeReferenceOperation.Cache != null; + return IsOperationSupported(property, CopySerializeReferenceOperation.Cache); } public void Perform(SerializedProperty property) { -#if UNITY_2019_3_OR_NEWER + var cache = CopySerializeReferenceOperation.Cache; + var entries = cache.Entries; + if (!IsAssignmentValid(property, entries)) + { + ToolboxEditorLog.LogWarning("Cannot perform paste operation, types are mismatched."); + return; + } + var targetProperty = property.Copy(); try { - var newValue = GetCachedManagedReferenceValue(); - if (!IsAssignmentValid(targetProperty, newValue)) + targetProperty.serializedObject.Update(); + if (targetProperty.isArray) + { + var arraySize = entries.Count; + targetProperty.arraySize = arraySize; + for (var i = 0; i < arraySize; i++) + { + var entry = entries[i]; + var childProperty = targetProperty.GetArrayElementAtIndex(i); + PasteEntry(childProperty, entry); + } + } + else { - ToolboxEditorLog.LogWarning("Cannot perform paste operation, types are mismatched."); - return; + var entry = entries[0]; + PasteEntry(targetProperty, entry); } - targetProperty.serializedObject.Update(); - targetProperty.managedReferenceValue = newValue; targetProperty.serializedObject.ApplyModifiedProperties(); } catch (Exception) { } -#endif } - public GUIContent Label => new GUIContent("Paste Serialize Reference"); + public GUIContent Label => new GUIContent("Paste Serialized References"); } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/Assets/Editor Toolbox/Editor/Utilities/PropertyUtility.cs b/Assets/Editor Toolbox/Editor/Utilities/PropertyUtility.cs index eb44aeec..8863f395 100644 --- a/Assets/Editor Toolbox/Editor/Utilities/PropertyUtility.cs +++ b/Assets/Editor Toolbox/Editor/Utilities/PropertyUtility.cs @@ -278,9 +278,7 @@ public static Type GetProperType(this SerializedProperty property, FieldInfo fie //handle situation when property is an array element if (IsSerializableArrayElement(property, fieldInfo)) { - return fieldType.IsGenericType - ? fieldType.GetGenericArguments()[0] - : fieldType.GetElementType(); + return GetElementTypeFromArrayType(fieldType); } //return fieldInfo type based on property's target object else @@ -306,6 +304,13 @@ public static Type GetScriptTypeFromProperty(SerializedProperty property) return scriptInstance.GetClass(); } + public static Type GetElementTypeFromArrayType(Type arrayType) + { + return arrayType.IsGenericType + ? arrayType.GetGenericArguments()[0] + : arrayType.GetElementType(); + } + public static FieldInfo GetFieldInfo(this SerializedProperty property) { return GetFieldInfo(property, out _); @@ -349,7 +354,7 @@ public static FieldInfo GetFieldInfoFromProperty(SerializedProperty property, ou { if (IsSerializableArrayType(type)) { - type = type.IsGenericType ? type.GetGenericArguments()[0] : type.GetElementType(); + type = GetElementTypeFromArrayType(type); } continue; @@ -637,6 +642,51 @@ internal static bool IsSerializableArrayElement(string indexField, out int index return false; } + internal static bool IsSerializeReferenceProperty(SerializedProperty property) + { + if (property == null) + { + return false; + } + + if (property.propertyType == SerializedPropertyType.ManagedReference) + { + return true; + } + + //NOTE: seems to bit slighlty better option than checking children (we need to support empty arrays) + // checking FieldInfo + Attribute will be much more expensive + const string managedReferenceType = "managedReference"; + if (property.isArray) + { + var elementType = property.arrayElementType; + return elementType.Contains(managedReferenceType); + } + + return false; + } + + internal static bool TryGetSerializeReferenceType(SerializedProperty property, out Type referenceType) + { + var fieldInfo = GetFieldInfo(property, out var propertyType); + if (fieldInfo == null) + { + referenceType = null; + return false; + } + + if (property.isArray) + { + referenceType = GetElementTypeFromArrayType(propertyType); + } + else + { + referenceType = fieldInfo.FieldType; + } + + return true; + } + internal static bool IsDefaultScriptProperty(SerializedProperty property) { return IsDefaultScriptPropertyByPath(property.propertyPath); diff --git a/Assets/Examples/Scenes/SampleScene.unity b/Assets/Examples/Scenes/SampleScene.unity index 78ad7593..9a5c05eb 100644 --- a/Assets/Examples/Scenes/SampleScene.unity +++ b/Assets/Examples/Scenes/SampleScene.unity @@ -318,6 +318,97 @@ Transform: m_Father: {fileID: 1414023087} m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &280163221 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 280163223} + - component: {fileID: 280163222} + m_Layer: 0 + m_Name: ToolboxPropertyDrawers [Sample] (2) + m_TagString: Untagged + m_Icon: {fileID: 2800000, guid: b105bf1fb7fe62e4baba0ffd7b433e7b, type: 3} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &280163222 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 280163221} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: cd6beebd3a14d014baec37e933957a0e, type: 3} + m_Name: + m_EditorClassIdentifier: + list: + - {fileID: 5059060190599569098, guid: e4263a04cf09ace4b8568f054ea426ee, type: 3} + - {fileID: 5059060190599569098, guid: 5573ca52cac7c2d4cb2536e37e9be1f1, type: 3} + - {fileID: 5059060190599569098, guid: 5573ca52cac7c2d4cb2536e37e9be1f1, type: 3} + - {fileID: 5059060190599569098, guid: 5573ca52cac7c2d4cb2536e37e9be1f1, type: 3} + strings: + - a + - b + - c + ints: 0200000006000000030000000c00000005000000 + component: {fileID: 2037155953} + material: {fileID: 2100000, guid: 7404c70251f9d0045a4aabaa49d83963, type: 2} + texture: {fileID: 2800000, guid: d7cf546aa64aceb488a523b6662319b6, type: 3} + audioClip: {fileID: 8300000, guid: e83fc6426e5f2e0448cc3e51102509aa, type: 3} + mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} + largeArray: + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + quaternion: {x: 0, y: 0, z: 0, w: 0} + q2: {x: 0, y: 0, z: 0, w: 0} + min: -1 + max: 5.5 + dynamicRange: 1.5 + dynamicMinMax: {x: 0, y: 3} + nestedObject: + i: 1 + strings: + - EditorOnly + - Player + - Untagged +--- !u!4 &280163223 +Transform: + m_ObjectHideFlags: 2 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 280163221} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 28 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &315584341 GameObject: m_ObjectHideFlags: 0 @@ -634,6 +725,108 @@ Rigidbody: m_Interpolate: 0 m_Constraints: 0 m_CollisionDetection: 0 +--- !u!1 &594223389 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 594223391} + - component: {fileID: 594223390} + m_Layer: 0 + m_Name: SerializeReference [Sample] (4) + m_TagString: Untagged + m_Icon: {fileID: 2800000, guid: b105bf1fb7fe62e4baba0ffd7b433e7b, type: 3} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &594223390 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 594223389} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a0323e91bfeae1c488545bee770d3fa7, type: 3} + m_Name: + m_EditorClassIdentifier: + var1: + rid: 7033609514626056197 + var2: + rid: 7033609514626056196 + var3: + rid: 7033609514626056198 + var4: + rid: 7033609514626056199 + vars: + - rid: 2222411121801560064 + - rid: 2222411121801560065 + - rid: 2222411121801560066 + - rid: 2222411121801560067 + references: + version: 2 + RefIds: + - rid: 2222411121801560064 + type: {class: SampleBehaviour6/ClassWithInterface2, ns: , asm: Assembly-CSharp} + data: + var1: 0 + mat: {fileID: 0} + - rid: 2222411121801560065 + type: {class: SampleBehaviour6/ClassWithInterface2, ns: , asm: Assembly-CSharp} + data: + var1: 0 + mat: {fileID: 0} + - rid: 2222411121801560066 + type: {class: SampleBehaviour6/ClassWithInterface4, ns: , asm: Assembly-CSharp} + data: + var1: 0 + mat: {fileID: 0} + var33: 0 + - rid: 2222411121801560067 + type: {class: SampleBehaviour6/SampleStruct, ns: , asm: Assembly-CSharp} + data: + var1: 0 + var2: 0 + - rid: 7033609514626056196 + type: {class: SampleBehaviour6/ClassWithInterface4, ns: , asm: Assembly-CSharp} + data: + var1: 1 + mat: {fileID: 0} + var33: 11 + - rid: 7033609514626056197 + type: {class: SampleBehaviour6/SampleStruct, ns: , asm: Assembly-CSharp} + data: + var1: 1 + var2: 1 + - rid: 7033609514626056198 + type: {class: SampleBehaviour6/ClassWithInterface2, ns: , asm: Assembly-CSharp} + data: + var1: 0 + mat: {fileID: 0} + - rid: 7033609514626056199 + type: {class: SampleBehaviour6/ClassWithInterface2, ns: , asm: Assembly-CSharp} + data: + var1: 0 + mat: {fileID: 2100000, guid: 26a618e7877b8c94f967effc172108f2, type: 2} +--- !u!4 &594223391 +Transform: + m_ObjectHideFlags: 2 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 594223389} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 26 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &661896457 GameObject: m_ObjectHideFlags: 0 @@ -1145,6 +1338,97 @@ Transform: m_Father: {fileID: 666544667} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &773117200 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 773117202} + - component: {fileID: 773117201} + m_Layer: 0 + m_Name: ToolboxPropertyDrawers [Sample] (1) + m_TagString: Untagged + m_Icon: {fileID: 2800000, guid: b105bf1fb7fe62e4baba0ffd7b433e7b, type: 3} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &773117201 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 773117200} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: cd6beebd3a14d014baec37e933957a0e, type: 3} + m_Name: + m_EditorClassIdentifier: + list: + - {fileID: 5059060190599569098, guid: e4263a04cf09ace4b8568f054ea426ee, type: 3} + - {fileID: 5059060190599569098, guid: 5573ca52cac7c2d4cb2536e37e9be1f1, type: 3} + - {fileID: 5059060190599569098, guid: 5573ca52cac7c2d4cb2536e37e9be1f1, type: 3} + - {fileID: 5059060190599569098, guid: 5573ca52cac7c2d4cb2536e37e9be1f1, type: 3} + strings: + - a + - b + - c + ints: 0200000006000000030000000c00000005000000 + component: {fileID: 2037155953} + material: {fileID: 2100000, guid: 7404c70251f9d0045a4aabaa49d83963, type: 2} + texture: {fileID: 2800000, guid: d7cf546aa64aceb488a523b6662319b6, type: 3} + audioClip: {fileID: 8300000, guid: e83fc6426e5f2e0448cc3e51102509aa, type: 3} + mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} + largeArray: + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + quaternion: {x: 0, y: 0, z: 0, w: 0} + q2: {x: 0, y: 0, z: 0, w: 0} + min: -1 + max: 5.5 + dynamicRange: 1.5 + dynamicMinMax: {x: 0, y: 3} + nestedObject: + i: 1 + strings: + - EditorOnly + - Player + - Untagged +--- !u!4 &773117202 +Transform: + m_ObjectHideFlags: 2 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 773117200} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 27 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &890935882 GameObject: m_ObjectHideFlags: 0 @@ -2414,6 +2698,108 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1584091011} m_CullTransparentMesh: 1 +--- !u!1 &1646572068 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1646572070} + - component: {fileID: 1646572069} + m_Layer: 0 + m_Name: SerializeReference [Sample] (3) + m_TagString: Untagged + m_Icon: {fileID: 2800000, guid: b105bf1fb7fe62e4baba0ffd7b433e7b, type: 3} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1646572069 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1646572068} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a0323e91bfeae1c488545bee770d3fa7, type: 3} + m_Name: + m_EditorClassIdentifier: + var1: + rid: 7033609514626056197 + var2: + rid: 7033609514626056196 + var3: + rid: 7033609514626056198 + var4: + rid: 7033609514626056199 + vars: + - rid: 2222411121801560069 + - rid: 2222411121801560070 + - rid: 2222411121801560071 + - rid: 2222411121801560072 + references: + version: 2 + RefIds: + - rid: 2222411121801560069 + type: {class: SampleBehaviour6/ClassWithInterface2, ns: , asm: Assembly-CSharp} + data: + var1: 0 + mat: {fileID: 0} + - rid: 2222411121801560070 + type: {class: SampleBehaviour6/ClassWithInterface2, ns: , asm: Assembly-CSharp} + data: + var1: 0 + mat: {fileID: 0} + - rid: 2222411121801560071 + type: {class: SampleBehaviour6/ClassWithInterface4, ns: , asm: Assembly-CSharp} + data: + var1: 0 + mat: {fileID: 0} + var33: 0 + - rid: 2222411121801560072 + type: {class: SampleBehaviour6/SampleStruct, ns: , asm: Assembly-CSharp} + data: + var1: 0 + var2: 0 + - rid: 7033609514626056196 + type: {class: SampleBehaviour6/ClassWithInterface4, ns: , asm: Assembly-CSharp} + data: + var1: 1 + mat: {fileID: 0} + var33: 11 + - rid: 7033609514626056197 + type: {class: SampleBehaviour6/SampleStruct, ns: , asm: Assembly-CSharp} + data: + var1: 1 + var2: 1 + - rid: 7033609514626056198 + type: {class: SampleBehaviour6/ClassWithInterface2, ns: , asm: Assembly-CSharp} + data: + var1: 0 + mat: {fileID: 0} + - rid: 7033609514626056199 + type: {class: SampleBehaviour6/ClassWithInterface2, ns: , asm: Assembly-CSharp} + data: + var1: 0 + mat: {fileID: 2100000, guid: 26a618e7877b8c94f967effc172108f2, type: 2} +--- !u!4 &1646572070 +Transform: + m_ObjectHideFlags: 2 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1646572068} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 25 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1661307763 GameObject: m_ObjectHideFlags: 0 @@ -2503,6 +2889,96 @@ MonoBehaviour: var40: 2 var41: 0 var42: 0 +--- !u!1 &1695402926 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1695402928} + - component: {fileID: 1695402927} + m_Layer: 0 + m_Name: SerializeReference [Sample] (2) + m_TagString: Untagged + m_Icon: {fileID: 2800000, guid: b105bf1fb7fe62e4baba0ffd7b433e7b, type: 3} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1695402927 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1695402926} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a0323e91bfeae1c488545bee770d3fa7, type: 3} + m_Name: + m_EditorClassIdentifier: + var1: + rid: 2222411169542701056 + var2: + rid: 7033609514626056196 + var3: + rid: 7033609514626056198 + var4: + rid: 7033609514626056199 + vars: + - rid: -2 + - rid: -2 + - rid: -2 + - rid: 2222411169542701061 + - rid: -2 + references: + version: 2 + RefIds: + - rid: -2 + type: {class: , ns: , asm: } + - rid: 2222411169542701056 + type: {class: SampleBehaviour6/ClassWithInterface4, ns: , asm: Assembly-CSharp} + data: + var1: 1 + mat: {fileID: 0} + var33: 11 + - rid: 2222411169542701061 + type: {class: SampleBehaviour6/ClassWithInterface2, ns: , asm: Assembly-CSharp} + data: + var1: 0 + mat: {fileID: 0} + - rid: 7033609514626056196 + type: {class: SampleBehaviour6/ClassWithInterface4, ns: , asm: Assembly-CSharp} + data: + var1: 1 + mat: {fileID: 0} + var33: 11 + - rid: 7033609514626056198 + type: {class: SampleBehaviour6/ClassWithInterface2, ns: , asm: Assembly-CSharp} + data: + var1: 0 + mat: {fileID: 0} + - rid: 7033609514626056199 + type: {class: SampleBehaviour6/ClassWithInterface2, ns: , asm: Assembly-CSharp} + data: + var1: 0 + mat: {fileID: 2100000, guid: 26a618e7877b8c94f967effc172108f2, type: 2} +--- !u!4 &1695402928 +Transform: + m_ObjectHideFlags: 2 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1695402926} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 24 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1707505305 GameObject: m_ObjectHideFlags: 0 @@ -2581,6 +3057,108 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1707505305} m_CullTransparentMesh: 1 +--- !u!1 &1840852554 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1840852556} + - component: {fileID: 1840852555} + m_Layer: 0 + m_Name: SerializeReference [Sample] (1) + m_TagString: Untagged + m_Icon: {fileID: 2800000, guid: b105bf1fb7fe62e4baba0ffd7b433e7b, type: 3} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1840852555 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1840852554} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a0323e91bfeae1c488545bee770d3fa7, type: 3} + m_Name: + m_EditorClassIdentifier: + var1: + rid: 7033609514626056197 + var2: + rid: 7033609514626056196 + var3: + rid: 7033609514626056198 + var4: + rid: 7033609514626056199 + vars: + - rid: 2222411169542701063 + - rid: 2222411169542701064 + - rid: 2222411169542701065 + - rid: 2222411169542701066 + references: + version: 2 + RefIds: + - rid: 2222411169542701063 + type: {class: SampleBehaviour6/ClassWithInterface2, ns: , asm: Assembly-CSharp} + data: + var1: 0 + mat: {fileID: 0} + - rid: 2222411169542701064 + type: {class: SampleBehaviour6/ClassWithInterface2, ns: , asm: Assembly-CSharp} + data: + var1: 0 + mat: {fileID: 0} + - rid: 2222411169542701065 + type: {class: SampleBehaviour6/ClassWithInterface4, ns: , asm: Assembly-CSharp} + data: + var1: 0 + mat: {fileID: 0} + var33: 0 + - rid: 2222411169542701066 + type: {class: SampleBehaviour6/SampleStruct, ns: , asm: Assembly-CSharp} + data: + var1: 0 + var2: 0 + - rid: 7033609514626056196 + type: {class: SampleBehaviour6/ClassWithInterface4, ns: , asm: Assembly-CSharp} + data: + var1: 1 + mat: {fileID: 0} + var33: 11 + - rid: 7033609514626056197 + type: {class: SampleBehaviour6/SampleStruct, ns: , asm: Assembly-CSharp} + data: + var1: 1 + var2: 1 + - rid: 7033609514626056198 + type: {class: SampleBehaviour6/ClassWithInterface2, ns: , asm: Assembly-CSharp} + data: + var1: 0 + mat: {fileID: 0} + - rid: 7033609514626056199 + type: {class: SampleBehaviour6/ClassWithInterface2, ns: , asm: Assembly-CSharp} + data: + var1: 0 + mat: {fileID: 2100000, guid: 26a618e7877b8c94f967effc172108f2, type: 2} +--- !u!4 &1840852556 +Transform: + m_ObjectHideFlags: 2 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1840852554} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 23 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1972418676 GameObject: m_ObjectHideFlags: 0 From 6fb2c93372792c6c4557f203e74b1ccb928c28dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=82osz=20Matkowski?= Date: Sun, 10 Aug 2025 00:22:24 +0200 Subject: [PATCH 21/46] Add confirmation box support for the ReferencePickerAttribute --- .../ReferencePickerAttributeDrawer.cs | 33 +- .../ReferencePickerAttribute.cs | 5 + Assets/Examples/Scenes/SampleScene.unity | 607 +----------------- Assets/Examples/Scripts/SampleBehaviour6.cs | 2 +- 4 files changed, 46 insertions(+), 601 deletions(-) diff --git a/Assets/Editor Toolbox/Editor/Drawers/Toolbox/PropertySelf/ReferencePickerAttributeDrawer.cs b/Assets/Editor Toolbox/Editor/Drawers/Toolbox/PropertySelf/ReferencePickerAttributeDrawer.cs index c152d051..64bb2a20 100644 --- a/Assets/Editor Toolbox/Editor/Drawers/Toolbox/PropertySelf/ReferencePickerAttributeDrawer.cs +++ b/Assets/Editor Toolbox/Editor/Drawers/Toolbox/PropertySelf/ReferencePickerAttributeDrawer.cs @@ -75,26 +75,41 @@ private static Type GetCurrentManagedReferenceType(SerializedProperty property, private void CreateTypeProperty(SerializedProperty property, Type parentType, ReferencePickerAttribute attribute, Rect position) { + var searchFieldRequired = attribute.AddTextSearchField; + var confirmationRequired = attribute.AddConfirmationBox; + var currentType = GetCurrentManagedReferenceType(property, out var hasMixedValues); var hadMixedValues = EditorGUI.showMixedValue; EditorGUI.showMixedValue = hasMixedValues; - typeField.OnGui(position, attribute.AddTextSearchField, (type) => + typeField.OnGui(position, searchFieldRequired, (type) => { try { - if (!property.serializedObject.isEditingMultipleObjects) + var isOperationAllowed = true; + if (confirmationRequired) { - UpdateTypeProperty(property, type, attribute); + isOperationAllowed = EditorUtility.DisplayDialog("Confirm Assignment", + "Are you sure you want to assign a new reference? This action will replace the current one.", + "Assign", + "Cancel"); } - else + + if (isOperationAllowed) { - var targets = property.serializedObject.targetObjects; - foreach (var target in targets) + if (!property.serializedObject.isEditingMultipleObjects) + { + UpdateTypeProperty(property, type, attribute); + } + else { - using (var so = new SerializedObject(target)) + var targets = property.serializedObject.targetObjects; + foreach (var target in targets) { - var sp = so.FindProperty(property.propertyPath); - UpdateTypeProperty(sp, type, attribute); + using (var so = new SerializedObject(target)) + { + var sp = so.FindProperty(property.propertyPath); + UpdateTypeProperty(sp, type, attribute); + } } } } diff --git a/Assets/Editor Toolbox/Runtime/Attributes/Property/Toolbox/PropertySelfAttributes/ReferencePickerAttribute.cs b/Assets/Editor Toolbox/Runtime/Attributes/Property/Toolbox/PropertySelfAttributes/ReferencePickerAttribute.cs index 7aff647f..0a6d2c52 100644 --- a/Assets/Editor Toolbox/Runtime/Attributes/Property/Toolbox/PropertySelfAttributes/ReferencePickerAttribute.cs +++ b/Assets/Editor Toolbox/Runtime/Attributes/Property/Toolbox/PropertySelfAttributes/ReferencePickerAttribute.cs @@ -42,6 +42,11 @@ public ReferencePickerAttribute(Type parentType, TypeGrouping typeGrouping) /// Indicates if created popup menu should have an additional search field. /// public bool AddTextSearchField { get; set; } = true; + + /// + /// Indicates if confirmation box should appear after picking a new reference. + /// + public bool AddConfirmationBox { get; set; } } } #endif \ No newline at end of file diff --git a/Assets/Examples/Scenes/SampleScene.unity b/Assets/Examples/Scenes/SampleScene.unity index 9a5c05eb..b56b3b9a 100644 --- a/Assets/Examples/Scenes/SampleScene.unity +++ b/Assets/Examples/Scenes/SampleScene.unity @@ -318,97 +318,6 @@ Transform: m_Father: {fileID: 1414023087} m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!1 &280163221 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 280163223} - - component: {fileID: 280163222} - m_Layer: 0 - m_Name: ToolboxPropertyDrawers [Sample] (2) - m_TagString: Untagged - m_Icon: {fileID: 2800000, guid: b105bf1fb7fe62e4baba0ffd7b433e7b, type: 3} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!114 &280163222 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 280163221} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: cd6beebd3a14d014baec37e933957a0e, type: 3} - m_Name: - m_EditorClassIdentifier: - list: - - {fileID: 5059060190599569098, guid: e4263a04cf09ace4b8568f054ea426ee, type: 3} - - {fileID: 5059060190599569098, guid: 5573ca52cac7c2d4cb2536e37e9be1f1, type: 3} - - {fileID: 5059060190599569098, guid: 5573ca52cac7c2d4cb2536e37e9be1f1, type: 3} - - {fileID: 5059060190599569098, guid: 5573ca52cac7c2d4cb2536e37e9be1f1, type: 3} - strings: - - a - - b - - c - ints: 0200000006000000030000000c00000005000000 - component: {fileID: 2037155953} - material: {fileID: 2100000, guid: 7404c70251f9d0045a4aabaa49d83963, type: 2} - texture: {fileID: 2800000, guid: d7cf546aa64aceb488a523b6662319b6, type: 3} - audioClip: {fileID: 8300000, guid: e83fc6426e5f2e0448cc3e51102509aa, type: 3} - mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} - largeArray: - - {fileID: 0} - - {fileID: 0} - - {fileID: 0} - - {fileID: 0} - - {fileID: 0} - - {fileID: 0} - - {fileID: 0} - - {fileID: 0} - - {fileID: 0} - - {fileID: 0} - - {fileID: 0} - - {fileID: 0} - - {fileID: 0} - - {fileID: 0} - - {fileID: 0} - - {fileID: 0} - - {fileID: 0} - - {fileID: 0} - - {fileID: 0} - quaternion: {x: 0, y: 0, z: 0, w: 0} - q2: {x: 0, y: 0, z: 0, w: 0} - min: -1 - max: 5.5 - dynamicRange: 1.5 - dynamicMinMax: {x: 0, y: 3} - nestedObject: - i: 1 - strings: - - EditorOnly - - Player - - Untagged ---- !u!4 &280163223 -Transform: - m_ObjectHideFlags: 2 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 280163221} - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: 0} - m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 - m_Children: [] - m_Father: {fileID: 0} - m_RootOrder: 28 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &315584341 GameObject: m_ObjectHideFlags: 0 @@ -725,108 +634,6 @@ Rigidbody: m_Interpolate: 0 m_Constraints: 0 m_CollisionDetection: 0 ---- !u!1 &594223389 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 594223391} - - component: {fileID: 594223390} - m_Layer: 0 - m_Name: SerializeReference [Sample] (4) - m_TagString: Untagged - m_Icon: {fileID: 2800000, guid: b105bf1fb7fe62e4baba0ffd7b433e7b, type: 3} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!114 &594223390 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 594223389} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: a0323e91bfeae1c488545bee770d3fa7, type: 3} - m_Name: - m_EditorClassIdentifier: - var1: - rid: 7033609514626056197 - var2: - rid: 7033609514626056196 - var3: - rid: 7033609514626056198 - var4: - rid: 7033609514626056199 - vars: - - rid: 2222411121801560064 - - rid: 2222411121801560065 - - rid: 2222411121801560066 - - rid: 2222411121801560067 - references: - version: 2 - RefIds: - - rid: 2222411121801560064 - type: {class: SampleBehaviour6/ClassWithInterface2, ns: , asm: Assembly-CSharp} - data: - var1: 0 - mat: {fileID: 0} - - rid: 2222411121801560065 - type: {class: SampleBehaviour6/ClassWithInterface2, ns: , asm: Assembly-CSharp} - data: - var1: 0 - mat: {fileID: 0} - - rid: 2222411121801560066 - type: {class: SampleBehaviour6/ClassWithInterface4, ns: , asm: Assembly-CSharp} - data: - var1: 0 - mat: {fileID: 0} - var33: 0 - - rid: 2222411121801560067 - type: {class: SampleBehaviour6/SampleStruct, ns: , asm: Assembly-CSharp} - data: - var1: 0 - var2: 0 - - rid: 7033609514626056196 - type: {class: SampleBehaviour6/ClassWithInterface4, ns: , asm: Assembly-CSharp} - data: - var1: 1 - mat: {fileID: 0} - var33: 11 - - rid: 7033609514626056197 - type: {class: SampleBehaviour6/SampleStruct, ns: , asm: Assembly-CSharp} - data: - var1: 1 - var2: 1 - - rid: 7033609514626056198 - type: {class: SampleBehaviour6/ClassWithInterface2, ns: , asm: Assembly-CSharp} - data: - var1: 0 - mat: {fileID: 0} - - rid: 7033609514626056199 - type: {class: SampleBehaviour6/ClassWithInterface2, ns: , asm: Assembly-CSharp} - data: - var1: 0 - mat: {fileID: 2100000, guid: 26a618e7877b8c94f967effc172108f2, type: 2} ---- !u!4 &594223391 -Transform: - m_ObjectHideFlags: 2 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 594223389} - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: 0} - m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 - m_Children: [] - m_Father: {fileID: 0} - m_RootOrder: 26 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &661896457 GameObject: m_ObjectHideFlags: 0 @@ -1244,9 +1051,9 @@ MonoBehaviour: var1: rid: 7033609514626056197 var2: - rid: 7033609514626056196 + rid: 2222411192291557380 var3: - rid: 7033609514626056198 + rid: 2222411192291557381 var4: rid: 7033609514626056199 vars: @@ -1255,6 +1062,20 @@ MonoBehaviour: references: version: 2 RefIds: + - rid: -2 + type: {class: , ns: , asm: } + - rid: 2222411192291557380 + type: {class: SampleBehaviour6/ClassWithInterface1, ns: , asm: Assembly-CSharp} + data: + go: {fileID: 580334316} + var1: + rid: -2 + - rid: 2222411192291557381 + type: {class: SampleBehaviour6/ClassWithInterface4, ns: , asm: Assembly-CSharp} + data: + var1: 1 + mat: {fileID: 2100000, guid: 7404c70251f9d0045a4aabaa49d83963, type: 2} + var33: 0 - rid: 7033609514626056193 type: {class: SampleBehaviour6/ClassWithInterface1, ns: , asm: Assembly-CSharp} data: @@ -1266,22 +1087,11 @@ MonoBehaviour: data: var1: 0 var2: 0 - - rid: 7033609514626056196 - type: {class: SampleBehaviour6/ClassWithInterface4, ns: , asm: Assembly-CSharp} - data: - var1: 1 - mat: {fileID: 0} - var33: 11 - rid: 7033609514626056197 type: {class: SampleBehaviour6/SampleStruct, ns: , asm: Assembly-CSharp} data: var1: 1 var2: 1 - - rid: 7033609514626056198 - type: {class: SampleBehaviour6/ClassWithInterface2, ns: , asm: Assembly-CSharp} - data: - var1: 0 - mat: {fileID: 0} - rid: 7033609514626056199 type: {class: SampleBehaviour6/ClassWithInterface2, ns: , asm: Assembly-CSharp} data: @@ -1338,97 +1148,6 @@ Transform: m_Father: {fileID: 666544667} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!1 &773117200 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 773117202} - - component: {fileID: 773117201} - m_Layer: 0 - m_Name: ToolboxPropertyDrawers [Sample] (1) - m_TagString: Untagged - m_Icon: {fileID: 2800000, guid: b105bf1fb7fe62e4baba0ffd7b433e7b, type: 3} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!114 &773117201 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 773117200} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: cd6beebd3a14d014baec37e933957a0e, type: 3} - m_Name: - m_EditorClassIdentifier: - list: - - {fileID: 5059060190599569098, guid: e4263a04cf09ace4b8568f054ea426ee, type: 3} - - {fileID: 5059060190599569098, guid: 5573ca52cac7c2d4cb2536e37e9be1f1, type: 3} - - {fileID: 5059060190599569098, guid: 5573ca52cac7c2d4cb2536e37e9be1f1, type: 3} - - {fileID: 5059060190599569098, guid: 5573ca52cac7c2d4cb2536e37e9be1f1, type: 3} - strings: - - a - - b - - c - ints: 0200000006000000030000000c00000005000000 - component: {fileID: 2037155953} - material: {fileID: 2100000, guid: 7404c70251f9d0045a4aabaa49d83963, type: 2} - texture: {fileID: 2800000, guid: d7cf546aa64aceb488a523b6662319b6, type: 3} - audioClip: {fileID: 8300000, guid: e83fc6426e5f2e0448cc3e51102509aa, type: 3} - mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} - largeArray: - - {fileID: 0} - - {fileID: 0} - - {fileID: 0} - - {fileID: 0} - - {fileID: 0} - - {fileID: 0} - - {fileID: 0} - - {fileID: 0} - - {fileID: 0} - - {fileID: 0} - - {fileID: 0} - - {fileID: 0} - - {fileID: 0} - - {fileID: 0} - - {fileID: 0} - - {fileID: 0} - - {fileID: 0} - - {fileID: 0} - - {fileID: 0} - quaternion: {x: 0, y: 0, z: 0, w: 0} - q2: {x: 0, y: 0, z: 0, w: 0} - min: -1 - max: 5.5 - dynamicRange: 1.5 - dynamicMinMax: {x: 0, y: 3} - nestedObject: - i: 1 - strings: - - EditorOnly - - Player - - Untagged ---- !u!4 &773117202 -Transform: - m_ObjectHideFlags: 2 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 773117200} - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: 0} - m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 - m_Children: [] - m_Father: {fileID: 0} - m_RootOrder: 27 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &890935882 GameObject: m_ObjectHideFlags: 0 @@ -2698,108 +2417,6 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1584091011} m_CullTransparentMesh: 1 ---- !u!1 &1646572068 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 1646572070} - - component: {fileID: 1646572069} - m_Layer: 0 - m_Name: SerializeReference [Sample] (3) - m_TagString: Untagged - m_Icon: {fileID: 2800000, guid: b105bf1fb7fe62e4baba0ffd7b433e7b, type: 3} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!114 &1646572069 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1646572068} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: a0323e91bfeae1c488545bee770d3fa7, type: 3} - m_Name: - m_EditorClassIdentifier: - var1: - rid: 7033609514626056197 - var2: - rid: 7033609514626056196 - var3: - rid: 7033609514626056198 - var4: - rid: 7033609514626056199 - vars: - - rid: 2222411121801560069 - - rid: 2222411121801560070 - - rid: 2222411121801560071 - - rid: 2222411121801560072 - references: - version: 2 - RefIds: - - rid: 2222411121801560069 - type: {class: SampleBehaviour6/ClassWithInterface2, ns: , asm: Assembly-CSharp} - data: - var1: 0 - mat: {fileID: 0} - - rid: 2222411121801560070 - type: {class: SampleBehaviour6/ClassWithInterface2, ns: , asm: Assembly-CSharp} - data: - var1: 0 - mat: {fileID: 0} - - rid: 2222411121801560071 - type: {class: SampleBehaviour6/ClassWithInterface4, ns: , asm: Assembly-CSharp} - data: - var1: 0 - mat: {fileID: 0} - var33: 0 - - rid: 2222411121801560072 - type: {class: SampleBehaviour6/SampleStruct, ns: , asm: Assembly-CSharp} - data: - var1: 0 - var2: 0 - - rid: 7033609514626056196 - type: {class: SampleBehaviour6/ClassWithInterface4, ns: , asm: Assembly-CSharp} - data: - var1: 1 - mat: {fileID: 0} - var33: 11 - - rid: 7033609514626056197 - type: {class: SampleBehaviour6/SampleStruct, ns: , asm: Assembly-CSharp} - data: - var1: 1 - var2: 1 - - rid: 7033609514626056198 - type: {class: SampleBehaviour6/ClassWithInterface2, ns: , asm: Assembly-CSharp} - data: - var1: 0 - mat: {fileID: 0} - - rid: 7033609514626056199 - type: {class: SampleBehaviour6/ClassWithInterface2, ns: , asm: Assembly-CSharp} - data: - var1: 0 - mat: {fileID: 2100000, guid: 26a618e7877b8c94f967effc172108f2, type: 2} ---- !u!4 &1646572070 -Transform: - m_ObjectHideFlags: 2 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1646572068} - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: 0} - m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 - m_Children: [] - m_Father: {fileID: 0} - m_RootOrder: 25 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1661307763 GameObject: m_ObjectHideFlags: 0 @@ -2889,96 +2506,6 @@ MonoBehaviour: var40: 2 var41: 0 var42: 0 ---- !u!1 &1695402926 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 1695402928} - - component: {fileID: 1695402927} - m_Layer: 0 - m_Name: SerializeReference [Sample] (2) - m_TagString: Untagged - m_Icon: {fileID: 2800000, guid: b105bf1fb7fe62e4baba0ffd7b433e7b, type: 3} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!114 &1695402927 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1695402926} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: a0323e91bfeae1c488545bee770d3fa7, type: 3} - m_Name: - m_EditorClassIdentifier: - var1: - rid: 2222411169542701056 - var2: - rid: 7033609514626056196 - var3: - rid: 7033609514626056198 - var4: - rid: 7033609514626056199 - vars: - - rid: -2 - - rid: -2 - - rid: -2 - - rid: 2222411169542701061 - - rid: -2 - references: - version: 2 - RefIds: - - rid: -2 - type: {class: , ns: , asm: } - - rid: 2222411169542701056 - type: {class: SampleBehaviour6/ClassWithInterface4, ns: , asm: Assembly-CSharp} - data: - var1: 1 - mat: {fileID: 0} - var33: 11 - - rid: 2222411169542701061 - type: {class: SampleBehaviour6/ClassWithInterface2, ns: , asm: Assembly-CSharp} - data: - var1: 0 - mat: {fileID: 0} - - rid: 7033609514626056196 - type: {class: SampleBehaviour6/ClassWithInterface4, ns: , asm: Assembly-CSharp} - data: - var1: 1 - mat: {fileID: 0} - var33: 11 - - rid: 7033609514626056198 - type: {class: SampleBehaviour6/ClassWithInterface2, ns: , asm: Assembly-CSharp} - data: - var1: 0 - mat: {fileID: 0} - - rid: 7033609514626056199 - type: {class: SampleBehaviour6/ClassWithInterface2, ns: , asm: Assembly-CSharp} - data: - var1: 0 - mat: {fileID: 2100000, guid: 26a618e7877b8c94f967effc172108f2, type: 2} ---- !u!4 &1695402928 -Transform: - m_ObjectHideFlags: 2 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1695402926} - m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: 0} - m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 - m_Children: [] - m_Father: {fileID: 0} - m_RootOrder: 24 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1707505305 GameObject: m_ObjectHideFlags: 0 @@ -3057,108 +2584,6 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1707505305} m_CullTransparentMesh: 1 ---- !u!1 &1840852554 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 1840852556} - - component: {fileID: 1840852555} - m_Layer: 0 - m_Name: SerializeReference [Sample] (1) - m_TagString: Untagged - m_Icon: {fileID: 2800000, guid: b105bf1fb7fe62e4baba0ffd7b433e7b, type: 3} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!114 &1840852555 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1840852554} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: a0323e91bfeae1c488545bee770d3fa7, type: 3} - m_Name: - m_EditorClassIdentifier: - var1: - rid: 7033609514626056197 - var2: - rid: 7033609514626056196 - var3: - rid: 7033609514626056198 - var4: - rid: 7033609514626056199 - vars: - - rid: 2222411169542701063 - - rid: 2222411169542701064 - - rid: 2222411169542701065 - - rid: 2222411169542701066 - references: - version: 2 - RefIds: - - rid: 2222411169542701063 - type: {class: SampleBehaviour6/ClassWithInterface2, ns: , asm: Assembly-CSharp} - data: - var1: 0 - mat: {fileID: 0} - - rid: 2222411169542701064 - type: {class: SampleBehaviour6/ClassWithInterface2, ns: , asm: Assembly-CSharp} - data: - var1: 0 - mat: {fileID: 0} - - rid: 2222411169542701065 - type: {class: SampleBehaviour6/ClassWithInterface4, ns: , asm: Assembly-CSharp} - data: - var1: 0 - mat: {fileID: 0} - var33: 0 - - rid: 2222411169542701066 - type: {class: SampleBehaviour6/SampleStruct, ns: , asm: Assembly-CSharp} - data: - var1: 0 - var2: 0 - - rid: 7033609514626056196 - type: {class: SampleBehaviour6/ClassWithInterface4, ns: , asm: Assembly-CSharp} - data: - var1: 1 - mat: {fileID: 0} - var33: 11 - - rid: 7033609514626056197 - type: {class: SampleBehaviour6/SampleStruct, ns: , asm: Assembly-CSharp} - data: - var1: 1 - var2: 1 - - rid: 7033609514626056198 - type: {class: SampleBehaviour6/ClassWithInterface2, ns: , asm: Assembly-CSharp} - data: - var1: 0 - mat: {fileID: 0} - - rid: 7033609514626056199 - type: {class: SampleBehaviour6/ClassWithInterface2, ns: , asm: Assembly-CSharp} - data: - var1: 0 - mat: {fileID: 2100000, guid: 26a618e7877b8c94f967effc172108f2, type: 2} ---- !u!4 &1840852556 -Transform: - m_ObjectHideFlags: 2 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1840852554} - m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: 0} - m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 - m_Children: [] - m_Father: {fileID: 0} - m_RootOrder: 23 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1972418676 GameObject: m_ObjectHideFlags: 0 diff --git a/Assets/Examples/Scripts/SampleBehaviour6.cs b/Assets/Examples/Scripts/SampleBehaviour6.cs index 259aa6e7..63b58fbb 100644 --- a/Assets/Examples/Scripts/SampleBehaviour6.cs +++ b/Assets/Examples/Scripts/SampleBehaviour6.cs @@ -12,7 +12,7 @@ public class SampleBehaviour6 : MonoBehaviour public ISampleInterface var1; [SerializeReference, ReferencePicker(ForceUninitializedInstance = true)] public ClassWithInterfaceBase var2; - [SerializeReference, ReferencePicker] + [SerializeReference, ReferencePicker(AddConfirmationBox = true)] public ClassWithInterfaceBase var3; [SerializeReference, ReferencePicker(ParentType = typeof(ClassWithInterface2), AddTextSearchField = false)] public ClassWithInterfaceBase var4; From 980fc047dbbd8c8fea3b8134472822ccae1a5b50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=82osz=20Matkowski?= Date: Sun, 10 Aug 2025 01:03:56 +0200 Subject: [PATCH 22/46] Update: README.md --- .../Editor/Utilities/PropertyUtility.cs | 3 +++ Assets/Editor Toolbox/README.md | 23 ++++++++++++++---- .../Examples/Scripts/UI/CustomSelectable.cs | 2 ++ Docs/serializereferenceoperations.png | Bin 14243 -> 9491 bytes README.md | 23 ++++++++++++++---- 5 files changed, 41 insertions(+), 10 deletions(-) diff --git a/Assets/Editor Toolbox/Editor/Utilities/PropertyUtility.cs b/Assets/Editor Toolbox/Editor/Utilities/PropertyUtility.cs index 8863f395..3b9a0dce 100644 --- a/Assets/Editor Toolbox/Editor/Utilities/PropertyUtility.cs +++ b/Assets/Editor Toolbox/Editor/Utilities/PropertyUtility.cs @@ -644,6 +644,7 @@ internal static bool IsSerializableArrayElement(string indexField, out int index internal static bool IsSerializeReferenceProperty(SerializedProperty property) { +#if UNITY_2019_3_OR_NEWER if (property == null) { return false; @@ -663,6 +664,8 @@ internal static bool IsSerializeReferenceProperty(SerializedProperty property) return elementType.Contains(managedReferenceType); } + return false; +#endif return false; } diff --git a/Assets/Editor Toolbox/README.md b/Assets/Editor Toolbox/README.md index bb41036d..5fd6fdf7 100644 --- a/Assets/Editor Toolbox/README.md +++ b/Assets/Editor Toolbox/README.md @@ -56,6 +56,11 @@ Unity 2018.x or newer - [Toolbox Custom Editors](#toolboxeditors) - [Material Drawers](#materialdrawers) - [Serialized Types](#serialized-types) + - [SerializedType](#serializedtype) + - [SerializedScene](#serializedscene) + - [SerializedDictionary](#serializeddictionary) + - [SerializedDateTime](#serializeddatetime) + - [SerializedDirectory](#serializeddictionary) - [Editor Extensions](#editor-extensions) - [Hierarchy](#hierarchy) - [Project](#project) @@ -777,6 +782,14 @@ public class ClassWithInterface3 : ClassWithInterfaceBase ![inspector](https://github.com/arimger/Unity-Editor-Toolbox/blob/develop/Docs/referencepicker.png) +##### ReferencePicker properties + +- **ParentType**: Indicates what *System.Type* should be used as 'base' to create a collection of all available inherited types. +- **ForceUninitializedInstance** (false): If *true* - a new reference instance will be created without the standard construction flow and object will be uninitialized (constructor won't be called). +- **TypeGrouping** (TypeGrouping.None): Indicates how the available types are displayed. +- **AddTextSearchField** (true): If *true* - the popup picker will be extended with a text search field. It may be useful for larger type collections. +- **AddConfimartionBox** (false): If *true* - creates an additional confirmation box to make sure that the new assignment is intended. + ##### SerializeReference generics support Unity 2023.x introduced support for serializing generic references. @@ -885,7 +898,7 @@ _HideIfExample ("Range", Range(0, 1)) = 0.75 ## Serialized Types -#### SerializedType +#### SerializedType Allows to serialize Types and pick them through a dedicated picker. @@ -901,7 +914,7 @@ public void Usage() ![inspector](https://github.com/arimger/Unity-Editor-Toolbox/blob/develop/Docs/serializedtype.png) -#### SerializedScene +#### SerializedScene Allows to serialize SceneAssets and use them in Runtime. @@ -926,7 +939,7 @@ Keep in mind that SerializedScene stores Scene's index, name and path. These pro Unfortunately, you need to handle associated objects reserialization by yourself, otherwise e.g. updated indexes won't be saved. I prepared for you a static event `SceneSerializationUtility.OnCacheRefreshed` that can be used to validate SerializedScenes in your project. You can link SerializedScene in a ScriptableObject and trigger reserialization (`EditorUtility.SetDirty()`) if needed, it's really convinient approach. -#### SerializedDictionary +#### SerializedDictionary Allows to serialize and use Dictionaries. The presented class implements the IDictionary interface, so it can be easily used like the standard version. @@ -952,7 +965,7 @@ public void Usage() ![inspector](https://github.com/arimger/Unity-Editor-Toolbox/blob/develop/Docs/dictionary2.png) -#### SerializedDateTime +#### SerializedDateTime Allows to serialize DateTime. @@ -967,7 +980,7 @@ public void Usage() ![inspector](https://github.com/arimger/Unity-Editor-Toolbox/blob/develop/Docs/serializeddate.png) -#### SerializedDirectory +#### SerializedDirectory Allows to serialize folders in form of assets and retrieve direct paths in Runtime. diff --git a/Assets/Examples/Scripts/UI/CustomSelectable.cs b/Assets/Examples/Scripts/UI/CustomSelectable.cs index 1c6dd0d1..b6d4fcd2 100644 --- a/Assets/Examples/Scripts/UI/CustomSelectable.cs +++ b/Assets/Examples/Scripts/UI/CustomSelectable.cs @@ -6,6 +6,8 @@ public class CustomSelectable : Selectable private interface ICustomLogic { } +#if UNITY_2019_3_OR_NEWER [SerializeReference] private ICustomLogic customLogic; +#endif } \ No newline at end of file diff --git a/Docs/serializereferenceoperations.png b/Docs/serializereferenceoperations.png index b23663f85037aca27bc637e2014cb44ce8a6b95c..57986264f39c0693faa892ba40c055842437953a 100644 GIT binary patch literal 9491 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 G(EkCC^O}YL literal 14243 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 diff --git a/README.md b/README.md index bb41036d..5fd6fdf7 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,11 @@ Unity 2018.x or newer - [Toolbox Custom Editors](#toolboxeditors) - [Material Drawers](#materialdrawers) - [Serialized Types](#serialized-types) + - [SerializedType](#serializedtype) + - [SerializedScene](#serializedscene) + - [SerializedDictionary](#serializeddictionary) + - [SerializedDateTime](#serializeddatetime) + - [SerializedDirectory](#serializeddictionary) - [Editor Extensions](#editor-extensions) - [Hierarchy](#hierarchy) - [Project](#project) @@ -777,6 +782,14 @@ public class ClassWithInterface3 : ClassWithInterfaceBase ![inspector](https://github.com/arimger/Unity-Editor-Toolbox/blob/develop/Docs/referencepicker.png) +##### ReferencePicker properties + +- **ParentType**: Indicates what *System.Type* should be used as 'base' to create a collection of all available inherited types. +- **ForceUninitializedInstance** (false): If *true* - a new reference instance will be created without the standard construction flow and object will be uninitialized (constructor won't be called). +- **TypeGrouping** (TypeGrouping.None): Indicates how the available types are displayed. +- **AddTextSearchField** (true): If *true* - the popup picker will be extended with a text search field. It may be useful for larger type collections. +- **AddConfimartionBox** (false): If *true* - creates an additional confirmation box to make sure that the new assignment is intended. + ##### SerializeReference generics support Unity 2023.x introduced support for serializing generic references. @@ -885,7 +898,7 @@ _HideIfExample ("Range", Range(0, 1)) = 0.75 ## Serialized Types -#### SerializedType +#### SerializedType Allows to serialize Types and pick them through a dedicated picker. @@ -901,7 +914,7 @@ public void Usage() ![inspector](https://github.com/arimger/Unity-Editor-Toolbox/blob/develop/Docs/serializedtype.png) -#### SerializedScene +#### SerializedScene Allows to serialize SceneAssets and use them in Runtime. @@ -926,7 +939,7 @@ Keep in mind that SerializedScene stores Scene's index, name and path. These pro Unfortunately, you need to handle associated objects reserialization by yourself, otherwise e.g. updated indexes won't be saved. I prepared for you a static event `SceneSerializationUtility.OnCacheRefreshed` that can be used to validate SerializedScenes in your project. You can link SerializedScene in a ScriptableObject and trigger reserialization (`EditorUtility.SetDirty()`) if needed, it's really convinient approach. -#### SerializedDictionary +#### SerializedDictionary Allows to serialize and use Dictionaries. The presented class implements the IDictionary interface, so it can be easily used like the standard version. @@ -952,7 +965,7 @@ public void Usage() ![inspector](https://github.com/arimger/Unity-Editor-Toolbox/blob/develop/Docs/dictionary2.png) -#### SerializedDateTime +#### SerializedDateTime Allows to serialize DateTime. @@ -967,7 +980,7 @@ public void Usage() ![inspector](https://github.com/arimger/Unity-Editor-Toolbox/blob/develop/Docs/serializeddate.png) -#### SerializedDirectory +#### SerializedDirectory Allows to serialize folders in form of assets and retrieve direct paths in Runtime. From 8cac626f07839de35690264541c27b3c1584d998 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=82osz=20Matkowski?= Date: Sun, 10 Aug 2025 01:05:36 +0200 Subject: [PATCH 23/46] Update: README.md --- Assets/Editor Toolbox/README.md | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Assets/Editor Toolbox/README.md b/Assets/Editor Toolbox/README.md index 5fd6fdf7..d806e1df 100644 --- a/Assets/Editor Toolbox/README.md +++ b/Assets/Editor Toolbox/README.md @@ -60,7 +60,7 @@ Unity 2018.x or newer - [SerializedScene](#serializedscene) - [SerializedDictionary](#serializeddictionary) - [SerializedDateTime](#serializeddatetime) - - [SerializedDirectory](#serializeddictionary) + - [SerializedDirectory](#serializeddirectory) - [Editor Extensions](#editor-extensions) - [Hierarchy](#hierarchy) - [Project](#project) diff --git a/README.md b/README.md index 5fd6fdf7..d806e1df 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ Unity 2018.x or newer - [SerializedScene](#serializedscene) - [SerializedDictionary](#serializeddictionary) - [SerializedDateTime](#serializeddatetime) - - [SerializedDirectory](#serializeddictionary) + - [SerializedDirectory](#serializeddirectory) - [Editor Extensions](#editor-extensions) - [Hierarchy](#hierarchy) - [Project](#project) From cbdde91ebb219f5fd8ff8b643850ac06f16f2907 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=82osz=20Matkowski?= Date: Sun, 10 Aug 2025 16:27:40 +0200 Subject: [PATCH 24/46] Minor refactor changes --- Assets/Editor Toolbox/Editor/Utilities/PropertyUtility.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Assets/Editor Toolbox/Editor/Utilities/PropertyUtility.cs b/Assets/Editor Toolbox/Editor/Utilities/PropertyUtility.cs index 3b9a0dce..d653839e 100644 --- a/Assets/Editor Toolbox/Editor/Utilities/PropertyUtility.cs +++ b/Assets/Editor Toolbox/Editor/Utilities/PropertyUtility.cs @@ -665,8 +665,9 @@ internal static bool IsSerializeReferenceProperty(SerializedProperty property) } return false; -#endif +#else return false; +#endif } internal static bool TryGetSerializeReferenceType(SerializedProperty property, out Type referenceType) From 75685dd3fb482eab527b6eeca8ff21024cb501c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=82osz=20Matkowski?= Date: Sun, 10 Aug 2025 16:28:09 +0200 Subject: [PATCH 25/46] Update: README.md, package.json, CHANGELOG.md --- Assets/Editor Toolbox/CHANGELOG.md | 13 +++++++++++++ Assets/Editor Toolbox/README.md | 6 +++--- Assets/Editor Toolbox/package.json | 2 +- README.md | 6 +++--- 4 files changed, 20 insertions(+), 7 deletions(-) diff --git a/Assets/Editor Toolbox/CHANGELOG.md b/Assets/Editor Toolbox/CHANGELOG.md index 5be8a09a..7a96036a 100644 --- a/Assets/Editor Toolbox/CHANGELOG.md +++ b/Assets/Editor Toolbox/CHANGELOG.md @@ -1,3 +1,16 @@ +## 0.14.2 [10.08.2025] + +### Added: +- `AddConfirmationBox` property to the [ReferencePicker]; allows to display an intermediate confirmation window before assigning a new reference + +### Changed: +- Fix [FormattedNumber] behaviour while multi-editing different values +- [LabelByChild] now displays ToString() value for UnityEngine.Object references +- Fix ReorderableLists indention level for nested objects +- Fix various smaller issues related to [SerializeReference]-based properties while being in the multi-editing mode +- Rename 'Copy Serialize Reference' to 'Copy Serialized References', from now this operation also works on arrays and lists +- Rename 'Paste Serialize Reference' to 'Paste Serialized References', from now this operation also works on arrays and lists + ## 0.14.1 [22.04.2025] ### Changed: diff --git a/Assets/Editor Toolbox/README.md b/Assets/Editor Toolbox/README.md index d806e1df..e776a691 100644 --- a/Assets/Editor Toolbox/README.md +++ b/Assets/Editor Toolbox/README.md @@ -825,9 +825,9 @@ public class GenericInterfaceImplementation : IGenericInterface ##### SerializeReference context menu operations You can use few custom context menu operations for the **[SerializeReference]** fields: -- **Copy Serialize Reference**: creates a deep copy of the linked reference -- **Paste Serialize Reference**: allows to paste preserved copy to a field -- **Duplicate Serialize Reference**: allows to duplicate the linked reference (works only on collection elements) +- **Copy Serialized References**: creates a deep copy of the linked reference +- **Paste Serialized References**: allows to paste preserved copy to a field +- **Duplicate Serialize Reference Array Element**: allows to duplicate the linked reference (works only on collection elements) ![inspector](https://github.com/arimger/Unity-Editor-Toolbox/blob/develop/Docs/serializereferenceoperations.png) diff --git a/Assets/Editor Toolbox/package.json b/Assets/Editor Toolbox/package.json index 2b692407..fa196562 100644 --- a/Assets/Editor Toolbox/package.json +++ b/Assets/Editor Toolbox/package.json @@ -1,7 +1,7 @@ { "name": "com.browar.editor-toolbox", "displayName": "Editor Toolbox", - "version": "0.14.1", + "version": "0.14.2", "unity": "2018.1", "description": "Tools, custom attributes, drawers, hierarchy overlay, and other extensions for the Unity Editor.", "keywords": [ diff --git a/README.md b/README.md index d806e1df..e776a691 100644 --- a/README.md +++ b/README.md @@ -825,9 +825,9 @@ public class GenericInterfaceImplementation : IGenericInterface ##### SerializeReference context menu operations You can use few custom context menu operations for the **[SerializeReference]** fields: -- **Copy Serialize Reference**: creates a deep copy of the linked reference -- **Paste Serialize Reference**: allows to paste preserved copy to a field -- **Duplicate Serialize Reference**: allows to duplicate the linked reference (works only on collection elements) +- **Copy Serialized References**: creates a deep copy of the linked reference +- **Paste Serialized References**: allows to paste preserved copy to a field +- **Duplicate Serialize Reference Array Element**: allows to duplicate the linked reference (works only on collection elements) ![inspector](https://github.com/arimger/Unity-Editor-Toolbox/blob/develop/Docs/serializereferenceoperations.png) From bf85e98c7944de154de23a290f99cec8ab24d66b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=82osz=20Matkowski?= Date: Sun, 10 Aug 2025 16:50:04 +0200 Subject: [PATCH 26/46] Update: CHANGELOG.md --- Assets/Editor Toolbox/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/Editor Toolbox/CHANGELOG.md b/Assets/Editor Toolbox/CHANGELOG.md index 7a96036a..16d1aa47 100644 --- a/Assets/Editor Toolbox/CHANGELOG.md +++ b/Assets/Editor Toolbox/CHANGELOG.md @@ -6,7 +6,7 @@ ### Changed: - Fix [FormattedNumber] behaviour while multi-editing different values - [LabelByChild] now displays ToString() value for UnityEngine.Object references -- Fix ReorderableLists indention level for nested objects +- Fix ReorderableLists indentation level for nested objects - Fix various smaller issues related to [SerializeReference]-based properties while being in the multi-editing mode - Rename 'Copy Serialize Reference' to 'Copy Serialized References', from now this operation also works on arrays and lists - Rename 'Paste Serialize Reference' to 'Paste Serialized References', from now this operation also works on arrays and lists From 6da24bc937464f63bbf612383c406937c9aa37a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=82osz=20Matkowski?= Date: Sun, 10 Aug 2025 16:54:47 +0200 Subject: [PATCH 27/46] Minor refactor changes --- Assets/Examples/Scripts/SampleBehaviour3.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Assets/Examples/Scripts/SampleBehaviour3.cs b/Assets/Examples/Scripts/SampleBehaviour3.cs index e35df632..37a4f4c9 100644 --- a/Assets/Examples/Scripts/SampleBehaviour3.cs +++ b/Assets/Examples/Scripts/SampleBehaviour3.cs @@ -1,5 +1,4 @@ -using Toolbox.Editor.Drawers; -using UnityEditor; +using UnityEditor; using UnityEngine; [ExecuteAlways] From 1d68a4213e02ec11c749cb9e01027cb9c7fee9f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=82osz=20Matkowski?= Date: Sun, 10 Aug 2025 17:06:41 +0200 Subject: [PATCH 28/46] Minor refactor changes --- Assets/Examples/Scripts/SampleBehaviour3.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Assets/Examples/Scripts/SampleBehaviour3.cs b/Assets/Examples/Scripts/SampleBehaviour3.cs index 37a4f4c9..df1b8f65 100644 --- a/Assets/Examples/Scripts/SampleBehaviour3.cs +++ b/Assets/Examples/Scripts/SampleBehaviour3.cs @@ -1,5 +1,4 @@ -using UnityEditor; -using UnityEngine; +using UnityEngine; [ExecuteAlways] [AddComponentMenu("Editor Toolbox/Cheat Sheet 3 (Toolbox Conditions)")] From 86ccf55b7e8f68d49ecbaad209de52fcac27ed54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=82osz=20Matkowski?= Date: Sun, 26 Oct 2025 22:48:44 +0100 Subject: [PATCH 29/46] Implement ValueStep property for the ProgressBarAttribute --- .../Editor/Drawers/Regular/ProgressBarAttributeDrawer.cs | 6 ++++++ .../Attributes/Property/Regular/ProgressBarAttribute.cs | 9 +++++++-- Assets/Examples/Scenes/SampleScene.unity | 2 +- Assets/Examples/Scripts/SampleBehaviour1.cs | 2 +- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/Assets/Editor Toolbox/Editor/Drawers/Regular/ProgressBarAttributeDrawer.cs b/Assets/Editor Toolbox/Editor/Drawers/Regular/ProgressBarAttributeDrawer.cs index dffbddf2..a7f42777 100644 --- a/Assets/Editor Toolbox/Editor/Drawers/Regular/ProgressBarAttributeDrawer.cs +++ b/Assets/Editor Toolbox/Editor/Drawers/Regular/ProgressBarAttributeDrawer.cs @@ -43,12 +43,18 @@ private void SetProgressValue(SerializedProperty property, Rect progressBarRect, { var minValue = Attribute.MinValue; var maxValue = Attribute.MaxValue; + var valueStep = Attribute.ValueStep; var range = progressBarRect.xMax - progressBarRect.xMin; xPosition = Mathf.Clamp(xPosition - progressBarRect.xMin, 0, range); var fill = Mathf.Clamp01(xPosition / range); var newValue = (maxValue - minValue) * fill + minValue; + if (!Mathf.Approximately(valueStep, 0.0f)) + { + newValue = Mathf.Round(newValue / valueStep) * valueStep; + newValue = Mathf.Clamp(newValue, minValue, maxValue); + } switch (property.propertyType) { diff --git a/Assets/Editor Toolbox/Runtime/Attributes/Property/Regular/ProgressBarAttribute.cs b/Assets/Editor Toolbox/Runtime/Attributes/Property/Regular/ProgressBarAttribute.cs index 0db77f9c..ec69cfc5 100644 --- a/Assets/Editor Toolbox/Runtime/Attributes/Property/Regular/ProgressBarAttribute.cs +++ b/Assets/Editor Toolbox/Runtime/Attributes/Property/Regular/ProgressBarAttribute.cs @@ -26,6 +26,13 @@ public ProgressBarAttribute(string name = "", float minValue = 0, float maxValue public float MaxValue { get; private set; } + /// + /// Indicates value change step if is interactable. + /// + public float ValueStep { get; set; } = 0.001f; + + public bool IsInteractable { get; set; } + public Color Color { get => ColorUtility.TryParseHtmlString(HexColor, out var color) @@ -34,7 +41,5 @@ public Color Color } public string HexColor { get; set; } - - public bool IsInteractable { get; set; } } } \ No newline at end of file diff --git a/Assets/Examples/Scenes/SampleScene.unity b/Assets/Examples/Scenes/SampleScene.unity index b56b3b9a..cefc5421 100644 --- a/Assets/Examples/Scenes/SampleScene.unity +++ b/Assets/Examples/Scenes/SampleScene.unity @@ -1287,7 +1287,7 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: targetTag: Untagged - progressBar1: 9.159664 + progressBar1: 30 progressBar2: 25.4 minMaxVector: {x: 10, y: 73.893524} minMaxVectorInt: {x: 2, y: 7} diff --git a/Assets/Examples/Scripts/SampleBehaviour1.cs b/Assets/Examples/Scripts/SampleBehaviour1.cs index 2f657de1..42383b86 100644 --- a/Assets/Examples/Scripts/SampleBehaviour1.cs +++ b/Assets/Examples/Scripts/SampleBehaviour1.cs @@ -11,7 +11,7 @@ public class SampleBehaviour1 : MonoBehaviour [Label("Progress Bar", skinStyle: SkinStyle.Box)] - [ProgressBar(minValue: -10.0f, maxValue: 50.0f, HexColor = "#234DEA", IsInteractable = true)] + [ProgressBar(minValue: -10.0f, maxValue: 50.0f, HexColor = "#234DEA", IsInteractable = true, ValueStep = 2.5f)] public float progressBar1 = 25.4f; [ProgressBar(minValue: -10.0f, maxValue: 50.0f, HexColor = "#32A852", IsInteractable = false)] public float progressBar2 = 25.4f; From 66e67fc86d2d657c9ec2911f2fb2f268784e04c5 Mon Sep 17 00:00:00 2001 From: Olli Pikkarainen Date: Mon, 8 Dec 2025 08:45:12 +0200 Subject: [PATCH 30/46] Add hierarchy objects to tab selector + wire cube highlight to hovered objects --- .../ToolboxEditorSceneViewObjectSelector.cs | 196 +++++++++++++++--- 1 file changed, 168 insertions(+), 28 deletions(-) diff --git a/Assets/Editor Toolbox/Editor/SceneView/ToolboxEditorSceneViewObjectSelector.cs b/Assets/Editor Toolbox/Editor/SceneView/ToolboxEditorSceneViewObjectSelector.cs index 0f6f834e..6556c12e 100644 --- a/Assets/Editor Toolbox/Editor/SceneView/ToolboxEditorSceneViewObjectSelector.cs +++ b/Assets/Editor Toolbox/Editor/SceneView/ToolboxEditorSceneViewObjectSelector.cs @@ -7,17 +7,20 @@ namespace Toolbox.Editor.SceneView public class ToolboxEditorSceneViewObjectSelector : EditorWindow { private static readonly Color selectionColor = new Color(0.50f, 0.70f, 1.00f); + private static readonly Color highlightWireColor = Color.yellow; + private const float outlineFillOpacity = 1f; private const float sizeXPadding = 2f; private const float sizeYPadding = 2f; private const float buttonYSpacing = 0.0f; private const float buttonYSize = 20f; private const float sizeXOffset = -30f; + private const float indentWidth = 12f; - private List gameObjects; - private List gameObjectPaths; + private List displayEntries; private GameObject highlightedObject; + private readonly List highlightedRenderers = new List(); private Vector2 size; private Vector2 buttonSize; @@ -32,12 +35,23 @@ public static void Show(List gameObjects, Vector2 position) var window = CreateInstance(); window.wantsMouseMove = true; window.wantsMouseEnterLeaveWindow = true; - window.gameObjects = gameObjects; - window.InitializeGameObjectPaths(); + window.displayEntries = window.BuildDisplayEntries(gameObjects); window.CalculateSize(); window.ShowAsDropDown(rect, window.size); } + private void OnEnable() + { + UnityEditor.SceneView.duringSceneGui += OnSceneViewDuringSceneGui; + } + + private void OnDisable() + { + UnityEditor.SceneView.duringSceneGui -= OnSceneViewDuringSceneGui; + highlightedRenderers.Clear(); + highlightedObject = null; + } + private void OnGUI() { if (Event.current.type == EventType.Layout) @@ -67,18 +81,24 @@ private void OnGuiMouseLeave() private void OnGuiMouseMove() { var rect = new Rect(sizeXPadding, sizeYPadding, buttonSize.x, buttonSize.y); - for (var i = 0; i < gameObjects.Count; i++) + for (var i = 0; i < displayEntries.Count; i++) { - var gameObject = gameObjects[i]; + var entry = displayEntries[i]; + var gameObject = entry.GameObject; if (gameObject == null) { //Can happen when something removes the gameobject during the window display. continue; } + var indentOffset = entry.Depth * indentWidth; + var drawRect = new Rect(rect); + drawRect.x += indentOffset; + drawRect.width -= indentOffset; + var content = EditorGUIUtility.ObjectContent(gameObject, typeof(GameObject)); - GUI.Button(rect, content, Style.buttonStyle); - if (rect.Contains(Event.current.mousePosition)) + GUI.Button(drawRect, content, Style.buttonStyle); + if (drawRect.Contains(Event.current.mousePosition)) { HighlightedObject = gameObject; } @@ -90,15 +110,21 @@ private void OnGuiMouseMove() private void OnGuiNormal() { var rect = new Rect(sizeXPadding, sizeYPadding, buttonSize.x, buttonSize.y); - for (var i = 0; i < gameObjects.Count; i++) + for (var i = 0; i < displayEntries.Count; i++) { - var gameObject = gameObjects[i]; + var entry = displayEntries[i]; + var gameObject = entry.GameObject; if (gameObject == null) { //Can happen when something removes the gameobject during the window display. continue; } + var indentOffset = entry.Depth * indentWidth; + var drawRect = new Rect(rect); + drawRect.x += indentOffset; + drawRect.width -= indentOffset; + var content = EditorGUIUtility.ObjectContent(gameObject, typeof(GameObject)); var objectSelected = Selection.Contains(gameObject); if (objectSelected) @@ -106,7 +132,7 @@ private void OnGuiNormal() GUI.backgroundColor = selectionColor; } - if (GUI.Button(rect, content, Style.buttonStyle)) + if (GUI.Button(drawRect, content, Style.buttonStyle)) { GameObjectButtonPress(i); } @@ -120,47 +146,107 @@ private Vector2 CalculateSize() { size = Vector2.zero; - foreach (var go in gameObjects) + var maxIndent = 0f; + foreach (var entry in displayEntries) { - var content = EditorGUIUtility.ObjectContent(go, typeof(GameObject)); + var content = EditorGUIUtility.ObjectContent(entry.GameObject, typeof(GameObject)); var currentSize = Style.buttonStyle.CalcSize(content); if (currentSize.x > size.x) { size.x = currentSize.x; } + + var currentIndent = entry.Depth * indentWidth; + if (currentIndent > maxIndent) + { + maxIndent = currentIndent; + } } + size.x += maxIndent; //This is needed because CalcSize calculates content drawing with icon at full size. size.x += sizeXOffset; buttonSize.x = size.x; buttonSize.y = buttonYSize; - size.y = gameObjects.Count * buttonYSize + sizeYPadding * 2.0f + buttonYSpacing * gameObjects.Count - 1; + size.y = displayEntries.Count * buttonYSize + sizeYPadding * 2.0f + buttonYSpacing * displayEntries.Count - 1; size.x += sizeXPadding * 2.0f; return size; } - private void InitializeGameObjectPaths() + private List BuildDisplayEntries(List objectsUnderCursor) { - gameObjectPaths = new List(); - var pathStack = new Stack(); + var entries = new List(); + + if (objectsUnderCursor == null || objectsUnderCursor.Count == 0) + { + return entries; + } + + var underCursorSet = new HashSet(); + foreach (var gameObject in objectsUnderCursor) + { + if (gameObject == null) + { + continue; + } + + underCursorSet.Add(gameObject.transform); + } + + var relevantTransforms = new HashSet(); + foreach (var transform in underCursorSet) + { + var current = transform; + while (current != null && relevantTransforms.Add(current)) + { + current = current.parent; + } + } + + var orderedRoots = new List(); + foreach (var gameObject in objectsUnderCursor) + { + if (gameObject == null) + { + continue; + } + + var top = gameObject.transform; + while (top.parent != null && relevantTransforms.Contains(top.parent)) + { + top = top.parent; + } + + if (!orderedRoots.Contains(top)) + { + orderedRoots.Add(top); + } + } - for (var i = 0; i < gameObjects.Count; i++) + foreach (var root in orderedRoots) { - pathStack.Clear(); - var transform = gameObjects[i].transform; - pathStack.Push(transform.gameObject.name); + AppendHierarchy(root, 0, relevantTransforms, entries); + } + + return entries; + } + + private void AppendHierarchy(Transform current, int depth, HashSet relevantTransforms, List entries) + { + entries.Add(new DisplayEntry(current.gameObject, depth)); - while (transform.parent != null) + for (var i = 0; i < current.childCount; i++) + { + var child = current.GetChild(i); + if (!relevantTransforms.Contains(child)) { - transform = transform.parent; - pathStack.Push(transform.gameObject.name); + continue; } - var path = string.Join("/", pathStack.ToArray()); - gameObjectPaths.Add(path); + AppendHierarchy(child, depth + 1, relevantTransforms, entries); } } @@ -218,7 +304,7 @@ private void UpdateShiftSelectionIDs(int id) private void SelectObject(int id, bool control, bool shift) { - var gameObject = gameObjects[id]; + var gameObject = displayEntries[id].GameObject; if (shift) { @@ -252,7 +338,7 @@ private void SelectObjects(int minId, int maxId) for (var i = minId; i <= maxId; i++) { - newSelection[index] = gameObjects[i]; + newSelection[index] = displayEntries[i].GameObject; index++; } @@ -307,9 +393,63 @@ private GameObject HighlightedObject { EditorGUIUtility.PingObject(highlightedObject); } + + UpdateHighlightedRenderers(); } } + private void UpdateHighlightedRenderers() + { + highlightedRenderers.Clear(); + + if (highlightedObject == null) + { + return; + } + + highlightedRenderers.AddRange(highlightedObject.GetComponentsInChildren(true)); + } + + private void OnSceneViewDuringSceneGui(UnityEditor.SceneView sceneView) + { + if (highlightedRenderers.Count == 0 || + Event.current.type != EventType.Repaint) + { + return; + } + + // Unity 6.1+ provides Handles.DrawOutline which also highlights children in one call. +#if UNITY_6000_1_OR_NEWER + Handles.DrawOutline(highlightedRenderers.ToArray(), highlightWireColor, highlightWireColor, outlineFillOpacity); +#else + using (new Handles.DrawingScope(highlightWireColor)) + { + foreach (var renderer in highlightedRenderers) + { + if (renderer == null) + { + continue; + } + + var bounds = renderer.bounds; + Handles.DrawWireCube(bounds.center, bounds.size); + } + } +#endif + } + + private readonly struct DisplayEntry + { + internal DisplayEntry(GameObject gameObject, int depth) + { + GameObject = gameObject; + Depth = depth; + } + + internal GameObject GameObject { get; } + internal int Depth { get; } + } + private static class Style { internal static readonly GUIStyle buttonStyle; From 20e3ce8da12f6a9ca51ebd220b143f6c7b72c9bf Mon Sep 17 00:00:00 2001 From: Demetri Date: Wed, 17 Dec 2025 16:14:28 +0500 Subject: [PATCH 31/46] Possibility to display managed reference by LabelByChildAttribute --- Assets/Editor Toolbox/Editor/Utilities/PropertyUtility.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Assets/Editor Toolbox/Editor/Utilities/PropertyUtility.cs b/Assets/Editor Toolbox/Editor/Utilities/PropertyUtility.cs index d653839e..5c038fed 100644 --- a/Assets/Editor Toolbox/Editor/Utilities/PropertyUtility.cs +++ b/Assets/Editor Toolbox/Editor/Utilities/PropertyUtility.cs @@ -428,6 +428,9 @@ public static void OverrideLabelByValue(GUIContent label, SerializedProperty pro case SerializedPropertyType.ObjectReference: label.text = property.objectReferenceValue ? property.objectReferenceValue.ToString() : "null"; break; + case SerializedPropertyType.ManagedReference: + label.text = property.managedReferenceValue?.ToString() ?? "null"; + break; case SerializedPropertyType.LayerMask: switch (property.intValue) { From e749e569e6cf390c74c54069b169267b62e5ac11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=82osz=20Matkowski?= Date: Mon, 22 Dec 2025 23:18:52 +0100 Subject: [PATCH 32/46] Fix Copy/Paste context menu operations for serialized references --- .../SerializeReference/CopySerializeReferenceCache.cs | 5 +++-- .../CopySerializeReferenceOperation.cs | 4 +++- .../PasteSerializeReferenceOperation.cs | 2 +- .../Editor Toolbox/Editor/Utilities/PropertyUtility.cs | 9 +++++---- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/Assets/Editor Toolbox/Editor/ContextMenu/Operations/SerializeReference/CopySerializeReferenceCache.cs b/Assets/Editor Toolbox/Editor/ContextMenu/Operations/SerializeReference/CopySerializeReferenceCache.cs index f8e4ae71..4c6d9d65 100644 --- a/Assets/Editor Toolbox/Editor/ContextMenu/Operations/SerializeReference/CopySerializeReferenceCache.cs +++ b/Assets/Editor Toolbox/Editor/ContextMenu/Operations/SerializeReference/CopySerializeReferenceCache.cs @@ -9,10 +9,11 @@ namespace Toolbox.Editor.ContextMenu.Operations /// internal class CopySerializeReferenceCache { - public CopySerializeReferenceCache(Type referenceType, IReadOnlyList entires) + public CopySerializeReferenceCache(Type referenceType, IReadOnlyList entires, bool isArrayCopy) { ReferenceType = referenceType; Entries = entires; + IsArrayCopy = isArrayCopy; } /// @@ -20,6 +21,6 @@ public CopySerializeReferenceCache(Type referenceType, IReadOnlyList public Type ReferenceType { get; } public IReadOnlyList Entries { get; } - public bool IsArrayCopy => Entries != null && Entries.Count > 1; + public bool IsArrayCopy { get; } } } \ No newline at end of file diff --git a/Assets/Editor Toolbox/Editor/ContextMenu/Operations/SerializeReference/CopySerializeReferenceOperation.cs b/Assets/Editor Toolbox/Editor/ContextMenu/Operations/SerializeReference/CopySerializeReferenceOperation.cs index 6d2293c8..e7850684 100644 --- a/Assets/Editor Toolbox/Editor/ContextMenu/Operations/SerializeReference/CopySerializeReferenceOperation.cs +++ b/Assets/Editor Toolbox/Editor/ContextMenu/Operations/SerializeReference/CopySerializeReferenceOperation.cs @@ -51,6 +51,7 @@ public bool IsEnabled(SerializedProperty property) public void Perform(SerializedProperty property) { + var isArrayCopy = false; var entries = new List(); if (property.propertyType == SerializedPropertyType.ManagedReference) { @@ -59,6 +60,7 @@ public void Perform(SerializedProperty property) } else if (property.isArray) { + isArrayCopy = true; var propertiesCount = property.arraySize; for (var i = 0; i < propertiesCount; i++) { @@ -69,7 +71,7 @@ public void Perform(SerializedProperty property) } PropertyUtility.TryGetSerializeReferenceType(property, out var referenceType); - Cache = new CopySerializeReferenceCache(referenceType, entries); + Cache = new CopySerializeReferenceCache(referenceType, entries, isArrayCopy); } public GUIContent Label => new GUIContent("Copy Serialized References"); diff --git a/Assets/Editor Toolbox/Editor/ContextMenu/Operations/SerializeReference/PasteSerializeReferenceOperation.cs b/Assets/Editor Toolbox/Editor/ContextMenu/Operations/SerializeReference/PasteSerializeReferenceOperation.cs index c8376054..65d560c8 100644 --- a/Assets/Editor Toolbox/Editor/ContextMenu/Operations/SerializeReference/PasteSerializeReferenceOperation.cs +++ b/Assets/Editor Toolbox/Editor/ContextMenu/Operations/SerializeReference/PasteSerializeReferenceOperation.cs @@ -36,7 +36,7 @@ private bool IsAssignmentValid(Type targetType, CopySerializeReferenceEntry entr return true; } - return TypeUtility.IsTypeAssignableFrom(targetType, entry.ReferenceType); + return TypeUtility.IsTypeAssignableFrom(targetType, entryType); } private bool IsOperationSupported(SerializedProperty targetProperty, CopySerializeReferenceCache cache) diff --git a/Assets/Editor Toolbox/Editor/Utilities/PropertyUtility.cs b/Assets/Editor Toolbox/Editor/Utilities/PropertyUtility.cs index d653839e..4aa3831e 100644 --- a/Assets/Editor Toolbox/Editor/Utilities/PropertyUtility.cs +++ b/Assets/Editor Toolbox/Editor/Utilities/PropertyUtility.cs @@ -672,20 +672,21 @@ internal static bool IsSerializeReferenceProperty(SerializedProperty property) internal static bool TryGetSerializeReferenceType(SerializedProperty property, out Type referenceType) { - var fieldInfo = GetFieldInfo(property, out var propertyType); + var fieldInfo = GetFieldInfo(property, propertyType: out _); if (fieldInfo == null) { referenceType = null; return false; } - if (property.isArray) + var fieldType = fieldInfo.FieldType; + if (property.isArray || IsSerializableArrayElement(property)) { - referenceType = GetElementTypeFromArrayType(propertyType); + referenceType = GetElementTypeFromArrayType(fieldType); } else { - referenceType = fieldInfo.FieldType; + referenceType = fieldType; } return true; From ee7031cdb047711eeb20d60edf293c0e2bba8615 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=82osz=20Matkowski?= Date: Sun, 28 Dec 2025 18:36:43 +0100 Subject: [PATCH 33/46] Unity 6.3 support: improvements to the Scene serialization process; new Toolbar changes adjustements --- .../Material/MaterialCompactTextureDrawer.cs | 4 + .../Material/MaterialConditionalDrawer.cs | 13 +- .../Material/MaterialMinMaxSliderDrawer.cs | 4 + .../Drawers/Material/MaterialVector2Drawer.cs | 4 + .../Drawers/Material/MaterialVector3Drawer.cs | 4 + .../Editor/ToolboxEditorHierarchy.cs | 6 +- .../Editor/ToolboxEditorToolbar.cs | 116 +++++++++++++++--- .../SceneSerializationUtility.cs | 59 +++++++-- .../Runtime/Serialization/SerializedScene.cs | 1 - Assets/Examples/Materials/TestMat.mat | 6 +- Assets/Examples/Scripts/SampleBehaviour6.cs | 4 + 11 files changed, 190 insertions(+), 31 deletions(-) diff --git a/Assets/Editor Toolbox/Editor/Drawers/Material/MaterialCompactTextureDrawer.cs b/Assets/Editor Toolbox/Editor/Drawers/Material/MaterialCompactTextureDrawer.cs index b0c39af5..e2720ca4 100644 --- a/Assets/Editor Toolbox/Editor/Drawers/Material/MaterialCompactTextureDrawer.cs +++ b/Assets/Editor Toolbox/Editor/Drawers/Material/MaterialCompactTextureDrawer.cs @@ -27,7 +27,11 @@ protected override void OnGUISafe(Rect position, MaterialProperty prop, string l protected override bool IsPropertyValid(MaterialProperty prop) { +#if UNITY_6000_3_OR_NEWER + return prop.propertyType == UnityEngine.Rendering.ShaderPropertyType.Texture; +#else return prop.type == MaterialProperty.PropType.Texture; +#endif } } } \ No newline at end of file diff --git a/Assets/Editor Toolbox/Editor/Drawers/Material/MaterialConditionalDrawer.cs b/Assets/Editor Toolbox/Editor/Drawers/Material/MaterialConditionalDrawer.cs index f7baa652..9930ad9d 100644 --- a/Assets/Editor Toolbox/Editor/Drawers/Material/MaterialConditionalDrawer.cs +++ b/Assets/Editor Toolbox/Editor/Drawers/Material/MaterialConditionalDrawer.cs @@ -12,6 +12,17 @@ protected MaterialConditionalDrawer(string togglePropertyName) this.togglePropertyName = togglePropertyName; } + private bool IsValidToggleType(MaterialProperty toggleProp) + { +#if UNITY_6000_3_OR_NEWER + return toggleProp.propertyType == UnityEngine.Rendering.ShaderPropertyType.Float || + toggleProp.propertyType == UnityEngine.Rendering.ShaderPropertyType.Range; +#else + return toggleProp.type == MaterialProperty.PropType.Float || + toggleProp.type == MaterialProperty.PropType.Range; +#endif + } + protected override float GetPropertyHeightSafe(MaterialProperty prop, string label, MaterialEditor editor) { if (!HasToggle(prop)) @@ -48,7 +59,7 @@ protected virtual bool HasToggle(MaterialProperty prop) { var targets = prop.targets; var toggle = MaterialEditor.GetMaterialProperty(targets, togglePropertyName); - return toggle != null && toggle.type == MaterialProperty.PropType.Float || toggle.type == MaterialProperty.PropType.Range; + return toggle != null && IsValidToggleType(toggle); } protected virtual bool? GetValue(MaterialProperty prop) diff --git a/Assets/Editor Toolbox/Editor/Drawers/Material/MaterialMinMaxSliderDrawer.cs b/Assets/Editor Toolbox/Editor/Drawers/Material/MaterialMinMaxSliderDrawer.cs index 0e9c2157..a649d601 100644 --- a/Assets/Editor Toolbox/Editor/Drawers/Material/MaterialMinMaxSliderDrawer.cs +++ b/Assets/Editor Toolbox/Editor/Drawers/Material/MaterialMinMaxSliderDrawer.cs @@ -44,7 +44,11 @@ protected override void OnGUISafe(Rect position, MaterialProperty prop, string l protected override bool IsPropertyValid(MaterialProperty prop) { +#if UNITY_6000_3_OR_NEWER + return prop.propertyType == UnityEngine.Rendering.ShaderPropertyType.Vector; +#else return prop.type == MaterialProperty.PropType.Vector; +#endif } } } \ No newline at end of file diff --git a/Assets/Editor Toolbox/Editor/Drawers/Material/MaterialVector2Drawer.cs b/Assets/Editor Toolbox/Editor/Drawers/Material/MaterialVector2Drawer.cs index 85e2e794..54cb2d65 100644 --- a/Assets/Editor Toolbox/Editor/Drawers/Material/MaterialVector2Drawer.cs +++ b/Assets/Editor Toolbox/Editor/Drawers/Material/MaterialVector2Drawer.cs @@ -30,7 +30,11 @@ protected override void OnGUISafe(Rect position, MaterialProperty prop, string l protected override bool IsPropertyValid(MaterialProperty prop) { +#if UNITY_6000_3_OR_NEWER + return prop.propertyType == UnityEngine.Rendering.ShaderPropertyType.Vector; +#else return prop.type == MaterialProperty.PropType.Vector; +#endif } } } \ No newline at end of file diff --git a/Assets/Editor Toolbox/Editor/Drawers/Material/MaterialVector3Drawer.cs b/Assets/Editor Toolbox/Editor/Drawers/Material/MaterialVector3Drawer.cs index 6768c73e..bd8e8cf5 100644 --- a/Assets/Editor Toolbox/Editor/Drawers/Material/MaterialVector3Drawer.cs +++ b/Assets/Editor Toolbox/Editor/Drawers/Material/MaterialVector3Drawer.cs @@ -30,7 +30,11 @@ protected override void OnGUISafe(Rect position, MaterialProperty prop, string l protected override bool IsPropertyValid(MaterialProperty prop) { +#if UNITY_6000_3_OR_NEWER + return prop.propertyType == UnityEngine.Rendering.ShaderPropertyType.Vector; +#else return prop.type == MaterialProperty.PropType.Vector; +#endif } } } \ No newline at end of file diff --git a/Assets/Editor Toolbox/Editor/ToolboxEditorHierarchy.cs b/Assets/Editor Toolbox/Editor/ToolboxEditorHierarchy.cs index 9baff2c9..5f947558 100644 --- a/Assets/Editor Toolbox/Editor/ToolboxEditorHierarchy.cs +++ b/Assets/Editor Toolbox/Editor/ToolboxEditorHierarchy.cs @@ -46,8 +46,12 @@ private static void OnItemCallback(int instanceId, Rect rect) } //use Unity's internal method to determinate the proper GameObject instance +#if UNITY_6000_3_OR_NEWER + var gameObject = EditorUtility.EntityIdToObject(instanceId) as GameObject; +#else var gameObject = EditorUtility.InstanceIDToObject(instanceId) as GameObject; - if (gameObject) +#endif + if (gameObject != null) { var type = GetLabelType(gameObject, out var label); //draw label using one of the possible forms diff --git a/Assets/Editor Toolbox/Editor/ToolboxEditorToolbar.cs b/Assets/Editor Toolbox/Editor/ToolboxEditorToolbar.cs index fff824c2..096242ea 100644 --- a/Assets/Editor Toolbox/Editor/ToolboxEditorToolbar.cs +++ b/Assets/Editor Toolbox/Editor/ToolboxEditorToolbar.cs @@ -5,9 +5,10 @@ using System.Reflection; using UnityEditor; -using UnityEngine; using Object = UnityEngine.Object; using Unity.EditorCoroutines.Editor; +using UnityEngine; + #if UNITY_2019_1_OR_NEWER using UnityEngine.UIElements; #else @@ -30,7 +31,11 @@ static ToolboxEditorToolbar() } private static readonly Type containterType = typeof(IMGUIContainer); +#if UNITY_6000_3_OR_NEWER + private static readonly Type toolbarType = typeof(UnityEditor.Editor).Assembly.GetType("UnityEditor.MainToolbarWindow"); +#else private static readonly Type toolbarType = typeof(UnityEditor.Editor).Assembly.GetType("UnityEditor.Toolbar"); +#endif private static readonly Type guiViewType = typeof(UnityEditor.Editor).Assembly.GetType("UnityEditor.GUIView"); #if UNITY_2020_1_OR_NEWER private static readonly Type backendType = typeof(UnityEditor.Editor).Assembly.GetType("UnityEditor.IWindowBackend"); @@ -55,24 +60,61 @@ private static IEnumerator Initialize() { while (toolbar == null) { - var toolbars = Resources.FindObjectsOfTypeAll(toolbarType); - if (toolbars == null || toolbars.Length == 0) + if (!TryGetToolbarInstance(out toolbar)) { yield return null; continue; } - else - { - toolbar = toolbars[0]; - } } +#if UNITY_6000_3_OR_NEWER + VisualElement root = null; + if (toolbar is EditorWindow editorWindow) + { + root = editorWindow.rootVisualElement; + } + + var builder = root.Query(name: "DockArea"); + var states = builder.Build(); + + var toolbarLeftZone = states.AtIndex(0); + var leftElement = new VisualElement(); + leftElement.name = "Editor Toolbox Left Area"; + leftElement.StretchToParentSize(); + leftElement.style.left = 10; + leftElement.style.right = 10; + leftElement.style.flexGrow = 1; + leftElement.style.flexDirection = FlexDirection.Row; + + var leftContainer = new IMGUIContainer(); + leftContainer.style.flexGrow = 1; + leftContainer.onGUIHandler = OnGuiLeft; + leftElement.Add(leftContainer); + toolbarLeftZone.Add(leftElement); + + var toolbarRightZone = states.AtIndex(1); + var rightElement = new VisualElement(); + rightElement.name = "Editor Toolbox Right Area"; + rightElement.StretchToParentSize(); + rightElement.style.left = 10; + rightElement.style.right = 10; + rightElement.style.flexGrow = 1; + rightElement.style.flexDirection = FlexDirection.Row; + + var rightContainer = new IMGUIContainer(); + rightContainer.style.flexGrow = 1; + rightContainer.onGUIHandler = OnGuiRight; + + rightElement.Add(rightContainer); + toolbarRightZone.Add(rightElement); + +#else #if UNITY_2021_1_OR_NEWER var rootField = toolbar.GetType().GetField("m_Root", BindingFlags.NonPublic | BindingFlags.Instance); var root = rootField.GetValue(toolbar) as VisualElement; var toolbarLeftZone = root.Q("ToolbarZoneLeftAlign"); - var element = new VisualElement() + var leftElement = new VisualElement() { style = { @@ -81,11 +123,11 @@ private static IEnumerator Initialize() } }; - var container = new IMGUIContainer(); - container.style.flexGrow = 1; - container.onGUIHandler += OnGuiLeft; - element.Add(container); - toolbarLeftZone.Add(element); + var leftContainer = new IMGUIContainer(); + leftContainer.style.flexGrow = 1; + leftContainer.onGUIHandler = OnGuiLeft; + leftElement.Add(leftContainer); + toolbarLeftZone.Add(leftElement); var toolbarRightZone = root.Q("ToolbarZoneRightAlign"); var rightElement = new VisualElement() @@ -99,7 +141,7 @@ private static IEnumerator Initialize() var rightContainer = new IMGUIContainer(); rightContainer.style.flexGrow = 1; - rightContainer.onGUIHandler += OnGuiRight; + rightContainer.onGUIHandler = OnGuiRight; rightElement.Add(rightContainer); toolbarRightZone.Add(rightElement); #else @@ -119,6 +161,27 @@ private static IEnumerator Initialize() handler -= OnGuiLeft; handler += OnGuiLeft; onGuiHandler.SetValue(container, handler); +#endif +#endif + } + + private static bool TryGetToolbarInstance(out Object toolbarInstance) + { +#if UNITY_6000_3_OR_NEWER + toolbarInstance = EditorWindow.GetWindow(toolbarType); + return toolbarInstance != null; +#else + var toolbars = Resources.FindObjectsOfTypeAll(toolbarType); + if (toolbars == null || toolbars.Length == 0) + { + toolbarInstance = null; + return false; + } + else + { + toolbarInstance = toolbars[0]; + return true; + } #endif } @@ -130,9 +193,15 @@ private static void OnGuiLeft() } #if UNITY_2021_1_OR_NEWER - using (new GUILayout.HorizontalScope()) + using (new EditorGUILayout.VerticalScope()) { - OnToolbarGuiLeft(); + GUILayout.FlexibleSpace(); + using (new EditorGUILayout.HorizontalScope()) + { + OnToolbarGuiLeft(); + } + + GUILayout.FlexibleSpace(); } #else var screenWidth = EditorGUIUtility.currentViewWidth; @@ -167,9 +236,15 @@ private static void OnGuiRight() return; } - using (new EditorGUILayout.HorizontalScope()) + using (new EditorGUILayout.VerticalScope()) { - OnToolbarGuiRight(); + GUILayout.FlexibleSpace(); + using (new EditorGUILayout.HorizontalScope()) + { + OnToolbarGuiRight(); + } + + GUILayout.FlexibleSpace(); } } @@ -180,7 +255,12 @@ public static void Repaint() return; } +#if UNITY_6000_3_OR_NEWER + var toolbarWindow = EditorWindow.GetWindow(toolbarType); + toolbarWindow.Repaint(); +#else repaintMethod?.Invoke(toolbar, null); +#endif } public static bool IsToolbarAllowed { get; set; } = true; diff --git a/Assets/Editor Toolbox/Runtime/Serialization/SceneSerializationUtility.cs b/Assets/Editor Toolbox/Runtime/Serialization/SceneSerializationUtility.cs index cd4d9d3d..62e09219 100644 --- a/Assets/Editor Toolbox/Runtime/Serialization/SceneSerializationUtility.cs +++ b/Assets/Editor Toolbox/Runtime/Serialization/SceneSerializationUtility.cs @@ -27,6 +27,30 @@ private static void Initialize() isInitialized = true; } + private static bool TryCreateSceneData(SceneAsset sceneAsset, out SceneData sceneData) + { + var path = AssetDatabase.GetAssetPath(sceneAsset); + if (string.IsNullOrEmpty(path)) + { + sceneData = null; + return false; + } + + sceneData = new SceneData() + { + BuildIndex = SceneUtility.GetBuildIndexByScenePath(path), + SceneName = sceneAsset.name, + ScenePath = path, + }; + + return true; + } + + private static bool CanRefreshCache() + { + return !EditorApplication.isUpdating; + } + internal static void ConfirmCache() { //NOTE: refresh data only if the cache is empty, @@ -39,6 +63,11 @@ internal static void ConfirmCache() internal static void RefreshCache() { + if (!CanRefreshCache()) + { + return; + } + cachedScenes.Clear(); foreach (var scene in EditorBuildSettings.scenes) { @@ -59,12 +88,10 @@ internal static void RefreshCache() continue; } - cachedScenes.Add(sceneAsset, new SceneData() + if (TryCreateSceneData(sceneAsset, out var sceneData)) { - BuildIndex = SceneUtility.GetBuildIndexByScenePath(path), - SceneName = sceneAsset.name, - ScenePath = path - }); + cachedScenes.Add(sceneAsset, sceneData); + } } OnCacheRefreshed?.Invoke(); @@ -72,14 +99,30 @@ internal static void RefreshCache() internal static bool TryGetSceneData(SceneAsset sceneAsset, out SceneData data) { - ConfirmCache(); - if (!sceneAsset || !cachedScenes.TryGetValue(sceneAsset, out data)) + if (sceneAsset == null) { data = null; return false; } - return true; + if (CanRefreshCache()) + { + ConfirmCache(); + if (cachedScenes.TryGetValue(sceneAsset, out data)) + { + return true; + } + } + else + { + if (TryCreateSceneData(sceneAsset, out data)) + { + return true; + } + } + + data = null; + return false; } #endif /// diff --git a/Assets/Editor Toolbox/Runtime/Serialization/SerializedScene.cs b/Assets/Editor Toolbox/Runtime/Serialization/SerializedScene.cs index 649afed9..4cd7a9ee 100644 --- a/Assets/Editor Toolbox/Runtime/Serialization/SerializedScene.cs +++ b/Assets/Editor Toolbox/Runtime/Serialization/SerializedScene.cs @@ -1,5 +1,4 @@ using System; - using Toolbox.Serialization; #if UNITY_EDITOR diff --git a/Assets/Examples/Materials/TestMat.mat b/Assets/Examples/Materials/TestMat.mat index dc71e8b7..e2060a2f 100644 --- a/Assets/Examples/Materials/TestMat.mat +++ b/Assets/Examples/Materials/TestMat.mat @@ -2,14 +2,15 @@ %TAG !u! tag:unity3d.com,2011: --- !u!21 &2100000 Material: - serializedVersion: 6 + serializedVersion: 8 m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_Name: TestMat m_Shader: {fileID: 4800000, guid: 026435d85e5fd1f429a0ff9e8492c6b8, type: 3} - m_ShaderKeywords: + m_ValidKeywords: [] + m_InvalidKeywords: [] m_LightmapFlags: 4 m_EnableInstancingVariants: 0 m_DoubleSidedGI: 0 @@ -59,6 +60,7 @@ Material: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} + m_Ints: [] m_Floats: - _BumpScale: 1 - _Cutoff: 0.5 diff --git a/Assets/Examples/Scripts/SampleBehaviour6.cs b/Assets/Examples/Scripts/SampleBehaviour6.cs index 63b58fbb..fcbb747a 100644 --- a/Assets/Examples/Scripts/SampleBehaviour6.cs +++ b/Assets/Examples/Scripts/SampleBehaviour6.cs @@ -96,6 +96,7 @@ public interface IGenericInterface TValue Value { get; } } + [Serializable] public class IntInterfaceImplementationInt : IGenericInterface { [SerializeField] @@ -104,6 +105,7 @@ public class IntInterfaceImplementationInt : IGenericInterface public int Value => value; } + [Serializable] public class StringInterfaceImplementation : IGenericInterface { [SerializeField] @@ -112,6 +114,7 @@ public class StringInterfaceImplementation : IGenericInterface public string Value => value; } + [Serializable] public class GenericInterfaceImplementation : IGenericInterface { [SerializeField] @@ -122,6 +125,7 @@ public class GenericInterfaceImplementation : IGenericInterface public TValue Value => value; } + [Serializable] public class WrongConstraintGenericInterfaceImplementation : IGenericInterface where TValue : struct { [SerializeField] From 2452f73bee374f815064b5be2c961350699afbbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=82osz=20Matkowski?= Date: Sun, 28 Dec 2025 18:57:02 +0100 Subject: [PATCH 34/46] Minor refactor changes --- .../Editor/ToolboxEditorToolbar.cs | 53 ++++++++----------- 1 file changed, 23 insertions(+), 30 deletions(-) diff --git a/Assets/Editor Toolbox/Editor/ToolboxEditorToolbar.cs b/Assets/Editor Toolbox/Editor/ToolboxEditorToolbar.cs index 096242ea..cbdc6c01 100644 --- a/Assets/Editor Toolbox/Editor/ToolboxEditorToolbar.cs +++ b/Assets/Editor Toolbox/Editor/ToolboxEditorToolbar.cs @@ -15,7 +15,10 @@ using UnityEngine.Experimental.UIElements; #endif -//NOTE: since everything in this class is reflection-based it is a little bit "hacky" +//NOTE: since everything in this class is reflection-based it is a little bit "hacky"; supporting each version makes it very hard to maintain - +// consider implementing different 'drawers' for each Toolbar iteration + +//NOTE: unfortunately latest (6.3) official API is very limited and can't be used to port this implementation namespace Toolbox.Editor { @@ -78,36 +81,27 @@ private static IEnumerator Initialize() var states = builder.Build(); var toolbarLeftZone = states.AtIndex(0); - var leftElement = new VisualElement(); - leftElement.name = "Editor Toolbox Left Area"; - leftElement.StretchToParentSize(); - leftElement.style.left = 10; - leftElement.style.right = 10; - leftElement.style.flexGrow = 1; - leftElement.style.flexDirection = FlexDirection.Row; - - var leftContainer = new IMGUIContainer(); - leftContainer.style.flexGrow = 1; - leftContainer.onGUIHandler = OnGuiLeft; - leftElement.Add(leftContainer); - toolbarLeftZone.Add(leftElement); - + AddIMGUIContainer(toolbarLeftZone, OnGuiLeft, "Editor Toolbox Left Area"); + var toolbarRightZone = states.AtIndex(1); - var rightElement = new VisualElement(); - rightElement.name = "Editor Toolbox Right Area"; - rightElement.StretchToParentSize(); - rightElement.style.left = 10; - rightElement.style.right = 10; - rightElement.style.flexGrow = 1; - rightElement.style.flexDirection = FlexDirection.Row; - - var rightContainer = new IMGUIContainer(); - rightContainer.style.flexGrow = 1; - rightContainer.onGUIHandler = OnGuiRight; - - rightElement.Add(rightContainer); - toolbarRightZone.Add(rightElement); + AddIMGUIContainer(toolbarRightZone, OnGuiRight, "Editor Toolbox Right Area"); + void AddIMGUIContainer(VisualElement parentElement, Action guiCallback, string name) + { + var element = new VisualElement(); + element.name = name; + element.StretchToParentSize(); + element.style.left = 10; + element.style.right = 10; + element.style.flexGrow = 1; + element.style.flexDirection = FlexDirection.Row; + + var guiContainer = new IMGUIContainer(); + guiContainer.style.flexGrow = 1; + guiContainer.onGUIHandler = guiCallback; + element.Add(guiContainer); + parentElement.Add(element); + } #else #if UNITY_2021_1_OR_NEWER var rootField = toolbar.GetType().GetField("m_Root", BindingFlags.NonPublic | BindingFlags.Instance); @@ -151,7 +145,6 @@ private static IEnumerator Initialize() #else var elements = visualTree.GetValue(toolbar, null) as VisualElement; #endif - #if UNITY_2019_1_OR_NEWER var container = elements[0]; #else From 7c2d6552a63a3c8332e2924ce3e6c7a26af710c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=82osz=20Matkowski?= Date: Sun, 28 Dec 2025 19:02:33 +0100 Subject: [PATCH 35/46] Update: README.md --- Assets/Editor Toolbox/README.md | 3 +++ README.md | 3 +++ 2 files changed, 6 insertions(+) diff --git a/Assets/Editor Toolbox/README.md b/Assets/Editor Toolbox/README.md index e776a691..b9c5ec5e 100644 --- a/Assets/Editor Toolbox/README.md +++ b/Assets/Editor Toolbox/README.md @@ -1029,6 +1029,9 @@ Properties that can be edited include: ### Toolbar +> [!IMPORTANT] +> Unity 6.3 provides a new [official API](https://docs.unity3d.com/6000.3/Documentation/ScriptReference/Toolbars.MainToolbarElement.html) for extending the main toolbar. Toolbox implementation still relies on the Reflection and overriding predefined VisualElements setup, therefore consider using the new API. + > Editor Toolbox/Editor/ToolboxEditorToolbar.cs Check **Examples** for more details. diff --git a/README.md b/README.md index e776a691..b9c5ec5e 100644 --- a/README.md +++ b/README.md @@ -1029,6 +1029,9 @@ Properties that can be edited include: ### Toolbar +> [!IMPORTANT] +> Unity 6.3 provides a new [official API](https://docs.unity3d.com/6000.3/Documentation/ScriptReference/Toolbars.MainToolbarElement.html) for extending the main toolbar. Toolbox implementation still relies on the Reflection and overriding predefined VisualElements setup, therefore consider using the new API. + > Editor Toolbox/Editor/ToolboxEditorToolbar.cs Check **Examples** for more details. From 707173723519212443f21b5c523fdc7d54acea82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=82osz=20Matkowski?= Date: Mon, 29 Dec 2025 11:24:24 +0100 Subject: [PATCH 36/46] Minor refactor changes --- Assets/Editor Toolbox/Editor/ToolboxEditorToolbar.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Assets/Editor Toolbox/Editor/ToolboxEditorToolbar.cs b/Assets/Editor Toolbox/Editor/ToolboxEditorToolbar.cs index cbdc6c01..fa3db88d 100644 --- a/Assets/Editor Toolbox/Editor/ToolboxEditorToolbar.cs +++ b/Assets/Editor Toolbox/Editor/ToolboxEditorToolbar.cs @@ -86,8 +86,13 @@ private static IEnumerator Initialize() var toolbarRightZone = states.AtIndex(1); AddIMGUIContainer(toolbarRightZone, OnGuiRight, "Editor Toolbox Right Area"); - void AddIMGUIContainer(VisualElement parentElement, Action guiCallback, string name) + static void AddIMGUIContainer(VisualElement parentElement, Action guiCallback, string name) { + if (parentElement == null) + { + return; + } + var element = new VisualElement(); element.name = name; element.StretchToParentSize(); From 724d6ee1f76983e306fa5aca7eea6ddf58ad3e63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=82osz=20Matkowski?= Date: Mon, 29 Dec 2025 12:10:31 +0100 Subject: [PATCH 37/46] Update: CHANGELOG.md, package.json --- Assets/Editor Toolbox/CHANGELOG.md | 22 ++++++++++++++++++++++ Assets/Editor Toolbox/package.json | 2 +- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/Assets/Editor Toolbox/CHANGELOG.md b/Assets/Editor Toolbox/CHANGELOG.md index 16d1aa47..1b2d0f21 100644 --- a/Assets/Editor Toolbox/CHANGELOG.md +++ b/Assets/Editor Toolbox/CHANGELOG.md @@ -1,3 +1,25 @@ +## 0.14.3 [29.12.2025] + +### Added: +- Ability to specify 'ValueStep' in the ProgressBar attribute + +### Changed: +- Toolbar support for Unity 6.3+ +- Material Drawers support for Unity 6.3+ +- Serialized Scene support for Unity 6.3+ +- Fix issues with copy/past operations for the [SerializeReference]-based arrays + +### Added: +- `AddConfirmationBox` property to the [ReferencePicker]; allows to display an intermediate confirmation window before assigning a new reference + +### Changed: +- Fix [FormattedNumber] behaviour while multi-editing different values +- [LabelByChild] now displays ToString() value for UnityEngine.Object references +- Fix ReorderableLists indentation level for nested objects +- Fix various smaller issues related to [SerializeReference]-based properties while being in the multi-editing mode +- Rename 'Copy Serialize Reference' to 'Copy Serialized References', from now this operation also works on arrays and lists +- Rename 'Paste Serialize Reference' to 'Paste Serialized References', from now this operation also works on arrays and lists + ## 0.14.2 [10.08.2025] ### Added: diff --git a/Assets/Editor Toolbox/package.json b/Assets/Editor Toolbox/package.json index fa196562..737b33e0 100644 --- a/Assets/Editor Toolbox/package.json +++ b/Assets/Editor Toolbox/package.json @@ -1,7 +1,7 @@ { "name": "com.browar.editor-toolbox", "displayName": "Editor Toolbox", - "version": "0.14.2", + "version": "0.14.3", "unity": "2018.1", "description": "Tools, custom attributes, drawers, hierarchy overlay, and other extensions for the Unity Editor.", "keywords": [ From 96d313debc5de238afbf543dde6250dcf6f69959 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=82osz=20Matkowski?= Date: Mon, 29 Dec 2025 12:16:52 +0100 Subject: [PATCH 38/46] Minor refactor changes --- .../ToolboxEditorSceneViewObjectSelector.cs | 58 ++++++++++--------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/Assets/Editor Toolbox/Editor/SceneView/ToolboxEditorSceneViewObjectSelector.cs b/Assets/Editor Toolbox/Editor/SceneView/ToolboxEditorSceneViewObjectSelector.cs index 6556c12e..c3ba1744 100644 --- a/Assets/Editor Toolbox/Editor/SceneView/ToolboxEditorSceneViewObjectSelector.cs +++ b/Assets/Editor Toolbox/Editor/SceneView/ToolboxEditorSceneViewObjectSelector.cs @@ -8,7 +8,6 @@ public class ToolboxEditorSceneViewObjectSelector : EditorWindow { private static readonly Color selectionColor = new Color(0.50f, 0.70f, 1.00f); private static readonly Color highlightWireColor = Color.yellow; - private const float outlineFillOpacity = 1f; private const float sizeXPadding = 2f; private const float sizeYPadding = 2f; @@ -17,10 +16,10 @@ public class ToolboxEditorSceneViewObjectSelector : EditorWindow private const float sizeXOffset = -30f; private const float indentWidth = 12f; - private List displayEntries; + private readonly List highlightedRenderers = new List(); + private List displayEntries; private GameObject highlightedObject; - private readonly List highlightedRenderers = new List(); private Vector2 size; private Vector2 buttonSize; @@ -42,12 +41,12 @@ public static void Show(List gameObjects, Vector2 position) private void OnEnable() { - UnityEditor.SceneView.duringSceneGui += OnSceneViewDuringSceneGui; + UnityEditor.SceneView.duringSceneGui += OnSceneViewGui; } private void OnDisable() { - UnityEditor.SceneView.duringSceneGui -= OnSceneViewDuringSceneGui; + UnityEditor.SceneView.duringSceneGui -= OnSceneViewGui; highlightedRenderers.Clear(); highlightedObject = null; } @@ -325,7 +324,10 @@ private void SelectObject(int id, bool control, bool shift) } else { - Selection.objects = new Object[] { gameObject }; + Selection.objects = new Object[] + { + gameObject + }; } } @@ -377,27 +379,6 @@ private void RemoveSelectedObject(GameObject gameObject) Selection.objects = newSelection; } - private GameObject HighlightedObject - { - set - { - if (highlightedObject == value) - { - return; - } - - highlightedObject = value; - UnityEditor.SceneView.RepaintAll(); - - if (highlightedObject != null) - { - EditorGUIUtility.PingObject(highlightedObject); - } - - UpdateHighlightedRenderers(); - } - } - private void UpdateHighlightedRenderers() { highlightedRenderers.Clear(); @@ -410,7 +391,7 @@ private void UpdateHighlightedRenderers() highlightedRenderers.AddRange(highlightedObject.GetComponentsInChildren(true)); } - private void OnSceneViewDuringSceneGui(UnityEditor.SceneView sceneView) + private void OnSceneViewGui(UnityEditor.SceneView sceneView) { if (highlightedRenderers.Count == 0 || Event.current.type != EventType.Repaint) @@ -438,6 +419,27 @@ private void OnSceneViewDuringSceneGui(UnityEditor.SceneView sceneView) #endif } + private GameObject HighlightedObject + { + set + { + if (highlightedObject == value) + { + return; + } + + highlightedObject = value; + UnityEditor.SceneView.RepaintAll(); + + if (highlightedObject != null) + { + EditorGUIUtility.PingObject(highlightedObject); + } + + UpdateHighlightedRenderers(); + } + } + private readonly struct DisplayEntry { internal DisplayEntry(GameObject gameObject, int depth) From 9e81cecdefd716082a8adf0a61fa5b4b9d8e8674 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=82osz=20Matkowski?= Date: Mon, 29 Dec 2025 12:18:54 +0100 Subject: [PATCH 39/46] Update: CHANGELOG.md --- Assets/Editor Toolbox/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Assets/Editor Toolbox/CHANGELOG.md b/Assets/Editor Toolbox/CHANGELOG.md index 1b2d0f21..f68f0f97 100644 --- a/Assets/Editor Toolbox/CHANGELOG.md +++ b/Assets/Editor Toolbox/CHANGELOG.md @@ -8,6 +8,8 @@ - Material Drawers support for Unity 6.3+ - Serialized Scene support for Unity 6.3+ - Fix issues with copy/past operations for the [SerializeReference]-based arrays +- Ability to display parent objects in the SceneView tool +- Ability to display managed reference values while using LabelByChild attribute ### Added: - `AddConfirmationBox` property to the [ReferencePicker]; allows to display an intermediate confirmation window before assigning a new reference From 8eae0d16e5b82a22bc5a419a920326815fa1932d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=82osz=20Matkowski?= Date: Mon, 29 Dec 2025 12:21:14 +0100 Subject: [PATCH 40/46] Update: CHANGELOG.md --- Assets/Editor Toolbox/CHANGELOG.md | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/Assets/Editor Toolbox/CHANGELOG.md b/Assets/Editor Toolbox/CHANGELOG.md index f68f0f97..a6a47413 100644 --- a/Assets/Editor Toolbox/CHANGELOG.md +++ b/Assets/Editor Toolbox/CHANGELOG.md @@ -11,17 +11,6 @@ - Ability to display parent objects in the SceneView tool - Ability to display managed reference values while using LabelByChild attribute -### Added: -- `AddConfirmationBox` property to the [ReferencePicker]; allows to display an intermediate confirmation window before assigning a new reference - -### Changed: -- Fix [FormattedNumber] behaviour while multi-editing different values -- [LabelByChild] now displays ToString() value for UnityEngine.Object references -- Fix ReorderableLists indentation level for nested objects -- Fix various smaller issues related to [SerializeReference]-based properties while being in the multi-editing mode -- Rename 'Copy Serialize Reference' to 'Copy Serialized References', from now this operation also works on arrays and lists -- Rename 'Paste Serialize Reference' to 'Paste Serialized References', from now this operation also works on arrays and lists - ## 0.14.2 [10.08.2025] ### Added: From 594083e397a378b773b8a229a646c8ce4b8da3fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=82osz=20Matkowski?= Date: Mon, 29 Dec 2025 12:26:00 +0100 Subject: [PATCH 41/46] Fix compilation errors for Unity 2018 --- .../SceneView/ToolboxEditorSceneViewObjectSelector.cs | 6 ++++++ Assets/Editor Toolbox/Editor/Utilities/PropertyUtility.cs | 2 ++ 2 files changed, 8 insertions(+) diff --git a/Assets/Editor Toolbox/Editor/SceneView/ToolboxEditorSceneViewObjectSelector.cs b/Assets/Editor Toolbox/Editor/SceneView/ToolboxEditorSceneViewObjectSelector.cs index c3ba1744..b3fbe60a 100644 --- a/Assets/Editor Toolbox/Editor/SceneView/ToolboxEditorSceneViewObjectSelector.cs +++ b/Assets/Editor Toolbox/Editor/SceneView/ToolboxEditorSceneViewObjectSelector.cs @@ -41,12 +41,16 @@ public static void Show(List gameObjects, Vector2 position) private void OnEnable() { +#if UNITY_2019_1_OR_NEWER UnityEditor.SceneView.duringSceneGui += OnSceneViewGui; +#endif } private void OnDisable() { +#if UNITY_2019_1_OR_NEWER UnityEditor.SceneView.duringSceneGui -= OnSceneViewGui; +#endif highlightedRenderers.Clear(); highlightedObject = null; } @@ -391,6 +395,7 @@ private void UpdateHighlightedRenderers() highlightedRenderers.AddRange(highlightedObject.GetComponentsInChildren(true)); } +#if UNITY_2019_1_OR_NEWER private void OnSceneViewGui(UnityEditor.SceneView sceneView) { if (highlightedRenderers.Count == 0 || @@ -418,6 +423,7 @@ private void OnSceneViewGui(UnityEditor.SceneView sceneView) } #endif } +#endif private GameObject HighlightedObject { diff --git a/Assets/Editor Toolbox/Editor/Utilities/PropertyUtility.cs b/Assets/Editor Toolbox/Editor/Utilities/PropertyUtility.cs index ae134234..6440dba4 100644 --- a/Assets/Editor Toolbox/Editor/Utilities/PropertyUtility.cs +++ b/Assets/Editor Toolbox/Editor/Utilities/PropertyUtility.cs @@ -428,9 +428,11 @@ public static void OverrideLabelByValue(GUIContent label, SerializedProperty pro case SerializedPropertyType.ObjectReference: label.text = property.objectReferenceValue ? property.objectReferenceValue.ToString() : "null"; break; +#if UNITY_2019_3_OR_NEWER case SerializedPropertyType.ManagedReference: label.text = property.managedReferenceValue?.ToString() ?? "null"; break; +#endif case SerializedPropertyType.LayerMask: switch (property.intValue) { From 706de3f1a7eb64bc46904706c8b96f4a4b58e7f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=82osz=20Matkowski?= Date: Mon, 29 Dec 2025 12:28:18 +0100 Subject: [PATCH 42/46] Fix compilation errors for Unity 2019 --- Assets/Editor Toolbox/Editor/Utilities/PropertyUtility.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/Editor Toolbox/Editor/Utilities/PropertyUtility.cs b/Assets/Editor Toolbox/Editor/Utilities/PropertyUtility.cs index 6440dba4..32e57b07 100644 --- a/Assets/Editor Toolbox/Editor/Utilities/PropertyUtility.cs +++ b/Assets/Editor Toolbox/Editor/Utilities/PropertyUtility.cs @@ -428,7 +428,7 @@ public static void OverrideLabelByValue(GUIContent label, SerializedProperty pro case SerializedPropertyType.ObjectReference: label.text = property.objectReferenceValue ? property.objectReferenceValue.ToString() : "null"; break; -#if UNITY_2019_3_OR_NEWER +#if UNITY_2021_3_OR_NEWER case SerializedPropertyType.ManagedReference: label.text = property.managedReferenceValue?.ToString() ?? "null"; break; From 54adca67f268ab6474ad3ebe8d5e5b19d74c2ec4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=82osz=20Matkowski?= Date: Mon, 29 Dec 2025 12:30:51 +0100 Subject: [PATCH 43/46] Update: README.md --- Assets/Editor Toolbox/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Assets/Editor Toolbox/README.md b/Assets/Editor Toolbox/README.md index b9c5ec5e..bedb6b66 100644 --- a/Assets/Editor Toolbox/README.md +++ b/Assets/Editor Toolbox/README.md @@ -1091,6 +1091,8 @@ public static class MyEditorUtility Select a specific object that is under the cursor (default key: tab). +Requires at least Unity 2019.1.x to work. + ![inspector](https://github.com/arimger/Unity-Editor-Toolbox/blob/develop/Docs/sceneview.png) ### Utilities From 5ec33e6c9661c27b03ab336a9472f8ea32a0bbaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=82osz=20Matkowski?= Date: Mon, 29 Dec 2025 12:31:00 +0100 Subject: [PATCH 44/46] Update: README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index b9c5ec5e..bedb6b66 100644 --- a/README.md +++ b/README.md @@ -1091,6 +1091,8 @@ public static class MyEditorUtility Select a specific object that is under the cursor (default key: tab). +Requires at least Unity 2019.1.x to work. + ![inspector](https://github.com/arimger/Unity-Editor-Toolbox/blob/develop/Docs/sceneview.png) ### Utilities From f3f6ec7c5b504eb756d868c29bf80b82d21a6072 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=82osz=20Matkowski?= Date: Mon, 29 Dec 2025 12:39:43 +0100 Subject: [PATCH 45/46] Fix compilation errors in Unity 6.3 --- .../Editor/SceneView/ToolboxEditorSceneViewObjectSelector.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/Editor Toolbox/Editor/SceneView/ToolboxEditorSceneViewObjectSelector.cs b/Assets/Editor Toolbox/Editor/SceneView/ToolboxEditorSceneViewObjectSelector.cs index b3fbe60a..f392b780 100644 --- a/Assets/Editor Toolbox/Editor/SceneView/ToolboxEditorSceneViewObjectSelector.cs +++ b/Assets/Editor Toolbox/Editor/SceneView/ToolboxEditorSceneViewObjectSelector.cs @@ -406,7 +406,7 @@ private void OnSceneViewGui(UnityEditor.SceneView sceneView) // Unity 6.1+ provides Handles.DrawOutline which also highlights children in one call. #if UNITY_6000_1_OR_NEWER - Handles.DrawOutline(highlightedRenderers.ToArray(), highlightWireColor, highlightWireColor, outlineFillOpacity); + Handles.DrawOutline(highlightedRenderers.ToArray(), highlightWireColor, highlightWireColor, 0.5f); #else using (new Handles.DrawingScope(highlightWireColor)) { From e9903c9785cbb197495a96e1a38ffe882062ac1c Mon Sep 17 00:00:00 2001 From: "Mayke Rodrigues (Mike)" <54370952+MaykerStudio@users.noreply.github.com> Date: Sat, 7 Feb 2026 23:03:07 -0300 Subject: [PATCH 46/46] Add tabbed inspector UI and attributes Introduce tabbed inspector support: add runtime attributes (BeginTabGroupAttribute, TabAttribute, EndTabGroupAttribute and TabGroupVisual) and an editor drawer (BeginTabGroupAttributeDrawer) with responsive, styled tab rendering (Default/Flat/Segmented), tab state management and type/field discovery caching. Register new drawers in Editor Toolbox settings and add TabAttributeDrawer/EndTabGroup handling. Update example scene and SampleBehaviour4 to demonstrate tab groups and polish formatting. Also include small EditorUserSettings tweaks. --- .../Decorator/BeginTabGroupAttributeDrawer.cs | 621 ++++++++++++++++++ .../BeginTabGroupAttributeDrawer.cs.meta | 11 + Assets/Editor Toolbox/EditorSettings.asset | 3 + .../BeginTabGroupAttribute.cs | 41 ++ .../BeginTabGroupAttribute.cs.meta | 11 + Assets/Examples/Scenes/SampleScene.unity | 18 + Assets/Examples/Scripts/SampleBehaviour4.cs | 111 +++- 7 files changed, 797 insertions(+), 19 deletions(-) create mode 100644 Assets/Editor Toolbox/Editor/Drawers/Toolbox/Decorator/BeginTabGroupAttributeDrawer.cs create mode 100644 Assets/Editor Toolbox/Editor/Drawers/Toolbox/Decorator/BeginTabGroupAttributeDrawer.cs.meta create mode 100644 Assets/Editor Toolbox/Runtime/Attributes/Property/Toolbox/DecoratorAttributes/BeginTabGroupAttribute.cs create mode 100644 Assets/Editor Toolbox/Runtime/Attributes/Property/Toolbox/DecoratorAttributes/BeginTabGroupAttribute.cs.meta diff --git a/Assets/Editor Toolbox/Editor/Drawers/Toolbox/Decorator/BeginTabGroupAttributeDrawer.cs b/Assets/Editor Toolbox/Editor/Drawers/Toolbox/Decorator/BeginTabGroupAttributeDrawer.cs new file mode 100644 index 00000000..83759418 --- /dev/null +++ b/Assets/Editor Toolbox/Editor/Drawers/Toolbox/Decorator/BeginTabGroupAttributeDrawer.cs @@ -0,0 +1,621 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using UnityEditor; +using UnityEngine; + +namespace Toolbox.Editor.Drawers +{ + public sealed class BeginTabGroupAttributeDrawer + : ToolboxDecoratorDrawer + { + #region CONSTANTS + private const float ViewWidthPadding = 32f; + private const float TabSpacing = 8f; + private const float MinTabWidth = 40f; + private const float TabHeight = 22f; + private const float RowSpacing = 4f; + private const float TopSpacing = 2f; + + private static readonly Color InactiveBgMultiplier = new(0.6f, 0.6f, 0.6f, 0.6f); + private static readonly Color ActiveBgColor = new(0.8f, 0.8f, 0.8f, 1f); + + #endregion + + #region STYLE + + private static GUIStyle _baseTabStyle; + private static GUIStyle _activeTabStyle; + private static GUIStyle _headerStyle; + + private static GUIStyle BaseTabStyle + { + get + { + _baseTabStyle ??= new GUIStyle(EditorStyles.toolbarButton) + { + fixedHeight = TabHeight, + padding = new RectOffset(10, 10, 4, 4), + }; + return _baseTabStyle; + } + } + + private static GUIStyle ActiveTabStyle + { + get + { + _activeTabStyle ??= new GUIStyle(BaseTabStyle) { fontStyle = FontStyle.Bold }; + return _activeTabStyle; + } + } + + private static GUIStyle HeaderStyle + { + get + { + _headerStyle ??= new GUIStyle(EditorStyles.boldLabel) + { + alignment = TextAnchor.MiddleCenter, + padding = new RectOffset(0, 0, 2, 2), + }; + return _headerStyle; + } + } + + private static GUIStyle _flatStyle; + private static GUIStyle _flatActiveStyle; + private static GUIStyle _segmentLeft; + private static GUIStyle _segmentMid; + private static GUIStyle _segmentRight; + private static GUIStyle _segmentActive; + + private static GUIStyle FlatStyle + { + get + { + if (_flatStyle == null) + { + _flatStyle = new GUIStyle(EditorStyles.label) + { + alignment = TextAnchor.MiddleCenter, + fixedHeight = TabHeight, + padding = new RectOffset(10, 10, 4, 4), + margin = new RectOffset(2, 2, 0, 0), + }; + } + return _flatStyle; + } + } + + private static GUIStyle FlatActiveStyle + { + get + { + if (_flatActiveStyle == null) + { + _flatActiveStyle = new GUIStyle(FlatStyle) { fontStyle = FontStyle.Bold }; + } + return _flatActiveStyle; + } + } + + private static GUIStyle SegmentLeft => + _segmentLeft ??= new GUIStyle(EditorStyles.miniButtonLeft) { fixedHeight = TabHeight }; + private static GUIStyle SegmentMid => + _segmentMid ??= new GUIStyle(EditorStyles.miniButtonMid) { fixedHeight = TabHeight }; + private static GUIStyle SegmentRight => + _segmentRight ??= new GUIStyle(EditorStyles.miniButtonRight) + { + fixedHeight = TabHeight, + }; + + private static GUIStyle SegmentActive + { + get + { + _segmentActive ??= new GUIStyle(EditorStyles.miniButtonMid) + { + fontStyle = FontStyle.Bold, + }; + return _segmentActive; + } + } + + #endregion + + protected override void OnGuiBeginSafe(BeginTabGroupAttribute attribute) + { + var targetType = TabDiscovery.GetContextType(attribute.GroupId); + if (targetType == null) + return; + + var tabs = TabDiscovery.GetTabsForGroup(targetType, attribute.GroupId); + if (tabs == null || tabs.Count == 0) + return; + + InitializeDefaultTab(attribute.GroupId, tabs); + + var currentTab = GetActiveTab(attribute.GroupId, tabs); + int currentIndex = tabs.IndexOf(currentTab); + + if (currentIndex == -1) + currentIndex = 0; + + DrawGroupHeader(attribute.GroupId); + EditorGUILayout.BeginVertical(EditorStyles.helpBox); + + int newIndex = DrawResponsiveTabs(currentIndex, tabs, attribute.Visual); + + if (newIndex != currentIndex) + { + TabState.Set(attribute.GroupId, tabs[newIndex]); + + GUI.FocusControl(null); + EditorGUIUtility.keyboardControl = 0; + EditorWindow.focusedWindow?.Repaint(); + GUIUtility.ExitGUI(); + } + } + + private static void InitializeDefaultTab(string groupId, List tabs) + { + if (!TabState.Has(groupId)) + { + TabState.Set(groupId, tabs[0]); + } + } + + private static string GetActiveTab(string groupId, List tabs) + { + if (TabState.TryGet(groupId, out var activeTab) && tabs.Contains(activeTab)) + return activeTab; + + return tabs[0]; + } + + private static void DrawGroupHeader(string groupId) + { + EditorGUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + GUILayout.Label(groupId, HeaderStyle); + GUILayout.FlexibleSpace(); + EditorGUILayout.EndHorizontal(); + + GUILayout.Space(TopSpacing); + } + + private static int DrawResponsiveTabs( + int currentIndex, + List tabs, + TabGroupVisual visual + ) + { + float viewWidth = EditorGUIUtility.currentViewWidth - ViewWidthPadding; + + var tabWidths = CalculateTabWidths(tabs, ActiveTabStyle, viewWidth); + var rows = DistributeTabsIntoRows(tabs, tabWidths, viewWidth); + + RotateRowsToShowActiveTabLast(rows, currentIndex); + + int newIndex = DrawTabRows(rows, currentIndex, tabs, tabWidths, visual); + + return newIndex; + } + + private static List CalculateTabWidths( + List tabs, + GUIStyle style, + float viewWidth + ) + { + var tabWidths = new List(tabs.Count); + + foreach (var tab in tabs) + { + var content = new GUIContent(tab); + float width = style.CalcSize(content).x + TabSpacing; + + width = Mathf.Max(width, MinTabWidth); + width = Mathf.Min(width, Mathf.Max(MinTabWidth, viewWidth - TabSpacing)); + + tabWidths.Add(width); + } + + return tabWidths; + } + + private static List> DistributeTabsIntoRows( + List tabs, + List tabWidths, + float viewWidth + ) + { + var rows = new List>(); + var currentRow = new List(); + float rowAccumulator = 0f; + + for (int i = 0; i < tabs.Count; i++) + { + float width = tabWidths[i]; + + if (currentRow.Count == 0) + { + currentRow.Add(i); + rowAccumulator = width; + continue; + } + + if (rowAccumulator + width > viewWidth) + { + rows.Add(currentRow); + currentRow = new List { i }; + rowAccumulator = width; + } + else + { + currentRow.Add(i); + rowAccumulator += width; + } + } + + if (currentRow.Count > 0) + rows.Add(currentRow); + + return rows; + } + + private static void RotateRowsToShowActiveTabLast(List> rows, int currentIndex) + { + if (rows.Count <= 1) + return; + + int activeRowIndex = FindRowContainingTab(rows, currentIndex); + + if (activeRowIndex == rows.Count - 1) + return; + + var rotated = new List>(); + + for (int r = activeRowIndex + 1; r < rows.Count; r++) + rotated.Add(rows[r]); + + for (int r = 0; r <= activeRowIndex; r++) + rotated.Add(rows[r]); + + rows.Clear(); + rows.AddRange(rotated); + } + + private static int FindRowContainingTab(List> rows, int tabIndex) + { + for (int r = 0; r < rows.Count; r++) + { + if (rows[r].Contains(tabIndex)) + return r; + } + return 0; + } + + private static int DrawTabRows( + List> rows, + int currentIndex, + List tabs, + List tabWidths, + TabGroupVisual visual + ) + { + int newIndex = currentIndex; + + EditorGUILayout.BeginVertical(); + + for (int r = 0; r < rows.Count; r++) + { + var row = rows[r]; + + EditorGUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + + newIndex = DrawTabRow(row, currentIndex, newIndex, tabs, tabWidths, visual); + + GUILayout.FlexibleSpace(); + EditorGUILayout.EndHorizontal(); + + if (r < rows.Count - 1) + GUILayout.Space(RowSpacing); + } + + GUILayout.Space(RowSpacing); + EditorGUILayout.EndVertical(); + + return newIndex; + } + + private static int DrawTabRow( + List row, + int currentIndex, + int newIndex, + List tabs, + List tabWidths, + TabGroupVisual visual + ) + { + for (int i = 0; i < row.Count; i++) + { + int tabIndex = row[i]; + bool isActive = tabIndex == currentIndex; + var content = new GUIContent(tabs[tabIndex]); + float width = tabWidths[tabIndex]; + + GUIStyle style = GetStyleForVisual(visual, i, row.Count, isActive); + + Color prevBg = GUI.backgroundColor; + + switch (visual) + { + case TabGroupVisual.Flat: + GUI.backgroundColor = isActive ? ActiveBgColor : GUI.backgroundColor; + break; + + case TabGroupVisual.Segmented: + + GUI.backgroundColor = isActive + ? ActiveBgColor + : GUI.backgroundColor * InactiveBgMultiplier; + break; + + default: + GUI.backgroundColor = isActive + ? ActiveBgColor + : GUI.backgroundColor * InactiveBgMultiplier; + style = isActive ? ActiveTabStyle : BaseTabStyle; + break; + } + + bool pressed = GUILayout.Toggle(isActive, content, style, GUILayout.Width(width)); + GUI.backgroundColor = prevBg; + + if (pressed && !isActive) + newIndex = tabIndex; + } + + return newIndex; + } + + private static GUIStyle GetStyleForVisual( + TabGroupVisual visual, + int index, + int count, + bool isActive + ) + { + switch (visual) + { + case TabGroupVisual.Flat: + return isActive ? FlatActiveStyle : FlatStyle; + + case TabGroupVisual.Segmented: + if (count == 1) + return isActive ? SegmentActive : SegmentMid; + + if (index == 0) + return SegmentLeft; + + if (index == count - 1) + return SegmentRight; + + return isActive ? SegmentActive : SegmentMid; + + default: + return isActive ? ActiveTabStyle : BaseTabStyle; + } + } + } + + public sealed class TabAttributeDrawer : ToolboxConditionDrawer + { + protected override PropertyCondition OnGuiValidateSafe( + SerializedProperty property, + TabAttribute attribute + ) + { + var targetType = property.serializedObject.targetObject.GetType(); + var groupId = TabDiscovery.GetGroupForField(targetType, property.name); + + if (string.IsNullOrEmpty(groupId)) + return PropertyCondition.Valid; + + return TabState.IsActive(groupId, attribute.Tab) + ? PropertyCondition.Valid + : PropertyCondition.NonValid; + } + } + + public sealed class EndTabGroupAttributeDrawer : ToolboxDecoratorDrawer + { + protected override void OnGuiCloseSafe(EndTabGroupAttribute attribute) + { + EditorGUILayout.EndVertical(); + } + } + + internal static class TabState + { + private static readonly Dictionary ActiveTabs = new(); + + public static void Set(string groupId, string tab) + { + ActiveTabs[groupId] = tab; + } + + public static bool TryGet(string groupId, out string tab) + { + return ActiveTabs.TryGetValue(groupId, out tab); + } + + public static bool IsActive(string groupId, string tab) + { + return ActiveTabs.TryGetValue(groupId, out var active) && active == tab; + } + + public static bool Has(string groupId) + { + return ActiveTabs.ContainsKey(groupId); + } + } + + internal static class TabDiscovery + { + private struct GroupData + { + public Dictionary> GroupToTabs; + public Dictionary FieldToGroup; + } + + private static readonly Dictionary TypeCache = new(); + + private struct ContextKey : IEquatable + { + public int InstanceId; + public string GroupId; + + public bool Equals(ContextKey other) => + InstanceId == other.InstanceId && GroupId == other.GroupId; + + public override int GetHashCode() => + (InstanceId * 397) ^ (GroupId != null ? GroupId.GetHashCode() : 0); + } + + private static readonly Dictionary ContextCache = new(); + private static int _lastSelectionId = -1; + + static TabDiscovery() + { + Selection.selectionChanged += ClearContextCache; + } + + private static void ClearContextCache() + { + ContextCache.Clear(); + _lastSelectionId = -1; + } + + public static Type GetContextType(string groupId) + { + var target = Selection.activeObject; + if (target == null) + return null; + + int instanceId = target.GetInstanceID(); + + if (_lastSelectionId != instanceId) + { + ContextCache.Clear(); + _lastSelectionId = instanceId; + } + + var key = new ContextKey { InstanceId = instanceId, GroupId = groupId }; + + if (ContextCache.TryGetValue(key, out var cached)) + return cached; + + Type result = ResolveContextType(target, groupId); + ContextCache[key] = result; + return result; + } + + private static Type ResolveContextType(UnityEngine.Object target, string groupId) + { + if (target is not GameObject go) + return target.GetType(); + + var components = go.GetComponents(); + for (int i = 0; i < components.Length; i++) + { + var comp = components[i]; + if (comp == null) + continue; + + var type = comp.GetType(); + EnsureCached(type); + + if (TypeCache[type].GroupToTabs.ContainsKey(groupId)) + return type; + } + + return null; + } + + public static List GetTabsForGroup(Type type, string groupId) + { + EnsureCached(type); + return TypeCache[type].GroupToTabs.TryGetValue(groupId, out var tabs) ? tabs : null; + } + + public static string GetGroupForField(Type type, string fieldName) + { + EnsureCached(type); + return TypeCache[type].FieldToGroup.TryGetValue(fieldName, out var group) + ? group + : null; + } + + private static void EnsureCached(Type type) + { + if (!TypeCache.ContainsKey(type)) + BuildCache(type); + } + + private static void BuildCache(Type type) + { + var groupToTabs = new Dictionary>(); + var fieldToGroup = new Dictionary(); + string currentGroup = null; + + var fields = type.GetFields( + BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic + ); + + for (int i = 0; i < fields.Length; i++) + { + var field = fields[i]; + + var groupAttr = field.GetCustomAttribute(); + if (groupAttr != null) + { + currentGroup = groupAttr.GroupId; + if (!groupToTabs.ContainsKey(currentGroup)) + groupToTabs[currentGroup] = new List(); + } + + var tabAttr = field.GetCustomAttribute(); + if (tabAttr != null && !string.IsNullOrEmpty(currentGroup)) + { + var tabs = groupToTabs[currentGroup]; + if (!tabs.Contains(tabAttr.Tab)) + tabs.Add(tabAttr.Tab); + + fieldToGroup[field.Name] = currentGroup; + } + } + + TypeCache[type] = new GroupData + { + GroupToTabs = groupToTabs, + FieldToGroup = fieldToGroup, + }; + } + + public static void ClearCache() + { + TypeCache.Clear(); + ContextCache.Clear(); + } + + [InitializeOnLoadMethod] + private static void ClearCachesOnDomainReload() + { + ClearCache(); + } + } +} diff --git a/Assets/Editor Toolbox/Editor/Drawers/Toolbox/Decorator/BeginTabGroupAttributeDrawer.cs.meta b/Assets/Editor Toolbox/Editor/Drawers/Toolbox/Decorator/BeginTabGroupAttributeDrawer.cs.meta new file mode 100644 index 00000000..42d8eeb5 --- /dev/null +++ b/Assets/Editor Toolbox/Editor/Drawers/Toolbox/Decorator/BeginTabGroupAttributeDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9418b0c77c4a3f8438cb67edad1cb58a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Editor Toolbox/EditorSettings.asset b/Assets/Editor Toolbox/EditorSettings.asset index 760893bd..9df20a40 100644 --- a/Assets/Editor Toolbox/EditorSettings.asset +++ b/Assets/Editor Toolbox/EditorSettings.asset @@ -55,6 +55,8 @@ MonoBehaviour: - typeReference: Toolbox.Editor.Drawers.BeginHorizontalAttributeDrawer, Toolbox.Editor - typeReference: Toolbox.Editor.Drawers.BeginHorizontalGroupAttributeDrawer, Toolbox.Editor - typeReference: Toolbox.Editor.Drawers.BeginIndentAttributeDrawer, Toolbox.Editor + - typeReference: Toolbox.Editor.Drawers.BeginTabGroupAttributeDrawer, Toolbox.Editor + - typeReference: Toolbox.Editor.Drawers.EndTabGroupAttributeDrawer, Toolbox.Editor - typeReference: Toolbox.Editor.Drawers.BeginVerticalAttributeDrawer, Toolbox.Editor - typeReference: Toolbox.Editor.Drawers.DynamicHelpAttributeDrawer, Toolbox.Editor - typeReference: Toolbox.Editor.Drawers.EditorButtonAttributeDrawer, Toolbox.Editor @@ -84,6 +86,7 @@ MonoBehaviour: - typeReference: Toolbox.Editor.Drawers.ShowDisabledIfAttributeDrawer, Toolbox.Editor - typeReference: Toolbox.Editor.Drawers.ShowIfAttributeDrawer, Toolbox.Editor - typeReference: Toolbox.Editor.Drawers.ShowWarningIfAttributeDrawer, Toolbox.Editor + - typeReference: Toolbox.Editor.Drawers.TabAttributeDrawer, Toolbox.Editor selfPropertyDrawerHandlers: - typeReference: Toolbox.Editor.Drawers.DynamicMinMaxSliderAttributeDrawer, Toolbox.Editor - typeReference: Toolbox.Editor.Drawers.DynamicRangeAttributeDrawer, Toolbox.Editor diff --git a/Assets/Editor Toolbox/Runtime/Attributes/Property/Toolbox/DecoratorAttributes/BeginTabGroupAttribute.cs b/Assets/Editor Toolbox/Runtime/Attributes/Property/Toolbox/DecoratorAttributes/BeginTabGroupAttribute.cs new file mode 100644 index 00000000..e807d089 --- /dev/null +++ b/Assets/Editor Toolbox/Runtime/Attributes/Property/Toolbox/DecoratorAttributes/BeginTabGroupAttribute.cs @@ -0,0 +1,41 @@ +using System; + +namespace UnityEngine +{ + public enum TabGroupVisual + { + Default, + Flat, // modern flat buttons + Segmented, // connected segmented control + } + + [AttributeUsage(AttributeTargets.Field)] + public sealed class BeginTabGroupAttribute : ToolboxDecoratorAttribute + { + public string GroupId { get; } + public TabGroupVisual Visual { get; } + + public BeginTabGroupAttribute( + string groupId = "Default", + TabGroupVisual visual = TabGroupVisual.Default + ) + { + GroupId = groupId; + Visual = visual; + } + } + + [AttributeUsage(AttributeTargets.Field)] + public sealed class TabAttribute : ToolboxConditionAttribute + { + public string Tab { get; } + + public TabAttribute(string tab) + { + Tab = tab; + } + } + + [AttributeUsage(AttributeTargets.Field)] + public sealed class EndTabGroupAttribute : ToolboxDecoratorAttribute { } +} diff --git a/Assets/Editor Toolbox/Runtime/Attributes/Property/Toolbox/DecoratorAttributes/BeginTabGroupAttribute.cs.meta b/Assets/Editor Toolbox/Runtime/Attributes/Property/Toolbox/DecoratorAttributes/BeginTabGroupAttribute.cs.meta new file mode 100644 index 00000000..0a565feb --- /dev/null +++ b/Assets/Editor Toolbox/Runtime/Attributes/Property/Toolbox/DecoratorAttributes/BeginTabGroupAttribute.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2943103fdabde2b42aa7746bbc0be679 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Examples/Scenes/SampleScene.unity b/Assets/Examples/Scenes/SampleScene.unity index cefc5421..e1824939 100644 --- a/Assets/Examples/Scenes/SampleScene.unity +++ b/Assets/Examples/Scenes/SampleScene.unity @@ -2262,6 +2262,24 @@ MonoBehaviour: var57: 0 nestedObject: var0: -12 + CharacterName: Name + Level: 0 + Health: 100 + Mana: 0 + Stamina: 0 + MoveSpeed: 4 + Acceleration: 10 + JumpForce: 9 + attackForce: {x: 0, y: 0} + attackDamage: 0 + testVar1: test string + testVar2: 0 + testVar3: 0 + testVar4: {x: 0, y: 0} + testVar5: {x: 0, y: 0, z: 0} + testVar6: {fileID: 0} + testVar7: 0 + testVar8: asdads --- !u!4 &1438743619 Transform: m_ObjectHideFlags: 2 diff --git a/Assets/Examples/Scripts/SampleBehaviour4.cs b/Assets/Examples/Scripts/SampleBehaviour4.cs index c52e0ff6..26525b85 100644 --- a/Assets/Examples/Scripts/SampleBehaviour4.cs +++ b/Assets/Examples/Scripts/SampleBehaviour4.cs @@ -12,7 +12,7 @@ private class SampleNestedClass [DynamicHelp(nameof(GetHelpMessage), UnityMessageType.Info)] [EditorButton(nameof(TestNestedMethod))] public int var0; - + private void TestNestedMethod() { Debug.Log(nameof(TestNestedMethod) + " is called"); @@ -25,7 +25,6 @@ private string GetHelpMessage() } [Label("Help", skinStyle: SkinStyle.Box)] - [Disable] [Help("Very useful warning", UnityMessageType.Warning)] [Help("This error example", UnityMessageType.Error, ApplyCondition = true)] @@ -33,9 +32,17 @@ private string GetHelpMessage() public int var0; [Label("Button", skinStyle: SkinStyle.Box)] - - [EditorButton(nameof(TestMethod), Tooltip = "Custom Tooltip", ValidateMethodName = nameof(ValidationMethod), PositionType = ButtonPositionType.Above)] - [EditorButton(nameof(TestCoroutine), "Test Coroutine", activityType: ButtonActivityType.OnPlayMode)] + [EditorButton( + nameof(TestMethod), + Tooltip = "Custom Tooltip", + ValidateMethodName = nameof(ValidationMethod), + PositionType = ButtonPositionType.Above + )] + [EditorButton( + nameof(TestCoroutine), + "Test Coroutine", + activityType: ButtonActivityType.OnPlayMode + )] [EditorButton(nameof(TestStaticMethod), activityType: ButtonActivityType.OnEditMode)] public int var1; @@ -64,95 +71,161 @@ private static void TestStaticMethod() } [Label("Vertical Layout", skinStyle: SkinStyle.Box)] - [BeginGroup("Parent group")] public int y; + [BeginGroup("Nested group", Style = GroupStyle.Boxed)] public int var14; + [Line] public int var15; + [SpaceArea(20, 20)] public int var16; + [BeginIndent] public int var17; public int var18; + [Title("Standard Header")] public GameObject go; + [Label("Custom Header")] [EndIndent] public int var19; + [EndGroup] [Line] [Line(HexColor = "#9800FF")] public int var20; + [EndGroup] public int x; [Label("Horizontal Layout", skinStyle: SkinStyle.Box)] - [BeginHorizontal(LabelWidth = 50.0f)] public int var29; + [SpaceArea(10)] public int var30; + [EndHorizontal] public int var31; [Label("Horizontal Layout (Group)", skinStyle: SkinStyle.Box)] - - [BeginHorizontalGroup(Label = "Horizontal Group", ControlFieldWidth = true, ElementsInLayout = 2, Style = GroupStyle.Round)] + [BeginHorizontalGroup( + Label = "Horizontal Group", + ControlFieldWidth = true, + ElementsInLayout = 2, + Style = GroupStyle.Round + )] [ReorderableList(Foldable = true), InLineEditor] public GameObject[] gameObjects; + [SpaceArea] [EndHorizontalGroup] [ReorderableList] public float[] floats; [Label("Indentation", skinStyle: SkinStyle.Box)] - public int var2; + [BeginIndent] public int var3; + [EndIndent] public int var4; + [IndentArea(3)] public int var5; [Label("Highlight", skinStyle: SkinStyle.Box)] - [Highlight(0.8f, 1.0f, 0.2f)] public GameObject var28; [Label("Dynamic Help", skinStyle: SkinStyle.Box)] - [DynamicHelp(nameof(MessageSource))] public int var39; - public string MessageSource => string.Format("Dynamic Message Source. {0} = {1}", nameof(var39), var39); + public string MessageSource => + string.Format("Dynamic Message Source. {0} = {1}", nameof(var39), var39); [Label("Image Area", skinStyle: SkinStyle.Box)] - [ImageArea("https://img.itch.zone/aW1nLzE5Mjc3NzUucG5n/original/Viawjm.png", 180.0f)] public int var55; [Label("GUI Color", skinStyle: SkinStyle.Box)] - [GuiColor(1, 0, 0)] public int var56; [Label("Label Width", skinStyle: SkinStyle.Box)] - [LabelWidth(220.0f)] public int veryVeryVeryVeryVeryLongName; [Label("Title", skinStyle: SkinStyle.Box)] - [Title("Standard Title")] public int var57; [Label("Nested Objects", skinStyle: SkinStyle.Box)] - [Help("You can use Toolbox Attributes inside serializable types without limitations.")] [SerializeField] private SampleNestedClass nestedObject; -} \ No newline at end of file + [BeginTabGroup("Tab Example")] + [Tab("General")] + public string CharacterName; + + [Tab("General")] + public int Level; + + [Tab("Stats")] + public int Health; + + [Tab("Stats")] + public int Mana; + + [Tab("Stats")] + public int Stamina; + + [Tab("Movement")] + public float MoveSpeed; + + [Tab("Movement")] + public float Acceleration; + + [Tab("Movement")] + public float JumpForce; + + [Tab("Attack")] + public Vector2 attackForce; + + [Tab("Attack")] + [EndTabGroup] + public int attackDamage; + + [BeginTabGroup("Test Tab Group", TabGroupVisual.Segmented)] + [Tab("Tab 1")] + public string testVar1; + + [Tab("Tab 1")] + public float testVar2; + + [Tab("Tab 1")] + public int testVar3; + + [Tab("Tab 2")] + public Vector2 testVar4; + + [Tab("Tab 2")] + public Vector3 testVar5; + + [Tab("Tab 2")] + public GameObject testVar6; + + [Tab("Tab 3")] + public int testVar7; + + [Tab("Tab 3")] + [EndTabGroup] + public string testVar8; +}