Is it possible to add a button to the top of the hierarchy window?

Hello. I am playing about with editor code, and was wondering how easy it is to add a new button and a small readout to the top bar of the hierarchy window?

I’ve managed to do it for items in the window using EditorApplication.hierarchyWindowItemOnGUI but I can’t seem to find a way to do it for the window itself. Is there any way?

I also felt need to customize my custom hierarchy (add toolbar) and came up with something.

Code
[InitializeOnLoad]
    public class CustomHierarchy
    {
        static CustomHierarchy()
        {
            // Get Window Type
            var type = typeof(EditorWindow).Assembly.GetType("UnityEditor.SceneHierarchyWindow");
            var window = EditorWindow.GetWindow(type, false, "Hierarchy", false);

            // Delay call to prevent NULL ref
            EditorApplication.delayCall += () =>
            {
                var rootVisualContainer = window.rootVisualElement;
                // Offset container to give space for our Toolbar
                rootVisualContainer.style.top = new StyleLength(45);

                var toolbar = new UnityEditor.UIElements.Toolbar();
                // Add "defaultcommondark_inter.uss" StyleSheet to our Toolbar so it can render correctly
                toolbar.styleSheets.Add(rootVisualContainer.styleSheets[0]);
                toolbar.StretchToParentSize();
                toolbar.style.position = new StyleEnum<Position>(Position.Absolute);
                // Shift toolbar to be under docker element
                toolbar.style.top = new StyleLength(24);
                toolbar.style.marginLeft = 1;

                var toolbarButton = new ToolbarButton();
                toolbarButton.text = "Button";
                toolbarButton.style.width = 100;

                toolbar.Add(toolbarButton);

                var editorPanelRootElement = rootVisualContainer.parent;
                editorPanelRootElement.Insert(1, toolbar);
            };
        }
    }

That will still nullref if the user doesn’t have a Hierarchy tab at all. Otherwise it’s great, and a very good example of why UITK is a great addition to the engine.

Ok, after some testing old code had many issues. Mostly about how after some manipulations with window it was resetted to the old state (Repositioning of elements).

Here is my sad try to improve situation:

Summary
[InitializeOnLoad]
    public class BEFHierarchy
    {
        #region FIELDS

        private static EditorWindow _editorWindow;

        private static float _rootVisualContainerOffset = 41;
        private static float _toolbarHeight = 18;
        private static float _toolbarOffset = 24;
        private static float _toolbarButtonWidth = 32;
        private static float _toolbarButtonIconSize = 14;

        #endregion
        
        #region CONSTRUCTORS
        
        static BEFHierarchy()
        {
            ValidateWindow();
            AddToolbar();
            
            EditorApplication.hierarchyWindowItemOnGUI -= HandleHierarchyWindowItemOnGUI;
            EditorApplication.hierarchyWindowItemOnGUI += HandleHierarchyWindowItemOnGUI;
        }
        
        #endregion

        #region TOOLBAR

        private static void AddToolbar()
        {
            if (ValidateWindow() == false)
            {
                return;
            }

            _editorWindow.rootVisualElement.UnregisterCallback<AttachToPanelEvent>(OnAttachToPanelEvent);
            _editorWindow.rootVisualElement.RegisterCallback<AttachToPanelEvent>(OnAttachToPanelEvent);
            
            _editorWindow.rootVisualElement.UnregisterCallback<GeometryChangedEvent>(RepositionVisualContainer);
            _editorWindow.rootVisualElement.RegisterCallback<GeometryChangedEvent>(RepositionVisualContainer);
            
            EditorApplication.delayCall += () =>
            {
                var rootVisualContainer = _editorWindow.rootVisualElement;
                rootVisualContainer.style.top = new StyleLength(_rootVisualContainerOffset);
                
                var toolbar = new UnityEditor.UIElements.Toolbar();
                toolbar.style.height = new StyleLength(_toolbarHeight);
                toolbar.style.flexDirection = FlexDirection.RowReverse;
                toolbar.styleSheets.Add(rootVisualContainer.styleSheets[0]);
                toolbar.StretchToParentSize();
                toolbar.style.position = new StyleEnum<Position>(Position.Absolute);
                toolbar.style.top = new StyleLength(_toolbarOffset);
                toolbar.style.marginLeft = new StyleLength(1);
                
                toolbar.Add(toggle);
                
                var editorPanelRootElement = rootVisualContainer.parent;
                editorPanelRootElement.Insert(1, toolbar);
            };
        }

        #endregion
        
        #region TOOLBAR_CALLBACKS

        private static void RepositionVisualContainer(GeometryChangedEvent evt)
        {
            var rootVisualContainer = _editorWindow.rootVisualElement;
            rootVisualContainer.style.top = new StyleLength(_rootVisualContainerOffset);
        }
        
        private static void OnAttachToPanelEvent(AttachToPanelEvent evt)
        {
            ValidateWindow();
            AddToolbar();
        }

        #endregion

        #region HIERARCHY

        private static void HandleHierarchyWindowItemOnGUI(int instanceID, Rect selectionRect)
        {
            if (_editorWindow == null)
            {
                AddToolbar();
            }
        }

        #region UTILITY

        private static bool HasOpenInstances(Type type)
        {
            var objectsOfTypeAll = Resources.FindObjectsOfTypeAll(type);
            return objectsOfTypeAll != null && objectsOfTypeAll.Length != 0;
        }

        private static bool ValidateWindow()
        {
            var type = typeof(EditorWindow).Assembly.GetType("UnityEditor.SceneHierarchyWindow");
            
            if (HasOpenInstances(type) == false)
            {
                return false;
            }
            
            _editorWindow = EditorWindow.GetWindow(type, false, "Hierarchy", false);

            return true;
        }

        #endregion
    }