当前位置: 首页 > news >正文

Unity Editor下拉框,支持搜索,多层级

Unity Editor下拉框,支持搜索,多层级

using Sirenix.OdinInspector;
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;namespace Tools
{public class TGDropdownView{private List<DropdownItem> rootItems;private DropdownItem selectedItem = null;private Action<DropdownItem> onSelect;private string searchText = "";private Vector2 scrollPos;private bool showAllItemsOnOpen = true;      // 控制弹窗是否显示所有项(空搜索时)private bool showFullPathOnHeader = true;   // 控制下拉框按钮显示完整路径还是只显示节点名private DropdownItem hoverItem = null;private GUIStyle itemStyle;private HashSet<DropdownItem> matchedItems = new();public TGDropdownView(List<DropdownItem> items, Action<DropdownItem> onSelectCallback){rootItems = items;selectedItem = GetFirstLeaf(items);onSelect = onSelectCallback;itemStyle = new GUIStyle(EditorStyles.label){normal = { textColor = EditorGUIUtility.isProSkin ? Color.white : Color.black }};RefreshMatchedItems();// 初始化时如果有默认选择项,触发回调if (selectedItem != null){onSelect?.Invoke(selectedItem);}}// 外部控制属性public bool ShowAllItemsOnOpen{get => showAllItemsOnOpen;set => showAllItemsOnOpen = value;}public bool ShowFullPathOnHeader{get => showFullPathOnHeader;set => showFullPathOnHeader = value;}// 获取当前选择的项public DropdownItem SelectedItem => selectedItem;// 只画按钮,点击弹出窗口public void DrawInline(string label = "", float labelWidth = 100f, bool showLabel = true){EditorGUILayout.BeginHorizontal();if (showLabel){EditorGUILayout.LabelField(label, GUILayout.Width(labelWidth));}DrawHeader();EditorGUILayout.EndHorizontal();}// 下拉框按钮绘制和弹窗触发private void DrawHeader(){string buttonText;if (rootItems == null || rootItems.Count == 0){buttonText = "当前无任务";}else if (selectedItem == null){buttonText = "请选择 ▼";}else{buttonText = showFullPathOnHeader ? selectedItem.GetFullPath() : selectedItem.Name;}float maxWidth = EditorGUIUtility.currentViewWidth / 2 + 50;Rect buttonRect = GUILayoutUtility.GetRect(new GUIContent(buttonText), EditorStyles.popup,GUILayout.ExpandWidth(true), GUILayout.MaxWidth(maxWidth));GUI.enabled = !(rootItems == null || rootItems.Count == 0);if (GUI.Button(buttonRect, buttonText, EditorStyles.popup)){if (rootItems != null && rootItems.Count > 0){PopupWindow.Show(buttonRect, new TGDropdownPopup(this, buttonRect.width));}}GUI.enabled = true;}// 弹窗内绘制内容public void DrawPopupContent(){DrawSearchBox();Rect lineRect = EditorGUILayout.GetControlRect(false, 1);EditorGUI.DrawRect(lineRect, new Color(0.5f, 0.5f, 0.5f, 1f));DrawItemList();}private void DrawSearchBox(){float lineHeight = 20f;EditorGUILayout.BeginHorizontal(GUILayout.Height(lineHeight));// 搜索图标,限制宽高并垂直居中GUILayout.Label(EditorGUIUtility.IconContent("Search Icon"), GUILayout.Width(lineHeight), GUILayout.Height(lineHeight));// 搜索文字,固定宽度,高度和图标一样GUILayout.Label("搜索", GUILayout.Width(40), GUILayout.Height(lineHeight));// 输入框,高度和图标一致,宽度自动扩展EditorGUI.BeginChangeCheck();searchText = EditorGUILayout.TextField(searchText, GUILayout.Height(lineHeight));if (EditorGUI.EndChangeCheck()){RefreshMatchedItems();}EditorGUILayout.EndHorizontal();}private void DrawItemList(){if (matchedItems.Count == 0){EditorGUILayout.LabelField("没有匹配的项目", EditorStyles.miniLabel);return;}scrollPos = EditorGUILayout.BeginScrollView(scrollPos, GUILayout.Height(400));foreach (var root in rootItems){DrawItemRecursive(root);}EditorGUILayout.EndScrollView();}private void DrawItemRecursive(DropdownItem item){if (!matchedItems.Contains(item)) return;// 先计算缩进后按钮区域的Rectfloat indent = item.Depth * 20;// 先获取整行rect(不含缩进)Rect fullRect = GUILayoutUtility.GetRect(1, 20, GUILayout.ExpandWidth(true));// 缩进后的按钮区域Rect buttonRect = new Rect(fullRect.x + indent, fullRect.y, fullRect.width - indent, fullRect.height);Vector2 mousePos = Event.current.mousePosition;bool isHover = buttonRect.Contains(mousePos);if (isHover){hoverItem = item;EditorWindow.focusedWindow.Repaint();}EditorGUI.DrawRect(fullRect, isHover ? new Color(0.24f, 0.48f, 0.90f, 1f) : Color.clear);GUI.enabled = item.IsLeaf;if (GUI.Button(buttonRect, item.Name, itemStyle)){if (item.IsLeaf){selectedItem = item;onSelect?.Invoke(item);EditorWindow.focusedWindow?.Close();}}GUI.enabled = true;var lineRect = GUILayoutUtility.GetRect(1, 1, GUILayout.ExpandWidth(true));EditorGUI.DrawRect(lineRect, new Color(0.3f, 0.3f, 0.3f, 0.4f));foreach (var child in item.Children){DrawItemRecursive(child);}}private bool MatchItemRecursive(DropdownItem item){// 去掉搜索词中的空格string trimmedSearchText = searchText.Replace(" ", "");// 去掉路径中的空格string itemPath = item.GetFullPath().Replace(" ", "");// 判断是否匹配bool matched = string.IsNullOrEmpty(trimmedSearchText) ||itemPath.IndexOf(trimmedSearchText, StringComparison.OrdinalIgnoreCase) >= 0;bool childMatched = false;foreach (var child in item.Children){childMatched |= MatchItemRecursive(child);}if (matched || childMatched){matchedItems.Add(item);if (item.Parent != null) matchedItems.Add(item.Parent);return true;}return false;}// 刷新匹配项的公共方法public void RefreshMatchedItems(){matchedItems.Clear();if (showAllItemsOnOpen && string.IsNullOrEmpty(searchText)){// 直接把所有节点及子节点都加进matchedItemsvoid AddAllItems(DropdownItem item){matchedItems.Add(item);foreach (var child in item.Children){AddAllItems(child);}}foreach (var root in rootItems){AddAllItems(root);}}else{foreach (var root in rootItems){MatchItemRecursive(root);}}}private DropdownItem GetFirstLeaf(List<DropdownItem> items){foreach (var item in items){if (item.IsLeaf) return item;var leaf = GetFirstLeaf(item.Children);if (leaf != null) return leaf;}return null;}}// 弹窗类public class TGDropdownPopup : PopupWindowContent{private TGDropdownView dropdown;private float popupWidth;public TGDropdownPopup(TGDropdownView dropdownView, float width){dropdown = dropdownView;popupWidth = width;}public override Vector2 GetWindowSize(){return new Vector2(popupWidth, 425);}public override void OnGUI(Rect rect){dropdown.DrawPopupContent();}}public class DropdownItem{public string Name;public int Depth;//public object[] UserData;public object UserData;public DropdownItem Parent;public List<DropdownItem> Children = new();public DropdownItem(string name, int depth = 0, object userData = null){Name = name;Depth = depth;UserData = userData;}public bool IsLeaf => Children.Count == 0;public string GetFullPath(){return Parent == null ? Name : Parent.GetFullPath() + "/" + Name;}}
}
using Newtonsoft.Json;
using Sirenix.OdinInspector;
using Sirenix.OdinInspector.Editor;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;namespace Tools
{[Serializable]public class TGDropdownViewBox{private TGDropdownView dropdown;public TGDropdownView Dropdown => dropdown;public TGDropdownViewBox(List<DropdownItem> items, Action<DropdownItem> onSelectCallback){dropdown = new TGDropdownView(items, onSelectCallback);}[OnInspectorGUI]private void DrawCustomInspector(){//GUILayout.Space(10);dropdown?.DrawInline(showLabel: false);}}}

文章转载自:
http://basque.sxnf.com.cn
http://canada.sxnf.com.cn
http://gout.sxnf.com.cn
http://savoury.sxnf.com.cn
http://cryosorption.sxnf.com.cn
http://cuticle.sxnf.com.cn
http://hunky.sxnf.com.cn
http://texian.sxnf.com.cn
http://intentness.sxnf.com.cn
http://homotherm.sxnf.com.cn
http://laver.sxnf.com.cn
http://butterboat.sxnf.com.cn
http://treasonous.sxnf.com.cn
http://courteously.sxnf.com.cn
http://cartridge.sxnf.com.cn
http://dungeon.sxnf.com.cn
http://sabulous.sxnf.com.cn
http://graphospasm.sxnf.com.cn
http://shakily.sxnf.com.cn
http://choleric.sxnf.com.cn
http://dex.sxnf.com.cn
http://dryest.sxnf.com.cn
http://martyrdom.sxnf.com.cn
http://eightpenny.sxnf.com.cn
http://adessive.sxnf.com.cn
http://bedsore.sxnf.com.cn
http://gaily.sxnf.com.cn
http://cohere.sxnf.com.cn
http://answer.sxnf.com.cn
http://sanitize.sxnf.com.cn
http://www.dtcms.com/a/280674.html

相关文章:

  • BGP服务器和多线服务器的不同之处
  • Python初学者笔记第十三期 -- (常用内置函数)
  • 原点安全签约金网络数科,共建一体化数据安全防护体系
  • Docker 镜像(Image)常用命令总结
  • ASP .NET Core 8结合JWT轻松实现身份验证和授权
  • CMake基础:覆盖项目开发的五大配套工具
  • LLM面试题及讲解 4
  • VSCode同时支持Vue2和Vue3开发的插件指南
  • 【编程】-环形缓冲区
  • 安全参綉25暑假第一次作业
  • 超详细 anji-captcha滑块验证uniapp微信小程序前端组件
  • 备忘录设计模式
  • asyncio 与 uvloop
  • 策略设计模式分析
  • 如何将华为文件传输到电脑
  • Linux的用户和用户组与权限解析、环境变量说明与配置、sudo配置解析和使用
  • HarmonyOS从入门到精通:自定义组件开发指南(七):自定义事件与回调
  • 涨停板池,跌停板池,炸板池,次新股池,强势股池数据接口
  • 单臂路由实现VLAN互通实验
  • e签宝电子合同成为白象食品数字化转型中的关键一环
  • PostgreSQL 超详细安装与使用教程:从入门到实战
  • 深入剖析 React Server Components:原理、应用与性能优势
  • 设计模式一: 模板方法模式 (Template Method Pattern)
  • Nexus 私服管理工具
  • 李宏毅《生成式人工智能导论》 | 第11讲-第14讲:大型语言模型的可解释性、能力评估、安全性
  • 20250715问答课题-基于BERT与混合检索问答系统
  • 电商缓存强一致方案:数据库锁保障
  • 设计模式开篇:设计模式的七大核心原则
  • kube-proxy 中 IPVS 与 iptables
  • PyTorch笔记7----------计算机视觉基础