diff options
Diffstat (limited to 'Library/PackageCache/com.unity.timeline@1.2.13/Editor/Utilities/ObjectReferenceField.cs')
| -rw-r--r-- | Library/PackageCache/com.unity.timeline@1.2.13/Editor/Utilities/ObjectReferenceField.cs | 195 |
1 files changed, 195 insertions, 0 deletions
diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Utilities/ObjectReferenceField.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Utilities/ObjectReferenceField.cs new file mode 100644 index 0000000..3ace197 --- /dev/null +++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Utilities/ObjectReferenceField.cs @@ -0,0 +1,195 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using UnityEngine; +using UnityEngine.Timeline; +using UnityEditor; +using UnityEditor.SceneManagement; +using UnityEngine.Playables; +using Object = UnityEngine.Object; + +namespace UnityEditor.Timeline +{ + // Describes the object references on a ScriptableObject, ignoring script fields + struct ObjectReferenceField + { + public string propertyPath; + public bool isSceneReference; + public System.Type type; + + private readonly static ObjectReferenceField[] None = new ObjectReferenceField[0]; + private readonly static Dictionary<System.Type, ObjectReferenceField[]> s_Cache = new Dictionary<System.Type, ObjectReferenceField[]>(); + + public static ObjectReferenceField[] FindObjectReferences(System.Type type) + { + if (type == null) + return None; + + if (type.IsAbstract || type.IsInterface) + return None; + + if (!typeof(ScriptableObject).IsAssignableFrom(type)) + return None; + + ObjectReferenceField[] result = null; + if (s_Cache.TryGetValue(type, out result)) + return result; + + result = SearchForFields(type); + s_Cache[type] = result; + return result; + } + + public static ObjectReferenceField[] FindObjectReferences<T>() where T : ScriptableObject, new() + { + return FindObjectReferences(typeof(T)); + } + + private static ObjectReferenceField[] SearchForFields(System.Type t) + { + Object instance = ScriptableObject.CreateInstance(t); + var list = new List<ObjectReferenceField>(); + + var serializableObject = new SerializedObject(instance); + var prop = serializableObject.GetIterator(); + bool enterChildren = true; + while (prop.NextVisible(enterChildren)) + { + enterChildren = true; + var ppath = prop.propertyPath; + if (ppath == "m_Script") + { + enterChildren = false; + } + else if (prop.propertyType == SerializedPropertyType.ObjectReference || prop.propertyType == SerializedPropertyType.ExposedReference) + { + enterChildren = false; + var exposedType = GetTypeFromPath(t, prop.propertyPath); + if (exposedType != null && typeof(Object).IsAssignableFrom(exposedType)) + { + bool isSceneRef = prop.propertyType == SerializedPropertyType.ExposedReference; + list.Add( + new ObjectReferenceField() {propertyPath = prop.propertyPath, isSceneReference = isSceneRef, type = exposedType} + ); + } + } + } + + Object.DestroyImmediate(instance); + if (list.Count == 0) + return None; + return list.ToArray(); + } + + private static System.Type GetTypeFromPath(System.Type baseType, string path) + { + if (string.IsNullOrEmpty(path)) + return null; + + System.Type parentType = baseType; + FieldInfo field = null; + var pathTo = path.Split(new char[] {'.'}, StringSplitOptions.RemoveEmptyEntries); + var flags = BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.NonPublic | + BindingFlags.Instance; + foreach (string s in pathTo) + { + field = parentType.GetField(s, flags); + while (field == null) + { + if (parentType.BaseType == null) + return null; // Should not happen really. Means SerializedObject got the property, but the reflection missed it + parentType = parentType.BaseType; + field = parentType.GetField(s, flags); + } + + parentType = field.FieldType; + } + + // dig out exposed reference types + if (field.FieldType.IsGenericType && field.FieldType.GetGenericTypeDefinition() == typeof(ExposedReference<Object>).GetGenericTypeDefinition()) + { + return field.FieldType.GetGenericArguments()[0]; + } + + return field.FieldType; + } + + public Object Find(ScriptableObject sourceObject, Object context = null) + { + if (sourceObject == null) + return null; + + SerializedObject obj = new SerializedObject(sourceObject, context); + var prop = obj.FindProperty(propertyPath); + if (prop == null) + throw new InvalidOperationException("sourceObject is not of the proper type. It does not contain a path to " + propertyPath); + + Object result = null; + if (isSceneReference) + { + if (prop.propertyType != SerializedPropertyType.ExposedReference) + throw new InvalidOperationException(propertyPath + " is marked as a Scene Reference, but is not an exposed reference type"); + if (context == null) + Debug.LogWarning("ObjectReferenceField.Find " + " is called on a scene reference without a context, will always be null"); + + result = prop.exposedReferenceValue; + } + else + { + if (prop.propertyType != SerializedPropertyType.ObjectReference) + throw new InvalidOperationException(propertyPath + "is marked as an asset reference, but is not an object reference type"); + result = prop.objectReferenceValue; + } + + return result; + } + + /// <summary> + /// Check if an Object satisfies this field, including components + /// </summary> + public bool IsAssignable(Object obj) + { + if (obj == null) + return false; + + // types match + bool potentialMatch = type.IsAssignableFrom(obj.GetType()); + + // field is component, and it exists on the gameObject + if (!potentialMatch && typeof(Component).IsAssignableFrom(type) && obj is GameObject) + potentialMatch = ((GameObject)obj).GetComponent(type) != null; + + return potentialMatch && isSceneReference == obj.IsSceneObject(); + } + + /// <summary> + /// Assigns a value to the field + /// </summary> + public bool Assign(ScriptableObject scriptableObject, Object value, IExposedPropertyTable exposedTable = null) + { + var serializedObject = new SerializedObject(scriptableObject, exposedTable as Object); + var property = serializedObject.FindProperty(propertyPath); + if (property == null) + return false; + + // if the value is a game object, but the field is a component + if (value is GameObject && typeof(Component).IsAssignableFrom(type)) + value = ((GameObject)value).GetComponent(type); + + if (isSceneReference) + { + property.exposedReferenceValue = value; + + // the object gets dirtied, but not the scene which is where the reference is stored + var component = exposedTable as Component; + if (component != null && !EditorApplication.isPlaying) + EditorSceneManager.MarkSceneDirty(component.gameObject.scene); + } + else + property.objectReferenceValue = value; + + serializedObject.ApplyModifiedProperties(); + return true; + } + } +} |
