363 lines
13 KiB
C#
363 lines
13 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Reflection;
|
|
using AlicizaX.InputGlyph;
|
|
using UnityEditor;
|
|
using UnityEngine;
|
|
using UnityEngine.U2D;
|
|
using TMPro;
|
|
|
|
[CustomEditor(typeof(InputGlyphDatabase))]
|
|
public class InputGlyphDatabaseEditor : Editor
|
|
{
|
|
SerializedProperty tablesProp;
|
|
InputGlyphDatabase db;
|
|
|
|
int tabIndex = 0;
|
|
string[] tabNames = new string[] { "Keyboard", "Xbox", "PlayStation", "Other" };
|
|
|
|
// Pagination
|
|
const int itemsPerPage = 10; // 10 items per page as requested
|
|
int currentPage = 0;
|
|
|
|
void OnEnable()
|
|
{
|
|
db = target as InputGlyphDatabase;
|
|
tablesProp = serializedObject.FindProperty("tables");
|
|
|
|
// Ensure serialized list exists
|
|
if (tablesProp == null)
|
|
{
|
|
// If the field name is different, user should fix it in their class or here.
|
|
Debug.LogError("Could not find serialized property 'tables' on InputGlyphDatabase. Check field name.");
|
|
}
|
|
}
|
|
|
|
public override void OnInspectorGUI()
|
|
{
|
|
serializedObject.Update();
|
|
if (db == null || tablesProp == null) return;
|
|
|
|
EditorGUILayout.Space();
|
|
tabIndex = GUILayout.Toolbar(tabIndex, tabNames, GUILayout.Height(24));
|
|
var curDevice = (InputDeviceWatcher.InputDeviceCategory)tabIndex;
|
|
|
|
// Ensure all 4 tables exist (serialized-safe)
|
|
EnsureTableFor(InputDeviceWatcher.InputDeviceCategory.Keyboard);
|
|
EnsureTableFor(InputDeviceWatcher.InputDeviceCategory.Xbox);
|
|
EnsureTableFor(InputDeviceWatcher.InputDeviceCategory.PlayStation);
|
|
|
|
var tableProp = GetTablePropertyFor(curDevice);
|
|
if (tableProp == null)
|
|
{
|
|
EditorGUILayout.HelpBox("Table not found (this should not happen).", MessageType.Warning);
|
|
serializedObject.ApplyModifiedProperties();
|
|
return;
|
|
}
|
|
|
|
EditorGUILayout.BeginVertical("box");
|
|
EditorGUILayout.LabelField(curDevice.ToString(), EditorStyles.boldLabel);
|
|
|
|
var tmpAssetProp = tableProp.FindPropertyRelative("tmpAsset");
|
|
EditorGUILayout.PropertyField(tmpAssetProp, new GUIContent("TMP Sprite Asset"));
|
|
|
|
EditorGUILayout.BeginHorizontal();
|
|
if (GUILayout.Button("Parse TMP Asset"))
|
|
{
|
|
ParseTMPAssetIntoTableSerialized(tableProp);
|
|
}
|
|
|
|
if (GUILayout.Button("Clear"))
|
|
{
|
|
var entriesProp = tableProp.FindPropertyRelative("entries");
|
|
entriesProp.arraySize = 0;
|
|
serializedObject.ApplyModifiedProperties();
|
|
EditorUtility.SetDirty(db);
|
|
currentPage = 0;
|
|
}
|
|
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
EditorGUILayout.Space();
|
|
|
|
var entries = tableProp.FindPropertyRelative("entries");
|
|
if (entries != null)
|
|
{
|
|
int total = entries.arraySize;
|
|
int totalPages = Mathf.Max(1, (total + itemsPerPage - 1) / itemsPerPage);
|
|
currentPage = Mathf.Clamp(currentPage, 0, totalPages - 1);
|
|
|
|
// Pagination controls
|
|
EditorGUILayout.BeginHorizontal();
|
|
if (GUILayout.Button("<<", GUILayout.Width(40))) { currentPage = 0; }
|
|
if (GUILayout.Button("<", GUILayout.Width(40))) { currentPage = Mathf.Max(0, currentPage - 1); }
|
|
|
|
GUILayout.FlexibleSpace();
|
|
EditorGUILayout.LabelField(string.Format("Page {0}/{1}", currentPage + 1, totalPages), GUILayout.Width(100));
|
|
GUILayout.FlexibleSpace();
|
|
|
|
if (GUILayout.Button(">", GUILayout.Width(40))) { currentPage = Mathf.Min(totalPages - 1, currentPage + 1); }
|
|
if (GUILayout.Button(">>", GUILayout.Width(40))) { currentPage = totalPages - 1; }
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
EditorGUILayout.Space(4);
|
|
|
|
int start = currentPage * itemsPerPage;
|
|
int end = Mathf.Min(start + itemsPerPage, total);
|
|
|
|
for (int i = start; i < end; ++i)
|
|
{
|
|
var eProp = entries.GetArrayElementAtIndex(i);
|
|
|
|
if (eProp == null) continue;
|
|
|
|
using (new EditorGUILayout.HorizontalScope("box"))
|
|
{
|
|
// Left column: sprite preview (fixed width)
|
|
using (new EditorGUILayout.VerticalScope(GUILayout.Width(80)))
|
|
{
|
|
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(64), GUILayout.Height(64));
|
|
}
|
|
else
|
|
{
|
|
EditorGUILayout.PropertyField(spriteProp, GUIContent.none, GUILayout.Width(64), GUILayout.Height(64));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
EditorGUILayout.PropertyField(spriteProp, GUIContent.none, GUILayout.Width(64), GUILayout.Height(64));
|
|
}
|
|
}
|
|
|
|
// Right column: path + action should take the remaining width
|
|
EditorGUILayout.BeginVertical();
|
|
|
|
var actionProp = eProp.FindPropertyRelative("action");
|
|
EditorGUILayout.Space(2);
|
|
|
|
EditorGUILayout.PropertyField(actionProp, GUIContent.none, GUILayout.ExpandWidth(true));
|
|
// if (glyphEntry != null && glyphEntry.action != null)
|
|
// {
|
|
//
|
|
// }
|
|
EditorGUILayout.EndVertical();
|
|
}
|
|
|
|
EditorGUILayout.Space(6);
|
|
}
|
|
}
|
|
|
|
EditorGUILayout.EndVertical();
|
|
|
|
EditorGUILayout.Space();
|
|
if (GUILayout.Button("Save Asset"))
|
|
{
|
|
serializedObject.ApplyModifiedProperties();
|
|
EditorUtility.SetDirty(db);
|
|
AssetDatabase.SaveAssets();
|
|
}
|
|
|
|
serializedObject.ApplyModifiedProperties();
|
|
}
|
|
|
|
// Ensure a table for the device exists. Uses serialized properties (adds element if necessary).
|
|
void EnsureTableFor(InputDeviceWatcher.InputDeviceCategory device)
|
|
{
|
|
if (tablesProp == null) return;
|
|
|
|
// Search existing entries
|
|
for (int i = 0; i < tablesProp.arraySize; ++i)
|
|
{
|
|
var t = tablesProp.GetArrayElementAtIndex(i);
|
|
var devProp = t.FindPropertyRelative("deviceType");
|
|
if (devProp != null && devProp.enumValueIndex == (int)device)
|
|
return; // exists
|
|
}
|
|
|
|
// Not found -> append new element
|
|
int idx = tablesProp.arraySize;
|
|
tablesProp.InsertArrayElementAtIndex(idx); // Inserts a copy if exists, otherwise default
|
|
var newTable = tablesProp.GetArrayElementAtIndex(idx);
|
|
|
|
var deviceTypeProp = newTable.FindPropertyRelative("deviceType");
|
|
if (deviceTypeProp != null) deviceTypeProp.enumValueIndex = (int)device;
|
|
|
|
var tmpAssetProp = newTable.FindPropertyRelative("tmpAsset");
|
|
if (tmpAssetProp != null) tmpAssetProp.objectReferenceValue = null;
|
|
|
|
var entriesProp = newTable.FindPropertyRelative("entries");
|
|
if (entriesProp != null) entriesProp.arraySize = 0;
|
|
|
|
serializedObject.ApplyModifiedProperties();
|
|
EditorUtility.SetDirty(db);
|
|
}
|
|
|
|
// Return the SerializedProperty representing the DeviceGlyphTable that matches device
|
|
SerializedProperty GetTablePropertyFor(InputDeviceWatcher.InputDeviceCategory device)
|
|
{
|
|
if (tablesProp == null) return null;
|
|
for (int i = 0; i < tablesProp.arraySize; ++i)
|
|
{
|
|
var t = tablesProp.GetArrayElementAtIndex(i);
|
|
var devProp = t.FindPropertyRelative("deviceType");
|
|
if (devProp != null && devProp.enumValueIndex == (int)device)
|
|
return t;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
// Parse TMP Sprite Asset and populate entries (serialized)
|
|
void ParseTMPAssetIntoTableSerialized(SerializedProperty tableProp)
|
|
{
|
|
if (tableProp == null) return;
|
|
var tmpAssetProp = tableProp.FindPropertyRelative("tmpAsset");
|
|
var asset = tmpAssetProp.objectReferenceValue as TMP_SpriteAsset;
|
|
if (asset == null) return;
|
|
|
|
var entriesProp = tableProp.FindPropertyRelative("entries");
|
|
if (entriesProp == null) return;
|
|
|
|
entriesProp.arraySize = 0;
|
|
|
|
var chars = asset.spriteCharacterTable;
|
|
SpriteAtlas atlas = GetSpriteAtlasFromTMP(asset);
|
|
string assetPath = AssetDatabase.GetAssetPath(asset);
|
|
string assetFolder = Path.GetDirectoryName(assetPath);
|
|
|
|
for (int i = 0; i < chars.Count; ++i)
|
|
{
|
|
var ch = chars[i];
|
|
if (ch == null) continue;
|
|
var name = ch.name;
|
|
if (string.IsNullOrEmpty(name)) continue;
|
|
|
|
Sprite s = null;
|
|
try
|
|
{
|
|
var glyph = ch.glyph as TMP_SpriteGlyph;
|
|
if (glyph != null && glyph.sprite != null) s = glyph.sprite;
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
|
|
if (s == null && atlas != null)
|
|
{
|
|
try
|
|
{
|
|
s = atlas.GetSprite(name);
|
|
}
|
|
catch
|
|
{
|
|
s = null;
|
|
}
|
|
|
|
if (s == null)
|
|
{
|
|
try
|
|
{
|
|
var m = typeof(SpriteAtlas).GetMethod("GetSprite", new Type[] { typeof(string) });
|
|
if (m != null) s = m.Invoke(atlas, new object[] { name }) as Sprite;
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
}
|
|
}
|
|
|
|
if (s == null)
|
|
{
|
|
string[] scoped = AssetDatabase.FindAssets(name + " t:Sprite", new[] { assetFolder });
|
|
if (scoped != null && scoped.Length > 0)
|
|
{
|
|
foreach (var g in scoped)
|
|
{
|
|
var p = AssetDatabase.GUIDToAssetPath(g);
|
|
var sp = AssetDatabase.LoadAssetAtPath<Sprite>(p);
|
|
if (sp != null && sp.name == name)
|
|
{
|
|
s = sp;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (s == null)
|
|
{
|
|
string[] all = AssetDatabase.FindAssets(name + " t:Sprite");
|
|
if (all != null && all.Length > 0)
|
|
{
|
|
foreach (var g in all)
|
|
{
|
|
var p = AssetDatabase.GUIDToAssetPath(g);
|
|
var sp = AssetDatabase.LoadAssetAtPath<Sprite>(p);
|
|
if (sp != null && sp.name == name)
|
|
{
|
|
s = sp;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create new entry and assign fields
|
|
int newIndex = entriesProp.arraySize;
|
|
entriesProp.InsertArrayElementAtIndex(newIndex);
|
|
var entryProp = entriesProp.GetArrayElementAtIndex(newIndex);
|
|
|
|
var spriteProp = entryProp.FindPropertyRelative("Sprite");
|
|
|
|
|
|
if (spriteProp != null) spriteProp.objectReferenceValue = s;
|
|
}
|
|
|
|
serializedObject.ApplyModifiedProperties();
|
|
EditorUtility.SetDirty(db);
|
|
AssetDatabase.SaveAssets();
|
|
}
|
|
|
|
SpriteAtlas GetSpriteAtlasFromTMP(TMP_SpriteAsset asset)
|
|
{
|
|
if (asset == null) return null;
|
|
var t = asset.GetType();
|
|
foreach (var f in t.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
|
|
{
|
|
if (typeof(SpriteAtlas).IsAssignableFrom(f.FieldType))
|
|
{
|
|
var val = f.GetValue(asset) as SpriteAtlas;
|
|
if (val != null) return val;
|
|
}
|
|
}
|
|
|
|
foreach (var p in t.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
|
|
{
|
|
if (typeof(SpriteAtlas).IsAssignableFrom(p.PropertyType))
|
|
{
|
|
try
|
|
{
|
|
var val = p.GetValue(asset, null) as SpriteAtlas;
|
|
if (val != null) return val;
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|