com.alicizax.unity.ui.exten.../Editor/UX/UXTextMeshProEditor.cs
2025-10-15 13:05:27 +08:00

262 lines
9.1 KiB
C#

#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<GameLocaizationTable> allTables = new();
private Dictionary<int, TableSelectionData> allTableNames = new();
private Dictionary<string, string> previewLabelDic = new();
private List<string> 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<GameLocaizationTable>(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<LocalizationSearchProvider>().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<string> options;
private System.Action<string> onSelect;
public LocalizationSearchProvider Init(List<string> options, System.Action<string> onSelect)
{
this.options = options;
this.onSelect = onSelect;
return this;
}
public List<SearchTreeEntry> CreateSearchTree(SearchWindowContext context)
{
var tree = new List<SearchTreeEntry>
{
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