// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik // #if UNITY_EDITOR using System; using System.Collections.Generic; using System.Reflection; using UnityEditor; using UnityEngine; #if UNITY_6000_0_OR_NEWER using GetDrawerTypeForTypeDelegate = System.Func; #else using GetDrawerTypeForTypeDelegate = System.Func; #endif namespace Animancer.Editor { /// [Editor-Only] A cache of s mapped to their target type. /// https://kybernetik.com.au/animancer/api/Animancer.Editor/PropertyDrawers public static class PropertyDrawers { /************************************************************************************************************************/ private const string ScriptAttributeUtility = "UnityEditor.ScriptAttributeUtility", FieldFieldName = "m_FieldInfo", AttributeFieldName = "m_Attribute"; private static readonly GetDrawerTypeForTypeDelegate GetDrawerTypeForType; private static readonly FieldInfo FieldField, AttributeField; /************************************************************************************************************************/ static PropertyDrawers() { var editorAssembly = typeof(CustomPropertyDrawer).Assembly; var scriptAttributeUtility = editorAssembly.GetType(ScriptAttributeUtility); if (scriptAttributeUtility == null) return; var getDrawerTypeForType = scriptAttributeUtility.GetMethod( nameof(GetDrawerTypeForType), AnimancerReflection.StaticBindings, null, #if UNITY_6000_0_OR_NEWER new Type[] { typeof(Type), typeof(Type[]), typeof(bool) }, #else new Type[] { typeof(Type) }, #endif null); if (getDrawerTypeForType == null) return; GetDrawerTypeForType = (GetDrawerTypeForTypeDelegate)Delegate.CreateDelegate( typeof(GetDrawerTypeForTypeDelegate), getDrawerTypeForType); var propertyDrawer = typeof(PropertyDrawer); FieldField = propertyDrawer.GetField(FieldFieldName, AnimancerReflection.InstanceBindings); AttributeField = propertyDrawer.GetField(AttributeFieldName, AnimancerReflection.InstanceBindings); Selection.selectionChanged += OnSelectionChanged; } /************************************************************************************************************************/ private static readonly Dictionary ObjectTypeToDrawer = new(); private static readonly Dictionary DrawerTypeToInstance = new(); /// Tries to get a for the given `objectType`. public static bool TryGetDrawer( Type objectType, FieldInfo field, Attribute attribute, out PropertyDrawer drawer) { if (GetDrawerTypeForType == null) { drawer = null; return false; } if (ObjectTypeToDrawer.TryGetValue(objectType, out drawer)) return true; Type drawerType; try { #if UNITY_6000_0_OR_NEWER drawerType = GetDrawerTypeForType(objectType, Type.EmptyTypes, true); #else drawerType = GetDrawerTypeForType(objectType); #endif } catch (Exception exception) { Debug.LogException(exception); ObjectTypeToDrawer.Add(objectType, null); return false; } if (DrawerTypeToInstance.TryGetValue(drawerType, out drawer)) { ObjectTypeToDrawer.Add(objectType, drawer); return true; } try { drawer = (PropertyDrawer)Activator.CreateInstance(drawerType); FieldField?.SetValue(drawer, field); AttributeField?.SetValue(drawer, attribute); } catch (Exception exception) { Debug.LogException(exception); } ObjectTypeToDrawer.Add(objectType, drawer); DrawerTypeToInstance.Add(drawerType, drawer); return drawer != null; } /************************************************************************************************************************/ /// [Editor-Only] /// Indicates that a cached /// should not be kept in the cache after the selection changes. /// /// https://kybernetik.com.au/animancer/api/Animancer.Editor/IDiscardOnSelectionChange public interface IDiscardOnSelectionChange { } /************************************************************************************************************************/ /// Discards any cached drawers. private static void OnSelectionChanged() { DiscardOnSelectionChanged(ObjectTypeToDrawer); DiscardOnSelectionChanged(DrawerTypeToInstance); } /************************************************************************************************************************/ /// Discards any cached drawers. private static void DiscardOnSelectionChanged(Dictionary drawers) { var discard = ListPool.Instance.Acquire(); foreach (var drawer in drawers) if (drawer.Value is IDiscardOnSelectionChange) discard.Add(drawer.Key); for (int i = discard.Count - 1; i >= 0; i--) drawers.Remove(discard[i]); ListPool.Instance.Release(discard); } /************************************************************************************************************************/ } } #endif