InputReceiverEditor.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. using System;
  2. using System.Linq;
  3. using System.Collections.Generic;
  4. using UnityEditor;
  5. using UnityEngine;
  6. using UnityEngine.InputSystem;
  7. using UnityEngine.InputSystem.Users;
  8. using UnityEngine.InputSystem.Utilities;
  9. namespace Unity.RenderStreaming.InputSystem.Editor
  10. {
  11. [CustomEditor(typeof(InputReceiver))]
  12. internal class InputReceiverEditor : UnityEditor.Editor
  13. {
  14. public void OnEnable()
  15. {
  16. InputUser.onChange += OnUserChange;
  17. m_ActionsProperty = serializedObject.FindProperty("m_Actions");
  18. m_ActionEventsProperty = serializedObject.FindProperty("m_ActionEvents");
  19. m_DefaultActionMapProperty = serializedObject.FindProperty("m_DefaultActionMap");
  20. }
  21. private void OnUserChange(InputUser user, InputUserChange change, InputDevice device)
  22. {
  23. Repaint();
  24. }
  25. public override void OnInspectorGUI()
  26. {
  27. EditorGUI.BeginChangeCheck();
  28. EditorGUILayout.PropertyField(serializedObject.FindProperty("local"));
  29. EditorGUILayout.PropertyField(serializedObject.FindProperty("label"));
  30. // Action config section.
  31. EditorGUI.BeginChangeCheck();
  32. EditorGUILayout.PropertyField(m_ActionsProperty);
  33. if (EditorGUI.EndChangeCheck() || !m_ActionAssetInitialized)
  34. OnActionAssetChange();
  35. ++EditorGUI.indentLevel;
  36. if (m_ActionMapOptions != null && m_ActionMapOptions.Length > 0)
  37. {
  38. // Default action map picker.
  39. var selected = EditorGUILayout.Popup(m_DefaultActionMapText, m_SelectedDefaultActionMap,
  40. m_ActionMapOptions);
  41. if (selected != m_SelectedDefaultActionMap)
  42. {
  43. if (selected == 0)
  44. {
  45. m_DefaultActionMapProperty.stringValue = null;
  46. }
  47. else
  48. {
  49. // Use ID rather than name.
  50. var asset = (InputActionAsset)m_ActionsProperty.objectReferenceValue;
  51. var actionMap = asset.FindActionMap(m_ActionMapOptions[selected].text);
  52. if (actionMap != null)
  53. m_DefaultActionMapProperty.stringValue = actionMap.id.ToString();
  54. }
  55. m_SelectedDefaultActionMap = selected;
  56. }
  57. }
  58. --EditorGUI.indentLevel;
  59. // Notifications/event section.
  60. m_EventsGroupUnfolded = EditorGUILayout.Foldout(m_EventsGroupUnfolded, m_EventsGroupText, toggleOnLabelClick: true);
  61. if (m_EventsGroupUnfolded)
  62. {
  63. // Action events. Group by action map.
  64. if (m_ActionNames != null)
  65. {
  66. using (new EditorGUI.IndentLevelScope())
  67. {
  68. for (var n = 0; n < m_NumActionMaps; ++n)
  69. {
  70. m_ActionMapEventsUnfolded[n] = EditorGUILayout.Foldout(m_ActionMapEventsUnfolded[n],
  71. m_ActionMapNames[n], toggleOnLabelClick: true);
  72. using (new EditorGUI.IndentLevelScope())
  73. {
  74. if (m_ActionMapEventsUnfolded[n])
  75. {
  76. for (var i = 0; i < m_ActionNames.Length; ++i)
  77. {
  78. if (m_ActionMapIndices[i] != n)
  79. continue;
  80. EditorGUILayout.PropertyField(m_ActionEventsProperty.GetArrayElementAtIndex(i), m_ActionNames[i]);
  81. }
  82. }
  83. }
  84. }
  85. }
  86. }
  87. }
  88. if (EditorGUI.EndChangeCheck())
  89. serializedObject.ApplyModifiedProperties();
  90. // Debug UI.
  91. if (EditorApplication.isPlaying)
  92. DoDebugUI();
  93. }
  94. private void DoDebugUI()
  95. {
  96. var playerInput = (InputReceiver)target;
  97. if (!playerInput.user.valid)
  98. return;
  99. ////TODO: show actions when they happen
  100. var user = playerInput.user.index.ToString();
  101. //var controlScheme = playerInput.user.controlScheme?.name;
  102. var devices = playerInput.user.pairedDevices.Select(_ => _.ToString()).OrderBy(x => x).ToArray();
  103. EditorGUILayout.Space();
  104. EditorGUILayout.LabelField(m_DebugText, EditorStyles.boldLabel);
  105. ++EditorGUI.indentLevel;
  106. EditorGUILayout.LabelField("User", $"#{user}");
  107. //EditorGUILayout.LabelField("Control Scheme", controlScheme);
  108. m_DevicesGroupUnfolded = EditorGUILayout.Foldout(
  109. m_DevicesGroupUnfolded, m_DevicesGroupText, toggleOnLabelClick: true);
  110. if (m_DevicesGroupUnfolded)
  111. {
  112. using (new EditorGUI.IndentLevelScope())
  113. {
  114. foreach (var device in devices)
  115. {
  116. EditorGUILayout.LabelField(device.ToString());
  117. // todo: InputDeviceDebuggerWindow is an internal class
  118. // InputDeviceDebuggerWindow.CreateOrShowExisting(device);
  119. }
  120. }
  121. }
  122. --EditorGUI.indentLevel;
  123. }
  124. private void OnActionAssetChange()
  125. {
  126. serializedObject.ApplyModifiedProperties();
  127. m_ActionAssetInitialized = true;
  128. var playerInput = (InputReceiver)target;
  129. var asset = (InputActionAsset)m_ActionsProperty.objectReferenceValue;
  130. if (asset == null)
  131. {
  132. m_ActionMapOptions = null;
  133. return;
  134. }
  135. var newActionNames = new List<GUIContent>();
  136. var newActionEvents = new List<PlayerInput.ActionEvent>();
  137. var newActionMapIndices = new List<int>();
  138. void AddEntry(InputAction action, PlayerInput.ActionEvent actionEvent)
  139. {
  140. newActionNames.Add(new GUIContent(action.name));
  141. newActionEvents.Add(actionEvent);
  142. var actionMapIndex = asset.actionMaps.IndexOfReference(action.actionMap);
  143. newActionMapIndices.Add(actionMapIndex);
  144. if (actionMapIndex >= m_NumActionMaps)
  145. m_NumActionMaps = actionMapIndex + 1;
  146. ArrayHelpers.PutAtIfNotSet(ref m_ActionMapNames, actionMapIndex,
  147. () => new GUIContent(action.actionMap.name));
  148. }
  149. // Bring over any action events that we already have and that are still in the asset.
  150. var oldActionEvents = playerInput.m_ActionEvents;
  151. if (oldActionEvents != null)
  152. {
  153. foreach (var entry in oldActionEvents)
  154. {
  155. var guid = entry.actionId;
  156. var action = asset.FindAction(guid);
  157. if (action != null)
  158. AddEntry(action, entry);
  159. }
  160. }
  161. // Add any new actions.
  162. foreach (var action in asset)
  163. {
  164. // Skip if it was already in there.
  165. if (oldActionEvents != null && oldActionEvents.Any(x => x.actionId == action.id.ToString()))
  166. continue;
  167. ////FIXME: adds bindings to the name
  168. AddEntry(action, new PlayerInput.ActionEvent(action.id, action.ToString()));
  169. }
  170. m_ActionNames = newActionNames.ToArray();
  171. m_ActionMapIndices = newActionMapIndices.ToArray();
  172. Array.Resize(ref m_ActionMapEventsUnfolded, m_NumActionMaps);
  173. playerInput.m_ActionEvents = newActionEvents.ToArray();
  174. // Read out action maps.
  175. var selectedDefaultActionMap = !string.IsNullOrEmpty(playerInput.defaultActionMap)
  176. ? asset.FindActionMap(playerInput.defaultActionMap)
  177. : null;
  178. m_SelectedDefaultActionMap = asset.actionMaps.Count > 0 ? 1 : 0;
  179. var actionMaps = asset.actionMaps;
  180. m_ActionMapOptions = new GUIContent[actionMaps.Count + 1];
  181. m_ActionMapOptions[0] = new GUIContent(EditorGUIUtility.TrTextContent("<None>"));
  182. ////TODO: sort alphabetically
  183. for (var i = 0; i < actionMaps.Count; ++i)
  184. {
  185. var actionMap = actionMaps[i];
  186. m_ActionMapOptions[i + 1] = new GUIContent(actionMap.name);
  187. if (selectedDefaultActionMap != null && actionMap == selectedDefaultActionMap)
  188. m_SelectedDefaultActionMap = i + 1;
  189. }
  190. if (m_SelectedDefaultActionMap <= 0)
  191. playerInput.defaultActionMap = null;
  192. else
  193. playerInput.defaultActionMap = m_ActionMapOptions[m_SelectedDefaultActionMap].text;
  194. serializedObject.Update();
  195. }
  196. [SerializeField] private bool m_EventsGroupUnfolded;
  197. [SerializeField] private bool m_DevicesGroupUnfolded;
  198. [SerializeField] private bool[] m_ActionMapEventsUnfolded;
  199. [NonSerialized]
  200. private readonly GUIContent m_EventsGroupText =
  201. EditorGUIUtility.TrTextContent("Events", "UnityEvents triggered by the PlayerInput component");
  202. [NonSerialized]
  203. private readonly GUIContent m_DefaultActionMapText =
  204. EditorGUIUtility.TrTextContent("Default Map", "Action map to enable by default. If not set, no actions will be enabled by default.");
  205. [NonSerialized]
  206. private readonly GUIContent m_DebugText = EditorGUIUtility.TrTextContent("Debug");
  207. [NonSerialized]
  208. private readonly GUIContent m_DevicesGroupText =
  209. EditorGUIUtility.TrTextContent("Paired Devices", "InputDevices paired by the PlayerInput component");
  210. [NonSerialized] private GUIContent[] m_ActionNames;
  211. [NonSerialized] private GUIContent[] m_ActionMapNames;
  212. [NonSerialized] private int[] m_ActionMapIndices;
  213. [NonSerialized] private int m_NumActionMaps;
  214. [NonSerialized] private SerializedProperty m_ActionEventsProperty;
  215. [NonSerialized] private int m_SelectedDefaultActionMap;
  216. [NonSerialized] private GUIContent[] m_ActionMapOptions;
  217. [NonSerialized] private SerializedProperty m_ActionsProperty;
  218. [NonSerialized] private SerializedProperty m_DefaultActionMapProperty;
  219. [NonSerialized] private bool m_ActionAssetInitialized;
  220. }
  221. }