using System; using System.Collections.Generic; using UnityEngine; using System.Reflection; using System.Runtime.Serialization; using Object = UnityEngine.Object; #if UNITY_2021_3 || UNITY_2022_2 || UNITY_6000_0_OR_NEWER using UnityEditor; #endif #if UNITY_2019_1_OR_NEWER using UnityEngine.UIElements; #else using UnityEngine.Experimental.UIElements; #endif namespace Module.NavigationTool.Editor.Toolbar { internal static class ToolbarUtility { private static readonly Assembly ASSEMBLY = typeof(UnityEditor.Editor).Assembly; #if UNITY_2021_1_OR_NEWER private static ScriptableObject CURRENT_TOOLBAR; private static VisualElement CURRENT_ROOT; private static VisualElement CURRENT_PARENT_LEFT; private static VisualElement CURRENT_PARENT_RIGHT; private static int CURRENT_INSTANCE_ID; private static FieldInfo FIELD_INFO_ROOT; #endif #if UNITY_2021_1_OR_NEWER public static void OnUpdate(Action callback) { if (CURRENT_TOOLBAR == null) CURRENT_TOOLBAR = GetToolbarObject(); if (CURRENT_TOOLBAR != null && CURRENT_TOOLBAR.GetInstanceID() != CURRENT_INSTANCE_ID) { CURRENT_PARENT_LEFT?.RemoveFromHierarchy(); CURRENT_PARENT_LEFT = null; CURRENT_PARENT_RIGHT?.RemoveFromHierarchy(); CURRENT_PARENT_RIGHT = null; CURRENT_INSTANCE_ID = CURRENT_TOOLBAR.GetInstanceID(); } if (CURRENT_TOOLBAR == null) return; if (CURRENT_PARENT_LEFT == null || CURRENT_PARENT_RIGHT == null || CURRENT_ROOT == null) { CURRENT_INSTANCE_ID = CURRENT_TOOLBAR.GetInstanceID(); CURRENT_PARENT_LEFT?.RemoveFromHierarchy(); CURRENT_PARENT_LEFT = null; CURRENT_PARENT_RIGHT?.RemoveFromHierarchy(); CURRENT_PARENT_RIGHT = null; if (FIELD_INFO_ROOT == null) FIELD_INFO_ROOT = CURRENT_TOOLBAR.GetType().GetField("m_Root", BindingFlags.NonPublic | BindingFlags.Instance); object rawRoot = FIELD_INFO_ROOT?.GetValue(CURRENT_TOOLBAR); CURRENT_ROOT = rawRoot as VisualElement; if (CURRENT_ROOT != null) { CURRENT_PARENT_LEFT = CreateParent(CURRENT_ROOT, "ToolbarZoneLeftAlign", true); CURRENT_PARENT_RIGHT = CreateParent(CURRENT_ROOT, "ToolbarZoneRightAlign", false); } } if (CURRENT_PARENT_LEFT != null) callback?.Invoke(CURRENT_PARENT_LEFT, CURRENT_PARENT_RIGHT); } private static VisualElement CreateParent(VisualElement root, string query, bool isLeft) { VisualElement parent = root.Q(query); VisualElement result; if (isLeft) { result = new VisualElement { style = { flexGrow = 1, flexDirection = FlexDirection.Row, #if UNITY_6000_0_OR_NEWER height = 22f #endif } }; result.Add(new VisualElement { style = { flexGrow = 1 } }); } else { result = new VisualElement { style = { flexGrow = 1, flexDirection = FlexDirection.RowReverse, #if UNITY_6000_0_OR_NEWER height = 22f #endif } }; result.Add(new VisualElement { style = { flexGrow = 1 } }); } #if UNITY_6000_0_OR_NEWER result.RegisterCallbackOnce(OnDetachedFromPanel); #endif parent.Add(result); return result; } #else public static void AddGuiListener(Action action) { ScriptableObject so = GetToolbarObject(); if (so == null) return; Type typeGuiView = ASSEMBLY.GetType("UnityEditor.GUIView"); #if UNITY_2020_1_OR_NEWER Type typeWindowBackend = ASSEMBLY.GetType("UnityEditor.IWindowBackend"); PropertyInfo piWindowBackend = typeGuiView.GetProperty("windowBackend", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); PropertyInfo piVisualTree = typeWindowBackend.GetProperty("visualTree", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); if (piWindowBackend == null) return; #else PropertyInfo piVisualTree = typeGuiView.GetProperty("visualTree", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); #endif if (piVisualTree == null) return; FieldInfo fiImguiContainer = typeof(IMGUIContainer).GetField("m_OnGUIHandler", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); if (fiImguiContainer == null) return; #if UNITY_2020_1_OR_NEWER object windowBackend = piWindowBackend.GetValue(so); var visualTree = (VisualElement)piVisualTree.GetValue(windowBackend, null); #else var visualTree = (VisualElement)piVisualTree.GetValue(so, null); #endif var container = (IMGUIContainer)visualTree[0]; var handler = (Action)fiImguiContainer.GetValue(container); handler -= action; handler += action; fiImguiContainer.SetValue(container, handler); } #endif private static ScriptableObject GetToolbarObject() { Type typeToolbar = ASSEMBLY.GetType("UnityEditor.Toolbar"); Object[] toolbars = Resources.FindObjectsOfTypeAll(typeToolbar); return toolbars.Length > 0 ? (ScriptableObject)toolbars[0] : null; } public static AbstractToolbarDrawer[] GetAllDrawers() { var list = new List(8); try { #if UNITY_2021_3 || UNITY_2022_2 || UNITY_6000_0_OR_NEWER var types = TypeCache.GetTypesDerivedFrom(); for (var i = 0; i < types.Count; i++) { Type type = types[i]; if (!type.IsAbstract) list.Add((AbstractToolbarDrawer)FormatterServices.GetUninitializedObject(type)); } #else Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); Type iType = typeof(AbstractToolbarDrawer); for (var i = 0; i < assemblies.Length; i++) { Assembly assembly = assemblies[i]; Type[] types = assembly.GetTypes(); for (var j = 0; j < types.Length; j++) { Type type = types[j]; if (!type.IsAbstract && iType.IsAssignableFrom(type)) list.Add((AbstractToolbarDrawer)FormatterServices.GetUninitializedObject(type)); } } #endif list.Sort((t0, t1) => t0.Priority.CompareTo(t1.Priority)); } catch (Exception) { // Fail silently } return list.ToArray(); } #if UNITY_6000_0_OR_NEWER private static void OnDetachedFromPanel(DetachFromPanelEvent evt) { CURRENT_INSTANCE_ID = -1; } #endif } }