当前位置: 首页 > 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://www.lryc.cn/news/588855.html

相关文章:

  • Expression 类的静态方法
  • 用TensorFlow进行逻辑回归(五)
  • 简单明了的对比PyTorch与TensorFlow
  • VSCode同时支持Vue2和Vue3开发的插件指南
  • Spark 之 Join BoundCondition
  • 云手机隐私保护指南:如何保障账号与数据的云端安全?
  • Java单元测试JUnit
  • 静态补丁脚本 - 修改 libtolua.so
  • MySQL数据库----约束
  • 开源工具与框架:基于.NET Core 的 Modbus 网关开发(一)
  • 硬件与软件的桥梁:冯诺依曼体系、操作系统和初始进程的深度解析
  • 【目标追踪】MUTR3D: A Multi-camera Tracking Framework via 3D-to-2D Queries
  • S7-200 SMART PLC:不同CPU及数字量 IO 接线全解析
  • AUTOSAR进阶图解==>AUTOSAR_SWS_FlexRayISOTransportLayer
  • 读书笔记5:交易在供应链中的关键作用
  • AI产品经理面试宝典第20天:AI+金融场景相关面试题及回答指导
  • C#,List<T> 与 Vector<T>
  • 【记录】Ubuntu20.04安装mysql
  • k8s之Snapshots 详解
  • Apifox 和 Apipost如何选?2025企业API开发工具选型需求分析及建议
  • 前端打包自动压缩为zip--archiver
  • SpringBoot 2.x→3.0升级实战:Jakarta EE兼容性改造清单
  • Flink双流实时对账
  • GaussDB 数据库架构师修炼(三) 集群管理概览
  • 数据结构--树(1)
  • 同样是“跳转”,为何forward地址栏不变,redirect会变?
  • 20250715给荣品RD-RK3588开发板刷Android14时打开USB鼠标
  • MATLAB知识点总结
  • 物联网设备管理工具实战:用AR运维镜击穿6.8天修复魔咒
  • 构建企业级项目管理全面数字化运营体系︱易趋(蓝云软件)总裁唐智勇