com.alicizax.unity.framework/Editor/Localization/LocalizationTableWindow/LocalizationTableWindow.cs
2025-09-23 12:04:39 +08:00

578 lines
21 KiB
C#

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
private const float k_LanguagesWidth = 200f;
private const float k_TableSheetWidth = 200f;
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();
}
Rect languagesRect = new Rect(5f, 25f, k_LanguagesWidth, position.height - 35f);
languagesTreeView.OnGUI(languagesRect);
float tableSheetStartX = languagesRect.xMax + 5f;
Rect tableSheetRect = new Rect(tableSheetStartX, 25f, k_TableSheetWidth, position.height - 35f);
tableSheetTreeView.OnGUI(tableSheetRect);
if (selection != null)
{
float inspectorStartX = tableSheetRect.xMax + 5f;
Rect inspectorRect = new Rect(inspectorStartX, 25f, position.width - inspectorStartX - 5f, position.height - 30f);
if (selection is LanguageSelect language)
{
string title = language.Language.Entry.LanguageName;
GUIContent inspectorTitle = EditorGUIUtility.TrTextContentWithIcon($" INSPECTOR ({title})", "PrefabVariant On Icon");
EditorDrawing.DrawHeaderWithBorder(ref inspectorRect, inspectorTitle, 20f, false);
Rect inspectorViewRect = inspectorRect;
inspectorViewRect.y += Spacing;
inspectorViewRect.yMax -= Spacing;
inspectorViewRect.xMin += Spacing;
inspectorViewRect.xMax -= Spacing;
GUILayout.BeginArea(inspectorViewRect);
OnDrawLanguageInspector(language);
GUILayout.EndArea();
}
else if (selection is SectionSelect section)
{
string title = section.Section.Name;
GUIContent inspectorTitle = EditorGUIUtility.TrTextContentWithIcon($" INSPECTOR ({title})", "PrefabVariant On Icon");
EditorDrawing.DrawHeaderWithBorder(ref inspectorRect, inspectorTitle, 20f, false);
Rect inspectorViewRect = inspectorRect;
inspectorViewRect.y += Spacing;
inspectorViewRect.yMax -= Spacing;
inspectorViewRect.xMin += Spacing;
inspectorViewRect.xMax -= Spacing;
GUILayout.BeginArea(inspectorViewRect);
OnDrawSectionInspector(section);
GUILayout.EndArea();
}
else if (selection is ItemSelect item)
{
string title = item.Item.Key;
GUIContent inspectorTitle = EditorGUIUtility.TrTextContentWithIcon($" INSPECTOR ({title})", "PrefabVariant On Icon");
EditorDrawing.DrawHeaderWithBorder(ref inspectorRect, inspectorTitle, 20f, false);
Rect inspectorViewRect = inspectorRect;
inspectorViewRect.y += Spacing;
inspectorViewRect.yMax -= Spacing;
inspectorViewRect.xMin += Spacing;
inspectorViewRect.xMax -= Spacing;
GUILayout.BeginArea(inspectorViewRect);
OnDrawSectionItemInspector(item);
GUILayout.EndArea();
}
}
}
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 item)
{
// item key change
EditorGUI.BeginChangeCheck();
{
item.Item.Key = EditorGUILayout.TextField("Key", item.Item.Key);
}
if (EditorGUI.EndChangeCheck())
{
item.TreeViewItem.displayName = item.Item.Key;
}
EditorGUILayout.Space(2);
EditorDrawing.Separator();
EditorGUILayout.Space(1);
using (new EditorGUI.DisabledGroupScope(true))
{
string parentName = item.Item.Parent.Name;
string parentText = item.Item.Parent.Id + $" ({parentName})";
EditorGUILayout.LabelField("Parent Id: " + parentText, EditorStyles.miniBoldLabel);
EditorGUILayout.LabelField("Id: " + item.Item.Id, EditorStyles.miniBoldLabel);
}
}
private void OnDrawLanguageInspector(LanguageSelect selection)
{
var language = selection.Language;
var entry = language.Entry;
var treeView = selection.TreeViewItem;
// using (new EditorDrawing.BorderBoxScope(false))
// {
// // language name change
// Rect nameRect = EditorGUILayout.GetControlRect();
//
// Rect renameAssetRect = nameRect;
// renameAssetRect.xMin = nameRect.xMax + 2f;
// renameAssetRect.width = EditorGUIUtility.singleLineHeight;
//
// using (new EditorGUI.DisabledGroupScope(entry.Asset == null))
// {
// GUIContent editIcon = EditorGUIUtility.IconContent("editicon.sml", "Rename");
// if (GUI.Button(renameAssetRect, editIcon, EditorStyles.iconButton))
// {
// string assetPath = AssetDatabase.GetAssetPath(entry.Asset);
// string newName = "(Language) " + entry.LanguageName;
// AssetDatabase.RenameAsset(assetPath, newName);
// }
// }
//
// }
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);
}
}
}