Daniel Lip
Daniel Lip

Reputation: 11325

Why I don't see any more the TextField inside OnGUI in EditorWindow script?

using UnityEngine;
using UnityEditor;

public class SearchableWindow : EditorWindow
{
    string searchString = "";

    [MenuItem("Tools/Searching")]
    private static void Searching()
    {
        const int width = 340;
        const int height = 420;

        var x = (Screen.currentResolution.width - width) / 2;
        var y = (Screen.currentResolution.height - height) / 2;

        GetWindow<SearchableWindow>().position = new Rect(x, y, width, height);
    }


    void OnGUI()
    {
        GUILayout.BeginHorizontal(EditorStyles.toolbar);
        GUILayout.FlexibleSpace();
        searchString = GUILayout.TextField(searchString, EditorStyles.toolbarTextField);
        GUILayout.EndHorizontal();

        var items = Selection.gameObjects;
        // Do comparison here. For example
        for (int i = 0; i < items.Length; i++)
        {
            if (items[i].name.Contains(searchString))
            {
                GUILayout.Label(items[i].name);
            }
        }
    }
}

Before it was working fine but now everything is very slow and also I don't see the TextField and when selecting a GameObject in the Hierarchy it's taking almost 5 seconds to show it in the Editor Window.

And before it was all fast and showing the whole Hierarchy in the Editor Window.

Now it's empty:

Searchable window

I took the answer from this question:

Searchable

Upvotes: 0

Views: 276

Answers (1)

derHugo
derHugo

Reputation: 90779

OnGUI is only called while the mouse is (moved/clicked) over the according Window -> it is not taking almost 5 seconds but until you move your mouse over the window again.

In order to solve that you could implement EditorWindow.OnSelectionChange and force a EditorWindow.Repaint so your window is refreshed everytime the Selection changes.

private void OnSelectionChange()
{
    Repaint();
}

The second issue is produced as follows:

GUILayout.TextField together with GUILayout.FlexibleSpace if not defined different uses automatically the width of the inserted text -> Since you have an empty text at start the width is almost 0 ... you actually can see your TextField very thin there and it gets bigger as you fill it:

enter image description here

Using the drawers from the EditorGUILayout instead solves this:

EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
GUILayout.FlexibleSpace();
searchString = EditorGUILayout.TextField(searchString, EditorStyles.toolbarTextField);
EditorGUILayout.EndHorizontal();

var items = Selection.gameObjects;
// Do comparison here. For example
foreach (var selectedObject in selected)
{
    if (selectedObject .name.Contains(searchString))
    {
        EditorGUILayout.LabelField(selectedObject.name);
    }
}

Tip If you replace

EditorGUILayout.LabelField(items[i].name);

with

if (GUILayout.Button(selectedObject.name, EditorStyles.label))
{
    EditorGUIUtility.PingObject(selectedObject);
}

you can click on the name and "ping" the according GameObject in the hierachy!


Update since asked in the comments

If you want to include all children of the selection recursive you could do something like (there might be better ways but that's what I came up with within 10 minutes)

private static IEnumerable<GameObject> GetChildrenRecursive(GameObject root)
{
    var output = new List<GameObject>();

    //add the root object itself
    output.Add(root);

    // iterate over direct children
    foreach (Transform child in root.transform)
    {
        // add the child itslef
        output.Add(child.gameObject);

        // Recursion here: Get all subchilds of this child
        var childsOfchild = GetChildrenRecursive(child.gameObject);
        output.AddRange(childsOfchild);
    }

    return output;
}

private static IEnumerable<GameObject> GetChildrenRecursive(IEnumerable<GameObject> rootObjects)
{
    var output = new List<GameObject>();


    foreach (var root in rootObjects)
    {
        output.AddRange(GetChildrenRecursive(root));
    }

    // remove duplicates
    return output.Distinct().ToList();
}

and using

var selected = GetChildrenRecursive(Selection.gameObjects);

Another Update

Since this might not be very efficient you probably should move it to Searching and than only refresh the selected value also in OnSelectionChange like

public class SearchableWindow : EditorWindow
{
    private string searchString = "";
    private List<GameObject> selected;

    [MenuItem("Tools/Searching")]
    private static void Searching()
    {
        const int width = 340;
        const int height = 420;

        var x = (Screen.currentResolution.width - width) / 2;
        var y = (Screen.currentResolution.height - height) / 2;

        var window = GetWindow<SearchableWindow>();
        window.position = new Rect(x, y, width, height);

        window.selected = GetChildrenRecursive(Selection.gameObjects).ToList();
    }

    private void OnSelectionChange()
    {
        selected = GetChildrenRecursive(Selection.gameObjects).ToList();
        Repaint();
    }

    private void OnGUI()
    {
        EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
        GUILayout.FlexibleSpace();
        searchString = EditorGUILayout.TextField(searchString, EditorStyles.toolbarTextField);
        EditorGUILayout.EndHorizontal();

        // only as fallback
        if (selected == null)
        {
            selected = GetChildrenRecursive(Selection.gameObjects).ToList();
        }

        // Do comparison here. For example
        foreach (var selectedObject in selected)
        {
            if (selectedObject.name.Contains(searchString))
            {
                if (GUILayout.Button(selectedObject.name, EditorStyles.label))
                {
                    EditorGUIUtility.PingObject(selectedObject);
                }
            }
        }
    }

    private static IEnumerable<GameObject> GetChildrenRecursive(GameObject root)
    {
        var output = new List<GameObject>();

        //add the root object itself
        output.Add(root);

        // iterate over direct children
        foreach (Transform child in root.transform)
        {
            // add the children themselves
            output.Add(child.gameObject);

            var childsOfchild = GetChildrenRecursive(child.gameObject);
            output.AddRange(childsOfchild);
        }



        return output;
    }

    private static IEnumerable<GameObject> GetChildrenRecursive(IEnumerable<GameObject> rootObjects)
    {
        var output = new List<GameObject>();

        foreach (var root in rootObjects)
        {
            output.AddRange(GetChildrenRecursive(root));
        }

        // remove any duplicates that would e.g. appear if you select a parent and its child
        return output.Distinct();
    }
}

enter image description here

Upvotes: 2

Related Questions