重构反射生成属性界面

This commit is contained in:
2026-07-23 15:56:29 +08:00
parent c694115893
commit e63f1fb000
105 changed files with 2156 additions and 3280 deletions
@@ -1,257 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
using XericUI.ReflectionUIGenerator;
namespace XericUIEditor.ReflectionUIGenerator
{
[CustomEditor(typeof(XuiavrgPropertyGroupScriptableObject))]
internal class XuiavrgPropertyGroupScriptableObjectEditor : Editor
{
SerializedProperty _themeNameProp;
SerializedProperty _uiNameProp;
SerializedProperty _valueTypeNameProp;
SerializedProperty _prefabProp;
SerializedProperty _supportedTagsProp;
private XuiavrgPropertyGroupScriptableObject _target;
private bool _showDetectedComponents = true;
private void OnEnable()
{
_themeNameProp = serializedObject.FindProperty("themeName");
_uiNameProp = serializedObject.FindProperty("uiName");
_valueTypeNameProp = serializedObject.FindProperty("valueTypeName");
_prefabProp = serializedObject.FindProperty("prefab");
_supportedTagsProp = serializedObject.FindProperty("supportedTags");
_target = target as XuiavrgPropertyGroupScriptableObject;
}
public override void OnInspectorGUI()
{
serializedObject.Update();
// 标题
EditorGUILayout.LabelField("UI Property Group", EditorStyles.boldLabel);
EditorGUILayout.HelpBox(
"将一个 C# 数据类型绑定到一个 UI 预制体,并归属到某个主题下。\n" +
"Manager 根据当前主题 + 字段 Tag 自动匹配。",
MessageType.Info);
EditorGUILayout.Space();
// 基本属性
EditorGUILayout.PropertyField(_themeNameProp, new GUIContent("主题名称 (Theme)"));
EditorGUILayout.PropertyField(_uiNameProp, new GUIContent("UI 名称 (uiName)"));
// 绑定类型 — 带快捷选择 + 自动纠错
DrawValueTypeField();
EditorGUILayout.Space();
// 预制体 + 自动扫描
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(_prefabProp, new GUIContent("UI 预制体 (prefab)"));
if (EditorGUI.EndChangeCheck())
{
serializedObject.ApplyModifiedProperties();
if (_target.prefab != null)
{
_target.ScanPrefabComponents();
EditorUtility.SetDirty(_target);
}
}
EditorGUILayout.Space();
// 支持的功能标记
EditorGUILayout.PropertyField(_supportedTagsProp, new GUIContent("支持的功能标记 (supportedTags)"), true);
EditorGUILayout.Space();
// 组件审查区域
_showDetectedComponents = EditorGUILayout.Foldout(_showDetectedComponents, "预制体组件审查", true);
if (_showDetectedComponents)
{
EditorGUI.indentLevel++;
if (_target.prefab == null)
{
EditorGUILayout.HelpBox("请先拖入 UI 预制体,系统将自动扫描其中的 IXuiavrgFieldTagProvider 组件。", MessageType.Warning);
}
else if (_target.detectedComponents == null || _target.detectedComponents.Count == 0)
{
if (GUILayout.Button("扫描预制体组件"))
{
_target.ScanPrefabComponents();
EditorUtility.SetDirty(_target);
}
if (_target.detectedComponents != null && _target.detectedComponents.Count == 0)
EditorGUILayout.HelpBox("未检测到 IXuiavrgFieldTagProvider 组件。请确保预制体上已挂载 XericUIrAttribute* 组件。", MessageType.Info);
}
else
{
EditorGUILayout.LabelField($"检测到 {_target.detectedComponents.Count} 个标记组件:", EditorStyles.boldLabel);
// 按 Tag 分组显示
var groups = _target.detectedComponents
.Where(c => c != null)
.GroupBy(c =>
{
if (c is IXuiavrgFieldTagProvider tp)
return $"{tp.EntryTag}" + (tp.TagIndex > 0 ? $" [{tp.TagIndex}]" : "");
return c.GetType().Name;
});
foreach (var group in groups)
{
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
EditorGUILayout.LabelField($"Tag: {group.Key}", EditorStyles.miniBoldLabel);
foreach (var comp in group)
{
using (new EditorGUI.DisabledScope(true))
{
EditorGUILayout.ObjectField(comp, typeof(Component), true);
}
}
EditorGUILayout.EndVertical();
}
}
EditorGUI.indentLevel--;
}
EditorGUILayout.Space();
// 快捷操作按钮
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("重新扫描"))
{
_target.ScanPrefabComponents();
EditorUtility.SetDirty(_target);
}
if (GUILayout.Button("同步 Tags"))
{
_target.ScanPrefabComponents();
EditorUtility.SetDirty(_target);
}
EditorGUILayout.EndHorizontal();
serializedObject.ApplyModifiedProperties();
}
private static List<string> _cachedTypeNames;
private static List<string> _cachedFullNames;
/// <summary>
/// 从 AppDomain 动态收集所有可用类型名(Editor 侧缓存)
/// </summary>
private static void EnsureEditorTypeCache()
{
if (_cachedTypeNames != null) return;
_cachedTypeNames = new List<string>();
_cachedFullNames = new List<string>();
var seen = new HashSet<string>();
foreach (var asm in System.AppDomain.CurrentDomain.GetAssemblies())
{
try
{
foreach (var type in asm.GetTypes())
{
var full = type.FullName;
if (string.IsNullOrEmpty(full) || full.Contains("`") || full.Contains("<")) continue;
if (!seen.Add(full)) continue;
_cachedTypeNames.Add(type.Name);
_cachedFullNames.Add(full);
}
}
catch { }
}
}
private void DrawValueTypeField()
{
EditorGUILayout.LabelField("绑定类型 (valueType)", EditorStyles.label);
// 输入行
EditorGUILayout.BeginHorizontal();
var currentValue = _valueTypeNameProp.stringValue;
var newValue = EditorGUILayout.TextField(currentValue);
if (newValue != currentValue)
{
var corrected = XuiavrgPropertyGroupScriptableObject.NormalizeTypeName(newValue);
_valueTypeNameProp.stringValue = corrected;
currentValue = corrected;
}
// "搜索类型" 按钮
if (GUILayout.Button("...", GUILayout.Width(30)))
{
var menu = new GenericMenu();
EnsureEditorTypeCache();
// 如果有输入,用 LevenshteinDistance 排序,最近的排前面
var ranked = new List<(int dist, string name, string full)>();
for (int i = 0; i < _cachedTypeNames.Count; i++)
{
XericLibrary.Runtime.MacroLibrary.MacroMath.LevenshteinDistance(
new[] { _cachedTypeNames[i] }, currentValue, out int dist, out _);
ranked.Add((dist, _cachedTypeNames[i], _cachedFullNames[i]));
}
ranked.Sort((a, b) => a.dist.CompareTo(b.dist));
int shown = 0;
// 完全匹配的排最前面
foreach (var (dist, name, full) in ranked)
{
if (dist != 0 || shown >= 30) break;
menu.AddItem(new GUIContent($"{full} ({name})"), false, () =>
{
_valueTypeNameProp.stringValue = full;
serializedObject.ApplyModifiedProperties();
});
shown++;
}
// 用分隔线隔开,然后显示近匹配
if (shown > 0 && ranked.Count > shown)
menu.AddSeparator("");
int nearTotal = 0;
foreach (var (dist, name, full) in ranked)
{
if (dist == 0) continue;
if (nearTotal >= 20) break;
menu.AddItem(new GUIContent($"{full} ({name})"), false, () =>
{
_valueTypeNameProp.stringValue = full;
serializedObject.ApplyModifiedProperties();
});
nearTotal++;
}
menu.ShowAsContext();
}
EditorGUILayout.EndHorizontal();
// 纠错提示
var normalized = XuiavrgPropertyGroupScriptableObject.NormalizeTypeName(currentValue);
if (normalized != currentValue && !string.IsNullOrEmpty(currentValue))
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.HelpBox($"识别为: {normalized}", MessageType.Info);
if (GUILayout.Button("纠正", GUILayout.Width(60), GUILayout.Height(38)))
{
_valueTypeNameProp.stringValue = normalized;
}
EditorGUILayout.EndHorizontal();
}
}
}
}
@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: c7acec0d907c14a4f9f8247b6ccd8261
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,16 +0,0 @@
using UnityEditor;
using XericUI.OverrideUI;
namespace XericUIEditor.OverrideUI
{
[CustomEditor(typeof(XericUIrAttributeButton))]
[CanEditMultipleObjects]
internal class XericUIrAttributeButtonEditor : XericButtonEditor
{
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
XericUIrAttributeEditorHelper.DrawAttributeTagFields(serializedObject);
}
}
}
@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 326a7ebf62af0d5419630e20cf2861e7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,17 +0,0 @@
using UnityEditor;
using UnityEditor.UI;
using XericUI.OverrideUI;
namespace XericUIEditor.OverrideUI
{
[CustomEditor(typeof(XericUIrAttributeDropdown))]
[CanEditMultipleObjects]
internal class XericUIrAttributeDropdownEditor : DropdownEditor
{
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
XericUIrAttributeEditorHelper.DrawAttributeTagFields(serializedObject);
}
}
}
@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 8f8d011250fa17848a724fbb80e756fc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,33 +0,0 @@
using UnityEditor;
namespace XericUIEditor.OverrideUI
{
/// <summary>
/// XericUIrAttribute* 组件的 Editor 通用辅助方法
/// </summary>
internal static class XericUIrAttributeEditorHelper
{
/// <summary>
/// 绘制 _entryTag 和 _tagIndex 字段
/// 在 OnInspectorGUI 末尾调用
/// </summary>
public static void DrawAttributeTagFields(SerializedObject serializedObject)
{
EditorGUILayout.Space();
EditorGUILayout.LabelField("Reflection UI Attribute Tag", EditorStyles.boldLabel);
serializedObject.Update();
var entryTagProp = serializedObject.FindProperty("_entryTag");
var tagIndexProp = serializedObject.FindProperty("_tagIndex");
if (entryTagProp != null)
EditorGUILayout.PropertyField(entryTagProp);
if (tagIndexProp != null)
EditorGUILayout.PropertyField(tagIndexProp);
serializedObject.ApplyModifiedProperties();
}
}
}
@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 9a59db39a3dc2734bb5f3adaafbed06c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,17 +0,0 @@
using UnityEditor;
using UnityEditor.UI;
using XericUI.OverrideUI;
namespace XericUIEditor.OverrideUI
{
[CustomEditor(typeof(XericUIrAttributeInputField))]
[CanEditMultipleObjects]
internal class XericUIrAttributeInputFieldEditor : InputFieldEditor
{
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
XericUIrAttributeEditorHelper.DrawAttributeTagFields(serializedObject);
}
}
}
@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: b1ce54b154544da4c8cca4b4b8063e88
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,17 +0,0 @@
using UnityEditor;
using UnityEditor.UI;
using XericUI.OverrideUI;
namespace XericUIEditor.OverrideUI
{
[CustomEditor(typeof(XericUIrAttributeSlider))]
[CanEditMultipleObjects]
internal class XericUIrAttributeSliderEditor : SliderEditor
{
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
XericUIrAttributeEditorHelper.DrawAttributeTagFields(serializedObject);
}
}
}
@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 1d0a860b1f96faa44a2536f607030df9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,30 +0,0 @@
using UnityEditor;
using UnityEditor.UI;
using XericUI.OverrideUI;
namespace XericUIEditor.OverrideUI
{
[CustomEditor(typeof(XericUIrAttributeText))]
[CanEditMultipleObjects]
internal class XericUIrAttributeTextEditor : ImageEditor
{
SerializedProperty _targetTextProp;
protected override void OnEnable()
{
base.OnEnable();
_targetTextProp = serializedObject.FindProperty("_targetText");
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
EditorGUILayout.Space();
if (_targetTextProp != null)
EditorGUILayout.PropertyField(_targetTextProp);
XericUIrAttributeEditorHelper.DrawAttributeTagFields(serializedObject);
}
}
}
@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 0a589d7a31bab924dadbb7b510489e86
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,54 +0,0 @@
using UnityEditor;
using UnityEngine;
using XericUI.OverrideUI;
namespace XericUIEditor.OverrideUI
{
[CustomEditor(typeof(XericUIrAttributeTextMeshProInputField))]
[CanEditMultipleObjects]
internal class XericUIrAttributeTextMeshProInputFieldEditor : Editor
{
#if dUI_TextMeshPro
SerializedProperty _targetInputFieldProp;
#endif
SerializedProperty _entryTagProp;
SerializedProperty _tagIndexProp;
private void OnEnable()
{
#if dUI_TextMeshPro
_targetInputFieldProp = serializedObject.FindProperty("_targetInputField");
#endif
_entryTagProp = serializedObject.FindProperty("_entryTag");
_tagIndexProp = serializedObject.FindProperty("_tagIndex");
}
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorGUILayout.LabelField("XericUIrAttributeTextMeshProInputField", EditorStyles.boldLabel);
EditorGUILayout.Space();
EditorGUILayout.HelpBox(
"此组件为 TMP_InputField 的标记附加器,需挂在与 TMP_InputField 同一个 GameObject 上。\n" +
"_entryTag 和 _tagIndex 用于反射界面生成器自动绑定。",
MessageType.Info);
#if dUI_TextMeshPro
EditorGUILayout.PropertyField(_targetInputFieldProp);
#endif
EditorGUILayout.Space();
EditorGUILayout.LabelField("Reflection UI Attribute Tag", EditorStyles.boldLabel);
if (_entryTagProp != null)
EditorGUILayout.PropertyField(_entryTagProp);
if (_tagIndexProp != null)
EditorGUILayout.PropertyField(_tagIndexProp);
serializedObject.ApplyModifiedProperties();
}
}
}
@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: ed1041e7533d7024a9e5e62b888082ba
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,57 +0,0 @@
using UnityEditor;
using UnityEngine;
using XericUI.OverrideUI;
namespace XericUIEditor.OverrideUI
{
[CustomEditor(typeof(XericUIrAttributeTextMeshProText))]
[CanEditMultipleObjects]
internal class XericUIrAttributeTextMeshProTextEditor : Editor
{
#if dUI_TextMeshPro
SerializedProperty _targetTextProp;
#endif
SerializedProperty _entryTagProp;
SerializedProperty _tagIndexProp;
private void OnEnable()
{
#if dUI_TextMeshPro
_targetTextProp = serializedObject.FindProperty("_targetText");
#endif
_entryTagProp = serializedObject.FindProperty("_entryTag");
_tagIndexProp = serializedObject.FindProperty("_tagIndex");
}
public override void OnInspectorGUI()
{
serializedObject.Update();
var targetComp = target as XericUIrAttributeTextMeshProText;
EditorGUILayout.LabelField("XericUIrAttributeTextMeshProText", EditorStyles.boldLabel);
EditorGUILayout.Space();
// 提示信息
EditorGUILayout.HelpBox(
"此组件为 TMP_Text 的标记附加器,需挂在与 TMP_Text 同一个 GameObject 上。\n" +
"_entryTag 和 _tagIndex 用于反射界面生成器自动绑定。",
MessageType.Info);
#if dUI_TextMeshPro
EditorGUILayout.PropertyField(_targetTextProp);
#endif
EditorGUILayout.Space();
EditorGUILayout.LabelField("Reflection UI Attribute Tag", EditorStyles.boldLabel);
if (_entryTagProp != null)
EditorGUILayout.PropertyField(_entryTagProp);
if (_tagIndexProp != null)
EditorGUILayout.PropertyField(_tagIndexProp);
serializedObject.ApplyModifiedProperties();
}
}
}
@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 8226bbbc53d035e43b6811dc1f346f60
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,16 +0,0 @@
using UnityEditor;
using XericUI;
using XericUI.OverrideUI;
namespace XericUIEditor.OverrideUI
{
[CustomEditor(typeof(XericUIrAttributeToggle))]
internal class XericUIrAttributeToggleEditor : XericToggleEditor
{
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
XericUIrAttributeEditorHelper.DrawAttributeTagFields(serializedObject);
}
}
}
@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 8ae7bafc6acd1b345b62d3467fc23cbb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: