検索機能付きポップアップウィンドウ(Editor拡張)

検索機能付きポップアップウィンドウ(Editor拡張)

 以前作成したポップアップウィンドウ(ポップアップウィンドウの作成)に検索機能を追加しました。

Script

ポップアップウィンドウ

 作成したScriptは以下の通りです。

public class PopUpInfo
{
    public static PopUpInfo instance = null;
    private int id;
    private int m_selected;

    public PopUpInfo(int control_id)
    {
        id = control_id;
    }

    public static int GetSelectedValue(int control_id, int selected)
    {
        if (instance == null)
        {
            return selected;
        }

        if (instance.id == control_id)
        {
            selected = instance.m_selected;
        }
        return selected;
    }

    public void SetSelectedValue(int selected)
    {
        m_selected = selected;
    }
}

public class EditorPopUpSearchWindow : PopupWindowContent
{
    public static bool on_close = false;

    private string[] m_button_names;
    private bool[] m_hidden_flags;
    private int m_selected;
    private Rect m_rect_window;
    private PopUpInfo m_instance;
    private EditorWindow m_editorWindow;

    private GUIStyle m_style_label, m_style_search;
    private Texture2D tex_background;
    private Color m_color_backgraound = new Color(0.2f, 0.2f, 0.2f, 1.0f);
    private bool set_color = false;
    private string search_term;
    private Vector2 scroll_pos = Vector2.zero;

    //背景色変更なし
    public EditorPopUpSearchWindow(Rect rect_window, int selected, string[] button_names, GUIStyle style_popup_label, GUIStyle style_search_window, PopUpInfo instance, EditorWindow editorWindow)
    {
        m_button_names = button_names;
        m_selected = selected;
        m_rect_window = rect_window;
        m_instance = instance;
        m_style_label = style_popup_label;
        m_style_search = style_search_window;
        set_color = false;
        m_editorWindow = editorWindow;
        m_hidden_flags = new bool[m_button_names.Length];
        for(int i = 0; i < m_hidden_flags.Length; i++) { m_hidden_flags[i] = false; }
    }

    //背景色変更あり
    public EditorPopUpSearchWindow(Rect rect_window, int selected, string[] button_names, GUIStyle style_popup_label, GUIStyle style_search_window, Color color_background, PopUpInfo instance, EditorWindow editorWindow)
    {
        m_button_names = button_names;
        m_selected = selected;
        m_rect_window = rect_window;
        m_instance = instance;
        m_style_label = style_popup_label;
        m_style_search = style_search_window;
        m_color_backgraound = color_background;
        set_color = true;
        m_editorWindow = editorWindow;
        m_hidden_flags = new bool[m_button_names.Length];
        for (int i = 0; i < m_hidden_flags.Length; i++) { m_hidden_flags[i] = false; }
    }

    //背景色を変更する
    public static int Create(Vector2 window_size, string label, int selected, string[] button_names, GUIStyle style_label, GUIStyle style_button, GUIStyle style_popup, GUIStyle style_search_window, Color color_background, PopUpPosition popup_position, EditorWindow editorWindow)
    {
        selected = EditorPopUpSearchWindow.PopUp(window_size, label, selected, button_names, style_label, style_button, style_popup, style_search_window, color_background, popup_position, editorWindow);
        return selected;
    }

    //背景色を変更しない
    public static int Create(Vector2 window_size, string label, int selected, string[] button_names, GUIStyle style_label, GUIStyle style_button, GUIStyle style_popup, GUIStyle style_search_window, PopUpPosition popup_position, EditorWindow editorWindow)
    {
        selected = EditorPopUpSearchWindow.PopUp(window_size, label, selected, button_names, style_label, style_button, style_popup, style_search_window, Color.clear, popup_position, editorWindow);
        return selected;
    }

    static int PopUp(Vector2 window_size, string label, int selected, string[] button_names, GUIStyle style_label, GUIStyle style_button, GUIStyle style_popup, GUIStyle style_search_window, Color color_background, PopUpPosition popup_position, EditorWindow editorWindow)
    {
        int control_id = GUIUtility.GetControlID(FocusType.Passive);
        selected = PopUpInfo.GetSelectedValue(control_id, selected);
        Rect rect_control = GUILayoutUtility.GetRect(GUIContent.none, style_button);

        int margin = 3;
        int button_width = 85;
        int label_width = (int)rect_control.width - button_width - margin;

        Rect rect_label = new Rect(rect_control) { width = label_width };

        Rect rect_button = new Rect(rect_control) { x = rect_control.x + label_width + margin, width = button_width };

        Event event_current = Event.current;
        Vector2 mouse_pos = event_current.mousePosition;
        int label_id = GUIUtility.GetControlID(FocusType.Keyboard);
        int button_id = GUIUtility.GetControlID(FocusType.Passive);
        bool focus_button = false;
        bool on_button = false;
        bool on_click = false;

        if (event_current.button == 0)
        {
            switch (event_current.type)
            {
                case EventType.MouseDown:
                    if (rect_label.Contains(mouse_pos))
                    {
                        GUIUtility.keyboardControl = label_id;
                        event_current.Use();
                    }
                    else if (rect_button.Contains(mouse_pos))
                    {
                        GUIUtility.keyboardControl = button_id;
                        on_click = true;
                        event_current.Use();
                    }
                    break;
            }
        }

        //popupが閉じたとき
        if (EditorPopUpSearchWindow.on_close)
        {
            GUIUtility.keyboardControl = label_id;
            EditorPopUpSearchWindow.on_close = false;
        }

        if (GUIUtility.keyboardControl == label_id) focus_button = true;
        if (GUIUtility.keyboardControl == button_id) on_button = true;
        if (Event.current.type == EventType.Repaint)
        {
            style_label.Draw(rect_label, new GUIContent(label), false, false, false, focus_button || on_button);
            style_button.Draw(rect_button, new GUIContent(button_names[selected]), false, false, on_button, focus_button);
        }

        //ボタンがクリックされた時の処理
        if (on_click)
        {
            GUIStyle style_radio = new GUIStyle(EditorStyles.radioButton);
            int size = button_names.Length;
            int window_width = (int)window_size.x;
            int window_height;

            int search_field_height = (int)style_search_window.fixedHeight;
            if (search_field_height <= 0) search_field_height = 18;
            search_field_height += style_search_window.margin.top + style_search_window.margin.bottom;

            int radio_button_height = (int)style_radio.fixedHeight;
            if (radio_button_height <= 0) radio_button_height = 19;
            radio_button_height *= size;
            radio_button_height += style_radio.margin.top + style_radio.margin.bottom * size;

            int bottom_margin = 2;  //各要素の高さやmarginから計算した高さをwindowの高さとするとScrollBarが表示される。少し大きくすることで回避。
            window_height = search_field_height + radio_button_height + bottom_margin;

            if (window_height > window_size.y) window_height = (int)window_size.y;  //要素の高さより指定されたwindowの高さが大きい場合は、windowの高さを要素の高さに合わせる

            int window_x;
            int window_y = (int)rect_button.y + (int)rect_button.height - window_height;

            switch (popup_position)
            {
                case PopUpPosition.Left:
                    window_x = (int)rect_button.x;
                    break;

                case PopUpPosition.Center:
                    window_x = (int)rect_button.x - (window_width - button_width) / 2;
                    break;

                case PopUpPosition.Right:
                    window_x = (int)rect_button.x - (window_width - button_width);
                    break;

                default:
                    window_x = (int)rect_button.x;
                    break;
            }

            PopUpInfo.instance = new PopUpInfo(control_id);
            PopUpInfo.instance.SetSelectedValue(selected);
            Rect rect_window = new Rect(window_x, window_y, window_width, window_height);
            if (color_background != Color.clear)
            {
                var popup_content = new EditorPopUpSearchWindow(rect_window, selected, button_names, style_popup, style_search_window, color_background, PopUpInfo.instance, editorWindow);
                PopupWindow.Show(rect_window, popup_content);
            }
            else
            {
                var popup_content = new EditorPopUpSearchWindow(rect_window, selected, button_names, style_popup, style_search_window, PopUpInfo.instance, editorWindow);
                PopupWindow.Show(rect_window, popup_content);
            }
        }

        return selected;
    }

    public override Vector2 GetWindowSize()
    {
        return new Vector2(m_rect_window.width, m_rect_window.height);
    }

    public override void OnOpen()
    {
        if (set_color)
        {
            tex_background = new Texture2D(1, 1, TextureFormat.RGB24, false);
            tex_background.SetPixel(0, 0, m_color_backgraound);
            tex_background.Apply();
        }
    }

    public override void OnGUI(Rect rect)
    {
        if (set_color) GUI.DrawTexture(new Rect(0, 0, m_rect_window.width, m_rect_window.height), tex_background, ScaleMode.StretchToFill);

        int name_length = m_button_names.Length;
        if (name_length == 0) return;

        EditorGUI.BeginChangeCheck();
        search_term = EditorGUILayout.TextField(search_term, m_style_search);

        if (EditorGUI.EndChangeCheck())
        {
            if(search_term != "")
            {
                for (int i = 0; i < name_length; i++)
                {
                    m_hidden_flags[i] = true;

                    //大文字小文字の区別をなくす
                    string lower_name = m_button_names[i].ToLower();
                    int n = lower_name.IndexOf(search_term.ToLower());
                    //先頭から一致
                    //if (n == 0)
                    //{
                    //    m_hidden_flags[i] = false;
                    //}
                    //どこかしら一致
                    if (n != -1)
                    {
                        m_hidden_flags[i] = false;
                    }
                }
            }
            else
            {
                for (int i = 0; i < name_length; i++) { m_hidden_flags[i] = false; }
            }
        }

        GUIStyle style_radio = new GUIStyle(EditorStyles.radioButton);

        scroll_pos = EditorGUILayout.BeginScrollView(scroll_pos);
        int selected = CustomEditorGUI.RadioButton(m_button_names, m_selected, m_hidden_flags, m_style_label, style_radio);
        EditorGUILayout.EndScrollView();

        if (m_selected != selected)
        {
            m_editorWindow.Repaint();
            m_selected = selected;
        }
        if (m_instance != null)
        {
            PopUpInfo.instance.SetSelectedValue(m_selected);
        }
    }

    public override void OnClose()
    {
        on_close = true;
        PopUpInfo.instance = null;
        m_editorWindow.Repaint();
    }
}

検索を行うためのキーワードを入力するためのTextFieldを追加しています。EditorGUI.BeginChangeCheck()により、TextFieldの内容が変更された時のみ検索が実行されるようにしています。

EditorGUI.BeginChangeCheck();
search_term = EditorGUILayout.TextField(search_term, m_style_search);

始めにEditorGUI.EndChangeCheck()により、TextFiledの内容が変更された時に実行されるようにしています。

if (EditorGUI.EndChangeCheck())
{
・・・
}

次に、search_term != “”によりTextFieldに文字が入力されたか否かを判別します。文字列が入力されていない場合はm_hidden_flagsを全てfalseにすることで、ラジオボタンを全て表示するようにしています。

if(search_term != "")
{
・・・
}
else
{
    for (int i = 0; i < name_length; i++) { m_hidden_flags[i] = false; }
}

文字が入力された場合の処理です。各ラジオボタンのラベル(m_button_names)と検索用キーワード(search_term)をそれぞれ、string.ToLower()により全て小文字にしています。これにより、大文字と小文字を区別することなく検索を行うことができます。

string lower_name = m_button_names[i].ToLower();
int n = lower_name.IndexOf(search_term.ToLower());

string.IndexOf()により、キーワードがラジオボタンのラベルに一致するか否かを判定します。string.IndexOf()は一致する部分がなければ-1を、一致する部分があればその位置を返します。よって、n != -1の場合にm_hidden_flags[i] = falseとし、ラジオボタンを表示するようにすれば、キーワードと一致したラジオボタンのみを表示できます。また、n == 0とすれば、キーワードがラベルの先頭から一致した場合のみ表示するようにできます。

//先頭から一致
//if (n == 0)
//{
//    m_hidden_flags[i] = false;
//}
//どこかしら一致
if (n != -1)
{
    m_hidden_flags[i] = false;
}

ラジオボタン

 以前作成したラジオボタンへ表示、非表示の機能を追加したラジオボタンのScriptです。

public class CustomEditorGUI
{
    public static int RadioButton(string[] labels, int selected, bool[] hidden_flags, GUIStyle style_label, GUIStyle style_radio)
    {
        Event event_current = Event.current;
        Vector2 mouse_pos = event_current.mousePosition;
        int length = labels.Length;
        int margin = 3;
        int button_size = style_radio.normal.background.width;

        for (int i = 0; i < length; i++)
        {
            if (hidden_flags[i] == true) continue;
            int control_id = GUIUtility.GetControlID(FocusType.Keyboard);
            Rect rect_control = GUILayoutUtility.GetRect(GUIContent.none, style_radio);
            Rect rect_button = new Rect(rect_control) { width = button_size };
            Rect rect_label = new Rect(rect_control) { x = rect_button.width + margin, width = rect_control.width - rect_button.width - margin };

            EditorGUI.PrefixLabel(rect_label, control_id, new GUIContent(labels[i]), style_label);

            //radio button
            bool is_on = false;
            if (event_current.type == EventType.Repaint)
            {
                if (i == selected)
                {
                    is_on = true;
                }
                style_radio.Draw(rect_button, GUIContent.none, control_id, is_on);
            }

            if (event_current.type == EventType.MouseDown)
            {
                if (rect_control.Contains(mouse_pos))
                {
                    selected = i;
                    GUIUtility.keyboardControl = control_id;
                    HandleUtility.Repaint();
                }
            }
        }

        return selected;
    }
}

表示するか非表示にするかをhidden_flagsによって受け取ります。そして、非表示とすることを指定されていた場合(true)、continueによりfor文の処理を飛ばすことで非表示にしています。

if (hidden_flags[i] == true) continue;

実行結果

 以上のScriptを実行した結果は以下の通りです。キーワードによりラジオボタンの表示、非表示ができていることが分かります。