Major refactor.

Core now formed and contains all useful utility to create custom ui with restrictions.
Many methods was extracted from different classes and moved to core.

Undo is now supported(because unity's undo bug seems to be fixed).

Example now moved into same level separate folder.
This commit is contained in:
TextusGames 2020-05-16 20:15:24 +03:00
parent f47dd5d350
commit eeaadfc569
65 changed files with 138 additions and 86 deletions

View File

@ -0,0 +1,95 @@
#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
using Object = UnityEngine.Object;
public static class ManagedReferenceUtility
{
/// Creates instance of passed type and assigns it to managed reference
public static object AssignNewInstanceOfTypeToManagedReference(this SerializedProperty serializedProperty, Type type)
{
var 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<Type> GetAppropriateTypesForAssigningToManagedReference(this SerializedProperty property, List<Func<Type, bool>> filters = null)
{
var 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<Type> GetAppropriateTypesForAssigningToManagedReference(Type fieldType, List<Func<Type, bool>> filters = null)
{
var appropriateTypes = new List<Type>();
// Get and filter all appropriate types
var derivedTypes = TypeCache.GetTypesDerivedFrom(fieldType);
foreach (var 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;
// 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)
{
var 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)
{
var names = GetSplitNamesFromTypename(stringType);
var realType = Type.GetType($"{names.ClassName}, {names.AssemblyName}");
return realType;
}
/// Get assembly and class names from typeName
public static (string AssemblyName, string ClassName) GetSplitNamesFromTypename(string typename)
{
if (string.IsNullOrEmpty(typename))
return ("","");
var typeSplitString = typename.Split(char.Parse(" "));
var typeClassName = typeSplitString[1];
var typeAssemblyName = typeSplitString[0];
return (typeAssemblyName, typeClassName);
}
}
#endif

View File

@ -22,72 +22,39 @@ public static class SerializeReferenceGenericSelectionMenu
context.ShowAsContext(); context.ShowAsContext();
} }
private static void FillContextMenu(IEnumerable<Func<Type, bool>> enumerableFilters, GenericMenu contextMenu, SerializedProperty property) private static void FillContextMenu(IEnumerable<Func<Type, bool>> enumerableFilters, GenericMenu contextMenu, SerializedProperty property)
{ {
var filters = enumerableFilters.ToList();// Prevents possible multiple enumerations var filters = enumerableFilters.ToList();// Prevents possible multiple enumerations
// Adds "Make Null" menu command // Adds "Make Null" menu command
contextMenu.AddItem(new GUIContent("Null"), false, () => MakeSerializedPropertyNull(property)); contextMenu.AddItem(new GUIContent("Null"), false, property.SetManagedReferenceToNull);
// Find real type of managed reference // Collects appropriate types
var realPropertyType = SerializeReferenceTypeNameUtility.GetRealTypeFromTypename(property.managedReferenceFieldTypename); var appropriateTypes = property.GetAppropriateTypesForAssigningToManagedReference(filters);
if (realPropertyType == null)
{
Debug.LogError("Can not get type from");
return;
}
// Get and filter all appropriate types // Adds appropriate types to menu
var types = TypeCache.GetTypesDerivedFrom(realPropertyType); foreach (var appropriateType in appropriateTypes)
foreach (var type in types) AddItemToContextMenu(appropriateType, contextMenu, property);
{
// Skips unity engine Objects (because they are not serialized by SerializeReference)
if(type.IsSubclassOf(typeof(UnityEngine.Object)))
continue;
// Skip abstract classes because they should not be instantiated
if(type.IsAbstract)
continue;
// Filter types by provided filters if there is ones
if (FilterTypeByFilters(filters, type) == false)
continue;
AddItemToContextMenu(type, contextMenu, property);
}
}
private static void MakeSerializedPropertyNull(SerializedProperty serializedProperty)
{
serializedProperty.serializedObject.Update();
serializedProperty.managedReferenceValue = null;
serializedProperty.serializedObject.ApplyModifiedPropertiesWithoutUndo(); // undo is bugged for now
} }
private static void AddItemToContextMenu(Type type, GenericMenu genericMenuContext, SerializedProperty property) private static void AddItemToContextMenu(Type type, GenericMenu genericMenuContext, SerializedProperty property)
{ {
var assemblyName = type.Assembly.ToString().Split('(', ',')[0]; var assemblyName = type.Assembly.ToString().Split('(', ',')[0];
var entryName = type + " ( " + assemblyName + " )"; var entryName = type + " ( " + assemblyName + " )";
genericMenuContext.AddItem(new GUIContent(entryName), false, AssignNewInstanceOfType, new AssignInstanceGenericMenuParameter(type, property)); genericMenuContext.AddItem(new GUIContent(entryName), false, AssignNewInstanceCommand, new GenericMenuParameterForAssignInstanceCommand(type, property));
} }
private static void AssignNewInstanceOfType(object objectGenericMenuParameter ) private static void AssignNewInstanceCommand(object objectGenericMenuParameter )
{ {
var parameter = (AssignInstanceGenericMenuParameter) objectGenericMenuParameter; var parameter = (GenericMenuParameterForAssignInstanceCommand) objectGenericMenuParameter;
var type = parameter.Type; var type = parameter.Type;
var property = parameter.Property; var property = parameter.Property;
var instance = Activator.CreateInstance(type); property.AssignNewInstanceOfTypeToManagedReference(type);
property.serializedObject.Update();
property.managedReferenceValue = instance;
property.serializedObject.ApplyModifiedPropertiesWithoutUndo(); // undo is bugged for now
} }
private static bool FilterTypeByFilters (IEnumerable<Func<Type,bool>> filters, Type type) => private readonly struct GenericMenuParameterForAssignInstanceCommand
filters.All(f => f == null || f.Invoke(type));
private readonly struct AssignInstanceGenericMenuParameter
{ {
public AssignInstanceGenericMenuParameter(Type type, SerializedProperty property) public GenericMenuParameterForAssignInstanceCommand(Type type, SerializedProperty property)
{ {
Type = type; Type = type;
Property = property; Property = property;

View File

@ -1,24 +0,0 @@
using System;
/// This utility exists, because serialize reference managed reference typename returns combined string
/// and not data class that contains separate strings for assembly name and for class name (and possibly namespace name)
public static class SerializeReferenceTypeNameUtility
{
public static Type GetRealTypeFromTypename(string stringType)
{
var names = GetSplitNamesFromTypename(stringType);
var realType = Type.GetType($"{names.ClassName}, {names.AssemblyName}");
return realType;
}
public static (string AssemblyName, string ClassName) GetSplitNamesFromTypename(string typename)
{
if (string.IsNullOrEmpty(typename))
return ("","");
var typeSplitString = typename.Split(char.Parse(" "));
var typeClassName = typeSplitString[1];
var typeAssemblyName = typeSplitString[0];
return (typeAssemblyName, typeClassName);
}
}

View File

@ -1,4 +1,4 @@
using System;  using System;
using System.Linq; using System.Linq;
public static class SerializeReferenceTypeRestrictionFilters public static class SerializeReferenceTypeRestrictionFilters

View File

@ -20,7 +20,7 @@ public class SerializeReferenceButtonAttributeDrawer : PropertyDrawer
var labelPosition = new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight); var labelPosition = new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight);
EditorGUI.LabelField(labelPosition, label); EditorGUI.LabelField(labelPosition, label);
var typeRestrictions = SerializedReferenceUIBuiltInTypeRestrictions.GetAllBuiltInTypeRestrictions(fieldInfo); var typeRestrictions = SerializedReferenceUIDefaultTypeRestrictions.GetAllBuiltInTypeRestrictions(fieldInfo);
property.DrawSelectionButtonForManagedReference(position, typeRestrictions); property.DrawSelectionButtonForManagedReference(position, typeRestrictions);
EditorGUI.PropertyField(position, property, GUIContent.none, true); EditorGUI.PropertyField(position, property, GUIContent.none, true);

View File

@ -15,7 +15,7 @@ public class SerializeReferenceMenuAttributeDrawer : PropertyDrawer
{ {
EditorGUI.BeginProperty(position, label, property); EditorGUI.BeginProperty(position, label, property);
var typeRestrictions = SerializedReferenceUIBuiltInTypeRestrictions.GetAllBuiltInTypeRestrictions(fieldInfo); var typeRestrictions = SerializedReferenceUIDefaultTypeRestrictions.GetAllBuiltInTypeRestrictions(fieldInfo);
property.ShowContextMenuForManagedReferenceOnMouseMiddleButton(position, typeRestrictions); property.ShowContextMenuForManagedReferenceOnMouseMiddleButton(position, typeRestrictions);
EditorGUI.PropertyField(position, property, true); EditorGUI.PropertyField(position, property, true);

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3fbe55d72b5fd59459fdb52e0f301ea1
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c0c77910f7962a14cb85132596cb8d24
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -2,7 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection; using System.Reflection;
public static class SerializedReferenceUIBuiltInTypeRestrictions public static class SerializedReferenceUIDefaultTypeRestrictions
{ {
public static IEnumerable<Func<Type, bool>> GetAllBuiltInTypeRestrictions(FieldInfo fieldInfo) public static IEnumerable<Func<Type, bool>> GetAllBuiltInTypeRestrictions(FieldInfo fieldInfo)
{ {

View File

@ -31,7 +31,7 @@ public static class SerializeReferenceInspectorButton
GUI.backgroundColor = backgroundColor; GUI.backgroundColor = backgroundColor;
var names = SerializeReferenceTypeNameUtility.GetSplitNamesFromTypename(property.managedReferenceFullTypename); var names = ManagedReferenceUtility.GetSplitNamesFromTypename(property.managedReferenceFullTypename);
var className = string.IsNullOrEmpty(names.ClassName) ? "Null (Assign)" : names.ClassName; var className = string.IsNullOrEmpty(names.ClassName) ? "Null (Assign)" : names.ClassName;
var assemblyName = names.AssemblyName; var assemblyName = names.AssemblyName;
if (GUI.Button(buttonPosition, new GUIContent(className, className + " ( "+ assemblyName +" )" ))) if (GUI.Button(buttonPosition, new GUIContent(className, className + " ( "+ assemblyName +" )" )))

View File

@ -171,19 +171,17 @@ MonoBehaviour:
references: references:
version: 1 version: 1
00000000: 00000000:
type: {class: GreenApe, ns: , asm: SerializedReferenceExample} type: {class: GoldenApe, ns: , asm: SerializedReferenceExample}
data: data:
age: 0 age: 1231
food: {fileID: 0} food: {fileID: 1131539293}
numberOfBones: 0 numberOfBones: 33
Tag: Tag: dd
00000001: 00000001:
type: {class: RedCat, ns: , asm: SerializedReferenceExample} type: {class: AnimalChild, ns: , asm: AmimalWorld}
data: data:
age: 0 age: 3213
food: {fileID: 0} food: {fileID: 0}
numberOfBones: 0
Tag:
00000002: 00000002:
type: {class: , ns: , asm: } type: {class: , ns: , asm: }
00000003: 00000003: