using System; using UnityEngine; using UnityEditor; using System.Reflection; #if UNITY_2019_1_OR_NEWER using UnityEngine.UIElements; #else using UnityEngine.Experimental.UIElements; #endif namespace UnityToolbarExtender { public static class ToolbarCallback { static Type m_toolbarType = typeof(Editor).Assembly.GetType("UnityEditor.Toolbar"); static Type m_guiViewType = typeof(Editor).Assembly.GetType("UnityEditor.GUIView"); #if UNITY_2020_1_OR_NEWER static Type m_iWindowBackendType = typeof(Editor).Assembly.GetType("UnityEditor.IWindowBackend"); static PropertyInfo m_windowBackend = m_guiViewType.GetProperty("windowBackend", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); static PropertyInfo m_viewVisualTree = m_iWindowBackendType.GetProperty("visualTree", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); #else static PropertyInfo m_viewVisualTree = m_guiViewType.GetProperty("visualTree", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); #endif static FieldInfo m_imguiContainerOnGui = typeof(IMGUIContainer).GetField("m_OnGUIHandler", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); static ScriptableObject m_currentToolbar; /// /// Callback for toolbar OnGUI method. /// public static Action OnToolbarGUI; public static Action OnToolbarGUILeft; public static Action OnToolbarGUIRight; static ToolbarCallback() { EditorApplication.update -= OnUpdate; EditorApplication.update += OnUpdate; } static void OnUpdate() { // Relying on the fact that toolbar is ScriptableObject and gets deleted when layout changes if (m_currentToolbar == null) { // Find toolbar var toolbars = Resources.FindObjectsOfTypeAll(m_toolbarType); m_currentToolbar = toolbars.Length > 0 ? (ScriptableObject) toolbars[0] : null; if (m_currentToolbar != null) { #if UNITY_2021_1_OR_NEWER var root = m_currentToolbar.GetType().GetField("m_Root", BindingFlags.NonPublic | BindingFlags.Instance); var rawRoot = root.GetValue(m_currentToolbar); var mRoot = rawRoot as VisualElement; RegisterCallback("ToolbarZoneLeftAlign", OnToolbarGUILeft); RegisterCallback("ToolbarZoneRightAlign", OnToolbarGUIRight); void RegisterCallback(string root, Action cb) { var toolbarZone = mRoot.Q(root); var parent = new VisualElement() { style = { flexGrow = 1, flexDirection = FlexDirection.Row, } }; var container = new IMGUIContainer(); container.onGUIHandler += () => { cb?.Invoke(); }; parent.Add(container); toolbarZone.Add(parent); } #else #if UNITY_2020_1_OR_NEWER var windowBackend = m_windowBackend.GetValue(m_currentToolbar); // Get it's visual tree var visualTree = (VisualElement) m_viewVisualTree.GetValue(windowBackend, null); #else // Get it's visual tree var visualTree = (VisualElement) m_viewVisualTree.GetValue(m_currentToolbar, null); #endif // Get first child which 'happens' to be toolbar IMGUIContainer var container = (IMGUIContainer) visualTree[0]; // (Re)attach handler var handler = (Action) m_imguiContainerOnGui.GetValue(container); handler -= OnGUI; handler += OnGUI; m_imguiContainerOnGui.SetValue(container, handler); #endif } } } static void OnGUI() { var handler = OnToolbarGUI; if (handler != null) handler(); } } }