com.alicizax.unity.framework/Editor/Localization/LocalizationTableWindow/LocalizationTableWindow.cs

601 lines
21 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.IO;
using System.Collections.Generic;
using AlicizaX.Editor;
using Sirenix.OdinInspector;
using UnityEditor.IMGUI.Controls;
using UnityEditor;
using UnityEngine;
namespace AlicizaX.Localization.Editor
{
public class LocalizationTableWindow : EditorWindow
{
private List<GameLocaizationTable> allTables = new List<GameLocaizationTable>(); // 存储所有找到的GameLocaizationTable
private string[] tableDisplayNames; // 用于下拉框显示的名称数组
private int selectedTableIndex = 0; // 当前选中的索引
private string selectString = string.Empty;
private GameLocaizationTable currentTable; // 当前选中的GameLocaizationTable
// 原本是 const现在改成可调的字段
[SerializeField] private float languagesWidth = 200f;
[SerializeField] private float tableSheetWidth = 300f;
private const float SplitterWidth = 3f; // 拖拽条的宽度
private bool isResizingLeft, isResizingMiddle;
private float Spacing => EditorGUIUtility.standardVerticalSpacing * 2;
private GUIStyle miniLabelButton => new GUIStyle(EditorStyles.miniButton)
{
font = EditorStyles.miniBoldLabel.font,
fontSize = EditorStyles.miniBoldLabel.fontSize
};
public class WindowSelection
{
public TreeViewItem TreeViewItem;
}
public sealed class LanguageSelect : WindowSelection
{
public TempLanguageData Language;
}
public sealed class SectionSelect : WindowSelection
{
public SheetSectionTreeView Section;
}
public sealed class ItemSelect : WindowSelection
{
public SheetItemTreeView Item;
}
private LocalizationWindowData windowData;
private SearchField searchField;
private string searchString;
private Vector2 scrollPosition;
[SerializeField] private TreeViewState languagesTreeViewState;
private LanguagesTreeView languagesTreeView;
[SerializeField] private TreeViewState tableSheetTreeViewState;
private TableSheetTreeView tableSheetTreeView;
private WindowSelection selection = null;
private bool globalExpanded = false;
private void CreateGUI()
{
searchField = new SearchField();
}
private void OnDestroy()
{
SaveSelection();
}
private void OnEnable()
{
RefreshTableList();
}
private void OnDisable()
{
SaveSelection();
}
private void RefreshTableList()
{
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);
}
}
tableDisplayNames = new string[allTables.Count];
for (int i = 0; i < allTables.Count; i++)
{
tableDisplayNames[i] = allTables[i].name;
}
if (allTables.Count > 0 && selectedTableIndex >= allTables.Count)
{
selectedTableIndex = 0;
}
var selectIndex = selectedTableIndex;
var lastSelect = EditorPrefs.GetString("LastSelectedGameLocaizationTable", string.Empty);
if (!string.IsNullOrEmpty(lastSelect))
{
for (int i = 0; i < allTables.Count; i++)
{
var path = AssetDatabase.GetAssetPath(allTables[i]);
if (path.Equals(lastSelect))
{
selectIndex = i;
}
}
}
if (selectIndex <= tableDisplayNames.Length - 1 && selectIndex >= 0)
{
selectString = tableDisplayNames[selectIndex];
}
UpdateCurrentTable();
}
private void UpdateCurrentTable()
{
if (allTables.Count > 0 && selectedTableIndex >= 0 && selectedTableIndex < allTables.Count)
{
currentTable = allTables[selectedTableIndex];
SaveSelection();
}
else
{
currentTable = null;
}
InitializeTreeView();
}
private void SaveSelection()
{
if (currentTable != null)
{
string path = AssetDatabase.GetAssetPath(currentTable);
EditorPrefs.SetString("LastSelectedGameLocaizationTable", path);
}
}
private void InitializeTreeView()
{
if (!currentTable) return;
LocalizationWindowUtility.BuildWindowData(currentTable, out windowData);
foreach (var section in windowData.TableSheet)
{
section.IsExpanded = globalExpanded;
}
languagesTreeViewState = new TreeViewState();
languagesTreeView = new(languagesTreeViewState, windowData, currentTable)
{
OnLanguageSelect = (s) => selection = s
};
tableSheetTreeViewState = new TreeViewState();
tableSheetTreeView = new(tableSheetTreeViewState, windowData)
{
OnTableSheetSelect = (s) => selection = s
};
}
private void OnGUI()
{
// 顶部工具栏
Rect toolbarRect = new(0, 0, position.width, 20f);
GUI.Box(toolbarRect, GUIContent.none, EditorStyles.toolbar);
float buttonWidth = 100f;
float spacing = 5f;
Rect leftTitle = new(toolbarRect.xMin, 0, 40, 20f);
Rect leftPop = new(leftTitle.xMin + 40, 0, 200, 20f);
Rect saveBtn = new(toolbarRect.xMax - buttonWidth - spacing, 0, buttonWidth, 20f);
Rect genBtn = new(saveBtn.xMin - buttonWidth - spacing, 0, buttonWidth, 20f);
Rect importBtn = new(genBtn.xMin - buttonWidth - spacing, 0, buttonWidth, 20f);
Rect exportBtn = new(importBtn.xMin - buttonWidth - spacing, 0, buttonWidth, 20f);
EditorGUI.LabelField(leftTitle, "Table", EditorStyles.boldLabel);
EditorDrawing.DrawStringSelectPopup(leftPop, tableDisplayNames, selectString, (e) =>
{
selectString = e;
selectedTableIndex = allTables.FindIndex(table => table.name == e);
UpdateCurrentTable();
});
if (currentTable == null) return;
if (GUI.Button(exportBtn, "Export CSV", EditorStyles.toolbarButton))
{
string path = EditorUtility.SaveFilePanel("Export CSV", "", "Localization", "csv");
LocalizationExporter.ExportLocalizationToCSV(windowData, path);
}
if (GUI.Button(importBtn, "Import CSV", EditorStyles.toolbarButton))
{
string path = EditorUtility.OpenFilePanel("Export CSV", "", "csv");
LocalizationExporter.ImportLocalizationFromCSV(windowData, path);
}
if (GUI.Button(genBtn, "Gen Code", EditorStyles.toolbarButton))
{
LocalizationWindowUtility.GenerateCode(currentTable);
}
if (GUI.Button(saveBtn, "Save Asset", EditorStyles.toolbarButton))
{
BuildLocalizationTable();
EditorUtility.SetDirty(currentTable);
AssetDatabase.SaveAssets();
}
// ---------------- 这里开始是 SplitView ----------------
Rect fullRect = new Rect(0, 20f, position.width, position.height - 20f);
// 左侧 Languages
Rect languagesRect = new Rect(fullRect.x, fullRect.y, languagesWidth, fullRect.height);
languagesTreeView?.OnGUI(languagesRect);
// 左侧拖拽条
Rect leftSplitter = new Rect(languagesRect.xMax, fullRect.y, SplitterWidth, fullRect.height);
EditorGUIUtility.AddCursorRect(leftSplitter, MouseCursor.ResizeHorizontal);
HandleResize(ref languagesWidth, leftSplitter, ref isResizingLeft);
// 中间 TableSheet
Rect tableSheetRect = new Rect(leftSplitter.xMax, fullRect.y, tableSheetWidth, fullRect.height);
tableSheetTreeView?.OnGUI(tableSheetRect);
// 中间拖拽条
Rect midSplitter = new Rect(tableSheetRect.xMax, fullRect.y, SplitterWidth, fullRect.height);
EditorGUIUtility.AddCursorRect(midSplitter, MouseCursor.ResizeHorizontal);
HandleResize(ref tableSheetWidth, midSplitter, ref isResizingMiddle);
// 右侧 Inspector剩余空间
float inspectorWidth = fullRect.width - (languagesWidth + tableSheetWidth + SplitterWidth * 2);
Rect inspectorRect = new Rect(midSplitter.xMax, fullRect.y, inspectorWidth, fullRect.height);
if (selection != null)
{
GUILayout.BeginArea(inspectorRect);
{
if (selection is LanguageSelect lang) OnDrawLanguageInspector(lang);
else if (selection is SectionSelect sec) OnDrawSectionInspector(sec);
else if (selection is ItemSelect item) OnDrawSectionItemInspector(item);
}
GUILayout.EndArea();
}
}
private void HandleResize(ref float targetWidth, Rect splitterRect, ref bool isResizing)
{
Event e = Event.current;
switch (e.type)
{
case EventType.MouseDown:
if (splitterRect.Contains(e.mousePosition))
{
isResizing = true;
e.Use();
}
break;
case EventType.MouseDrag:
if (isResizing)
{
targetWidth += e.delta.x;
targetWidth = Mathf.Max(100f, targetWidth); // 设置最小宽度
e.Use();
Repaint();
}
break;
case EventType.MouseUp:
if (isResizing)
{
isResizing = false;
e.Use();
}
break;
}
}
private void OnDrawSectionInspector(SectionSelect section)
{
// section name change
EditorGUI.BeginChangeCheck();
{
section.Section.Name = EditorGUILayout.TextField("Name", section.Section.Name);
}
if (EditorGUI.EndChangeCheck())
{
section.TreeViewItem.displayName = section.Section.Name;
}
using (new EditorGUI.DisabledGroupScope(true))
{
int childerCount = section.TreeViewItem.children?.Count ?? 0;
EditorGUILayout.IntField(new GUIContent("Keys"), childerCount);
}
EditorGUILayout.Space(2);
EditorDrawing.Separator();
EditorGUILayout.Space(1);
using (new EditorGUI.DisabledGroupScope(true))
{
EditorGUILayout.LabelField("Id: " + section.Section.Id, EditorStyles.miniBoldLabel);
}
}
private void OnDrawSectionItemInspector(ItemSelect itemSelect)
{
var item = itemSelect.Item;
EditorGUILayout.LabelField("Editing Key Across Languages", EditorStyles.boldLabel);
// 显示 Key 和 ID只读
using (new EditorGUI.DisabledGroupScope(true))
{
EditorGUILayout.TextField("Key", item.Key);
EditorGUILayout.LabelField("Id: " + item.Id);
}
EditorGUILayout.Space(4);
EditorDrawing.Separator();
EditorGUILayout.Space(4);
// 遍历所有语言
foreach (var lang in windowData.Languages)
{
if (lang.Entry.Asset == null)
continue;
var languageName = lang.Entry.LanguageName;
TempSheetItem targetItem = null;
// 在语言的 TableSheet 里找到对应的 Item
foreach (var section in lang.TableSheet)
{
targetItem = section.Items.Find(i => i.Id == item.Id);
if (targetItem != null)
break;
}
if (targetItem == null)
{
EditorGUILayout.HelpBox($"Language [{languageName}] does not contain this key.", MessageType.Info);
continue;
}
// 绘制语言名标题
EditorGUILayout.LabelField(languageName, EditorStyles.miniBoldLabel);
// 多行文本框
EditorGUI.BeginChangeCheck();
targetItem.Value = EditorGUILayout.TextArea(targetItem.Value, GUILayout.MinHeight(40));
if (EditorGUI.EndChangeCheck())
{
// 标记已修改
EditorUtility.SetDirty(lang.Entry.Asset);
}
EditorGUILayout.Space(6);
}
}
private void OnDrawLanguageInspector(LanguageSelect selection)
{
var language = selection.Language;
var entry = language.Entry;
var treeView = selection.TreeViewItem;
using (new EditorGUI.DisabledGroupScope(entry.Asset == null))
{
// Draw search field
EditorGUILayout.Space();
GUIContent expandText = new GUIContent("Expand");
float expandWidth = miniLabelButton.CalcSize(expandText).x;
var searchRect = EditorGUILayout.GetControlRect();
searchRect.xMax -= (expandWidth + 2f);
searchString = searchField.OnGUI(searchRect, searchString);
Rect expandRect = new Rect(searchRect.xMax + 2f, searchRect.y, expandWidth, searchRect.height);
expandRect.y -= 1f;
using (new EditorDrawing.BackgroundColorScope("#F7E987"))
{
if (GUI.Button(expandRect, expandText, miniLabelButton))
{
globalExpanded = !globalExpanded;
foreach (var section in language.TableSheet)
{
section.Reference.IsExpanded = globalExpanded;
}
}
}
if (entry.Asset != null)
{
// Draw localization data
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
{
foreach (var section in GetSearchResult(language, searchString))
{
DrawLocalizationKey(section);
}
}
EditorGUILayout.EndScrollView();
}
else
{
EditorGUILayout.HelpBox("To begin editing localization data, you must first assign a localization asset.", MessageType.Warning);
}
}
}
private void DrawLocalizationKey(TempSheetSection section)
{
if (section.Items == null || section.Items.Count == 0)
return;
using (new EditorDrawing.BorderBoxScope(false))
{
string sectionName = section.Name.Replace(" ", "");
section.Reference.IsExpanded = EditorGUILayout.Foldout(section.Reference.IsExpanded, new GUIContent(sectionName), true, EditorDrawing.Styles.miniBoldLabelFoldout);
// Show section keys when expanded
if (section.Reference.IsExpanded)
{
foreach (var item in section.Items)
{
string keyName = item.Key.Replace(" ", "");
string key = sectionName + "." + keyName;
if (IsMultiline(item.Value))
key += " (Multiline)";
using (new EditorGUILayout.VerticalScope(GUI.skin.box))
{
// Display the expandable toggle
using (new EditorGUILayout.HorizontalScope(GUI.skin.box))
{
item.IsExpanded = EditorGUILayout.Foldout(item.IsExpanded, new GUIContent(key), true, EditorDrawing.Styles.miniBoldLabelFoldout);
}
if (item.IsExpanded)
{
// Show TextArea when expanded
float height = (EditorGUIUtility.standardVerticalSpacing + EditorGUIUtility.singleLineHeight) * 3;
height += EditorGUIUtility.standardVerticalSpacing;
item.Scroll = EditorGUILayout.BeginScrollView(item.Scroll, GUILayout.Height(height));
item.Value = EditorGUILayout.TextArea(item.Value, GUILayout.ExpandHeight(true));
EditorGUILayout.EndScrollView();
}
else
{
// Show TextField when collapsed
item.Value = EditorGUILayout.TextField(item.Value);
}
}
}
}
}
EditorGUILayout.Space(1f);
}
private IEnumerable<TempSheetSection> GetSearchResult(TempLanguageData languageData, string search)
{
if (!string.IsNullOrEmpty(search))
{
List<TempSheetSection> searchResult = new();
foreach (var section in languageData.TableSheet)
{
List<TempSheetItem> sectionItems = new();
string sectionName = section.Name.Replace(" ", "");
foreach (var item in section.Items)
{
string keyName = item.Key.Replace(" ", "");
string key = sectionName + "." + keyName;
if (key.Contains(search))
sectionItems.Add(item);
}
searchResult.Add(new TempSheetSection()
{
Items = sectionItems,
Reference = section.Reference
});
}
return searchResult;
}
return languageData.TableSheet;
}
private bool IsMultiline(string text)
{
return text.Contains("\n") || text.Contains("\r");
}
private void BuildLocalizationTable()
{
// 1. build table sheet
currentTable.TableSheet = new();
foreach (var section in windowData.TableSheet)
{
GameLocaizationTable.TableData tableData = new GameLocaizationTable.TableData(section.Name, section.Id);
foreach (var item in section.Items)
{
GameLocaizationTable.SheetItem sheetItem = new GameLocaizationTable.SheetItem(item.Key, item.Id, item.isGen);
tableData.SectionSheet.Add(sheetItem);
}
currentTable.TableSheet.Add(tableData);
}
// 2. build table sheet for each language
IList<LocalizationLanguage> languages = new List<LocalizationLanguage>();
foreach (var language in windowData.Languages)
{
if (language.Entry.Asset == null)
continue;
LocalizationLanguage asset = language.Entry.Asset;
IList<LocalizationLanguage.LocalizationString> strings = new List<LocalizationLanguage.LocalizationString>();
foreach (var section in language.TableSheet)
{
string sectionKey = section.Name.Replace(" ", "");
foreach (var item in section.Items)
{
string itemKey = item.Key.Replace(" ", "");
string key = sectionKey + "." + itemKey;
strings.Add(new()
{
SectionId = section.Id,
EntryId = item.Id,
Key = key,
Value = item.Value
});
}
}
asset.LanguageName = language.Entry.LanguageName;
asset.Strings = new(strings);
languages.Add(asset);
EditorUtility.SetDirty(asset);
}
currentTable.Languages = new(languages);
}
}
}