123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152 |
- // 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
- {
- /// <summary>[Editor-Only] Draws a custom GUI for an object.</summary>
- /// <remarks>
- /// Every non-abstract type implementing this interface must have at least one <see cref="CustomGUIAttribute"/>.
- /// </remarks>
- /// https://kybernetik.com.au/animancer/api/Animancer.Editor/CustomGUIFactory
- ///
- public static class CustomGUIFactory
- {
- /************************************************************************************************************************/
- private static readonly Dictionary<Type, Type>
- 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<CustomGUIAttribute>();
- if (attribute.TargetType != null)
- {
- TargetTypeToGUIType.Add(attribute.TargetType, guiType);
- }
- }
- }
- /************************************************************************************************************************/
- private static readonly ConditionalWeakTable<object, ICustomGUI>
- TargetToGUI = new();
- /// <summary>Returns an existing <see cref="ICustomGUI"/> for the `targetType` or creates one if necessary.</summary>
- /// <remarks>Returns null if the `targetType` is null or no valid <see cref="ICustomGUI"/> type is found.</remarks>
- 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;
- }
- /// <summary>Returns an existing <see cref="ICustomGUI"/> for the `value` or creates one if necessary.</summary>
- /// <remarks>Returns null if the `value` is null or no valid <see cref="ICustomGUI"/> type is found.</remarks>
- 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;
- }
- /************************************************************************************************************************/
- /// <summary>Creates an <see cref="ICustomGUI"/> for the `targetType`.</summary>
- /// <remarks>Returns null if the `value` is null or no valid <see cref="ICustomGUI"/> type is found.</remarks>
- 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;
- }
- }
- /************************************************************************************************************************/
- /// <summary>Tries to determine the valid <see cref="ICustomGUI"/> type for drawing the `target`.</summary>
- 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
|