A game about forced loneliness, made by TACStudios
1#if UNITY_INPUT_SYSTEM_ENABLE_UI && UNITY_EDITOR
2using System;
3using System.Linq;
4using UnityEditor;
5using UnityEditor.EventSystems;
6using UnityEngine.InputSystem.Editor;
7
8////TODO: add button to automatically set up gamepad mouse cursor support
9
10namespace UnityEngine.InputSystem.UI.Editor
11{
12 [CustomEditor(typeof(InputSystemUIInputModule))]
13 [InitializeOnLoad]
14 internal class InputSystemUIInputModuleEditor : UnityEditor.Editor
15 {
16 static InputSystemUIInputModuleEditor()
17 {
18#if UNITY_6000_0_OR_NEWER && ENABLE_INPUT_SYSTEM
19 InputModuleComponentFactory.SetInputModuleComponentOverride(
20 go => ObjectFactory.AddComponent<InputSystemUIInputModule>(go));
21#endif
22 }
23
24 private static InputActionReference GetActionReferenceFromAssets(InputActionReference[] actions, params string[] actionNames)
25 {
26 foreach (var actionName in actionNames)
27 {
28 foreach (var action in actions)
29 {
30 if (action.action != null && string.Compare(action.action.name, actionName, StringComparison.InvariantCultureIgnoreCase) == 0)
31 return action;
32 }
33 }
34 return null;
35 }
36
37 private static InputActionReference[] GetAllAssetReferencesFromAssetDatabase(InputActionAsset actions)
38 {
39 if (actions == null)
40 return null;
41
42 var path = AssetDatabase.GetAssetPath(actions);
43 var assets = AssetDatabase.LoadAllAssetsAtPath(path);
44 return assets.Where(asset => asset is InputActionReference)
45 .Cast<InputActionReference>()
46 .OrderBy(x => x.name)
47 .ToArray();
48 }
49
50 private static readonly string[] s_ActionNames =
51 {
52 "Point",
53 "LeftClick",
54 "MiddleClick",
55 "RightClick",
56 "ScrollWheel",
57 "Move",
58 "Submit",
59 "Cancel",
60 "TrackedDevicePosition",
61 "TrackedDeviceOrientation"
62 };
63
64 private static readonly string[] s_ActionNiceNames =
65 {
66 "Point",
67 "Left Click",
68 "Middle Click",
69 "Right Click",
70 "Scroll Wheel",
71 "Move",
72 "Submit",
73 "Cancel",
74 "Tracked Position",
75 "Tracked Orientation"
76 };
77
78 private SerializedProperty[] m_ReferenceProperties;
79 private SerializedProperty m_ActionsAsset;
80 private InputActionReference[] m_AvailableActionReferencesInAssetDatabase;
81 private string[] m_AvailableActionsInAssetNames;
82 private bool m_AdvancedFoldoutState;
83
84 private string MakeActionReferenceNameUsableInGenericMenu(string name)
85 {
86 // Ugly hack: GenericMenu interprets "/" as a submenu path. But luckily, "/" is not the only slash we have in Unicode.
87 return name.Replace("/", "\uFF0F");
88 }
89
90 public void OnEnable()
91 {
92 var numActions = s_ActionNames.Length;
93 m_ReferenceProperties = new SerializedProperty[numActions];
94 for (var i = 0; i < numActions; i++)
95 m_ReferenceProperties[i] = serializedObject.FindProperty($"m_{s_ActionNames[i]}Action");
96
97 m_ActionsAsset = serializedObject.FindProperty("m_ActionsAsset");
98 m_AvailableActionReferencesInAssetDatabase = GetAllAssetReferencesFromAssetDatabase(m_ActionsAsset.objectReferenceValue as InputActionAsset);
99 m_AvailableActionsInAssetNames = new[] { "None" }
100 .Concat(m_AvailableActionReferencesInAssetDatabase?.Select(x => MakeActionReferenceNameUsableInGenericMenu(x.name)) ?? new string[0]).ToArray();
101 }
102
103 public void OnDisable()
104 {
105 new InputComponentEditorAnalytic(InputSystemComponent.InputSystemUIInputModule).Send();
106 }
107
108 public static void ReassignActions(InputSystemUIInputModule module, InputActionAsset action)
109 {
110 module.actionsAsset = action;
111 var assets = GetAllAssetReferencesFromAssetDatabase(action);
112 if (assets != null)
113 {
114 module.point = GetActionReferenceFromAssets(assets, module.point?.action?.name, "Point", "MousePosition", "Mouse Position");
115 module.leftClick = GetActionReferenceFromAssets(assets, module.leftClick?.action?.name, "Click", "LeftClick", "Left Click");
116 module.rightClick = GetActionReferenceFromAssets(assets, module.rightClick?.action?.name, "RightClick", "Right Click", "ContextClick", "Context Click", "ContextMenu", "Context Menu");
117 module.middleClick = GetActionReferenceFromAssets(assets, module.middleClick?.action?.name, "MiddleClick", "Middle Click");
118 module.scrollWheel = GetActionReferenceFromAssets(assets, module.scrollWheel?.action?.name, "ScrollWheel", "Scroll Wheel", "Scroll", "Wheel");
119 module.move = GetActionReferenceFromAssets(assets, module.move?.action?.name, "Navigate", "Move");
120 module.submit = GetActionReferenceFromAssets(assets, module.submit?.action?.name, "Submit");
121 module.cancel = GetActionReferenceFromAssets(assets, module.cancel?.action?.name, "Cancel", "Esc", "Escape");
122 module.trackedDevicePosition = GetActionReferenceFromAssets(assets, module.trackedDevicePosition?.action?.name, "TrackedDevicePosition", "Position");
123 module.trackedDeviceOrientation = GetActionReferenceFromAssets(assets, module.trackedDeviceOrientation?.action?.name, "TrackedDeviceOrientation", "Orientation");
124 }
125 }
126
127 public override void OnInspectorGUI()
128 {
129 base.OnInspectorGUI();
130
131 EditorGUI.BeginChangeCheck();
132 EditorGUILayout.PropertyField(m_ActionsAsset);
133 if (EditorGUI.EndChangeCheck())
134 {
135 var actions = m_ActionsAsset.objectReferenceValue as InputActionAsset;
136 if (actions != null)
137 {
138 serializedObject.ApplyModifiedProperties();
139
140 ReassignActions(target as InputSystemUIInputModule, actions);
141
142 serializedObject.Update();
143 }
144
145 // reinitialize action types
146 OnEnable();
147 }
148
149 var numActions = s_ActionNames.Length;
150 if ((m_AvailableActionReferencesInAssetDatabase != null && m_AvailableActionReferencesInAssetDatabase.Length > 0) || m_ActionsAsset.objectReferenceValue == null)
151 {
152 for (var i = 0; i < numActions; i++)
153 {
154 // find the input action reference from the asset that matches the input action reference from the
155 // InputSystemUIInputModule that is currently selected. Note we can't use reference equality of the
156 // two InputActionReference objects here because in ReassignActions above, we create new instances
157 // every time it runs.
158 var index = IndexOfInputActionInAsset(
159 ((InputActionReference)m_ReferenceProperties[i]?.objectReferenceValue)?.action);
160
161 EditorGUI.BeginChangeCheck();
162 index = EditorGUILayout.Popup(s_ActionNiceNames[i], index, m_AvailableActionsInAssetNames);
163
164 if (EditorGUI.EndChangeCheck())
165 m_ReferenceProperties[i].objectReferenceValue =
166 index > 0 ? m_AvailableActionReferencesInAssetDatabase[index - 1] : null;
167 }
168 }
169 else
170 {
171 // Somehow we have an asset but no asset references from the database, pull out references manually and show them in read only UI
172 EditorGUILayout.HelpBox("Showing fields as read-only because current action asset seems to be created by a script and assigned programmatically.", MessageType.Info);
173
174 EditorGUI.BeginDisabledGroup(true);
175 for (var i = 0; i < numActions; i++)
176 {
177 var retrievedName = "None";
178 if (m_ReferenceProperties[i].objectReferenceValue != null &&
179 (m_ReferenceProperties[i].objectReferenceValue is InputActionReference reference))
180 retrievedName = MakeActionReferenceNameUsableInGenericMenu(reference.ToDisplayName());
181
182 EditorGUILayout.Popup(s_ActionNiceNames[i], 0, new[] {retrievedName});
183 }
184 EditorGUI.EndDisabledGroup();
185 }
186
187 m_AdvancedFoldoutState = EditorGUILayout.Foldout(m_AdvancedFoldoutState, new GUIContent("Advanced"), true);
188 if (m_AdvancedFoldoutState)
189 EditorGUILayout.PropertyField(serializedObject.FindProperty("m_CursorLockBehavior"),
190 EditorGUIUtility.TrTextContent("Cursor Lock Behavior",
191 $"Controls the origin point of UI raycasts when the cursor is locked. {InputSystemUIInputModule.CursorLockBehavior.OutsideScreen} " +
192 $"is the default behavior and will force the raycast to miss all objects. {InputSystemUIInputModule.CursorLockBehavior.ScreenCenter} " +
193 $"will cast the ray from the center of the screen."));
194
195 if (GUI.changed)
196 serializedObject.ApplyModifiedProperties();
197 }
198
199 private int IndexOfInputActionInAsset(InputAction inputAction)
200 {
201 // return 0 instead of -1 here because the zero-th index refers to the 'None' binding.
202 if (inputAction == null)
203 return 0;
204 if (m_AvailableActionReferencesInAssetDatabase == null)
205 return 0;
206
207 var index = 0;
208 for (var j = 0; j < m_AvailableActionReferencesInAssetDatabase.Length; j++)
209 {
210 if (m_AvailableActionReferencesInAssetDatabase[j].action != null &&
211 m_AvailableActionReferencesInAssetDatabase[j].action == inputAction)
212 {
213 index = j + 1;
214 break;
215 }
216 }
217
218 return index;
219 }
220 }
221}
222#endif