2025-12-05 19:04:53 +08:00
|
|
|
|
using System;
|
2025-12-05 20:57:29 +08:00
|
|
|
|
using System.Collections.Generic;
|
2025-12-05 19:04:53 +08:00
|
|
|
|
using UnityEditor;
|
|
|
|
|
|
using UnityEngine;
|
|
|
|
|
|
|
|
|
|
|
|
[CustomEditor(typeof(InputGlyphDatabase))]
|
|
|
|
|
|
public class InputGlyphDatabaseEditor : Editor
|
|
|
|
|
|
{
|
2025-12-05 20:57:29 +08:00
|
|
|
|
SerializedProperty tablesProp;
|
2025-12-10 17:38:31 +08:00
|
|
|
|
SerializedProperty placeholderSpriteProp;
|
2025-12-05 19:04:53 +08:00
|
|
|
|
InputGlyphDatabase db;
|
2025-12-05 20:57:29 +08:00
|
|
|
|
|
2025-12-05 19:04:53 +08:00
|
|
|
|
int tabIndex = 0;
|
2025-12-10 17:38:31 +08:00
|
|
|
|
bool showAddField = false;
|
|
|
|
|
|
string newTableName = "";
|
|
|
|
|
|
|
|
|
|
|
|
List<string> searchStrings = new List<string>();
|
|
|
|
|
|
List<int> currentPages = new List<int>();
|
|
|
|
|
|
|
2026-03-09 20:38:15 +08:00
|
|
|
|
// per-table temporary fields for adding single entry (only sprite now)
|
|
|
|
|
|
List<Sprite> newEntrySprites = new List<Sprite>();
|
|
|
|
|
|
|
2025-12-10 17:38:31 +08:00
|
|
|
|
const int itemsPerPage = 10;
|
|
|
|
|
|
const int previewSize = 52;
|
2025-12-05 20:57:29 +08:00
|
|
|
|
|
2025-12-05 19:04:53 +08:00
|
|
|
|
void OnEnable()
|
|
|
|
|
|
{
|
|
|
|
|
|
db = target as InputGlyphDatabase;
|
2025-12-05 20:57:29 +08:00
|
|
|
|
tablesProp = serializedObject.FindProperty("tables");
|
2025-12-10 17:38:31 +08:00
|
|
|
|
placeholderSpriteProp = serializedObject.FindProperty("placeholderSprite");
|
2025-12-05 20:57:29 +08:00
|
|
|
|
|
|
|
|
|
|
if (tablesProp == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError("Could not find serialized property 'tables' on InputGlyphDatabase. Check field name.");
|
2025-12-10 17:38:31 +08:00
|
|
|
|
return;
|
2025-12-05 20:57:29 +08:00
|
|
|
|
}
|
2025-12-10 17:38:31 +08:00
|
|
|
|
|
|
|
|
|
|
EnsureDefaultTable("Keyboard");
|
|
|
|
|
|
EnsureDefaultTable("Xbox");
|
|
|
|
|
|
EnsureDefaultTable("PlayStation");
|
|
|
|
|
|
|
|
|
|
|
|
SyncEditorListsWithTables();
|
2025-12-05 19:04:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public override void OnInspectorGUI()
|
|
|
|
|
|
{
|
|
|
|
|
|
serializedObject.Update();
|
2025-12-05 20:57:29 +08:00
|
|
|
|
if (db == null || tablesProp == null) return;
|
|
|
|
|
|
|
2025-12-10 17:38:31 +08:00
|
|
|
|
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
|
|
|
|
|
|
GUILayout.Space(4);
|
2025-12-05 19:04:53 +08:00
|
|
|
|
|
2025-12-10 17:38:31 +08:00
|
|
|
|
if (GUILayout.Button("Save Asset", EditorStyles.toolbarButton))
|
2025-12-05 20:57:29 +08:00
|
|
|
|
{
|
|
|
|
|
|
serializedObject.ApplyModifiedProperties();
|
2025-12-10 17:38:31 +08:00
|
|
|
|
EditorUtility.SetDirty(db);
|
|
|
|
|
|
AssetDatabase.SaveAssets();
|
2025-12-05 20:57:29 +08:00
|
|
|
|
}
|
2025-12-05 19:04:53 +08:00
|
|
|
|
|
2025-12-10 17:38:31 +08:00
|
|
|
|
GUILayout.FlexibleSpace();
|
2025-12-05 20:57:29 +08:00
|
|
|
|
|
2025-12-10 17:38:31 +08:00
|
|
|
|
if (GUILayout.Button(showAddField ? "Cancel +" : "+ Add Table", EditorStyles.toolbarButton, GUILayout.Width(110)))
|
2025-12-05 19:04:53 +08:00
|
|
|
|
{
|
2025-12-10 17:38:31 +08:00
|
|
|
|
showAddField = !showAddField;
|
|
|
|
|
|
newTableName = "";
|
2025-12-05 19:04:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-10 17:38:31 +08:00
|
|
|
|
int settingsIndex = tablesProp != null ? tablesProp.arraySize : 0;
|
|
|
|
|
|
bool settingsSelected = (tabIndex == settingsIndex);
|
|
|
|
|
|
if (GUILayout.Toggle(settingsSelected, "Settings", EditorStyles.toolbarButton, GUILayout.Width(90)) != settingsSelected)
|
2025-12-05 19:04:53 +08:00
|
|
|
|
{
|
2025-12-10 17:38:31 +08:00
|
|
|
|
tabIndex = (tabIndex == settingsIndex) ? 0 : settingsIndex;
|
2025-12-05 19:04:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
|
|
|
2025-12-10 17:38:31 +08:00
|
|
|
|
if (showAddField)
|
|
|
|
|
|
{
|
|
|
|
|
|
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
|
|
|
|
|
|
GUILayout.Label("Name:", GUILayout.Width(40));
|
|
|
|
|
|
newTableName = EditorGUILayout.TextField(newTableName);
|
|
|
|
|
|
if (GUILayout.Button("Add", EditorStyles.toolbarButton, GUILayout.Width(80)))
|
|
|
|
|
|
{
|
|
|
|
|
|
string trimmed = newTableName != null ? newTableName.Trim() : "";
|
|
|
|
|
|
if (string.IsNullOrEmpty(trimmed))
|
|
|
|
|
|
{
|
|
|
|
|
|
EditorUtility.DisplayDialog("Invalid Name", "Table name cannot be empty.", "OK");
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
bool exists = false;
|
|
|
|
|
|
for (int i = 0; i < tablesProp.arraySize; ++i)
|
|
|
|
|
|
{
|
|
|
|
|
|
var t = tablesProp.GetArrayElementAtIndex(i);
|
|
|
|
|
|
var nameProp = t.FindPropertyRelative("deviceName");
|
|
|
|
|
|
if (nameProp != null && string.Equals(nameProp.stringValue, trimmed, StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
|
{
|
2026-03-09 20:38:15 +08:00
|
|
|
|
exists = true;
|
|
|
|
|
|
break;
|
2025-12-10 17:38:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-03-09 20:38:15 +08:00
|
|
|
|
|
2025-12-10 17:38:31 +08:00
|
|
|
|
if (exists)
|
|
|
|
|
|
{
|
|
|
|
|
|
EditorUtility.DisplayDialog("Duplicate", "A table with that name already exists.", "OK");
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
int newIndex = tablesProp.arraySize;
|
|
|
|
|
|
tablesProp.InsertArrayElementAtIndex(newIndex);
|
|
|
|
|
|
var newTable = tablesProp.GetArrayElementAtIndex(newIndex);
|
|
|
|
|
|
var nameProp = newTable.FindPropertyRelative("deviceName");
|
|
|
|
|
|
if (nameProp != null) nameProp.stringValue = trimmed;
|
2025-12-17 20:03:29 +08:00
|
|
|
|
var sheetProp = newTable.FindPropertyRelative("spriteSheetTexture");
|
|
|
|
|
|
if (sheetProp != null) sheetProp.objectReferenceValue = null;
|
2025-12-10 17:38:31 +08:00
|
|
|
|
var entriesProp = newTable.FindPropertyRelative("entries");
|
|
|
|
|
|
if (entriesProp != null) entriesProp.arraySize = 0;
|
|
|
|
|
|
|
|
|
|
|
|
serializedObject.ApplyModifiedProperties();
|
|
|
|
|
|
EditorUtility.SetDirty(db);
|
|
|
|
|
|
|
|
|
|
|
|
SyncEditorListsWithTables();
|
|
|
|
|
|
|
|
|
|
|
|
showAddField = false;
|
2025-12-17 20:03:29 +08:00
|
|
|
|
tabIndex = tablesProp.arraySize - 1;
|
2025-12-10 17:38:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-03-09 20:38:15 +08:00
|
|
|
|
|
2025-12-10 17:38:31 +08:00
|
|
|
|
if (GUILayout.Button("Cancel", EditorStyles.toolbarButton, GUILayout.Width(80)))
|
|
|
|
|
|
{
|
|
|
|
|
|
showAddField = false;
|
|
|
|
|
|
newTableName = "";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
EditorGUILayout.Space(6);
|
2025-12-05 19:04:53 +08:00
|
|
|
|
|
2025-12-10 17:38:31 +08:00
|
|
|
|
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
|
|
|
|
|
|
int tablesCount = tablesProp.arraySize;
|
|
|
|
|
|
for (int i = 0; i < tablesCount; ++i)
|
2025-12-05 19:04:53 +08:00
|
|
|
|
{
|
2025-12-10 17:38:31 +08:00
|
|
|
|
var t = tablesProp.GetArrayElementAtIndex(i);
|
|
|
|
|
|
var nameProp = t.FindPropertyRelative("deviceName");
|
|
|
|
|
|
string name = nameProp != null ? nameProp.stringValue : ("Table " + i);
|
|
|
|
|
|
bool selected = (tabIndex == i);
|
|
|
|
|
|
if (GUILayout.Toggle(selected, name, EditorStyles.toolbarButton, GUILayout.MinWidth(60)))
|
|
|
|
|
|
{
|
|
|
|
|
|
tabIndex = i;
|
|
|
|
|
|
}
|
2025-12-05 19:04:53 +08:00
|
|
|
|
|
2025-12-10 17:38:31 +08:00
|
|
|
|
if (GUILayout.Button("×", EditorStyles.toolbarButton, GUILayout.Width(22)))
|
|
|
|
|
|
{
|
|
|
|
|
|
if (EditorUtility.DisplayDialog("Delete Table?",
|
2026-03-09 20:38:15 +08:00
|
|
|
|
$"Delete table '{name}' and all its entries? This cannot be undone.", "Delete", "Cancel"))
|
2025-12-10 17:38:31 +08:00
|
|
|
|
{
|
|
|
|
|
|
tablesProp.DeleteArrayElementAtIndex(i);
|
|
|
|
|
|
serializedObject.ApplyModifiedProperties();
|
|
|
|
|
|
EditorUtility.SetDirty(db);
|
|
|
|
|
|
|
|
|
|
|
|
SyncEditorListsWithTables();
|
|
|
|
|
|
tabIndex = Mathf.Clamp(tabIndex, 0, Math.Max(0, tablesProp.arraySize - 1));
|
2025-12-17 20:03:29 +08:00
|
|
|
|
return;
|
2025-12-10 17:38:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-12-05 20:57:29 +08:00
|
|
|
|
|
2025-12-10 17:38:31 +08:00
|
|
|
|
EditorGUILayout.EndHorizontal();
|
2025-12-05 20:57:29 +08:00
|
|
|
|
|
2025-12-10 17:38:31 +08:00
|
|
|
|
EditorGUILayout.Space(8);
|
2025-12-05 19:04:53 +08:00
|
|
|
|
|
2025-12-10 17:38:31 +08:00
|
|
|
|
EditorGUILayout.BeginVertical("box");
|
|
|
|
|
|
if (tabIndex == tablesProp.arraySize)
|
|
|
|
|
|
{
|
2025-12-17 20:03:29 +08:00
|
|
|
|
// Settings
|
2025-12-10 17:38:31 +08:00
|
|
|
|
EditorGUILayout.LabelField("Settings", EditorStyles.boldLabel);
|
2025-12-05 20:57:29 +08:00
|
|
|
|
EditorGUILayout.Space(4);
|
2025-12-10 17:38:31 +08:00
|
|
|
|
EditorGUILayout.PropertyField(placeholderSpriteProp, new GUIContent("Placeholder Sprite"));
|
|
|
|
|
|
Sprite placeholder = placeholderSpriteProp.objectReferenceValue as Sprite;
|
|
|
|
|
|
EditorGUILayout.Space(6);
|
|
|
|
|
|
EditorGUILayout.LabelField("Preview", EditorStyles.miniBoldLabel);
|
|
|
|
|
|
if (placeholder != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Texture2D preview = AssetPreview.GetAssetPreview(placeholder);
|
|
|
|
|
|
if (preview == null) preview = AssetPreview.GetMiniThumbnail(placeholder);
|
|
|
|
|
|
if (preview != null) GUILayout.Label(preview, GUILayout.Width(previewSize), GUILayout.Height(previewSize));
|
|
|
|
|
|
else EditorGUILayout.ObjectField(placeholder, typeof(Sprite), false, GUILayout.Width(previewSize), GUILayout.Height(previewSize));
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
EditorGUILayout.HelpBox("No placeholder sprite assigned. If FindEntryByControlPath receives an empty path, it will return null.", MessageType.Info);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
if (tabIndex < 0 || tabIndex >= tablesProp.arraySize)
|
|
|
|
|
|
{
|
|
|
|
|
|
EditorGUILayout.HelpBox("Invalid table index.", MessageType.Error);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
var tableProp = tablesProp.GetArrayElementAtIndex(tabIndex);
|
2025-12-05 19:04:53 +08:00
|
|
|
|
|
2025-12-10 17:38:31 +08:00
|
|
|
|
EnsureEditorListsLength();
|
2025-12-05 19:04:53 +08:00
|
|
|
|
|
2026-03-09 20:38:15 +08:00
|
|
|
|
// compute deviceName & runtime index for this table (used when deleting single entry)
|
|
|
|
|
|
var nameProp = tableProp.FindPropertyRelative("deviceName");
|
|
|
|
|
|
string deviceName = nameProp != null ? nameProp.stringValue : "";
|
|
|
|
|
|
int runtimeTableIndex = MapSerializedTableToRuntimeIndex(deviceName);
|
|
|
|
|
|
|
2025-12-10 17:38:31 +08:00
|
|
|
|
GUILayout.BeginHorizontal();
|
|
|
|
|
|
GUIStyle searchStyle = EditorStyles.toolbarSearchField ?? EditorStyles.textField;
|
|
|
|
|
|
searchStrings[tabIndex] = GUILayout.TextField(searchStrings[tabIndex] ?? "", searchStyle);
|
|
|
|
|
|
GUILayout.EndHorizontal();
|
|
|
|
|
|
|
|
|
|
|
|
EditorGUILayout.Space(6);
|
2025-12-09 20:31:44 +08:00
|
|
|
|
|
2026-03-09 20:38:15 +08:00
|
|
|
|
var sheetProp = tableProp.FindPropertyRelative("spriteSheetTexture");
|
2025-12-10 17:38:31 +08:00
|
|
|
|
EditorGUILayout.BeginHorizontal();
|
2026-03-09 20:38:15 +08:00
|
|
|
|
GUILayout.Label("Sprite Sheet (Texture2D)", GUILayout.Width(140));
|
|
|
|
|
|
EditorGUILayout.PropertyField(sheetProp, GUIContent.none, GUILayout.ExpandWidth(true));
|
|
|
|
|
|
if (GUILayout.Button("Parse Sprite Sheet", GUILayout.Width(120)))
|
2025-12-17 20:03:29 +08:00
|
|
|
|
{
|
2026-03-09 20:38:15 +08:00
|
|
|
|
ParseSpriteSheetIntoTableSerialized(tableProp);
|
2025-12-17 20:03:29 +08:00
|
|
|
|
}
|
2026-03-09 20:38:15 +08:00
|
|
|
|
|
|
|
|
|
|
|
2025-12-17 20:03:29 +08:00
|
|
|
|
if (GUILayout.Button("Clear", GUILayout.Width(80)))
|
|
|
|
|
|
{
|
|
|
|
|
|
var entriesProp = tableProp.FindPropertyRelative("entries");
|
|
|
|
|
|
if (entriesProp != null) entriesProp.arraySize = 0;
|
2026-03-09 20:38:15 +08:00
|
|
|
|
if (runtimeTableIndex >= 0 && db != null)
|
2025-12-17 20:03:29 +08:00
|
|
|
|
{
|
2026-03-09 20:38:15 +08:00
|
|
|
|
var table = db.tables[runtimeTableIndex];
|
2025-12-17 20:03:29 +08:00
|
|
|
|
if (table != null) table.entries.Clear();
|
|
|
|
|
|
}
|
2026-03-09 20:38:15 +08:00
|
|
|
|
|
2025-12-17 20:03:29 +08:00
|
|
|
|
serializedObject.ApplyModifiedProperties();
|
|
|
|
|
|
EditorUtility.SetDirty(db);
|
|
|
|
|
|
currentPages[tabIndex] = 0;
|
|
|
|
|
|
}
|
2026-03-09 20:38:15 +08:00
|
|
|
|
|
2025-12-17 20:03:29 +08:00
|
|
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
|
|
|
2026-03-09 20:38:15 +08:00
|
|
|
|
var platformProp = tableProp.FindPropertyRelative("platformIcons");
|
|
|
|
|
|
EditorGUILayout.PropertyField(platformProp, new GUIContent("Platforms Icons"));
|
|
|
|
|
|
Sprite placeholder = platformProp.objectReferenceValue as Sprite;
|
|
|
|
|
|
EditorGUILayout.Space(6);
|
|
|
|
|
|
EditorGUILayout.LabelField("Preview", EditorStyles.miniBoldLabel);
|
|
|
|
|
|
if (placeholder != null)
|
2025-12-17 20:03:29 +08:00
|
|
|
|
{
|
2026-03-09 20:38:15 +08:00
|
|
|
|
Texture2D preview = AssetPreview.GetAssetPreview(placeholder);
|
|
|
|
|
|
if (preview == null) preview = AssetPreview.GetMiniThumbnail(placeholder);
|
|
|
|
|
|
if (preview != null) GUILayout.Label(preview, GUILayout.Width(previewSize), GUILayout.Height(previewSize));
|
|
|
|
|
|
else EditorGUILayout.ObjectField(placeholder, typeof(Sprite), false, GUILayout.Width(previewSize), GUILayout.Height(previewSize));
|
2025-12-17 20:03:29 +08:00
|
|
|
|
}
|
2026-03-09 20:38:15 +08:00
|
|
|
|
else
|
2025-12-10 17:38:31 +08:00
|
|
|
|
{
|
2026-03-09 20:38:15 +08:00
|
|
|
|
EditorGUILayout.HelpBox("No PlatformIcons.", MessageType.Info);
|
2025-12-17 20:03:29 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-09 20:38:15 +08:00
|
|
|
|
EditorGUILayout.Space(6);
|
|
|
|
|
|
|
|
|
|
|
|
// ---- 新增:单个新增 Entry 的 UI(只支持 Sprite) ----
|
|
|
|
|
|
EditorGUILayout.BeginVertical("box");
|
|
|
|
|
|
EditorGUILayout.LabelField("Add Single Entry", EditorStyles.boldLabel);
|
2025-12-17 20:03:29 +08:00
|
|
|
|
EditorGUILayout.BeginHorizontal();
|
2026-03-09 20:38:15 +08:00
|
|
|
|
GUILayout.Label("Sprite", GUILayout.Width(50));
|
|
|
|
|
|
newEntrySprites[tabIndex] = (Sprite)EditorGUILayout.ObjectField(newEntrySprites[tabIndex], typeof(Sprite), false, GUILayout.Width(80), GUILayout.Height(80));
|
|
|
|
|
|
GUILayout.FlexibleSpace();
|
|
|
|
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
|
|
EditorGUILayout.Space(6);
|
|
|
|
|
|
EditorGUILayout.BeginHorizontal();
|
|
|
|
|
|
GUILayout.FlexibleSpace();
|
|
|
|
|
|
if (GUILayout.Button("Add Entry", GUILayout.Width(110)))
|
2025-12-17 20:03:29 +08:00
|
|
|
|
{
|
2026-03-09 20:38:15 +08:00
|
|
|
|
if (newEntrySprites[tabIndex] == null)
|
2025-12-17 20:03:29 +08:00
|
|
|
|
{
|
2026-03-09 20:38:15 +08:00
|
|
|
|
EditorUtility.DisplayDialog("Missing Sprite", "Please assign a Sprite to add.", "OK");
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
AddEntryToTableSerialized(tableProp, newEntrySprites[tabIndex]);
|
|
|
|
|
|
// reset temp field
|
|
|
|
|
|
newEntrySprites[tabIndex] = null;
|
|
|
|
|
|
currentPages[tabIndex] = 0;
|
2025-12-17 20:03:29 +08:00
|
|
|
|
}
|
2025-12-10 17:38:31 +08:00
|
|
|
|
}
|
2026-03-09 20:38:15 +08:00
|
|
|
|
|
2025-12-10 17:38:31 +08:00
|
|
|
|
EditorGUILayout.EndHorizontal();
|
2026-03-09 20:38:15 +08:00
|
|
|
|
EditorGUILayout.EndVertical();
|
|
|
|
|
|
// ---- end add-single-entry UI ----
|
2025-12-10 17:38:31 +08:00
|
|
|
|
|
|
|
|
|
|
EditorGUILayout.Space(6);
|
2025-12-05 20:57:29 +08:00
|
|
|
|
|
2025-12-10 17:38:31 +08:00
|
|
|
|
var entries = tableProp.FindPropertyRelative("entries");
|
|
|
|
|
|
if (entries != null)
|
2025-12-05 19:04:53 +08:00
|
|
|
|
{
|
2025-12-10 17:38:31 +08:00
|
|
|
|
int total = entries.arraySize;
|
|
|
|
|
|
List<int> matchedIndices = new List<int>();
|
|
|
|
|
|
string query = (searchStrings[tabIndex] ?? "").Trim();
|
|
|
|
|
|
for (int i = 0; i < total; ++i)
|
2025-12-05 19:04:53 +08:00
|
|
|
|
{
|
2025-12-10 17:38:31 +08:00
|
|
|
|
var eProp = entries.GetArrayElementAtIndex(i);
|
|
|
|
|
|
if (eProp == null) continue;
|
2025-12-05 20:57:29 +08:00
|
|
|
|
var spriteProp = eProp.FindPropertyRelative("Sprite");
|
|
|
|
|
|
Sprite s = spriteProp.objectReferenceValue as Sprite;
|
2025-12-10 17:38:31 +08:00
|
|
|
|
string name = s != null ? s.name : "";
|
|
|
|
|
|
if (string.IsNullOrEmpty(query) || name.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
matchedIndices.Add(i);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int matchedTotal = matchedIndices.Count;
|
|
|
|
|
|
int totalPages = Mathf.Max(1, (matchedTotal + itemsPerPage - 1) / itemsPerPage);
|
|
|
|
|
|
currentPages[tabIndex] = Mathf.Clamp(currentPages[tabIndex], 0, totalPages - 1);
|
|
|
|
|
|
|
|
|
|
|
|
EditorGUILayout.BeginHorizontal();
|
2026-03-09 20:38:15 +08:00
|
|
|
|
if (GUILayout.Button("<<", EditorStyles.miniButtonLeft, GUILayout.Width(36)))
|
|
|
|
|
|
{
|
|
|
|
|
|
currentPages[tabIndex] = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (GUILayout.Button("<", EditorStyles.miniButtonMid, GUILayout.Width(36)))
|
|
|
|
|
|
{
|
|
|
|
|
|
currentPages[tabIndex] = Mathf.Max(0, currentPages[tabIndex] - 1);
|
|
|
|
|
|
}
|
2025-12-10 17:38:31 +08:00
|
|
|
|
|
|
|
|
|
|
GUILayout.FlexibleSpace();
|
|
|
|
|
|
EditorGUILayout.LabelField(string.Format("Page {0}/{1}", currentPages[tabIndex] + 1, totalPages), GUILayout.Width(120));
|
|
|
|
|
|
GUILayout.FlexibleSpace();
|
|
|
|
|
|
|
2026-03-09 20:38:15 +08:00
|
|
|
|
if (GUILayout.Button(">", EditorStyles.miniButtonMid, GUILayout.Width(36)))
|
|
|
|
|
|
{
|
|
|
|
|
|
currentPages[tabIndex] = Mathf.Min(totalPages - 1, currentPages[tabIndex] + 1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (GUILayout.Button(">>", EditorStyles.miniButtonRight, GUILayout.Width(36)))
|
|
|
|
|
|
{
|
|
|
|
|
|
currentPages[tabIndex] = totalPages - 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-10 17:38:31 +08:00
|
|
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
|
|
|
|
|
|
|
|
EditorGUILayout.Space(4);
|
|
|
|
|
|
|
|
|
|
|
|
int start = currentPages[tabIndex] * itemsPerPage;
|
|
|
|
|
|
int end = Math.Min(start + itemsPerPage, matchedTotal);
|
|
|
|
|
|
|
|
|
|
|
|
for (int mi = start; mi < end; ++mi)
|
|
|
|
|
|
{
|
|
|
|
|
|
int i = matchedIndices[mi];
|
|
|
|
|
|
var eProp = entries.GetArrayElementAtIndex(i);
|
|
|
|
|
|
if (eProp == null) continue;
|
2025-12-05 20:57:29 +08:00
|
|
|
|
|
2026-03-09 20:38:15 +08:00
|
|
|
|
// display one entry with a small remove button on the right
|
2025-12-10 17:38:31 +08:00
|
|
|
|
using (new EditorGUILayout.HorizontalScope("box"))
|
2025-12-05 19:04:53 +08:00
|
|
|
|
{
|
2026-03-09 20:38:15 +08:00
|
|
|
|
// left: preview column
|
2025-12-10 17:38:31 +08:00
|
|
|
|
using (new EditorGUILayout.VerticalScope(GUILayout.Width(80)))
|
2025-12-05 20:57:29 +08:00
|
|
|
|
{
|
2025-12-10 17:38:31 +08:00
|
|
|
|
var spriteProp = eProp.FindPropertyRelative("Sprite");
|
|
|
|
|
|
Sprite s = spriteProp.objectReferenceValue as Sprite;
|
|
|
|
|
|
EditorGUILayout.LabelField(s != null ? s.name : "<missing>", EditorStyles.boldLabel);
|
|
|
|
|
|
|
|
|
|
|
|
if (s != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Texture2D preview = AssetPreview.GetAssetPreview(s);
|
|
|
|
|
|
if (preview == null) preview = AssetPreview.GetMiniThumbnail(s);
|
|
|
|
|
|
if (preview != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
GUILayout.Label(preview, GUILayout.Width(previewSize), GUILayout.Height(previewSize));
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
EditorGUILayout.PropertyField(spriteProp, GUIContent.none, GUILayout.Width(previewSize), GUILayout.Height(previewSize));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
EditorGUILayout.PropertyField(spriteProp, GUIContent.none, GUILayout.Width(previewSize), GUILayout.Height(previewSize));
|
|
|
|
|
|
}
|
2025-12-05 20:57:29 +08:00
|
|
|
|
}
|
2025-12-05 19:04:53 +08:00
|
|
|
|
|
2026-03-09 20:38:15 +08:00
|
|
|
|
// middle: action column
|
2025-12-10 17:38:31 +08:00
|
|
|
|
EditorGUILayout.BeginVertical();
|
|
|
|
|
|
var actionProp = eProp.FindPropertyRelative("action");
|
|
|
|
|
|
EditorGUILayout.Space(2);
|
|
|
|
|
|
EditorGUILayout.PropertyField(actionProp, GUIContent.none, GUILayout.ExpandWidth(true));
|
|
|
|
|
|
EditorGUILayout.EndVertical();
|
2026-03-09 20:38:15 +08:00
|
|
|
|
|
|
|
|
|
|
// right: small remove button
|
|
|
|
|
|
GUILayout.Space(6);
|
|
|
|
|
|
if (GUILayout.Button("×", EditorStyles.toolbarButton, GUILayout.Width(24)))
|
|
|
|
|
|
{
|
|
|
|
|
|
string spriteName = null;
|
|
|
|
|
|
var sProp = eProp.FindPropertyRelative("Sprite");
|
|
|
|
|
|
if (sProp != null) spriteName = (sProp.objectReferenceValue as Sprite)?.name;
|
|
|
|
|
|
|
|
|
|
|
|
if (EditorUtility.DisplayDialog("Remove Entry?",
|
|
|
|
|
|
$"Remove entry '{(string.IsNullOrEmpty(spriteName) ? "<missing>" : spriteName)}' from table '{deviceName}'?", "Remove", "Cancel"))
|
|
|
|
|
|
{
|
|
|
|
|
|
// remove from serialized array
|
|
|
|
|
|
var entriesProp = tableProp.FindPropertyRelative("entries");
|
|
|
|
|
|
if (entriesProp != null && i >= 0 && i < entriesProp.arraySize)
|
|
|
|
|
|
{
|
|
|
|
|
|
entriesProp.DeleteArrayElementAtIndex(i);
|
|
|
|
|
|
// apply then remove from runtime to keep both in sync
|
|
|
|
|
|
serializedObject.ApplyModifiedProperties();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// remove from runtime list (db.tables)
|
|
|
|
|
|
if (runtimeTableIndex >= 0 && db != null && db.tables != null && runtimeTableIndex < db.tables.Count)
|
|
|
|
|
|
{
|
|
|
|
|
|
var runtimeTable = db.tables[runtimeTableIndex];
|
|
|
|
|
|
if (runtimeTable != null && i >= 0 && i < runtimeTable.entries.Count)
|
|
|
|
|
|
{
|
|
|
|
|
|
runtimeTable.entries.RemoveAt(i);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
EditorUtility.SetDirty(db);
|
|
|
|
|
|
AssetDatabase.SaveAssets();
|
|
|
|
|
|
|
|
|
|
|
|
// reset paging and return to avoid continuing to iterate mutated serialized array
|
|
|
|
|
|
currentPages[tabIndex] = 0;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-12-10 17:38:31 +08:00
|
|
|
|
}
|
2025-12-05 19:04:53 +08:00
|
|
|
|
|
2025-12-10 17:38:31 +08:00
|
|
|
|
EditorGUILayout.Space(4);
|
|
|
|
|
|
}
|
2025-12-05 19:04:53 +08:00
|
|
|
|
|
2025-12-10 17:38:31 +08:00
|
|
|
|
if (matchedTotal == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
EditorGUILayout.HelpBox("No entries match the search.", MessageType.Info);
|
|
|
|
|
|
}
|
2025-12-05 20:57:29 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-12-05 19:04:53 +08:00
|
|
|
|
|
|
|
|
|
|
EditorGUILayout.EndVertical();
|
|
|
|
|
|
|
2025-12-10 17:38:31 +08:00
|
|
|
|
EditorGUILayout.Space(6);
|
2025-12-05 19:04:53 +08:00
|
|
|
|
|
|
|
|
|
|
serializedObject.ApplyModifiedProperties();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-10 17:38:31 +08:00
|
|
|
|
void EnsureDefaultTable(string name)
|
2025-12-05 19:04:53 +08:00
|
|
|
|
{
|
2025-12-05 20:57:29 +08:00
|
|
|
|
if (tablesProp == null) return;
|
|
|
|
|
|
for (int i = 0; i < tablesProp.arraySize; ++i)
|
2025-12-05 19:04:53 +08:00
|
|
|
|
{
|
2025-12-05 20:57:29 +08:00
|
|
|
|
var t = tablesProp.GetArrayElementAtIndex(i);
|
2025-12-10 17:38:31 +08:00
|
|
|
|
var nameProp = t.FindPropertyRelative("deviceName");
|
|
|
|
|
|
if (nameProp != null && string.Equals(nameProp.stringValue, name, StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
|
return;
|
2025-12-05 20:57:29 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int idx = tablesProp.arraySize;
|
2025-12-10 17:38:31 +08:00
|
|
|
|
tablesProp.InsertArrayElementAtIndex(idx);
|
2025-12-05 20:57:29 +08:00
|
|
|
|
var newTable = tablesProp.GetArrayElementAtIndex(idx);
|
2025-12-10 17:38:31 +08:00
|
|
|
|
var deviceNameProp = newTable.FindPropertyRelative("deviceName");
|
|
|
|
|
|
if (deviceNameProp != null) deviceNameProp.stringValue = name;
|
2025-12-17 20:03:29 +08:00
|
|
|
|
var sheetProp = newTable.FindPropertyRelative("spriteSheetTexture");
|
|
|
|
|
|
if (sheetProp != null) sheetProp.objectReferenceValue = null;
|
2025-12-05 20:57:29 +08:00
|
|
|
|
var entriesProp = newTable.FindPropertyRelative("entries");
|
|
|
|
|
|
if (entriesProp != null) entriesProp.arraySize = 0;
|
|
|
|
|
|
|
|
|
|
|
|
serializedObject.ApplyModifiedProperties();
|
|
|
|
|
|
EditorUtility.SetDirty(db);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-10 17:38:31 +08:00
|
|
|
|
void SyncEditorListsWithTables()
|
2025-12-05 20:57:29 +08:00
|
|
|
|
{
|
2025-12-10 17:38:31 +08:00
|
|
|
|
int count = tablesProp != null ? tablesProp.arraySize : 0;
|
|
|
|
|
|
if (searchStrings == null) searchStrings = new List<string>();
|
|
|
|
|
|
if (currentPages == null) currentPages = new List<int>();
|
2026-03-09 20:38:15 +08:00
|
|
|
|
if (newEntrySprites == null) newEntrySprites = new List<Sprite>();
|
2025-12-05 20:57:29 +08:00
|
|
|
|
|
2025-12-10 17:38:31 +08:00
|
|
|
|
while (searchStrings.Count < count) searchStrings.Add("");
|
|
|
|
|
|
while (currentPages.Count < count) currentPages.Add(0);
|
2026-03-09 20:38:15 +08:00
|
|
|
|
while (newEntrySprites.Count < count) newEntrySprites.Add(null);
|
2025-12-10 17:38:31 +08:00
|
|
|
|
|
|
|
|
|
|
while (searchStrings.Count > count) searchStrings.RemoveAt(searchStrings.Count - 1);
|
|
|
|
|
|
while (currentPages.Count > count) currentPages.RemoveAt(currentPages.Count - 1);
|
2026-03-09 20:38:15 +08:00
|
|
|
|
while (newEntrySprites.Count > count) newEntrySprites.RemoveAt(newEntrySprites.Count - 1);
|
2025-12-10 17:38:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void EnsureEditorListsLength()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (tablesProp == null) return;
|
|
|
|
|
|
SyncEditorListsWithTables();
|
2025-12-05 19:04:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-09 20:38:15 +08:00
|
|
|
|
// ----- 新增:把单个 Sprite 加入到序列化表和 runtime 表 -----
|
|
|
|
|
|
void AddEntryToTableSerialized(SerializedProperty tableProp, Sprite sprite)
|
2025-12-05 19:04:53 +08:00
|
|
|
|
{
|
2025-12-05 20:57:29 +08:00
|
|
|
|
if (tableProp == null) return;
|
2026-03-09 20:38:15 +08:00
|
|
|
|
var entriesProp = tableProp.FindPropertyRelative("entries");
|
|
|
|
|
|
if (entriesProp == null) return;
|
2025-12-05 20:57:29 +08:00
|
|
|
|
|
2026-03-09 20:38:15 +08:00
|
|
|
|
int insertIndex = entriesProp.arraySize;
|
|
|
|
|
|
entriesProp.InsertArrayElementAtIndex(insertIndex);
|
|
|
|
|
|
var newE = entriesProp.GetArrayElementAtIndex(insertIndex);
|
|
|
|
|
|
if (newE != null)
|
2025-12-17 20:03:29 +08:00
|
|
|
|
{
|
2026-03-09 20:38:15 +08:00
|
|
|
|
var spriteProp = newE.FindPropertyRelative("Sprite");
|
|
|
|
|
|
var actionProp = newE.FindPropertyRelative("action");
|
2025-12-17 20:03:29 +08:00
|
|
|
|
|
2026-03-09 20:38:15 +08:00
|
|
|
|
if (spriteProp != null) spriteProp.objectReferenceValue = sprite;
|
2025-12-05 19:04:53 +08:00
|
|
|
|
|
2026-03-09 20:38:15 +08:00
|
|
|
|
// leave action serialized as-is (most projects can't serialize InputAction directly here)
|
|
|
|
|
|
if (actionProp != null)
|
2025-12-05 19:04:53 +08:00
|
|
|
|
{
|
2025-12-17 20:03:29 +08:00
|
|
|
|
try
|
2025-12-05 19:04:53 +08:00
|
|
|
|
{
|
2026-03-09 20:38:15 +08:00
|
|
|
|
actionProp.objectReferenceValue = null;
|
2025-12-17 20:03:29 +08:00
|
|
|
|
}
|
2026-03-09 20:38:15 +08:00
|
|
|
|
catch
|
2025-12-05 19:04:53 +08:00
|
|
|
|
{
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-12-17 20:03:29 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
serializedObject.ApplyModifiedProperties();
|
|
|
|
|
|
|
2026-03-09 20:38:15 +08:00
|
|
|
|
// also add to runtime list
|
2025-12-17 20:03:29 +08:00
|
|
|
|
var nameProp = tableProp.FindPropertyRelative("deviceName");
|
|
|
|
|
|
string deviceName = nameProp != null ? nameProp.stringValue : "";
|
|
|
|
|
|
int tableIndex = MapSerializedTableToRuntimeIndex(deviceName);
|
2026-03-09 20:38:15 +08:00
|
|
|
|
if (tableIndex >= 0 && db != null && db.tables != null && tableIndex < db.tables.Count)
|
2025-12-17 20:03:29 +08:00
|
|
|
|
{
|
2026-03-09 20:38:15 +08:00
|
|
|
|
var tableObj = db.tables[tableIndex];
|
|
|
|
|
|
GlyphEntry e = new GlyphEntry();
|
|
|
|
|
|
e.Sprite = sprite;
|
|
|
|
|
|
e.action = null; // runtime only: none provided here
|
|
|
|
|
|
tableObj.entries.Add(e);
|
2025-12-17 20:03:29 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
EditorUtility.SetDirty(db);
|
|
|
|
|
|
AssetDatabase.SaveAssets();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-09 20:38:15 +08:00
|
|
|
|
|
|
|
|
|
|
// ----- Parse Sprite Sheet (Texture2D with Multiple) -----
|
|
|
|
|
|
// 只用名称匹配覆盖 Sprite,不改变已有 action
|
2025-12-17 20:03:29 +08:00
|
|
|
|
void ParseSpriteSheetIntoTableSerialized(SerializedProperty tableProp)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (tableProp == null) return;
|
|
|
|
|
|
|
|
|
|
|
|
var sheetProp = tableProp.FindPropertyRelative("spriteSheetTexture");
|
|
|
|
|
|
var tex = sheetProp != null ? sheetProp.objectReferenceValue as Texture2D : null;
|
|
|
|
|
|
if (tex == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogWarning("[InputGlyphDatabase] spriteSheetTexture is null for table.");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var nameProp = tableProp.FindPropertyRelative("deviceName");
|
|
|
|
|
|
string deviceName = nameProp != null ? nameProp.stringValue : "";
|
|
|
|
|
|
|
|
|
|
|
|
int tableIndex = MapSerializedTableToRuntimeIndex(deviceName);
|
|
|
|
|
|
if (tableIndex < 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError($"[InputGlyphDatabase] Could not map serialized table '{deviceName}' to runtime db.tables.");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var tableObj = db.tables[tableIndex];
|
2026-03-09 20:38:15 +08:00
|
|
|
|
if (tableObj == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError($"[InputGlyphDatabase] Runtime table object is null for '{deviceName}'.");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-12-17 20:03:29 +08:00
|
|
|
|
|
|
|
|
|
|
string path = AssetDatabase.GetAssetPath(tex);
|
|
|
|
|
|
if (string.IsNullOrEmpty(path))
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogWarning("[InputGlyphDatabase] Could not get asset path for texture.");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var assets = AssetDatabase.LoadAllAssetsAtPath(path);
|
|
|
|
|
|
if (assets == null || assets.Length == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogWarning("[InputGlyphDatabase] No sub-assets found at path: " + path);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-09 20:38:15 +08:00
|
|
|
|
// 收集 sprites(按照文件内顺序;你如果想按名字排序可以在这里加)
|
2025-12-17 20:03:29 +08:00
|
|
|
|
List<Sprite> sprites = new List<Sprite>();
|
|
|
|
|
|
foreach (var a in assets)
|
|
|
|
|
|
{
|
2026-03-09 20:38:15 +08:00
|
|
|
|
if (a is Sprite sp) sprites.Add(sp);
|
2025-12-17 20:03:29 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-09 20:38:15 +08:00
|
|
|
|
var entriesProp = tableProp.FindPropertyRelative("entries");
|
|
|
|
|
|
if (entriesProp == null)
|
2025-12-17 20:03:29 +08:00
|
|
|
|
{
|
2026-03-09 20:38:15 +08:00
|
|
|
|
Debug.LogWarning("[InputGlyphDatabase] entries property not found on table.");
|
|
|
|
|
|
return;
|
2025-12-17 20:03:29 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-09 20:38:15 +08:00
|
|
|
|
// 构建序列化表名 -> 索引 映射(忽略大小写)
|
|
|
|
|
|
var serializedNameToIndex = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
|
|
|
|
|
|
for (int i = 0; i < entriesProp.arraySize; ++i)
|
2025-12-17 20:03:29 +08:00
|
|
|
|
{
|
2026-03-09 20:38:15 +08:00
|
|
|
|
var eProp = entriesProp.GetArrayElementAtIndex(i);
|
|
|
|
|
|
if (eProp == null) continue;
|
|
|
|
|
|
var sProp = eProp.FindPropertyRelative("Sprite");
|
|
|
|
|
|
var sRef = sProp != null ? sProp.objectReferenceValue as Sprite : null;
|
|
|
|
|
|
if (sRef != null && !serializedNameToIndex.ContainsKey(sRef.name))
|
|
|
|
|
|
{
|
|
|
|
|
|
serializedNameToIndex[sRef.name] = i;
|
|
|
|
|
|
}
|
2025-12-17 20:03:29 +08:00
|
|
|
|
}
|
2025-12-05 19:04:53 +08:00
|
|
|
|
|
2026-03-09 20:38:15 +08:00
|
|
|
|
// runtime 名称 -> 索引 映射
|
|
|
|
|
|
var runtimeNameToIndex = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
|
|
|
|
|
|
for (int i = 0; i < tableObj.entries.Count; ++i)
|
2025-12-05 19:04:53 +08:00
|
|
|
|
{
|
2026-03-09 20:38:15 +08:00
|
|
|
|
var re = tableObj.entries[i];
|
|
|
|
|
|
if (re != null && re.Sprite != null)
|
2025-12-05 19:04:53 +08:00
|
|
|
|
{
|
2026-03-09 20:38:15 +08:00
|
|
|
|
var rn = re.Sprite.name;
|
|
|
|
|
|
if (!runtimeNameToIndex.ContainsKey(rn))
|
|
|
|
|
|
runtimeNameToIndex[rn] = i;
|
2025-12-05 19:04:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-09 20:38:15 +08:00
|
|
|
|
int replaced = 0, added = 0;
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var sp in sprites)
|
2025-12-05 19:04:53 +08:00
|
|
|
|
{
|
2026-03-09 20:38:15 +08:00
|
|
|
|
if (sp == null) continue;
|
|
|
|
|
|
string nm = sp.name;
|
|
|
|
|
|
|
|
|
|
|
|
// -- 序列化层:同名则替换 Sprite 引用(不触碰 action),否则新增元素并把 action 设为 null --
|
|
|
|
|
|
if (serializedNameToIndex.TryGetValue(nm, out int sIndex))
|
2025-12-05 19:04:53 +08:00
|
|
|
|
{
|
2026-03-09 20:38:15 +08:00
|
|
|
|
var eProp = entriesProp.GetArrayElementAtIndex(sIndex);
|
|
|
|
|
|
if (eProp != null)
|
2025-12-05 19:04:53 +08:00
|
|
|
|
{
|
2026-03-09 20:38:15 +08:00
|
|
|
|
var spriteProp = eProp.FindPropertyRelative("Sprite");
|
|
|
|
|
|
if (spriteProp != null) spriteProp.objectReferenceValue = sp;
|
|
|
|
|
|
// 不修改 actionProp,保持原有 action(如果有的话)
|
2025-12-05 19:04:53 +08:00
|
|
|
|
}
|
2026-03-09 20:38:15 +08:00
|
|
|
|
|
|
|
|
|
|
replaced++;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
int insertIndex = entriesProp.arraySize;
|
|
|
|
|
|
entriesProp.InsertArrayElementAtIndex(insertIndex);
|
|
|
|
|
|
var newE = entriesProp.GetArrayElementAtIndex(insertIndex);
|
|
|
|
|
|
if (newE != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
var spriteProp = newE.FindPropertyRelative("Sprite");
|
|
|
|
|
|
var actionProp = newE.FindPropertyRelative("action");
|
|
|
|
|
|
if (spriteProp != null) spriteProp.objectReferenceValue = sp;
|
|
|
|
|
|
if (actionProp != null) actionProp.objectReferenceValue = null; // 新增项 action 为空
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
serializedNameToIndex[nm] = insertIndex;
|
|
|
|
|
|
added++;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// -- 运行时层:同名则替换 Sprite,否则新增 runtime entry(action 设 null,保持之前 runtime entry 的 action 不变) --
|
|
|
|
|
|
if (runtimeNameToIndex.TryGetValue(nm, out int rIndex))
|
|
|
|
|
|
{
|
|
|
|
|
|
var runtimeEntry = tableObj.entries[rIndex];
|
|
|
|
|
|
if (runtimeEntry != null) runtimeEntry.Sprite = sp;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
GlyphEntry ge = new GlyphEntry();
|
|
|
|
|
|
ge.Sprite = sp;
|
|
|
|
|
|
ge.action = null;
|
|
|
|
|
|
tableObj.entries.Add(ge);
|
|
|
|
|
|
runtimeNameToIndex[nm] = tableObj.entries.Count - 1;
|
2025-12-05 19:04:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-09 20:38:15 +08:00
|
|
|
|
// 应用并保存修改(序列化层与 runtime 层保持同步)
|
|
|
|
|
|
EditorUtility.SetDirty(db);
|
|
|
|
|
|
serializedObject.Update();
|
|
|
|
|
|
serializedObject.ApplyModifiedProperties();
|
|
|
|
|
|
AssetDatabase.SaveAssets();
|
|
|
|
|
|
|
|
|
|
|
|
Debug.Log($"[InputGlyphDatabase] Merged sprite sheet '{tex.name}' into table '{deviceName}'. spritesFound={sprites.Count}, replaced={replaced}, added={added}, totalEntries={tableObj.entries.Count}");
|
2025-12-05 19:04:53 +08:00
|
|
|
|
}
|
2025-12-17 20:03:29 +08:00
|
|
|
|
|
2026-03-09 20:38:15 +08:00
|
|
|
|
int MapSerializedTableToRuntimeIndex(string deviceName)
|
2025-12-17 20:03:29 +08:00
|
|
|
|
{
|
2026-03-09 20:38:15 +08:00
|
|
|
|
if (db == null || db.tables == null) return -1;
|
|
|
|
|
|
for (int ti = 0; ti < db.tables.Count; ++ti)
|
2025-12-17 20:03:29 +08:00
|
|
|
|
{
|
2026-03-09 20:38:15 +08:00
|
|
|
|
if (string.Equals(db.tables[ti].deviceName, deviceName, StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
|
return ti;
|
2025-12-17 20:03:29 +08:00
|
|
|
|
}
|
2026-03-09 20:38:15 +08:00
|
|
|
|
|
|
|
|
|
|
return -1;
|
2025-12-17 20:03:29 +08:00
|
|
|
|
}
|
2025-12-05 19:04:53 +08:00
|
|
|
|
}
|