// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik // #if UNITY_EDITOR //#define LOG_CUSTOM_GUI_FACTORY using System; using System.Collections.Generic; using System.Reflection; using System.Runtime.CompilerServices; using UnityEditor; using UnityEngine; namespace Animancer.Editor { /// [Editor-Only] Draws a custom GUI for an object. /// /// Every non-abstract type implementing this interface must have at least one . /// /// https://kybernetik.com.au/animancer/api/Animancer.Editor/CustomGUIFactory /// public static class CustomGUIFactory { /************************************************************************************************************************/ private static readonly Dictionary TargetTypeToGUIType = new(); static CustomGUIFactory() { foreach (var guiType in TypeCache.GetTypesWithAttribute(typeof(CustomGUIAttribute))) { if (guiType.IsAbstract || guiType.IsInterface) continue; if (!typeof(ICustomGUI).IsAssignableFrom(guiType)) { Debug.LogWarning( $"{guiType.FullName} has a {nameof(CustomGUIAttribute)}" + $" but doesn't implement {nameof(ICustomGUI)}."); continue; } var attribute = guiType.GetCustomAttribute(); if (attribute.TargetType != null) { TargetTypeToGUIType.Add(attribute.TargetType, guiType); } } } /************************************************************************************************************************/ private static readonly ConditionalWeakTable TargetToGUI = new(); /// Returns an existing for the `targetType` or creates one if necessary. /// Returns null if the `targetType` is null or no valid type is found. public static ICustomGUI GetOrCreateForType(Type targetType) { if (targetType == null) return null; if (TargetToGUI.TryGetValue(targetType, out var gui)) return gui; gui = Create(targetType); TargetToGUI.Add(targetType, gui); return gui; } /// Returns an existing for the `value` or creates one if necessary. /// Returns null if the `value` is null or no valid type is found. public static ICustomGUI GetOrCreateForObject(object value) { if (value == null) return null; if (TargetToGUI.TryGetValue(value, out var gui)) return gui; gui = Create(value.GetType()); if (gui != null) gui.Value = value; TargetToGUI.Add(value, gui); return gui; } /************************************************************************************************************************/ /// Creates an for the `targetType`. /// Returns null if the `value` is null or no valid type is found. public static ICustomGUI Create(Type targetType) { if (!TryGetGUIType(targetType, out var guiType)) return null; try { if (guiType.IsGenericTypeDefinition) guiType = guiType.MakeGenericType(targetType); var gui = (ICustomGUI)Activator.CreateInstance(guiType); return gui; } catch (Exception exception) { Debug.LogException(exception); return null; } } /************************************************************************************************************************/ /// Tries to determine the valid type for drawing the `target`. public static bool TryGetGUIType(Type target, out Type gui) { // Try the target and its base types. var type = target; while (type != null && type != typeof(object)) { if (TargetTypeToGUIType.TryGetValue(type, out gui)) return true; type = type.BaseType; } // Try any interfaces. var interfaces = target.GetInterfaces(); for (int i = 0; i < interfaces.Length; i++) if (TargetTypeToGUIType.TryGetValue(interfaces[i], out gui)) return true; // Try base object. return TargetTypeToGUIType.TryGetValue(typeof(object), out gui); } /************************************************************************************************************************/ } } #endif