480 lines
18 KiB
C#
480 lines
18 KiB
C#
using UnityEditor;
|
||
using UnityEditor.Callbacks;
|
||
using UnityEngine;
|
||
using UnityEngine.InputSystem;
|
||
|
||
[CustomEditor(typeof(InputGlyphDatabase))]
|
||
public sealed class InputGlyphDatabaseEditor : Editor
|
||
{
|
||
public override void OnInspectorGUI()
|
||
{
|
||
}
|
||
|
||
[OnOpenAsset(0)]
|
||
private static bool OpenAsset(int instanceID, int line)
|
||
{
|
||
InputGlyphDatabase database = EditorUtility.InstanceIDToObject(instanceID) as InputGlyphDatabase;
|
||
if (database == null)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
InputGlyphDatabaseWindow.OpenFromAsset(database);
|
||
return true;
|
||
}
|
||
}
|
||
|
||
internal sealed class InputGlyphDatabaseWindow : EditorWindow
|
||
{
|
||
private const string TablesPropertyName = "tables";
|
||
private const string PlaceholderSpritePropertyName = "placeholderSprite";
|
||
private const string DeviceNamePropertyName = "deviceName";
|
||
private const string SpriteSheetPropertyName = "spriteSheetTexture";
|
||
private const string PlatformIconPropertyName = "platformIcons";
|
||
private const string EntriesPropertyName = "entries";
|
||
private const string EntrySpritePropertyName = "Sprite";
|
||
private const string EntryActionPropertyName = "action";
|
||
private const float SidebarWidth = 220f;
|
||
private const float EntryMinHeight = 76f;
|
||
private const float PreviewSize = 42f;
|
||
private static readonly string[] DefaultTableNames = { "Keyboard", "Xbox", "PlayStation", "Other" };
|
||
|
||
private InputGlyphDatabase _database;
|
||
private SerializedObject _serializedDatabase;
|
||
private SerializedProperty _tablesProperty;
|
||
private SerializedProperty _placeholderSpriteProperty;
|
||
private Vector2 _tableScroll;
|
||
private Vector2 _entryScroll;
|
||
private int _selectedTable;
|
||
private string _search = string.Empty;
|
||
private GUIStyle _sidebarStyle;
|
||
private GUIStyle _selectedTableStyle;
|
||
private GUIStyle _tableStyle;
|
||
private GUIStyle _entryCardStyle;
|
||
private GUIStyle _headerStyle;
|
||
private GUIContent _addIcon;
|
||
private GUIContent _removeIcon;
|
||
private GUIContent _saveIcon;
|
||
private GUIContent _refreshIcon;
|
||
private GUIContent _searchIcon;
|
||
private GUIContent _settingsIcon;
|
||
|
||
internal static void OpenFromAsset(InputGlyphDatabase database)
|
||
{
|
||
InputGlyphDatabaseWindow window = GetWindow<InputGlyphDatabaseWindow>("Input Glyph Database", true);
|
||
window.SetDatabase(database);
|
||
window.minSize = new Vector2(940f, 560f);
|
||
window.Show();
|
||
}
|
||
|
||
private void OnEnable()
|
||
{
|
||
BuildStyles();
|
||
}
|
||
|
||
private void SetDatabase(InputGlyphDatabase database)
|
||
{
|
||
_database = database;
|
||
_serializedDatabase = new SerializedObject(_database);
|
||
_tablesProperty = _serializedDatabase.FindProperty(TablesPropertyName);
|
||
_placeholderSpriteProperty = _serializedDatabase.FindProperty(PlaceholderSpritePropertyName);
|
||
_selectedTable = Mathf.Clamp(_selectedTable, 0, Mathf.Max(0, _tablesProperty.arraySize - 1));
|
||
titleContent = new GUIContent(database.name, EditorGUIUtility.IconContent("d_ScriptableObject Icon").image);
|
||
}
|
||
|
||
private void OnGUI()
|
||
{
|
||
if (_database == null || _serializedDatabase == null)
|
||
{
|
||
EditorApplication.delayCall += CloseIfDatabaseMissing;
|
||
return;
|
||
}
|
||
|
||
BuildStyles();
|
||
_serializedDatabase.Update();
|
||
DrawToolbar();
|
||
|
||
Rect contentRect = new Rect(0f, EditorGUIUtility.singleLineHeight + 8f, position.width, position.height - EditorGUIUtility.singleLineHeight - 8f);
|
||
Rect sidebarRect = new Rect(contentRect.x, contentRect.y, SidebarWidth, contentRect.height);
|
||
Rect mainRect = new Rect(sidebarRect.xMax + 1f, contentRect.y, contentRect.width - SidebarWidth - 1f, contentRect.height);
|
||
|
||
DrawSidebar(sidebarRect);
|
||
DrawMainPanel(mainRect);
|
||
|
||
if (_serializedDatabase.ApplyModifiedProperties())
|
||
{
|
||
MarkDirty();
|
||
}
|
||
}
|
||
|
||
private void CloseIfDatabaseMissing()
|
||
{
|
||
if (this != null && _database == null)
|
||
{
|
||
Close();
|
||
}
|
||
}
|
||
|
||
private void DrawToolbar()
|
||
{
|
||
using (new EditorGUILayout.HorizontalScope(EditorStyles.toolbar))
|
||
{
|
||
if (GUILayout.Button(_saveIcon, EditorStyles.toolbarButton, GUILayout.Width(28f)))
|
||
{
|
||
Save();
|
||
}
|
||
|
||
if (GUILayout.Button(_refreshIcon, EditorStyles.toolbarButton, GUILayout.Width(28f)))
|
||
{
|
||
_database.EditorRefreshCache();
|
||
Repaint();
|
||
}
|
||
|
||
GUILayout.Space(8f);
|
||
GUILayout.Label(_searchIcon, GUILayout.Width(20f));
|
||
_search = GUILayout.TextField(_search, EditorStyles.toolbarSearchField, GUILayout.Width(240f));
|
||
if (!string.IsNullOrEmpty(_search) && GUILayout.Button("×", EditorStyles.toolbarButton, GUILayout.Width(22f)))
|
||
{
|
||
_search = string.Empty;
|
||
GUI.FocusControl(null);
|
||
}
|
||
|
||
GUILayout.FlexibleSpace();
|
||
DrawObjectName();
|
||
}
|
||
}
|
||
|
||
private void DrawSidebar(Rect rect)
|
||
{
|
||
GUI.Box(rect, GUIContent.none, _sidebarStyle);
|
||
GUILayout.BeginArea(new Rect(rect.x + 8f, rect.y + 8f, rect.width - 16f, rect.height - 16f));
|
||
|
||
using (new EditorGUILayout.HorizontalScope())
|
||
{
|
||
GUILayout.Label("Tables", _headerStyle);
|
||
GUILayout.FlexibleSpace();
|
||
if (GUILayout.Button(_addIcon, EditorStyles.iconButton, GUILayout.Width(24f), GUILayout.Height(22f)))
|
||
{
|
||
AddTable(NextTableName());
|
||
}
|
||
}
|
||
|
||
_tableScroll = EditorGUILayout.BeginScrollView(_tableScroll);
|
||
for (int i = 0; i < _tablesProperty.arraySize; i++)
|
||
{
|
||
SerializedProperty table = _tablesProperty.GetArrayElementAtIndex(i);
|
||
DrawTableButton(i, table);
|
||
}
|
||
|
||
EditorGUILayout.EndScrollView();
|
||
|
||
GUILayout.Space(6f);
|
||
using (new EditorGUILayout.HorizontalScope())
|
||
{
|
||
if (GUILayout.Button(_settingsIcon, EditorStyles.iconButton, GUILayout.Width(28f), GUILayout.Height(24f)))
|
||
{
|
||
_selectedTable = -1;
|
||
}
|
||
|
||
GUILayout.FlexibleSpace();
|
||
if (GUILayout.Button(_addIcon, EditorStyles.iconButton, GUILayout.Width(28f), GUILayout.Height(24f)))
|
||
{
|
||
CreateMissingDefaultTables();
|
||
}
|
||
}
|
||
|
||
GUILayout.EndArea();
|
||
}
|
||
|
||
private void DrawTableButton(int index, SerializedProperty table)
|
||
{
|
||
SerializedProperty nameProperty = table.FindPropertyRelative(DeviceNamePropertyName);
|
||
SerializedProperty iconProperty = table.FindPropertyRelative(PlatformIconPropertyName);
|
||
string tableName = string.IsNullOrWhiteSpace(nameProperty.stringValue) ? "Table " + (index + 1) : nameProperty.stringValue;
|
||
bool selected = _selectedTable == index;
|
||
GUIStyle style = selected ? _selectedTableStyle : _tableStyle;
|
||
|
||
Rect rect = GUILayoutUtility.GetRect(1f, 44f, GUILayout.ExpandWidth(true));
|
||
if (GUI.Button(rect, GUIContent.none, style))
|
||
{
|
||
_selectedTable = index;
|
||
GUI.FocusControl(null);
|
||
}
|
||
|
||
Rect iconRect = new Rect(rect.x + 8f, rect.y + 6f, 32f, 32f);
|
||
DrawSprite(iconRect, iconProperty.objectReferenceValue as Sprite);
|
||
GUI.Label(new Rect(iconRect.xMax + 8f, rect.y + 6f, rect.width - 76f, 18f), tableName, EditorStyles.boldLabel);
|
||
GUI.Label(new Rect(iconRect.xMax + 8f, rect.y + 24f, rect.width - 76f, 16f), EntryCountLabel(table), EditorStyles.miniLabel);
|
||
|
||
Rect deleteRect = new Rect(rect.xMax - 28f, rect.y + 10f, 22f, 22f);
|
||
if (GUI.Button(deleteRect, _removeIcon, EditorStyles.iconButton))
|
||
{
|
||
RemoveTable(index);
|
||
GUIUtility.ExitGUI();
|
||
}
|
||
}
|
||
|
||
private void DrawMainPanel(Rect rect)
|
||
{
|
||
GUILayout.BeginArea(new Rect(rect.x + 18f, rect.y + 14f, rect.width - 36f, rect.height - 22f));
|
||
if (_selectedTable < 0 || _selectedTable >= _tablesProperty.arraySize)
|
||
{
|
||
DrawSettings();
|
||
}
|
||
else
|
||
{
|
||
DrawTable(_selectedTable);
|
||
}
|
||
|
||
GUILayout.EndArea();
|
||
}
|
||
|
||
private void DrawSettings()
|
||
{
|
||
GUILayout.Label("Database", _headerStyle);
|
||
EditorGUILayout.Space(8f);
|
||
EditorGUILayout.PropertyField(_placeholderSpriteProperty, GUIContent.none);
|
||
Rect previewRect = GUILayoutUtility.GetRect(PreviewSize, PreviewSize, GUILayout.Width(PreviewSize), GUILayout.Height(PreviewSize));
|
||
DrawSprite(previewRect, _placeholderSpriteProperty.objectReferenceValue as Sprite);
|
||
}
|
||
|
||
private void DrawTable(int tableIndex)
|
||
{
|
||
SerializedProperty table = _tablesProperty.GetArrayElementAtIndex(tableIndex);
|
||
SerializedProperty nameProperty = table.FindPropertyRelative(DeviceNamePropertyName);
|
||
SerializedProperty sheetProperty = table.FindPropertyRelative(SpriteSheetPropertyName);
|
||
SerializedProperty iconProperty = table.FindPropertyRelative(PlatformIconPropertyName);
|
||
SerializedProperty entriesProperty = table.FindPropertyRelative(EntriesPropertyName);
|
||
|
||
using (new EditorGUILayout.HorizontalScope())
|
||
{
|
||
DrawSprite(GUILayoutUtility.GetRect(52f, 52f, GUILayout.Width(52f), GUILayout.Height(52f)), iconProperty.objectReferenceValue as Sprite);
|
||
using (new EditorGUILayout.VerticalScope())
|
||
{
|
||
nameProperty.stringValue = EditorGUILayout.TextField(nameProperty.stringValue, _headerStyle);
|
||
using (new EditorGUILayout.HorizontalScope())
|
||
{
|
||
EditorGUILayout.PropertyField(iconProperty, GUIContent.none, GUILayout.MinWidth(160f));
|
||
EditorGUILayout.PropertyField(sheetProperty, GUIContent.none, GUILayout.MinWidth(160f));
|
||
if (GUILayout.Button(_addIcon, EditorStyles.iconButton, GUILayout.Width(28f), GUILayout.Height(22f)))
|
||
{
|
||
AddEntry(entriesProperty);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
EditorGUILayout.Space(12f);
|
||
_entryScroll = EditorGUILayout.BeginScrollView(_entryScroll);
|
||
for (int i = 0; i < entriesProperty.arraySize; i++)
|
||
{
|
||
SerializedProperty entry = entriesProperty.GetArrayElementAtIndex(i);
|
||
if (!EntryMatches(entry))
|
||
{
|
||
continue;
|
||
}
|
||
|
||
DrawEntry(entriesProperty, i, entry);
|
||
}
|
||
|
||
EditorGUILayout.EndScrollView();
|
||
}
|
||
|
||
private void DrawEntry(SerializedProperty entriesProperty, int index, SerializedProperty entry)
|
||
{
|
||
SerializedProperty spriteProperty = entry.FindPropertyRelative(EntrySpritePropertyName);
|
||
SerializedProperty actionProperty = entry.FindPropertyRelative(EntryActionPropertyName);
|
||
float spriteHeight = EditorGUI.GetPropertyHeight(spriteProperty, GUIContent.none, true);
|
||
float actionHeight = EditorGUI.GetPropertyHeight(actionProperty, GUIContent.none, true);
|
||
float contentHeight = Mathf.Max(PreviewSize, spriteHeight + actionHeight + 8f);
|
||
Rect rect = GUILayoutUtility.GetRect(1f, Mathf.Max(EntryMinHeight, contentHeight + 16f), GUILayout.ExpandWidth(true));
|
||
GUI.Box(rect, GUIContent.none, _entryCardStyle);
|
||
|
||
Rect previewRect = new Rect(rect.x + 8f, rect.y + 8f, PreviewSize, PreviewSize);
|
||
DrawSprite(previewRect, spriteProperty.objectReferenceValue as Sprite);
|
||
|
||
Rect removeRect = new Rect(rect.xMax - 32f, rect.y + 8f, 24f, 24f);
|
||
float fieldX = previewRect.xMax + 12f;
|
||
float fieldWidth = Mathf.Max(120f, removeRect.x - fieldX - 10f);
|
||
Rect spriteRect = new Rect(fieldX, rect.y + 8f, fieldWidth, spriteHeight);
|
||
Rect actionRect = new Rect(fieldX, spriteRect.yMax + 8f, fieldWidth, actionHeight);
|
||
EditorGUI.PropertyField(spriteRect, spriteProperty, GUIContent.none, true);
|
||
EditorGUI.PropertyField(actionRect, actionProperty, GUIContent.none, true);
|
||
|
||
if (GUI.Button(removeRect, _removeIcon, EditorStyles.iconButton))
|
||
{
|
||
entriesProperty.DeleteArrayElementAtIndex(index);
|
||
MarkDirty();
|
||
GUIUtility.ExitGUI();
|
||
}
|
||
}
|
||
|
||
private bool EntryMatches(SerializedProperty entry)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(_search))
|
||
{
|
||
return true;
|
||
}
|
||
|
||
SerializedProperty spriteProperty = entry.FindPropertyRelative(EntrySpritePropertyName);
|
||
SerializedProperty actionProperty = entry.FindPropertyRelative(EntryActionPropertyName);
|
||
string search = _search.Trim();
|
||
Sprite sprite = spriteProperty.objectReferenceValue as Sprite;
|
||
Object actionObject = actionProperty.objectReferenceValue;
|
||
return Contains(sprite != null ? sprite.name : string.Empty, search)
|
||
|| Contains(actionObject != null ? actionObject.name : string.Empty, search);
|
||
}
|
||
|
||
private static bool Contains(string value, string search)
|
||
{
|
||
return !string.IsNullOrEmpty(value) && value.IndexOf(search, System.StringComparison.OrdinalIgnoreCase) >= 0;
|
||
}
|
||
|
||
private void AddEntry(SerializedProperty entriesProperty)
|
||
{
|
||
int index = entriesProperty.arraySize;
|
||
entriesProperty.InsertArrayElementAtIndex(index);
|
||
SerializedProperty entry = entriesProperty.GetArrayElementAtIndex(index);
|
||
entry.FindPropertyRelative(EntrySpritePropertyName).objectReferenceValue = null;
|
||
entry.FindPropertyRelative(EntryActionPropertyName).objectReferenceValue = null;
|
||
MarkDirty();
|
||
}
|
||
|
||
private void AddTable(string tableName)
|
||
{
|
||
int index = _tablesProperty.arraySize;
|
||
_tablesProperty.InsertArrayElementAtIndex(index);
|
||
SerializedProperty table = _tablesProperty.GetArrayElementAtIndex(index);
|
||
table.FindPropertyRelative(DeviceNamePropertyName).stringValue = tableName;
|
||
table.FindPropertyRelative(SpriteSheetPropertyName).objectReferenceValue = null;
|
||
table.FindPropertyRelative(PlatformIconPropertyName).objectReferenceValue = null;
|
||
table.FindPropertyRelative(EntriesPropertyName).ClearArray();
|
||
_selectedTable = index;
|
||
MarkDirty();
|
||
}
|
||
|
||
private void RemoveTable(int index)
|
||
{
|
||
_tablesProperty.DeleteArrayElementAtIndex(index);
|
||
_selectedTable = Mathf.Clamp(_selectedTable, -1, _tablesProperty.arraySize - 1);
|
||
MarkDirty();
|
||
}
|
||
|
||
private void CreateMissingDefaultTables()
|
||
{
|
||
for (int i = 0; i < DefaultTableNames.Length; i++)
|
||
{
|
||
if (!HasTable(DefaultTableNames[i]))
|
||
{
|
||
AddTable(DefaultTableNames[i]);
|
||
}
|
||
}
|
||
}
|
||
|
||
private bool HasTable(string tableName)
|
||
{
|
||
for (int i = 0; i < _tablesProperty.arraySize; i++)
|
||
{
|
||
SerializedProperty table = _tablesProperty.GetArrayElementAtIndex(i);
|
||
if (string.Equals(table.FindPropertyRelative(DeviceNamePropertyName).stringValue, tableName, System.StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
private string NextTableName()
|
||
{
|
||
for (int i = 0; i < DefaultTableNames.Length; i++)
|
||
{
|
||
if (!HasTable(DefaultTableNames[i]))
|
||
{
|
||
return DefaultTableNames[i];
|
||
}
|
||
}
|
||
|
||
return "Table " + (_tablesProperty.arraySize + 1);
|
||
}
|
||
|
||
private static string EntryCountLabel(SerializedProperty table)
|
||
{
|
||
SerializedProperty entries = table.FindPropertyRelative(EntriesPropertyName);
|
||
return entries.arraySize + " glyphs";
|
||
}
|
||
|
||
private void DrawObjectName()
|
||
{
|
||
using (new EditorGUI.DisabledScope(true))
|
||
{
|
||
EditorGUILayout.ObjectField(_database, typeof(InputGlyphDatabase), false, GUILayout.Width(260f));
|
||
}
|
||
}
|
||
|
||
private static void DrawSprite(Rect rect, Sprite sprite)
|
||
{
|
||
if (sprite == null)
|
||
{
|
||
GUI.Box(rect, GUIContent.none, EditorStyles.helpBox);
|
||
return;
|
||
}
|
||
|
||
Texture2D texture = AssetPreview.GetAssetPreview(sprite) ?? AssetPreview.GetMiniThumbnail(sprite);
|
||
if (texture != null)
|
||
{
|
||
GUI.DrawTexture(rect, texture, ScaleMode.ScaleToFit, true);
|
||
}
|
||
}
|
||
|
||
private void Save()
|
||
{
|
||
_serializedDatabase.ApplyModifiedProperties();
|
||
MarkDirty();
|
||
AssetDatabase.SaveAssetIfDirty(_database);
|
||
}
|
||
|
||
private void MarkDirty()
|
||
{
|
||
_database.EditorRefreshCache();
|
||
EditorUtility.SetDirty(_database);
|
||
}
|
||
|
||
private void BuildStyles()
|
||
{
|
||
_addIcon ??= EditorGUIUtility.IconContent("Toolbar Plus");
|
||
_removeIcon ??= EditorGUIUtility.IconContent("TreeEditor.Trash");
|
||
_saveIcon ??= EditorGUIUtility.IconContent("SaveActive");
|
||
_refreshIcon ??= EditorGUIUtility.IconContent("Refresh");
|
||
_searchIcon ??= EditorGUIUtility.IconContent("Search Icon");
|
||
_settingsIcon ??= EditorGUIUtility.IconContent("d__Popup");
|
||
|
||
_sidebarStyle ??= new GUIStyle(EditorStyles.helpBox)
|
||
{
|
||
padding = new RectOffset(8, 8, 8, 8)
|
||
};
|
||
|
||
_tableStyle ??= new GUIStyle(EditorStyles.toolbarButton)
|
||
{
|
||
alignment = TextAnchor.MiddleLeft,
|
||
fixedHeight = 44f
|
||
};
|
||
|
||
_selectedTableStyle ??= new GUIStyle(_tableStyle)
|
||
{
|
||
normal = { background = Texture2D.grayTexture },
|
||
fontStyle = FontStyle.Bold
|
||
};
|
||
|
||
_entryCardStyle ??= new GUIStyle(EditorStyles.helpBox)
|
||
{
|
||
padding = new RectOffset(8, 8, 8, 8)
|
||
};
|
||
|
||
_headerStyle ??= new GUIStyle(EditorStyles.boldLabel)
|
||
{
|
||
fontSize = 15
|
||
};
|
||
}
|
||
}
|