Expand/Collapse all components in a Gameobject. (Refresh issue)

Hi,

I’m trying to create a tools which expand/collapse all components on a gameobject with a button in the context menu.

using UnityEditor;
using UnityEngine;
using UnityEditorInternal;

public static class ComponentTools
{
    [MenuItem("CONTEXT/Component/Collapse All")]
    public static void CollapseAll(MenuCommand command)
    {
        GameObject gameObject = (command.context as Component).gameObject;
        Component[] components = gameObject.GetComponents<Component>();
        foreach (Component component in components)
        {     
            InternalEditorUtility.SetIsInspectorExpanded(component, false);
        }
        // Refresh inspector.
    }

    [MenuItem("CONTEXT/Component/Expand All")]
    public static void ExpandAll(MenuCommand command)
    {
        GameObject gameObject = (command.context as Component).gameObject;
        Component[] components = gameObject.GetComponents<Component>();
        foreach (Component component in components)
        {
            InternalEditorUtility.SetIsInspectorExpanded(component, true);
        }
        // Resfresh inspector.
    }
}

That code works but i need to reselect the gameobject to refresh the inspector window.

What is the right method to refresh/repaint/redraw the inspector window without reselecting the gameobject ?

Thanks for your help.

Best regards.

Adrien Gannerie

1 Like

Up

Try getting the editor and forcing a Repaint. Unity - Scripting API: Editor.CreateCachedEditor

Thank you very much for your help and sorry to bother you by private message.

If I understood correctly your answer I must find the editor of each component and force a repaint?

Best regards.

Yes give that a try and see how it goes.

Thanks for your help. I tried this but this change nothing.

using UnityEditor;
using UnityEngine;
using UnityEditorInternal;

public static class ComponentTools
{
    [MenuItem("CONTEXT/Component/Collapse All")]
    public static void CollapseAll(MenuCommand command)
    {
        GameObject gameObject = (command.context as Component).gameObject;
        Component[] components = gameObject.GetComponents<Component>();
        foreach (Component component in components)
        {      
            InternalEditorUtility.SetIsInspectorExpanded(component, false);
            Editor editor = null;
            Editor.CreateCachedEditor(component, null, ref editor);
            editor.Repaint();
        }
    }

    [MenuItem("CONTEXT/Component/Expand All")]
    public static void ExpandAll(MenuCommand command)
    {
        GameObject gameObject = (command.context as Component).gameObject;
        Component[] components = gameObject.GetComponents<Component>();
        foreach (Component component in components)
        {
            InternalEditorUtility.SetIsInspectorExpanded(component, true);
            Editor editor = null;
            Editor.CreateCachedEditor(component, null, ref editor);
            editor.Repaint();
        }
    }
}

Have you a other idea ?

Best regards.

1 Like

You could try InternalEditorUtility.RepaintAllViews(). It’s undocumented, but will repaint everything. Unfortunately, there’s no public API to repaint specifically inspector windows.

1 Like

I don’t know if this has any bad side effects, but I found a solution to make the editor refresh from an example that uses a shortcut-key to lock/unlock the inspector. I modified the code below with what is working for me. There is a slight delay (1-2 seconds) before the refresh, but it does refresh and show all the components expanded/collapsed, and now that I’ve used it for a couple of days, this should absolutely be a standard feature in the inspector AND hierarchy! :wink:

using UnityEditor;
using UnityEngine;
using UnityEditorInternal;

public static class ComponentTools
{
    [MenuItem("CONTEXT/Component/Collapse All")]
    public static void CollapseAll(MenuCommand command)
    {
        GameObject gameObject = (command.context as Component).gameObject;
        Component[] components = gameObject.GetComponents<Component>();
        foreach (Component component in components)
        {     
            InternalEditorUtility.SetIsInspectorExpanded(component, false);
        }
        ActiveEditorTracker.sharedTracker.ForceRebuild();
    }

    [MenuItem("CONTEXT/Component/Expand All")]
    public static void ExpandAll(MenuCommand command)
    {
        GameObject gameObject = (command.context as Component).gameObject;
        Component[] components = gameObject.GetComponents<Component>();
        foreach (Component component in components)
        {
            InternalEditorUtility.SetIsInspectorExpanded(component, true);
        }
        ActiveEditorTracker.sharedTracker.ForceRebuild();
    }
}
7 Likes

Hey this script is super handy!
I noticed though it doesn’t collapse material components, do know why that would be? I’m thinking it might be because they aren’t really regular components as such and just displayed to simplify things for the user.
Might be a little tricky but does anyone know how to add them?

This also collapses materials.

using UnityEditor;
using UnityEngine;
using UnityEditorInternal;

public static class ComponentTools
{
    [MenuItem("CONTEXT/Component/Collapse All")]
    private static void CollapseAll(MenuCommand command)
    {
        SetAllInspectorsExpanded((command.context as Component).gameObject, false);
    }

    [MenuItem("CONTEXT/Component/Expand All")]
    private static void ExpandAll(MenuCommand command)
    {
        SetAllInspectorsExpanded((command.context as Component).gameObject, true);
    }

    public static void SetAllInspectorsExpanded(GameObject go, bool expanded)
    {
        Component[] components = go.GetComponents<Component>();
        foreach (Component component in components)
        {
            if (component is Renderer)
            {
                var mats = ((Renderer)component).sharedMaterials;
                for (int i = 0; i < mats.Length; ++i)
                {
                    InternalEditorUtility.SetIsInspectorExpanded(mats[i], expanded);
                }
            }
            InternalEditorUtility.SetIsInspectorExpanded(component, expanded);
        }
        ActiveEditorTracker.sharedTracker.ForceRebuild();
    }
}
6 Likes

Thanks @villevli ! That’s awesome!

Nice script! I was looking for something like this.
It seems like it doesn’t refresh though?
So after you Collapse All, you have to reselect the object to see the changes?

Appminis,

It should refresh automatically (Unity 2017.4.10f1), however, if you have more than 1 inspector window open, it may be expanding/collapsing the other inspector window(s). I don’t know why it does not do all of the open inspector windows, or the selected inspector window, but it does not. I have not been able to find a work-around or how to determine which inspector window is considered the “Active” one to the sharedTracker.

I put this ComponentTools.cs in my 2018.3.8f1 editor folder and it does not appear in menu after Unity restart. I had to put it under and exitsting Tools menu itme then it showed up. But then I received:
NullReferenceException: Object reference not set to an instance of an object
ComponentTools.CollapseAll (UnityEditor.MenuCommand command) (at Assets/Editor/ComponentTools.cs:10)
with a heirarchy item selected.

Is there any way to scroll inspector to a newly expanded component via script? Unfortunately, setting Selection.activeObject to the expanded component does not scroll the inspector view to it.

For anyone else finding this thread in 2020 (Using Unity 2019.4+):

The little menu beside the lock in the inspector now has this functionality built in

There is also the activeEditorTracker.SetVisible API available to do this without repaiting the GUI as done in the examples above

Full code for collapsing / expanding fast below (might need some null-checks, just tested it quickly:

    public static class ComponentTools
    {
        [MenuItem("CONTEXT/Component/Collapse All")]
        private static void CollapseAll()
        {
            SetAllInspectorsExpanded(false);
        }

        [MenuItem("CONTEXT/Component/Expand All")]
        private static void ExpandAll(MenuCommand command)
        {
            SetAllInspectorsExpanded(true);
        }

        private static void SetAllInspectorsExpanded(bool expanded)
        {
            var activeEditorTracker = ActiveEditorTracker.sharedTracker;

            for (var i = 0; i < activeEditorTracker.activeEditors.Length; i++)
            {
                activeEditorTracker.SetVisible(i, expanded ? 1 : 0);
            }
        }
8 Likes

ActiveEditorTracker.sharedTracker is not suitable: it only works if you work with a single Inspector window

I tried using myEditor.Repaint() and InternalEditorUtility.RepaintAllViews() but it doesn’t do anything. I still have to deselect and reselect the game object.

Thank you guys for your awesome scripts! :grinning: :+1:

I slighty changed it. I have deleted method parameter and also added hot-keys to allow collapse / expand Inspector without mouse:


using UnityEngine;
using UnityEditor;
using UnityEditorInternal;
using Object = UnityEngine.Object;

public static class MyEditorHotkeys
{
    // Hot key: ALT + W
    [MenuItem("Tools/Inspector/Collapse All &w")]
    public static void InspectorCollapseAll() {
        if (Selection.objects == null)
            return;

        foreach (Object obj in Selection.objects) {
            if (obj is GameObject go) {
                Component[] components = go.GetComponents<Component>();
                foreach (Component component in components) {
                    InternalEditorUtility.SetIsInspectorExpanded(component, false);

                    // Fix for renders and materials.
                    if (component is Renderer) {
                        Material[] mats = ((Renderer)component).sharedMaterials;
                        for (int i = 0; i < mats.Length; ++i) {
                            InternalEditorUtility.SetIsInspectorExpanded(mats[i], false);
                        }
                    }
                }
                ActiveEditorTracker.sharedTracker.ForceRebuild();
            }
        }
    }

    // Hot key: ALT + Q
    [MenuItem("Tools/Inspector/Expand All &q")]
    public static void InspectorExpandAll() {
        if (Selection.objects == null)
            return;

        foreach (Object obj in Selection.objects) {
            if (obj is GameObject go) {
                // GameObject gameObject = (command.context as Component).gameObject;
                Component[] components = go.GetComponents<Component>();
                foreach (Component component in components) {
                    InternalEditorUtility.SetIsInspectorExpanded(component, true);

                    //// Uncomment those lines to also expand materials.
                    //// Fix for renders and materials.
                    //if (component is Renderer) {
                    //    Material[] mats = ((Renderer)component).sharedMaterials;
                    //    for (int i = 0; i < mats.Length; ++i) {
                    //        InternalEditorUtility.SetIsInspectorExpanded(mats[i], true);
                    //    }
                    //}
                }
                ActiveEditorTracker.sharedTracker.ForceRebuild();
            }
        }
    }
}

Put this script to any ‘Editor’ folder in your project end then use shortcuts (hotkeys) for collapsing and expanding your Inspector.

You can change hotkeys in script by your own by changing very last characters in line:

  [MenuItem("Tools/Inspector/Collapse All &w")] 

There some special symbols are used:
% = CTRL, & = ALT, # = SHIFT
For example %&#W = CTRL + ALT + SHIFT + W