Unity 扩展弹出菜单窗口

原因

Unity 编辑器下提供了系统级的菜单弹窗,会阻塞消息,卡住渲染,以及不能进行搜索等功能,而它自己有非阻塞版本的弹窗,但不开放给用户使用,比如这个界面:
Unity 扩展弹出菜单窗口_非阻塞

解决

仿照编辑器源码,修改实现一份:

登录后复制

using System;
using System.Collections;
using System.Globalization;
using UnityEditor;
using UnityEngine;

/// <summary>
/// GenericMenu的Unity风格版本
/// </summary>
public class GenericPopupMenu : PopupWindowContent
{
    private class Styles
    {
        public readonly GUIStyle header = "In BigTitle";
        public readonly GUIStyle menuItem = "MenuItem";
        public readonly GUIStyle customTextField = "ToolbarSeachTextField";
        public readonly GUIStyle customTextFieldCancelButton = "ToolbarSeachCancelButton";
        public readonly GUIStyle customTextFieldCancelButtonEmpty = "ToolbarSeachCancelButtonEmpty";
    }

    private class MenuItem
    {
        public GUIContent content;
        public bool separator;
        public bool on;
        public GenericMenu.MenuFunction func;
        public GenericMenu.MenuFunction2 func2;
        public object userData;
        public bool enabled = true;

        public MenuItem(GUIContent _content, bool _separator, bool _on, GenericMenu.MenuFunction _func)
        {
            content = _content;
            separator = _separator;
            on = _on;
            func = _func;
        }

        public MenuItem(GUIContent _content, bool _separator, bool _on, GenericMenu.MenuFunction2 _func, object _userData)
        {
            content = _content;
            separator = _separator;
            on = _on;
            func2 = _func;
            userData = _userData;
        }

        public MenuItem(GUIContent _content, bool _separator, bool _on, bool _enabled)
        {
            content = _content;
            separator = _separator;
            on = _on;
            enabled = _enabled;
        }
    }

    private static Styles s_Styles;
    private static string s_TextFieldName = "GenericPopupMenuTextField";
    private readonly ArrayList m_MenuItems = new ArrayList();

    private readonly ArrayList m_MenuFilteredItems = new ArrayList();
    private bool m_EnableFiltered = true;
    private string m_FilteredText = string.Empty;
    private int m_SelectedIndex;
    private bool m_FocusText = true;
    private double m_FocusTextTime;

    private Vector2 m_ScrollPos;
    private bool m_IgnoreCase;

    public GenericPopupMenu(bool ignoreCase = false)
    {
        m_IgnoreCase = ignoreCase;
    }

    public void AddItem(GUIContent content, bool on, GenericMenu.MenuFunction func)
    {
        m_MenuItems.Add(new MenuItem(content, false, on, func));
    }

    public void AddItem(GUIContent content, bool on, GenericMenu.MenuFunction2 func, object userData)
    {
        m_MenuItems.Add(new MenuItem(content, false, on, func, userData));
    }

    public void AddDisabledItem(GUIContent content)
    {
        m_MenuItems.Add(new MenuItem(content, false, false, false));
    }

    public void AddSeparator(string path)
    {
        m_MenuItems.Add(new MenuItem(new GUIContent(path), true, false, null));
    }

    public int GetItemCount()
    {
        return m_MenuItems.Count;
    }

    public void ShowAsContext()
    {
        if (Event.current == null)
        {
            return;
        }
        DropDown(new Rect(Event.current.mousePosition.x, Event.current.mousePosition.y, 0f, 0f));
    }

    public void DropDown(Rect position)
    {
        PopupWindow.Show(position, this);
    }

    public override void OnOpen()
    {
        m_MenuFilteredItems.AddRange(m_MenuItems);
    }

    public override Vector2 GetWindowSize()
    {
        int maxLen = 0;
        for (int i = 0; i < m_MenuItems.Count; i++)
        {
            string contentText = ((MenuItem) m_MenuItems[i]).content.text;
            maxLen = Mathf.Max(maxLen, contentText.Length);
        }

        float w = 230f;
        if (maxLen > 25)
        {
            w += (maxLen - 25) * 5f;
        }
        return new Vector2(w, 320f);
    }

    public override void OnGUI(Rect rect)
    {
        if (s_Styles == null)
        {
            s_Styles = new Styles();
        }

        DrawCustomTextField(rect);
        DrawList(rect);

        if (Event.current.type == EventType.MouseMove)
        {
            Event.current.Use();
        }
    }

    private void DrawCustomTextField(Rect rect)
    {
        if (!m_EnableFiltered)
        {
            return;
        }

        Event current = Event.current;
        if (current.type == EventType.KeyDown)
        {
            KeyCode keyCode = current.keyCode;
            switch (keyCode)
            {
                case KeyCode.UpArrow:
                    ChangeSelectedIndex(-1);
                    break;
                case KeyCode.DownArrow:
                    ChangeSelectedIndex(1);
                    break;
                case KeyCode.Return:
                    break;
            }
        }

        string text = m_FilteredText;
        Rect rect2 = new Rect(rect.x + 5f, rect.y + 5f, rect.width - 10f - 14f, 16f);
        GUI.SetNextControlName(s_TextFieldName);
        if (m_FocusText)
        {
            EditorGUI.FocusTextInControl(s_TextFieldName);
        }
        else
        {
            if (m_FocusTextTime > 0 && m_FocusTextTime < EditorApplication.timeSinceStartup)
            {
                m_FocusText = true;
            }
        }
        string text2 = EditorGUI.TextField(rect2, text, s_Styles.customTextField);
        Rect position = rect2;
        position.x += rect2.width;
        position.width = 14f;
        if (GUI.Button(position, GUIContent.none, text2 != string.Empty ? s_Styles.customTextFieldCancelButton : s_Styles.customTextFieldCancelButtonEmpty))
        {
            text2 = string.Empty;
            GUI.FocusControl(null);
            m_FocusText = false;
            m_FocusTextTime = EditorApplication.timeSinceStartup + 0.3f;
        }
        if (text != text2)
        {
            m_FilteredText = text2.ToLower();

            m_MenuFilteredItems.Clear();
            for (int i = 0; i < m_MenuItems.Count; i++)
            {
                string contentText = ((MenuItem) m_MenuItems[i]).content.text;
                if (m_IgnoreCase)
                {
                    contentText = contentText.ToLower();
                }
                if (contentText.Contains(m_FilteredText))
                {
                    m_MenuFilteredItems.Add(m_MenuItems[i]);
                }
            }
        }
    }

    private void DrawList(Rect rect)
    {
        Rect position = rect;
        position.x = 1f;
        position.y = 30f;
        position.height -= 30f;
        position.width -= 2f;
        GUILayout.BeginArea(position);

        // 绘制标题头,可做分层菜单
        //Rect rect3 = GUILayoutUtility.GetRect(10f, 25f);
        //string name = String.Empty;
        //GUI.Label(rect3, name, s_Styles.header);

        m_ScrollPos = GUILayout.BeginScrollView(m_ScrollPos);
        for (int i = 0; i < m_MenuFilteredItems.Count; i++)
        {
            Rect rect2 = GUILayoutUtility.GetRect(16f, 20f, GUILayout.ExpandWidth(true));
            DrawListElement(rect2, i, (MenuItem)m_MenuFilteredItems[i]);
        }
        GUILayout.EndScrollView();

        GUILayout.EndArea();
    }

    private void DrawListElement(Rect rect, int index, MenuItem menuItem)
    {
        Event current = Event.current;
        EventType type = current.type;
        switch (type)
        {
            case EventType.MouseUp:
                {
                    if (Event.current.button == 0 && rect.Contains(current.mousePosition) && menuItem.enabled)
                    {
                        current.Use();
                        if (menuItem.func2 != null)
                        {
                            menuItem.func2(menuItem.userData);
                        }
                        else if (menuItem.func != null)
                        {
                            menuItem.func();
                        }

                        editorWindow.Close();
                        GUIUtility.ExitGUI();
                    }
                }
                break;
            case EventType.MouseMove:
                if (rect.Contains(current.mousePosition))
                {
                    m_SelectedIndex = index;
                    current.Use();
                }
                break;
            case EventType.Repaint:
                {
                    using (new EditorGUI.DisabledScope(!menuItem.enabled))
                    {
                        s_Styles.menuItem.Draw(rect, menuItem.content, index == m_SelectedIndex, menuItem.on, menuItem.on, false);
                    }
                }
                break;
        }
    }

    private void ChangeSelectedIndex(int change)
    {
        int filteredCount = m_MenuFilteredItems.Count;
        if (m_SelectedIndex == -1 && change < 0)
        {
            m_SelectedIndex = filteredCount;
        }
        int index = filteredCount <= 0 ? 0 : (m_SelectedIndex + change + filteredCount) % filteredCount;
        SetSelectedIndex(index);
    }

    private void SetSelectedIndex(int index)
    {
        m_SelectedIndex = index;
    }
}


效果类似如下:
Unity 扩展弹出菜单窗口_ico_02



免责声明:本文系网络转载或改编,未找到原创作者,版权归原作者所有。如涉及版权,请联系删

QR Code
微信扫一扫,欢迎咨询~

联系我们
武汉格发信息技术有限公司
湖北省武汉市经开区科技园西路6号103孵化器
电话:155-2731-8020 座机:027-59821821
邮件:tanzw@gofarlic.com
Copyright © 2023 Gofarsoft Co.,Ltd. 保留所有权利
遇到许可问题?该如何解决!?
评估许可证实际采购量? 
不清楚软件许可证使用数据? 
收到软件厂商律师函!?  
想要少购买点许可证,节省费用? 
收到软件厂商侵权通告!?  
有正版license,但许可证不够用,需要新购? 
联系方式 155-2731-8020
预留信息,一起解决您的问题
* 姓名:
* 手机:

* 公司名称:

姓名不为空

手机不正确

公司不为空