框架结构优化解耦

This commit is contained in:
陈思海 2025-09-25 11:09:09 +08:00
parent af4b77e2c3
commit 784016415e
22 changed files with 366 additions and 9 deletions

View File

@ -5,7 +5,10 @@
"GUID:6546d7765b4165b40850b3667f981c26",
"GUID:6055be8ebefd69e48b49212b09b47b2f",
"GUID:760f1778adc613f49a4394fb41ff0bbc",
"GUID:75b6f2078d190f14dbda4a5b747d709c"
"GUID:75b6f2078d190f14dbda4a5b747d709c",
"GUID:1619e00706139ce488ff80c0daeea8e7",
"GUID:fb064c8bf96bac94e90d2f39090daa94",
"GUID:acfef7cabed3b0a42b25edb1cd4fa259"
],
"includePlatforms": [
"Editor"

View File

@ -1,19 +1,126 @@
#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 text;
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 override void OnEnable()
{
text = serializedObject.FindProperty("m_text");
localizationID = serializedObject.FindProperty("m_localizationID");
m_localizationKey = serializedObject.FindProperty("m_localizationKey");
RefreshAllTables();
FindSelectSelection();
base.OnEnable();
}
@ -21,13 +128,125 @@ namespace UnityEngine.UI
{
serializedObject.Update();
// m_localizationKey 只读
EditorGUI.BeginDisabledGroup(true);
EditorGUILayout.PropertyField(m_localizationKey);
EditorGUILayout.LabelField("Text", GetPreviewLabel());
EditorGUI.EndDisabledGroup();
EditorGUILayout.PropertyField(localizationID);
// 检查是否找不到对应 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

View File

@ -4,7 +4,9 @@
"references": [
"GUID:6055be8ebefd69e48b49212b09b47b2f",
"GUID:80ecb87cae9c44d19824e70ea7229748",
"GUID:75469ad4d38634e559750d17036d5f7c"
"GUID:75469ad4d38634e559750d17036d5f7c",
"GUID:1619e00706139ce488ff80c0daeea8e7",
"GUID:189d55e03d78888459720d730f4d2424"
],
"includePlatforms": [],
"excludePlatforms": [],

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0e8bd5b405994da288d5a14f65ceb81b
timeCreated: 1758683565

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4474c46008f24d2ab4817687760f7c45
timeCreated: 1758683576

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 24fbe05aa8721e14db91f27f5f5cfe6a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,66 @@
using System;
using System.Collections.Generic;
using AlicizaX.UI.Runtime;
using Sirenix.OdinInspector;
using UnityEngine;
namespace AlicizaX.UI.Extension.UXComponent.Hotkey
{
public class HotkeyBindComponent : MonoBehaviour
{
private UIHolderObjectBase _holderObjectBase;
private void Awake()
{
_holderObjectBase = GetComponent<UIHolderObjectBase>();
_holderObjectBase.OnWindowShowEvent += BindHotKeys;
_holderObjectBase.OnWindowClosedEvent += UnBindHotKeys;
}
private void OnDestroy()
{
_holderObjectBase.OnWindowShowEvent -= BindHotKeys;
_holderObjectBase.OnWindowClosedEvent -= UnBindHotKeys;
}
#if UNITY_EDITOR
[InlineButton("SetHotKeyButtons")]
#endif
[SerializeField]
[HideLabel]
private UXButton[] hotButtons;
#if UNITY_EDITOR
private void SetHotKeyButtons()
{
var btns = transform.GetComponentsInChildren<UXButton>(true);
var hotBtnList = new List<UXButton>();
for (int i = 0; i < btns.Length; i++)
{
if (btns[i].HasHotKeyRefrenced())
{
hotBtnList.Add(btns[i]);
}
}
hotButtons = hotBtnList.ToArray();
}
#endif
internal void BindHotKeys()
{
for (int i = 0; i < hotButtons.Length; i++)
{
hotButtons[i].BindHotKey();
}
}
internal void UnBindHotKeys()
{
for (int i = 0; i < hotButtons.Length; i++)
{
hotButtons[i].UnBindHotKey();
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: fdcda7a93f3c4639a0cb68dd00509bb1
timeCreated: 1758683821

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d4b70cfbd2d7436085d694af49f29b79
timeCreated: 1758683583

View File

@ -6,7 +6,8 @@ namespace UnityEngine.UI
{
public class UXTextMeshPro : TextMeshProUGUI
{
[SerializeField] private string m_localizationID = "";
[SerializeField] private int m_localizationID;
[SerializeField] private string m_localizationKey = "";
protected override void Start()
{
@ -17,9 +18,9 @@ namespace UnityEngine.UI
protected void ChangeLanguage()
{
if (!string.IsNullOrEmpty(m_localizationID) && !"None".Equals(m_localizationID) && UXComponentExtensionsHelper.LocalizationHelper != null)
if (!string.IsNullOrEmpty(m_localizationKey) && !"None".Equals(m_localizationKey) && UXComponentExtensionsHelper.LocalizationHelper != null)
{
text = UXComponentExtensionsHelper.LocalizationHelper.GetString(m_localizationID);
text = UXComponentExtensionsHelper.LocalizationHelper.GetString(m_localizationKey);
}
}
@ -29,7 +30,7 @@ namespace UnityEngine.UI
/// <param name="localizationID"></param>
public void SetLocalization(string localizationID)
{
m_localizationID = localizationID;
m_localizationKey = localizationID;
ChangeLanguage();
}
}

View File

@ -0,0 +1,43 @@
using System;
using AlicizaX.UI.Runtime;
using UnityEngine;
namespace AlicizaX.UI.Extension.UXComponent
{
[RequireComponent(typeof(AnimationFlow.Runtime.AnimationFlow))]
public class UXUIAnimation : MonoBehaviour
{
[SerializeField] private AnimationFlow.Runtime.AnimationFlow animationFlow;
[SerializeField] private string ShowAnimationName = "Show";
[SerializeField] private string HideAnimationName = "Close";
private UIHolderObjectBase _holderObjectBase;
private void OnValidate()
{
animationFlow = GetComponent<AnimationFlow.Runtime.AnimationFlow>();
}
private void Awake()
{
_holderObjectBase = GetComponent<UIHolderObjectBase>();
_holderObjectBase.OnWindowShowEvent += ShowAnimation;
_holderObjectBase.OnWindowClosedEvent += CloseAnimation;
}
private void OnDestroy()
{
_holderObjectBase.OnWindowShowEvent -= ShowAnimation;
_holderObjectBase.OnWindowClosedEvent -= CloseAnimation;
}
internal void ShowAnimation()
{
animationFlow.Play(ShowAnimationName);
}
internal void CloseAnimation()
{
animationFlow.Play(HideAnimationName);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 72bd03ed7aed41ad81879dbb4dc96f71
timeCreated: 1758701015