#if TEXTMESHPRO_SUPPORT using System.Collections.Generic; using System.Linq; using AlicizaX.Localization; using AlicizaX.Localization.Editor; using UnityEditor; using UnityEngine; using UnityEngine.UIElements; using UnityEditor.Experimental.GraphView; namespace UnityEngine.UI { internal readonly struct TableSelectionData { public readonly int Id; public readonly string CombineKey; // SectionName/Key → 菜单层级 public readonly string CombineValue; // SectionName.Key → 存储用 public TableSelectionData(int id, string combineKey, string combineValue) { Id = id; CombineKey = combineKey; CombineValue = combineValue; } } [CustomEditor(typeof(UXTextMeshPro), true)] [CanEditMultipleObjects] internal class UXTextMeshProEditor : TMPro.EditorUtilities.TMP_EditorPanelUI { private SerializedProperty localizationID; private SerializedProperty m_localizationKey; private List allTables = new(); private Dictionary allTableNames = new(); private Dictionary previewLabelDic = new(); private List allSelection = new(); private int selectedSelectionIndex = 0; public override VisualElement CreateInspectorGUI() { RefreshAllTables(); return base.CreateInspectorGUI(); } private void RefreshAllTables() { allTables.Clear(); string[] guids = AssetDatabase.FindAssets("t:GameLocaizationTable"); foreach (string guid in guids) { string assetPath = AssetDatabase.GUIDToAssetPath(guid); GameLocaizationTable table = AssetDatabase.LoadAssetAtPath(assetPath); if (table != null) { allTables.Add(table); } } InitAllTables(); } private void InitAllTables() { allTableNames.Clear(); allSelection.Clear(); previewLabelDic.Clear(); allSelection.Add("None"); allTableNames.TryAdd(0, new TableSelectionData(0, "None", string.Empty)); var localizationLanguage = allTables.Select(e => e.Languages.Find(t => t.LanguageName == LocalizationConfiguration.Instance.GenerateScriptCodeFirstConfig)).ToList(); foreach (var localization in localizationLanguage) { foreach (var item in localization.Strings) { previewLabelDic.TryAdd(item.Key, item.Value); } } foreach (var table in allTables) { foreach (var sheet in table.TableSheet) { foreach (var selection in sheet.SectionSheet) { string combineKey = $"{table.name}/{sheet.SectionName}/{selection.Key}"; string combineValue = $"{sheet.SectionName}.{selection.Key}"; int id = selection.Id; allTableNames.TryAdd(id, new TableSelectionData(id, combineKey, combineValue)); allSelection.Add(combineKey); } } } } protected string GetPreviewLabel() { return previewLabelDic.TryGetValue(m_localizationKey.stringValue, out var label) ? label : "None"; } protected void FindSelectSelection() { selectedSelectionIndex = 0; if (allTableNames.TryGetValue(localizationID.intValue, out TableSelectionData data)) { int idx = allSelection.FindIndex(t => t == data.CombineKey); if (idx >= 0) selectedSelectionIndex = idx; } } protected void RefreshKeyValue() { if (allTableNames.TryGetValue(localizationID.intValue, out TableSelectionData data)) { m_localizationKey.stringValue = data.CombineValue; serializedObject.ApplyModifiedProperties(); } } protected override void OnEnable() { localizationID = serializedObject.FindProperty("m_localizationID"); m_localizationKey = serializedObject.FindProperty("m_localizationKey"); RefreshAllTables(); FindSelectSelection(); RefreshKeyValue(); base.OnEnable(); } public override void OnInspectorGUI() { serializedObject.Update(); // m_localizationKey 只读 EditorGUI.BeginDisabledGroup(true); EditorGUILayout.PropertyField(m_localizationKey); EditorGUILayout.LabelField("Text", GetPreviewLabel()); EditorGUI.EndDisabledGroup(); // 检查是否找不到对应 ID 的 key if (localizationID.intValue > 0 && !allTableNames.ContainsKey(localizationID.intValue)) { EditorGUILayout.HelpBox($"已选择的多语言 Key (ID={localizationID.intValue}) 已被删除,但仍然保留该 ID。", MessageType.Warning); } EditorGUILayout.Space(); // 下拉按钮 if (GUILayout.Button(allSelection[selectedSelectionIndex], EditorStyles.popup)) { var mousePos = GUIUtility.GUIToScreenPoint(Event.current.mousePosition); SearchWindow.Open( new SearchWindowContext(mousePos), ScriptableObject.CreateInstance().Init(allSelection, ApplySelection) ); } serializedObject.ApplyModifiedProperties(); base.OnInspectorGUI(); } private void ApplySelection(string selectedKey) { if (selectedKey == "None") { localizationID.intValue = 0; m_localizationKey.stringValue = string.Empty; selectedSelectionIndex = 0; } else { foreach (var kvp in allTableNames) { if (kvp.Value.CombineKey == selectedKey) { localizationID.intValue = kvp.Value.Id; m_localizationKey.stringValue = kvp.Value.CombineValue; selectedSelectionIndex = allSelection.IndexOf(kvp.Value.CombineKey); break; } } } serializedObject.ApplyModifiedProperties(); } } internal class LocalizationSearchProvider : ScriptableObject, ISearchWindowProvider { private List options; private System.Action onSelect; public LocalizationSearchProvider Init(List options, System.Action onSelect) { this.options = options; this.onSelect = onSelect; return this; } public List CreateSearchTree(SearchWindowContext context) { var tree = new List { new SearchTreeGroupEntry(new GUIContent("Localization Keys"), 0) }; foreach (var option in options) { if (option == "None") { tree.Add(new SearchTreeEntry(new GUIContent("None")) { level = 1, userData = "None" }); } else { // `/` 自动分层 string[] parts = option.Split('/'); if (parts.Length == 1) { tree.Add(new SearchTreeEntry(new GUIContent(parts[0])) { level = 1, userData = option }); } else { // 前面的部分作为 group for (int i = 0; i < parts.Length - 1; i++) { // 确保 group 唯一 string groupName = string.Join("/", parts, 0, i + 1); if (!tree.Exists(e => e.content.text == parts[i] && e.level == i + 1)) { tree.Add(new SearchTreeGroupEntry(new GUIContent(parts[i])) { level = i + 1 }); } } // 最后部分作为 Entry tree.Add(new SearchTreeEntry(new GUIContent(parts[^1])) { level = parts.Length, userData = option }); } } } return tree; } public bool OnSelectEntry(SearchTreeEntry searchTreeEntry, SearchWindowContext context) { if (searchTreeEntry.userData is string key) { onSelect?.Invoke(key); return true; } return false; } } } #endif