238 lines
8.1 KiB
C#
238 lines
8.1 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Reflection;
|
|
using SepCore.InputModule.Runtime;
|
|
using UnityEditor;
|
|
using UnityEngine;
|
|
using UnityEngine.InputSystem;
|
|
|
|
namespace SepCore.InputModule.Editor
|
|
{
|
|
public sealed class InputModuleDebugger : EditorWindow
|
|
{
|
|
private static readonly FieldInfo ContextStackField = typeof(InputModuleComponent).GetField("_contextStack", BindingFlags.Instance | BindingFlags.NonPublic);
|
|
private static readonly FieldInfo ActionMapsField = typeof(InputModuleComponent).GetField("_actionMaps", BindingFlags.Instance | BindingFlags.NonPublic);
|
|
private static readonly PropertyInfo StackItemsProperty = typeof(InputContextStack).GetProperty("Items", BindingFlags.Instance | BindingFlags.Public);
|
|
private static readonly PropertyInfo StackCountProperty = typeof(InputContextStack).GetProperty("Count", BindingFlags.Instance | BindingFlags.Public);
|
|
|
|
private InputModuleComponent _target;
|
|
private Vector2 _scrollPosition;
|
|
private readonly Dictionary<InputContextId, bool> _mapFoldouts = new Dictionary<InputContextId, bool>();
|
|
private double _nextRefresh;
|
|
private const double RefreshInterval = 0.1;
|
|
|
|
[MenuItem("Window/Input Module Debugger")]
|
|
public static void ShowWindow()
|
|
{
|
|
GetWindow<InputModuleDebugger>("Input Module");
|
|
}
|
|
|
|
private void OnGUI()
|
|
{
|
|
double now = EditorApplication.timeSinceStartup;
|
|
if (now >= _nextRefresh)
|
|
{
|
|
_nextRefresh = now + RefreshInterval;
|
|
Repaint();
|
|
}
|
|
|
|
_scrollPosition = EditorGUILayout.BeginScrollView(_scrollPosition);
|
|
|
|
DrawHeader();
|
|
DrawRuntimeStatus();
|
|
DrawContextStack();
|
|
DrawActionMaps();
|
|
|
|
EditorGUILayout.EndScrollView();
|
|
}
|
|
|
|
private void DrawHeader()
|
|
{
|
|
EditorGUILayout.Space(4);
|
|
EditorGUILayout.LabelField("Input Module Debugger", EditorStyles.largeLabel);
|
|
EditorGUILayout.Space(4);
|
|
}
|
|
|
|
private void DrawRuntimeStatus()
|
|
{
|
|
if (_target == null)
|
|
{
|
|
_target = FindInputModuleComponent();
|
|
}
|
|
|
|
if (_target == null)
|
|
{
|
|
EditorGUILayout.HelpBox("No InputModuleComponent found in the current scene.", MessageType.Warning);
|
|
return;
|
|
}
|
|
|
|
EditorGUILayout.LabelField("Runtime Status", EditorStyles.boldLabel);
|
|
|
|
EditorGUI.BeginDisabledGroup(true);
|
|
EditorGUILayout.Toggle("Initialized", _target.IsInitialized);
|
|
EditorGUILayout.EnumPopup("Device Kind", _target.CurrentDeviceKind);
|
|
EditorGUILayout.EnumPopup("Current Context", _target.CurrentContext);
|
|
EditorGUILayout.Toggle("Rebinding", _target.IsRebinding);
|
|
EditorGUI.EndDisabledGroup();
|
|
|
|
EditorGUILayout.Space(8);
|
|
}
|
|
|
|
private void DrawContextStack()
|
|
{
|
|
if (_target == null || !_target.IsInitialized)
|
|
{
|
|
return;
|
|
}
|
|
|
|
object stack = ContextStackField?.GetValue(_target);
|
|
if (stack == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
int count = StackCountProperty != null ? (int)StackCountProperty.GetValue(stack) : 0;
|
|
IReadOnlyList<InputContextId> items = StackItemsProperty?.GetValue(stack) as IReadOnlyList<InputContextId>;
|
|
|
|
EditorGUILayout.LabelField($"Context Stack (depth: {count})", EditorStyles.boldLabel);
|
|
|
|
if (items == null || count == 0)
|
|
{
|
|
EditorGUILayout.LabelField(" (empty)", EditorStyles.miniLabel);
|
|
}
|
|
else
|
|
{
|
|
for (int i = 0; i < items.Count; i++)
|
|
{
|
|
bool isTop = i == items.Count - 1;
|
|
string prefix = isTop ? "▶" : " ";
|
|
GUIStyle style = isTop ? EditorStyles.label : EditorStyles.miniLabel;
|
|
EditorGUILayout.LabelField($" {prefix} [{i}] {items[i]}", style);
|
|
}
|
|
}
|
|
|
|
EditorGUILayout.Space(8);
|
|
}
|
|
|
|
private void DrawActionMaps()
|
|
{
|
|
if (_target == null || !_target.IsInitialized)
|
|
{
|
|
return;
|
|
}
|
|
|
|
object mapsObj = ActionMapsField?.GetValue(_target);
|
|
if (!(mapsObj is Dictionary<InputContextId, InputActionMap> maps))
|
|
{
|
|
return;
|
|
}
|
|
|
|
EditorGUILayout.LabelField("Action Maps", EditorStyles.boldLabel);
|
|
|
|
foreach (KeyValuePair<InputContextId, InputActionMap> pair in maps)
|
|
{
|
|
InputContextId contextId = pair.Key;
|
|
InputActionMap map = pair.Value;
|
|
|
|
bool enabled = map?.enabled ?? false;
|
|
bool isActive = contextId == InputContextId.Global || contextId == _target.CurrentContext;
|
|
|
|
Color previousColor = GUI.color;
|
|
if (enabled)
|
|
{
|
|
GUI.color = isActive ? Color.green : Color.yellow;
|
|
}
|
|
else
|
|
{
|
|
GUI.color = Color.gray;
|
|
}
|
|
|
|
if (!_mapFoldouts.ContainsKey(contextId))
|
|
{
|
|
_mapFoldouts[contextId] = false;
|
|
}
|
|
|
|
_mapFoldouts[contextId] = EditorGUILayout.Foldout(_mapFoldouts[contextId], $"{contextId} [{StateLabel(enabled, isActive)}]", true);
|
|
|
|
GUI.color = previousColor;
|
|
|
|
if (_mapFoldouts[contextId] && map != null)
|
|
{
|
|
EditorGUI.indentLevel++;
|
|
foreach (InputAction action in map.actions)
|
|
{
|
|
DrawAction(action);
|
|
}
|
|
EditorGUI.indentLevel--;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void DrawAction(InputAction action)
|
|
{
|
|
EditorGUILayout.BeginHorizontal();
|
|
EditorGUILayout.LabelField(action.name, GUILayout.Width(120));
|
|
|
|
if (action.bindings.Count > 0)
|
|
{
|
|
var parts = new System.Collections.Generic.List<string>(action.bindings.Count);
|
|
foreach (InputBinding binding in action.bindings)
|
|
{
|
|
string path = string.IsNullOrEmpty(binding.overridePath) ? binding.path : binding.overridePath;
|
|
bool hasOverride = !string.IsNullOrEmpty(binding.overridePath);
|
|
string display = string.IsNullOrEmpty(path) ? "(none)" : path;
|
|
if (hasOverride)
|
|
{
|
|
display += " *";
|
|
}
|
|
|
|
parts.Add(display);
|
|
}
|
|
|
|
bool anyOverride = false;
|
|
foreach (InputBinding binding in action.bindings)
|
|
{
|
|
if (!string.IsNullOrEmpty(binding.overridePath))
|
|
{
|
|
anyOverride = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
GUIStyle style = anyOverride ? EditorStyles.boldLabel : EditorStyles.miniLabel;
|
|
EditorGUILayout.LabelField(string.Join(" | ", parts), style);
|
|
}
|
|
else
|
|
{
|
|
EditorGUILayout.LabelField("(no bindings)", EditorStyles.miniLabel);
|
|
}
|
|
|
|
EditorGUILayout.EndHorizontal();
|
|
}
|
|
|
|
private static string StateLabel(bool enabled, bool isActive)
|
|
{
|
|
if (!enabled)
|
|
{
|
|
return "disabled";
|
|
}
|
|
|
|
return isActive ? "active" : "enabled";
|
|
}
|
|
|
|
private static InputModuleComponent FindInputModuleComponent()
|
|
{
|
|
if (Application.isPlaying)
|
|
{
|
|
InputModuleComponent found = UnityEngine.Object.FindObjectOfType<InputModuleComponent>();
|
|
if (found != null)
|
|
{
|
|
return found;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|
|
}
|