优化动态反射界面类型
This commit is contained in:
+118
-1
@@ -43,7 +43,10 @@ namespace XericUIEditor.ReflectionUIGenerator
|
||||
// 基本属性
|
||||
EditorGUILayout.PropertyField(_themeNameProp, new GUIContent("主题名称 (Theme)"));
|
||||
EditorGUILayout.PropertyField(_uiNameProp, new GUIContent("UI 名称 (uiName)"));
|
||||
EditorGUILayout.PropertyField(_valueTypeNameProp, new GUIContent("绑定类型 (valueType)"));
|
||||
|
||||
// 绑定类型 — 带快捷选择 + 自动纠错
|
||||
DrawValueTypeField();
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
// 预制体 + 自动扫描
|
||||
@@ -136,5 +139,119 @@ namespace XericUIEditor.ReflectionUIGenerator
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: be0acbdac8e549578dcccf8c076a5433, type: 3}
|
||||
m_Name: Xeric UI avr Property
|
||||
m_EditorClassIdentifier:
|
||||
valueType:
|
||||
propertyName:
|
||||
prefab: {fileID: 0}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: bcd1c64483d2457ab9143f086dff511d, type: 3}
|
||||
m_Name: XuiavrgButton-Default
|
||||
m_EditorClassIdentifier:
|
||||
themeName: Default
|
||||
uiName: Default
|
||||
valueTypeName: System.Single
|
||||
prefab: {fileID: 6894309230704738580, guid: a58d335e7df1a994d9f1c0fd58aff68f, type: 3}
|
||||
supportedTags: 010000001000000002000000
|
||||
+6
-4
@@ -10,8 +10,10 @@ MonoBehaviour:
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: bcd1c64483d2457ab9143f086dff511d, type: 3}
|
||||
m_Name: Xeric UI avr Property group
|
||||
m_Name: XuiavrgDropdown-Default
|
||||
m_EditorClassIdentifier:
|
||||
groupName:
|
||||
supportedTags:
|
||||
propertys: []
|
||||
themeName: Default
|
||||
uiName: Default
|
||||
valueTypeName: System.Enum
|
||||
prefab: {fileID: 1100600009542095382, guid: 4f25b16125eac234e820506fff28a4b0, type: 3}
|
||||
supportedTags: 01000000
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6beec83f76806e947add57932ae56c3b
|
||||
guid: 8a13481739bcb4d468fced5fde896f0f
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: bcd1c64483d2457ab9143f086dff511d, type: 3}
|
||||
m_Name: XuiavrgFieldInput-Default
|
||||
m_EditorClassIdentifier:
|
||||
themeName: Default
|
||||
uiName: Default
|
||||
valueTypeName: System.String
|
||||
prefab: {fileID: 8186464640817935025, guid: 1715f06fee075eb45b68d43575e9aa88, type: 3}
|
||||
supportedTags: 0100000080000000
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4aa3fe7aa30f1b240a4486c12152cd4d
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: bcd1c64483d2457ab9143f086dff511d, type: 3}
|
||||
m_Name: XuiavrgFloatSlider-Default
|
||||
m_EditorClassIdentifier:
|
||||
themeName: Default
|
||||
uiName: Default
|
||||
valueTypeName: System.Single
|
||||
prefab: {fileID: 3950712135413418637, guid: f601d441cf135fe479d319c5eb01abd8, type: 3}
|
||||
supportedTags: 010000002000000024000000
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 496a772f348d94c419faf2f5c09b773f
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: bcd1c64483d2457ab9143f086dff511d, type: 3}
|
||||
m_Name: XuiavrgToggle-Default
|
||||
m_EditorClassIdentifier:
|
||||
themeName: Default
|
||||
uiName: Default
|
||||
valueTypeName: System.Boolean
|
||||
prefab: {fileID: 8328042880981269257, guid: 0dc894baa18f5cb4eb9c82fd3c40d2bd, type: 3}
|
||||
supportedTags: 0100000008000000
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 41a9df21e888bf3409ef31a6079aab61
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+16
@@ -12,6 +12,7 @@ GameObject:
|
||||
- component: {fileID: 5465193508297998041}
|
||||
- component: {fileID: 5807829770662586291}
|
||||
- component: {fileID: 3872489455446479228}
|
||||
- component: {fileID: 5034032560219102209}
|
||||
m_Layer: 0
|
||||
m_Name: InputField (TMP)
|
||||
m_TagString: Untagged
|
||||
@@ -175,6 +176,21 @@ MonoBehaviour:
|
||||
m_isRichTextEditingAllowed: 0
|
||||
m_LineLimit: 0
|
||||
m_InputValidator: {fileID: 0}
|
||||
--- !u!114 &5034032560219102209
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 504238472980676093}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: eb8f3a8825c4b06458021d0414b254cf, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
_targetInputField: {fileID: 3872489455446479228}
|
||||
_entryTag: 128
|
||||
_tagIndex: 0
|
||||
--- !u!1 &2824130853841284688
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
|
||||
+92
@@ -111,6 +111,98 @@ namespace XericUI.ReflectionUIGenerator
|
||||
}
|
||||
private Type _valueType;
|
||||
|
||||
/// <summary>
|
||||
/// 所有程序集中类型名的缓存 — 简单名列表(如 "Boolean"、"Int32"、"String")
|
||||
/// 与 _allTypeFullNames 下标一一对应
|
||||
/// </summary>
|
||||
private static List<string> _allTypeSimpleNames;
|
||||
|
||||
/// <summary>
|
||||
/// 所有程序集中类型名的缓存 — 全名列表(如 "System.Boolean"、"System.Int32")
|
||||
/// 与 _allTypeSimpleNames 下标一一对应
|
||||
/// </summary>
|
||||
private static List<string> _allTypeFullNames;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化类型名缓存 — 反射扫描所有已加载程序集中的类型
|
||||
/// </summary>
|
||||
private static void EnsureTypeCacheBuilt()
|
||||
{
|
||||
if (_allTypeFullNames != null) return;
|
||||
|
||||
_allTypeSimpleNames = new List<string>();
|
||||
_allTypeFullNames = new List<string>();
|
||||
|
||||
var seen = new HashSet<string>();
|
||||
|
||||
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (var type in asm.GetTypes())
|
||||
{
|
||||
var fullName = type.FullName;
|
||||
if (string.IsNullOrEmpty(fullName)) continue;
|
||||
// 跳过泛型参数位置和编译器生成的类型
|
||||
if (fullName.Contains("`") || fullName.Contains("<")) continue;
|
||||
|
||||
if (!seen.Add(fullName)) continue;
|
||||
|
||||
_allTypeSimpleNames.Add(type.Name);
|
||||
_allTypeFullNames.Add(fullName);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 某些程序集可能无法反射(如动态程序集),忽略
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试将用户输入的类型名纠正为标准全名。
|
||||
/// 通过 LevenshteinDistance 在所有已知类型中找到最匹配的名称。
|
||||
/// 如 "boolean" → "System.Boolean", "single" → "System.Single"。
|
||||
/// 如果已经是标准全名或无法匹配则返回原值(不做修改)。
|
||||
/// </summary>
|
||||
/// <param name="userInput">用户输入的类型名</param>
|
||||
/// <returns>纠正后的标准类型全名,无法识别时返回原值</returns>
|
||||
public static string NormalizeTypeName(string userInput)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(userInput))
|
||||
return userInput;
|
||||
|
||||
userInput = userInput.Trim();
|
||||
|
||||
// 已经是标准全名(包含 "."),直接视为 OK,不做修改
|
||||
if (userInput.Contains('.'))
|
||||
return userInput;
|
||||
|
||||
// 初始化类型缓存
|
||||
EnsureTypeCacheBuilt();
|
||||
|
||||
// 通过 Levenshtein 距离在所有类型简单名中找到最匹配的
|
||||
var bestMatch = XericLibrary.Runtime.MacroLibrary.MacroMath.LevenshteinDistance(
|
||||
_allTypeSimpleNames, userInput,
|
||||
out int dis, out int index);
|
||||
|
||||
// 阈值:距离超过输入长度 2 倍 + 2,认为不匹配,返回原值
|
||||
if (bestMatch == null || dis > userInput.Length * 2 + 2)
|
||||
return userInput;
|
||||
|
||||
// 返回对应的全名
|
||||
return _allTypeFullNames[index];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试将此实例的 valueTypeName 纠正为标准全名
|
||||
/// </summary>
|
||||
public void NormalizeValueTypeName()
|
||||
{
|
||||
valueTypeName = NormalizeTypeName(valueTypeName);
|
||||
_valueType = null; // 清空缓存,下次访问时重新解析
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 对应的 UI 预制体
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user