using System; using System.Collections.Generic; using System.Linq; using UnityEngine; using Object = UnityEngine.Object; namespace XericUI.ReflectionUIGenerator { /// /// 界面属性编组 /// /// 将一种 C# 数据类型与一个 UI 预制体绑定,并归属于一个主题。 /// 类似 Newtonsoft.Json 的 Converter 概念 — 针对特定 Type,指定特定的 UI 模板。 /// /// 同一类型可以有多个不同 uiName/主题的重载(如 "简单输入框" vs "带标签的输入框")。 /// Manager 根据当前主题名 + 字段 Tag 自动匹配对应的编组。 /// /// 全局注册表以 themeName 为键分组管理,OnEnable/OnDisable 自动注册/注销。 /// [Serializable] [CreateAssetMenu(fileName = "Xeric UI avr Property group", menuName = "Xeric Library/UI Action Vessel/Reflection UI Gen/UI avr Property Group", order = 10)] public class XuiavrgPropertyGroupScriptableObject : ScriptableObject { #region 全局注册表 /// /// 全局预制体组字典,以主题名称为键分组 /// internal static Dictionary> GlobalPrefabDict = new Dictionary>(); /// /// 根据主题名获取该主题下所有编组 /// public static List GetGroupsByTheme(string themeName) { if (string.IsNullOrEmpty(themeName)) themeName = DefaultThemeName; GlobalPrefabDict.TryGetValue(themeName, out var list); return list ?? new List(); } /// /// 根据主题名 + 标记查找编组 /// public static XuiavrgPropertyGroupScriptableObject FindGroup(string themeName, params XuiavrgFieldTag[] tags) { var groups = GetGroupsByTheme(themeName); return groups.FirstOrDefault(g => tags.All(t => g.supportedTags.Contains(t))); } /// /// 根据主题名 + UI 名称查找编组 /// public static XuiavrgPropertyGroupScriptableObject FindGroupByUiName(string themeName, string uiName) { var groups = GetGroupsByTheme(themeName); return groups.FirstOrDefault(g => g.uiName == uiName); } /// /// 根据主题名 + 绑定类型查找所有匹配的编组 /// public static List FindGroupsByType(string themeName, string valueTypeName) { var groups = GetGroupsByTheme(themeName); return groups.Where(g => g.valueTypeName == valueTypeName).ToList(); } /// /// 默认主题名称 /// public const string DefaultThemeName = "Default"; #endregion #region 属性字段 /// /// 主题名称。同一主题下的 UI 组件共享统一风格。 /// [Tooltip("主题名称。同一主题下的 UI 组件共享统一风格。Manager 根据其 currentTheme 查找对应编组")] public string themeName = DefaultThemeName; /// /// UI 个体名称。同一类型可有多个不同名称的重载。 /// 在数据字段上通过 XuiavrgFieldPrefabOverride 按此名称指定。 /// [Tooltip("UI 个体名称。用于 XuiavrgFieldPrefabOverride 按名指定,同一类型可有多个重载")] public string uiName = "Default"; /// /// 此编组绑定的 C# 数据类型全名(如 System.Single、System.String) /// [Tooltip("绑定的 C# 数据类型全名,如 System.Single、System.Boolean")] public string valueTypeName = "System.Single"; /// /// 解析后的类型缓存 /// public Type ValueType { get { if (_valueType != null) return _valueType; if (!string.IsNullOrEmpty(valueTypeName)) _valueType = Type.GetType(valueTypeName); return _valueType; } } private Type _valueType; /// /// 对应的 UI 预制体 /// [Tooltip("拖入 UI 预制体,系统会自动扫描其中的 IXuiavrgFieldTagProvider 组件")] public GameObject prefab; /// /// 此编组支持的 UI 功能标记集合 /// [Tooltip("此编组包含的 UI 功能标记。Manager 根据字段 Tag 匹配对应编组")] public List supportedTags = new List(); #endregion #region 组件自动识别(运行时 + Editor 共用) /// /// 从预制体中自动扫描到的 IXuiavrgFieldTagProvider 组件列表(非序列化,仅供 Editor 展示) /// [NonSerialized] public List detectedComponents = new List(); /// /// 重新扫描预制体中的标记组件 /// public void ScanPrefabComponents() { detectedComponents.Clear(); if (prefab == null) return; var components = prefab.GetComponentsInChildren(true); foreach (var comp in components) { if (comp is IXuiavrgFieldTagProvider tagProvider && tagProvider.EntryTag != XuiavrgFieldTag.None) { detectedComponents.Add(comp); } } // 自动同步 supportedTags HashSet autoTags = new HashSet(); foreach (var comp in detectedComponents) { if (comp is IXuiavrgFieldTagProvider tp && tp.EntryTag != XuiavrgFieldTag.None) autoTags.Add(tp.EntryTag); } foreach (var tag in autoTags) { if (!supportedTags.Contains(tag)) supportedTags.Add(tag); } } #endregion #region 生命周期 void OnEnable() { RegisterToGlobalDict(); } void OnDisable() { UnregisterFromGlobalDict(); } private void RegisterToGlobalDict() { string key = string.IsNullOrEmpty(themeName) ? DefaultThemeName : themeName; if (!GlobalPrefabDict.TryGetValue(key, out var list)) { list = new List(); GlobalPrefabDict[key] = list; } if (!list.Contains(this)) list.Add(this); } private void UnregisterFromGlobalDict() { string key = string.IsNullOrEmpty(themeName) ? DefaultThemeName : themeName; if (GlobalPrefabDict.TryGetValue(key, out var list)) { list.Remove(this); if (list.Count == 0) GlobalPrefabDict.Remove(key); } } #endregion } }