// // Copyright (c) 2017 Anthony Marmont. All rights reserved. // Licensed for use under the Unity Asset Store EULA. See https://unity3d.com/legal/as_terms for full license information. // #if UNITY_EDITOR #pragma warning disable using System; using System.Reflection; using UnityEditor; using UnityEngine; namespace AssetIcons.Editors { /// /// AssetIcons uses this to manage the rendering callbacks. /// public static class AssetIconsProjectHooks { /// /// Delgate used by AssetIcons to draw an icon. /// /// The GUID of the asset to be drawn. /// The target area to draw the assets icon. public delegate void DrawAssetIconCallback(string guid, Rect area); private delegate void ObjectListDelegate(Rect area, string guid, bool isSmall); private delegate void AssetTreeViewDelegate(Rect area, string GUID); /// /// The callback that AssetIcons uses to draw draw icons. /// internal static event DrawAssetIconCallback OnInternalDrawIcon; /// /// The callback that AssetIcons uses to draw draw icons. /// public static event DrawAssetIconCallback OnDrawIcon; static AssetIconsProjectHooks() { var editorAssembly = Assembly.GetAssembly(typeof(SerializedProperty)); #if UNITY_2017_1_OR_NEWER try { var objectListAreaType = editorAssembly.GetType("UnityEditor.ObjectListArea"); var hookEvent = objectListAreaType.GetEvent("postAssetIconDrawCallback", BindingFlags.Static | BindingFlags.NonPublic); var hookField = objectListAreaType.GetField("postAssetIconDrawCallback", BindingFlags.Static | BindingFlags.NonPublic); var hookDelegateType = objectListAreaType.GetNestedType("OnAssetIconDrawDelegate", BindingFlags.NonPublic); SubscribeAsFirst(hookEvent, hookField, (ObjectListDelegate)ObjectListAreaCallback, hookDelegateType); } catch (Exception exception) { Debug.LogWarning("AssetIcons is using fallback method of rendering due to incompatiblity with this version of Unity. Internal Exception: " + exception); EditorApplication.projectWindowItemOnGUI += ItemOnGUI; } try { var assetsTreeViewGUIType = editorAssembly.GetType("UnityEditor.AssetsTreeViewGUI"); var hookEvent = assetsTreeViewGUIType.GetEvent("postAssetIconDrawCallback", BindingFlags.Static | BindingFlags.NonPublic); var hookField = assetsTreeViewGUIType.GetField("postAssetIconDrawCallback", BindingFlags.Static | BindingFlags.NonPublic); var hookDelegateType = assetsTreeViewGUIType.GetNestedType("OnAssetIconDrawDelegate", BindingFlags.NonPublic); SubscribeAsFirst(hookEvent, hookField, (AssetTreeViewDelegate)AssetTreeViewCallback, hookDelegateType); } catch { } #else EditorApplication.projectWindowItemOnGUI += ItemOnGUI; #endif } private static void SubscribeAsFirst(EventInfo hookEvent, FieldInfo hookField, Delegate callback, Type delegateType) { var hookAddMethod = hookEvent.GetAddMethod(true); // Collect all previous subscribers to the delegate Delegate[] resubTargsList = null; var del = (Delegate)hookField.GetValue(null); if (del != null) { resubTargsList = del.GetInvocationList(); } // Remove all previous subscribers to the delegate hookField.SetValue(null, null); // Add AssetIcons handler to the delegate now that it's empty. This should make // AssetIcons render below all other handlers. hookAddMethod.Invoke(null, new object[1] { Cast(callback, delegateType) }); // Resubscribe all of the delegates that I had previously removed. if (resubTargsList != null) { for (int i = 0; i < resubTargsList.Length; i++) { var resubTarg = resubTargsList[i]; hookAddMethod.Invoke(null, new object[1] { resubTarg }); } } } private static void AssetTreeViewCallback(Rect rect, string guid) { ItemOnGUI(guid, rect); } private static void ItemOnGUI(string guid, Rect rect) { if (OnInternalDrawIcon != null) { OnInternalDrawIcon(guid, rect); } if (OnDrawIcon != null) { OnDrawIcon(guid, rect); } } #if UNITY_2017_1_OR_NEWER private static void ObjectListAreaCallback(Rect rect, string guid, bool isSmall) { ItemOnGUI(guid, rect); } #endif private static Delegate Cast(Delegate source, Type type) { if (source == null) { return null; } var delegates = source.GetInvocationList(); if (delegates.Length == 1) { return Delegate.CreateDelegate(type, delegates[0].Target, delegates[0].Method); } var delegatesDest = new Delegate[delegates.Length]; for (int i = 0; i < delegates.Length; i++) { delegatesDest[i] = Delegate.CreateDelegate(type, delegates[i].Target, delegates[i].Method); } return Delegate.Combine(delegatesDest); } } } #pragma warning restore #endif