diff --git a/Editor/Attributes/SerializeReferenceButtonAttributeDrawer.cs b/Editor/Attributes/SerializeReferenceButtonAttributeDrawer.cs index 3353d9b..ca8bc60 100644 --- a/Editor/Attributes/SerializeReferenceButtonAttributeDrawer.cs +++ b/Editor/Attributes/SerializeReferenceButtonAttributeDrawer.cs @@ -4,27 +4,30 @@ using System.Collections.Generic; using UnityEditor; using UnityEngine; -[CustomPropertyDrawer(typeof(SerializeReferenceButtonAttribute))] -public class SerializeReferenceButtonAttributeDrawer : PropertyDrawer +namespace Textus.SerializeReferenceUI.Editor { - public override float GetPropertyHeight(SerializedProperty property, GUIContent label) + [CustomPropertyDrawer(typeof(SerializeReferenceButtonAttribute))] + public class SerializeReferenceButtonAttributeDrawer : PropertyDrawer { - return EditorGUI.GetPropertyHeight(property, true); + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) + { + return EditorGUI.GetPropertyHeight(property, true); + } + + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) + { + EditorGUI.BeginProperty(position, label, property); + + Rect labelPosition = new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight); + EditorGUI.LabelField(labelPosition, label); + + IEnumerable> typeRestrictions = SerializedReferenceUIDefaultTypeRestrictions.GetAllBuiltInTypeRestrictions(fieldInfo); + property.DrawSelectionButtonForManagedReference(position, typeRestrictions); + + EditorGUI.PropertyField(position, property, GUIContent.none, true); + + EditorGUI.EndProperty(); + } } - - public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) - { - EditorGUI.BeginProperty(position, label, property); - - Rect labelPosition = new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight); - EditorGUI.LabelField(labelPosition, label); - - IEnumerable> typeRestrictions = SerializedReferenceUIDefaultTypeRestrictions.GetAllBuiltInTypeRestrictions(fieldInfo); - property.DrawSelectionButtonForManagedReference(position, typeRestrictions); - - EditorGUI.PropertyField(position, property, GUIContent.none, true); - - EditorGUI.EndProperty(); - } } #endif \ No newline at end of file diff --git a/Editor/Attributes/SerializeReferenceMenuAttributeDrawer.cs b/Editor/Attributes/SerializeReferenceMenuAttributeDrawer.cs index 89aa9ab..69130d1 100644 --- a/Editor/Attributes/SerializeReferenceMenuAttributeDrawer.cs +++ b/Editor/Attributes/SerializeReferenceMenuAttributeDrawer.cs @@ -4,23 +4,26 @@ using System.Collections.Generic; using UnityEditor; using UnityEngine; -[CustomPropertyDrawer(typeof(SerializeReferenceMenuAttribute))] -public class SerializeReferenceMenuAttributeDrawer : PropertyDrawer +namespace Textus.SerializeReferenceUI.Editor { - public override float GetPropertyHeight(SerializedProperty property, GUIContent label) + [CustomPropertyDrawer(typeof(SerializeReferenceMenuAttribute))] + public class SerializeReferenceMenuAttributeDrawer : PropertyDrawer { - return EditorGUI.GetPropertyHeight(property, true); - } + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) + { + return EditorGUI.GetPropertyHeight(property, true); + } - public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) - { - EditorGUI.BeginProperty(position, label, property); + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) + { + EditorGUI.BeginProperty(position, label, property); - IEnumerable> typeRestrictions = SerializedReferenceUIDefaultTypeRestrictions.GetAllBuiltInTypeRestrictions(fieldInfo); - property.ShowContextMenuForManagedReferenceOnMouseMiddleButton(position, typeRestrictions); - - EditorGUI.PropertyField(position, property, true); - EditorGUI.EndProperty(); - } + IEnumerable> typeRestrictions = SerializedReferenceUIDefaultTypeRestrictions.GetAllBuiltInTypeRestrictions(fieldInfo); + property.ShowContextMenuForManagedReferenceOnMouseMiddleButton(position, typeRestrictions); + + EditorGUI.PropertyField(position, property, true); + EditorGUI.EndProperty(); + } + } } #endif \ No newline at end of file diff --git a/Editor/Core/ManagedReferenceUtility.cs b/Editor/Core/ManagedReferenceUtility.cs index ff81572..dc9ba37 100644 --- a/Editor/Core/ManagedReferenceUtility.cs +++ b/Editor/Core/ManagedReferenceUtility.cs @@ -6,111 +6,114 @@ using UnityEditor; using UnityEngine; using Object = UnityEngine.Object; -public static class ManagedReferenceUtility +namespace Textus.SerializeReferenceUI.Editor { - /// Creates instance of passed type and assigns it to managed reference - public static object AssignNewInstanceOfTypeToManagedReference(this SerializedProperty serializedProperty, Type type) + public static class ManagedReferenceUtility { - object instance = Activator.CreateInstance(type); - - serializedProperty.serializedObject.Update(); - serializedProperty.managedReferenceValue = instance; - serializedProperty.serializedObject.ApplyModifiedProperties(); - - return instance; - } - - /// Sets managed reference to null - public static void SetManagedReferenceToNull(this SerializedProperty serializedProperty) - { - serializedProperty.serializedObject.Update(); - serializedProperty.managedReferenceValue = null; - serializedProperty.serializedObject.ApplyModifiedProperties(); - } - - /// Collects appropriate types based on managed reference field type and filters. Filters all derive - public static IEnumerable GetAppropriateTypesForAssigningToManagedReference(this SerializedProperty property, List> filters = null) - { - Type fieldType = property.GetManagedReferenceFieldType(); - return GetAppropriateTypesForAssigningToManagedReference(fieldType, filters); - } - - /// Filters derived types of field typ parameter and finds ones whose are compatible with managed reference and filters. - public static IEnumerable GetAppropriateTypesForAssigningToManagedReference(Type fieldType, List> filters = null) - { - List appropriateTypes = new List(); - - // Get and filter all appropriate types - TypeCache.TypeCollection derivedTypes = TypeCache.GetTypesDerivedFrom(fieldType); - foreach (Type type in derivedTypes) + /// Creates instance of passed type and assigns it to managed reference + public static object AssignNewInstanceOfTypeToManagedReference(this SerializedProperty serializedProperty, Type type) { - // Skips unity engine Objects (because they are not serialized by SerializeReference) - if (type.IsSubclassOf(typeof(Object))) - { - continue; - } - // Skip abstract classes because they should not be instantiated - if (type.IsAbstract) - { - continue; - } - // Skip generic classes because they can not be instantiated - if (type.ContainsGenericParameters) - { - continue; - } - // Skip types that has no public empty constructors (activator can not create them) - if (type.IsClass && type.GetConstructor(Type.EmptyTypes) == null) // Structs still can be created (strangely) - { - continue; - } - // Filter types by provided filters if there is ones - if (filters != null && filters.All(f => f == null || f.Invoke(type)) == false) - { - continue; - } + object instance = Activator.CreateInstance(type); - appropriateTypes.Add(type); + serializedProperty.serializedObject.Update(); + serializedProperty.managedReferenceValue = instance; + serializedProperty.serializedObject.ApplyModifiedProperties(); + + return instance; } - return appropriateTypes; - } - - /// Gets real type of managed reference - public static Type GetManagedReferenceFieldType(this SerializedProperty property) - { - Type realPropertyType = GetRealTypeFromTypename(property.managedReferenceFieldTypename); - if (realPropertyType != null) + /// Sets managed reference to null + public static void SetManagedReferenceToNull(this SerializedProperty serializedProperty) { - return realPropertyType; - } - - Debug.LogError($"Can not get field type of managed reference : {property.managedReferenceFieldTypename}"); - return null; - } - - /// Gets real type of managed reference's field typeName - public static Type GetRealTypeFromTypename(string stringType) - { - (string AssemblyName, string ClassName) = GetSplitNamesFromTypename(stringType); - Type realType = Type.GetType($"{ClassName}, {AssemblyName}"); - - return realType; - } - - /// Get assembly and class names from typeName - public static (string AssemblyName, string ClassName) GetSplitNamesFromTypename(string typename) - { - if (string.IsNullOrEmpty(typename)) - { - return ("",""); + serializedProperty.serializedObject.Update(); + serializedProperty.managedReferenceValue = null; + serializedProperty.serializedObject.ApplyModifiedProperties(); } - string[] typeSplitString = typename.Split(char.Parse(" ")); - string typeClassName = typeSplitString[1]; - string typeAssemblyName = typeSplitString[0]; + /// Collects appropriate types based on managed reference field type and filters. Filters all derive + public static IEnumerable GetAppropriateTypesForAssigningToManagedReference(this SerializedProperty property, List> filters = null) + { + Type fieldType = property.GetManagedReferenceFieldType(); + return GetAppropriateTypesForAssigningToManagedReference(fieldType, filters); + } - return (typeAssemblyName, typeClassName); - } + /// Filters derived types of field typ parameter and finds ones whose are compatible with managed reference and filters. + public static IEnumerable GetAppropriateTypesForAssigningToManagedReference(Type fieldType, List> filters = null) + { + List appropriateTypes = new List(); + + // Get and filter all appropriate types + TypeCache.TypeCollection derivedTypes = TypeCache.GetTypesDerivedFrom(fieldType); + foreach (Type type in derivedTypes) + { + // Skips unity engine Objects (because they are not serialized by SerializeReference) + if (type.IsSubclassOf(typeof(Object))) + { + continue; + } + // Skip abstract classes because they should not be instantiated + if (type.IsAbstract) + { + continue; + } + // Skip generic classes because they can not be instantiated + if (type.ContainsGenericParameters) + { + continue; + } + // Skip types that has no public empty constructors (activator can not create them) + if (type.IsClass && type.GetConstructor(Type.EmptyTypes) == null) // Structs still can be created (strangely) + { + continue; + } + // Filter types by provided filters if there is ones + if (filters != null && filters.All(f => f == null || f.Invoke(type)) == false) + { + continue; + } + + appropriateTypes.Add(type); + } + + return appropriateTypes; + } + + /// Gets real type of managed reference + public static Type GetManagedReferenceFieldType(this SerializedProperty property) + { + Type realPropertyType = GetRealTypeFromTypename(property.managedReferenceFieldTypename); + if (realPropertyType != null) + { + return realPropertyType; + } + + Debug.LogError($"Can not get field type of managed reference : {property.managedReferenceFieldTypename}"); + return null; + } + + /// Gets real type of managed reference's field typeName + public static Type GetRealTypeFromTypename(string stringType) + { + (string AssemblyName, string ClassName) = GetSplitNamesFromTypename(stringType); + Type realType = Type.GetType($"{ClassName}, {AssemblyName}"); + + return realType; + } + + /// Get assembly and class names from typeName + public static (string AssemblyName, string ClassName) GetSplitNamesFromTypename(string typename) + { + if (string.IsNullOrEmpty(typename)) + { + return ("", ""); + } + + string[] typeSplitString = typename.Split(char.Parse(" ")); + string typeClassName = typeSplitString[1]; + string typeAssemblyName = typeSplitString[0]; + + return (typeAssemblyName, typeClassName); + } + } } #endif \ No newline at end of file diff --git a/Editor/Core/SerializeReferenceGenericSelectionMenu.cs b/Editor/Core/SerializeReferenceGenericSelectionMenu.cs index d16cb05..ed5d652 100644 --- a/Editor/Core/SerializeReferenceGenericSelectionMenu.cs +++ b/Editor/Core/SerializeReferenceGenericSelectionMenu.cs @@ -4,79 +4,82 @@ using System.Collections.Generic; using System.Linq; using UnityEditor; using UnityEngine; - -public static class SerializeReferenceGenericSelectionMenu + +namespace Textus.SerializeReferenceUI.Editor { - /// Purpose. - /// This is generic selection menu (will appear at position). - /// Filtering. - /// You can add substring filter here to filter by search string. - /// As well ass type or interface restrictions. - /// As well as any custom restriction that is based on input type. - /// And it will be performed on each Appropriate type found by TypeCache. - public static void ShowContextMenuForManagedReference(this SerializedProperty property, Rect position, IEnumerable> filters = null) + public static class SerializeReferenceGenericSelectionMenu { - GenericMenu context = new GenericMenu(); - FillContextMenu(filters, context, property); - context.DropDown(position); - } - - /// Purpose. - /// This is generic selection menu (will appear at click position). - /// Filtering. - /// You can add substring filter here to filter by search string. - /// As well ass type or interface restrictions. - /// As well as any custom restriction that is based on input type. - /// And it will be performed on each Appropriate type found by TypeCache. - public static void ShowContextMenuForManagedReference(this SerializedProperty property, IEnumerable> filters = null) - { - GenericMenu context = new GenericMenu(); - FillContextMenu(filters, context, property); - context.ShowAsContext(); - } - - private static void FillContextMenu(IEnumerable> enumerableFilters, GenericMenu contextMenu, SerializedProperty property) - { - List> filters = enumerableFilters.ToList();// Prevents possible multiple enumerations - - // Adds "Make Null" menu command - contextMenu.AddItem(new GUIContent("Null"), false, property.SetManagedReferenceToNull); - - // Collects appropriate types - IEnumerable appropriateTypes = property.GetAppropriateTypesForAssigningToManagedReference(filters); - - // Adds appropriate types to menu - foreach (Type appropriateType in appropriateTypes) + /// Purpose. + /// This is generic selection menu (will appear at position). + /// Filtering. + /// You can add substring filter here to filter by search string. + /// As well ass type or interface restrictions. + /// As well as any custom restriction that is based on input type. + /// And it will be performed on each Appropriate type found by TypeCache. + public static void ShowContextMenuForManagedReference(this SerializedProperty property, Rect position, IEnumerable> filters = null) { - AddItemToContextMenu(appropriateType, contextMenu, property); + GenericMenu context = new GenericMenu(); + FillContextMenu(filters, context, property); + context.DropDown(position); } - } - - private static void AddItemToContextMenu(Type type, GenericMenu genericMenuContext, SerializedProperty property) - { - string assemblyName = type.Assembly.ToString().Split('(', ',')[0]; - string entryName = type + " ( " + assemblyName + " )"; - genericMenuContext.AddItem(new GUIContent(entryName), false, AssignNewInstanceCommand, new GenericMenuParameterForAssignInstanceCommand(type, property)); - } - - private static void AssignNewInstanceCommand(object objectGenericMenuParameter ) - { - GenericMenuParameterForAssignInstanceCommand parameter = (GenericMenuParameterForAssignInstanceCommand) objectGenericMenuParameter; - Type type = parameter.Type; - SerializedProperty property = parameter.Property; - property.AssignNewInstanceOfTypeToManagedReference(type); - } - private readonly struct GenericMenuParameterForAssignInstanceCommand - { - public GenericMenuParameterForAssignInstanceCommand(Type type, SerializedProperty property) + /// Purpose. + /// This is generic selection menu (will appear at click position). + /// Filtering. + /// You can add substring filter here to filter by search string. + /// As well ass type or interface restrictions. + /// As well as any custom restriction that is based on input type. + /// And it will be performed on each Appropriate type found by TypeCache. + public static void ShowContextMenuForManagedReference(this SerializedProperty property, IEnumerable> filters = null) { - Type = type; - Property = property; + GenericMenu context = new GenericMenu(); + FillContextMenu(filters, context, property); + context.ShowAsContext(); } - - public readonly SerializedProperty Property; - public readonly Type Type; - } -} + + private static void FillContextMenu(IEnumerable> enumerableFilters, GenericMenu contextMenu, SerializedProperty property) + { + List> filters = enumerableFilters.ToList();// Prevents possible multiple enumerations + + // Adds "Make Null" menu command + contextMenu.AddItem(new GUIContent("Null"), false, property.SetManagedReferenceToNull); + + // Collects appropriate types + IEnumerable appropriateTypes = property.GetAppropriateTypesForAssigningToManagedReference(filters); + + // Adds appropriate types to menu + foreach (Type appropriateType in appropriateTypes) + { + AddItemToContextMenu(appropriateType, contextMenu, property); + } + } + + private static void AddItemToContextMenu(Type type, GenericMenu genericMenuContext, SerializedProperty property) + { + string assemblyName = type.Assembly.ToString().Split('(', ',')[0]; + string entryName = type + " ( " + assemblyName + " )"; + genericMenuContext.AddItem(new GUIContent(entryName), false, AssignNewInstanceCommand, new GenericMenuParameterForAssignInstanceCommand(type, property)); + } + + private static void AssignNewInstanceCommand(object objectGenericMenuParameter) + { + GenericMenuParameterForAssignInstanceCommand parameter = (GenericMenuParameterForAssignInstanceCommand)objectGenericMenuParameter; + Type type = parameter.Type; + SerializedProperty property = parameter.Property; + property.AssignNewInstanceOfTypeToManagedReference(type); + } + + private readonly struct GenericMenuParameterForAssignInstanceCommand + { + public GenericMenuParameterForAssignInstanceCommand(Type type, SerializedProperty property) + { + Type = type; + Property = property; + } + + public readonly SerializedProperty Property; + public readonly Type Type; + } + } +} #endif \ No newline at end of file diff --git a/Editor/DefaultUI/SerializeReferenceInspectorButton.cs b/Editor/DefaultUI/SerializeReferenceInspectorButton.cs index ba34cdb..c3c8468 100644 --- a/Editor/DefaultUI/SerializeReferenceInspectorButton.cs +++ b/Editor/DefaultUI/SerializeReferenceInspectorButton.cs @@ -4,35 +4,38 @@ using System.Collections.Generic; using UnityEditor; using UnityEngine; -public static class SerializeReferenceInspectorButton +namespace Textus.SerializeReferenceUI.Editor { - /// Must be drawn before DefaultProperty in order to receive input - public static void DrawSelectionButtonForManagedReference(this SerializedProperty property, Rect position, IEnumerable> filters = null) + public static class SerializeReferenceInspectorButton { - Rect buttonPosition = position; - buttonPosition.x += EditorGUIUtility.labelWidth + 1 * EditorGUIUtility.standardVerticalSpacing; - buttonPosition.width = position.width - EditorGUIUtility.labelWidth - 1 * EditorGUIUtility.standardVerticalSpacing; - buttonPosition.height = EditorGUIUtility.singleLineHeight; - - Color storedColor = GUI.backgroundColor; - int storedIndent = EditorGUI.indentLevel; - EditorGUI.indentLevel = 0; - - (string AssemblyName, string ClassName) = ManagedReferenceUtility.GetSplitNamesFromTypename(property.managedReferenceFullTypename); - - bool isNull = string.IsNullOrEmpty(ClassName); - GUI.backgroundColor = isNull ? Color.red : storedColor; - - string className = isNull ? "Null (Assign)" : ClassName; - string assemblyName = AssemblyName; - - if (GUI.Button(buttonPosition, new GUIContent(className, className + " ( "+ assemblyName +" )"))) + /// Must be drawn before DefaultProperty in order to receive input + public static void DrawSelectionButtonForManagedReference(this SerializedProperty property, Rect position, IEnumerable> filters = null) { - property.ShowContextMenuForManagedReference(buttonPosition, filters); + Rect buttonPosition = position; + buttonPosition.x += EditorGUIUtility.labelWidth + 1 * EditorGUIUtility.standardVerticalSpacing; + buttonPosition.width = position.width - EditorGUIUtility.labelWidth - 1 * EditorGUIUtility.standardVerticalSpacing; + buttonPosition.height = EditorGUIUtility.singleLineHeight; + + Color storedColor = GUI.backgroundColor; + int storedIndent = EditorGUI.indentLevel; + EditorGUI.indentLevel = 0; + + (string AssemblyName, string ClassName) = ManagedReferenceUtility.GetSplitNamesFromTypename(property.managedReferenceFullTypename); + + bool isNull = string.IsNullOrEmpty(ClassName); + GUI.backgroundColor = isNull ? Color.red : storedColor; + + string className = isNull ? "Null (Assign)" : ClassName; + string assemblyName = AssemblyName; + + if (GUI.Button(buttonPosition, new GUIContent(className, className + " ( " + assemblyName + " )"))) + { + property.ShowContextMenuForManagedReference(buttonPosition, filters); + } + + GUI.backgroundColor = storedColor; + EditorGUI.indentLevel = storedIndent; } - - GUI.backgroundColor = storedColor; - EditorGUI.indentLevel = storedIndent; - } + } } #endif \ No newline at end of file diff --git a/Editor/DefaultUI/SerializeReferenceInspectorMiddleMouseMenu.cs b/Editor/DefaultUI/SerializeReferenceInspectorMiddleMouseMenu.cs index c5883d3..7175f32 100644 --- a/Editor/DefaultUI/SerializeReferenceInspectorMiddleMouseMenu.cs +++ b/Editor/DefaultUI/SerializeReferenceInspectorMiddleMouseMenu.cs @@ -4,17 +4,20 @@ using System.Collections.Generic; using UnityEditor; using UnityEngine; -public static class SerializeReferenceInspectorMiddleMouseMenu +namespace Textus.SerializeReferenceUI.Editor { - public static void ShowContextMenuForManagedReferenceOnMouseMiddleButton(this SerializedProperty property, Rect position, IEnumerable> filters = null) + public static class SerializeReferenceInspectorMiddleMouseMenu { - Event e = Event.current; - if (e.type != EventType.MouseDown || !position.Contains(e.mousePosition) || e.button != 2) + public static void ShowContextMenuForManagedReferenceOnMouseMiddleButton(this SerializedProperty property, Rect position, IEnumerable> filters = null) { - return; + Event e = Event.current; + if (e.type != EventType.MouseDown || !position.Contains(e.mousePosition) || e.button != 2) + { + return; + } + + property.ShowContextMenuForManagedReference(filters); } - - property.ShowContextMenuForManagedReference(filters); } } #endif \ No newline at end of file