A game about forced loneliness, made by TACStudios
1#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
2using System;
3using System.Collections.Generic;
4using System.Linq;
5using UnityEditor;
6using UnityEngine.InputSystem.Utilities;
7
8namespace UnityEngine.InputSystem.Editor
9{
10 internal static class ControlSchemeCommands
11 {
12 private const string kAllControlSchemesName = "All Control Schemes";
13 private const string kNewControlSchemeName = "New Control Scheme";
14
15 public static Command AddNewControlScheme()
16 {
17 return (in InputActionsEditorState state) =>
18 {
19 state.m_Analytics?.RegisterControlSchemeEdit();
20 return state.With(selectedControlScheme: new InputControlScheme(
21 MakeUniqueControlSchemeName(state, kNewControlSchemeName)));
22 };
23 }
24
25 public static Command AddDeviceRequirement(InputControlScheme.DeviceRequirement requirement)
26 {
27 return (in InputActionsEditorState state) =>
28 {
29 state.m_Analytics?.RegisterControlSchemeEdit();
30 return state.With(selectedControlScheme: new InputControlScheme(state.selectedControlScheme.name,
31 state.selectedControlScheme.deviceRequirements.Append(requirement)));
32 };
33 }
34
35 public static Command RemoveDeviceRequirement(int selectedDeviceIndex)
36 {
37 return (in InputActionsEditorState state) =>
38 {
39 state.m_Analytics?.RegisterControlSchemeEdit();
40
41 var newDeviceIndex =
42 Mathf.Clamp(
43 selectedDeviceIndex <= state.selectedDeviceRequirementIndex
44 ? state.selectedDeviceRequirementIndex - 1
45 : state.selectedDeviceRequirementIndex, -1, state.selectedDeviceRequirementIndex);
46 return state.With(selectedControlScheme: new InputControlScheme(state.selectedControlScheme.name,
47 state.selectedControlScheme.deviceRequirements.Where((r, i) => i != selectedDeviceIndex)), selectedDeviceRequirementIndex: newDeviceIndex);
48 };
49 }
50
51 public static Command SaveControlScheme(string newControlSchemeName = "", bool updateExisting = false)
52 {
53 return (in InputActionsEditorState state) =>
54 {
55 var controlSchemeName = state.selectedControlScheme.name;
56
57 var controlSchemesArray = state.serializedObject.FindProperty(nameof(InputActionAsset.m_ControlSchemes));
58 var controlScheme = controlSchemesArray
59 .FirstOrDefault(sp => sp.FindPropertyRelative(nameof(InputControlScheme.m_Name)).stringValue == controlSchemeName);
60
61 var actionMaps = state.serializedObject.FindProperty(nameof(InputActionAsset.m_ActionMaps));
62
63 // If the control scheme is null, we're saving a new control scheme, otherwise editing an existing one
64 if (controlScheme == null && updateExisting)
65 throw new InvalidOperationException("Tried to update a non-existent control scheme.");
66
67 if (updateExisting == false)
68 {
69 controlSchemeName = MakeUniqueControlSchemeName(state, controlSchemeName);
70 controlSchemesArray.InsertArrayElementAtIndex(controlSchemesArray.arraySize);
71 controlScheme = controlSchemesArray.GetArrayElementAtIndex(controlSchemesArray.arraySize - 1);
72 }
73 // If we're renaming a control scheme, we need to update the bindings that use it and make a unique name
74 if (!string.IsNullOrEmpty(newControlSchemeName))
75 {
76 newControlSchemeName = MakeUniqueControlSchemeName(state, newControlSchemeName);
77 RenameBindingsControlSchemeHelper(controlScheme, actionMaps, controlSchemeName, newControlSchemeName);
78 }
79
80 controlScheme.FindPropertyRelative(nameof(InputControlScheme.m_Name)).stringValue = string.IsNullOrWhiteSpace(newControlSchemeName) ? controlSchemeName : newControlSchemeName;
81 controlScheme.FindPropertyRelative(nameof(InputControlScheme.m_BindingGroup)).stringValue = string.IsNullOrWhiteSpace(newControlSchemeName) ? controlSchemeName : newControlSchemeName;
82
83 var serializedDeviceRequirements = controlScheme.FindPropertyRelative(nameof(InputControlScheme.m_DeviceRequirements));
84 serializedDeviceRequirements.ClearArray();
85 for (var i = 0; i < state.selectedControlScheme.deviceRequirements.Count; i++)
86 {
87 var deviceRequirement = state.selectedControlScheme.deviceRequirements[i];
88 serializedDeviceRequirements.InsertArrayElementAtIndex(i);
89
90 var serializedRequirement = serializedDeviceRequirements.GetArrayElementAtIndex(i);
91 serializedRequirement
92 .FindPropertyRelative(nameof(InputControlScheme.DeviceRequirement.m_ControlPath))
93 .stringValue = deviceRequirement.controlPath;
94 serializedRequirement.FindPropertyRelative(nameof(InputControlScheme.DeviceRequirement.m_Flags))
95 .enumValueFlag = (int)deviceRequirement.m_Flags;
96 }
97
98 state.serializedObject.ApplyModifiedProperties();
99 return state.With(
100 selectedControlScheme: new InputControlScheme(controlScheme),
101 // Select the control scheme updated, otherwise select the new one it was added
102 selectedControlSchemeIndex: updateExisting? state.selectedControlSchemeIndex: controlSchemesArray.arraySize - 1);
103 };
104 }
105
106 static void RenameBindingsControlSchemeHelper(SerializedProperty controlScheme, SerializedProperty actionMaps, string controlSchemeName, string newName)
107 {
108 foreach (SerializedProperty actionMap in actionMaps)
109 {
110 var bindings = actionMap
111 .FindPropertyRelative(nameof(InputActionMap.m_Bindings))
112 .Select(sp => new SerializedInputBinding(sp))
113 .ToList();
114
115 var bindingsToRename = bindings.Where(b => b.controlSchemes.Contains(controlSchemeName)).ToList();
116
117 foreach (var binding in bindingsToRename)
118 {
119 var bindingGroups = binding.controlSchemes.ToList();
120 bindingGroups.Remove(controlSchemeName);
121 bindingGroups.Add(newName);
122 binding.wrappedProperty.FindPropertyRelative(nameof(InputBinding.m_Groups)).stringValue = bindingGroups.Join(InputBinding.kSeparatorString);
123 }
124 }
125 }
126
127 public static Command SelectControlScheme(int controlSchemeIndex)
128 {
129 return (in InputActionsEditorState state) =>
130 {
131 if (controlSchemeIndex == -1)
132 return state.With(selectedControlSchemeIndex: controlSchemeIndex, selectedControlScheme: new InputControlScheme());
133
134 var controlSchemeSerializedProperty = state.serializedObject
135 .FindProperty(nameof(InputActionAsset.m_ControlSchemes))
136 .GetArrayElementAtIndex(controlSchemeIndex);
137
138 return state.With(
139 selectedControlSchemeIndex: controlSchemeIndex,
140 selectedControlScheme: new InputControlScheme(controlSchemeSerializedProperty));
141 };
142 }
143
144 public static Command ResetSelectedControlScheme()
145 {
146 return (in InputActionsEditorState state) =>
147 {
148 var controlSchemeSerializedProperty = state.selectedControlSchemeIndex == -1 ? null :
149 state.serializedObject
150 .FindProperty(nameof(InputActionAsset.m_ControlSchemes))
151 .GetArrayElementAtIndex(state.selectedControlSchemeIndex);
152
153 if (controlSchemeSerializedProperty == null)
154 {
155 return state.With(
156 selectedControlSchemeIndex: -1,
157 selectedControlScheme: new InputControlScheme());
158 }
159
160 return state.With(
161 selectedControlScheme: new InputControlScheme(controlSchemeSerializedProperty));
162 };
163 }
164
165 public static Command SelectDeviceRequirement(int deviceRequirementIndex)
166 {
167 return (in InputActionsEditorState state) => state.With(selectedDeviceRequirementIndex: deviceRequirementIndex);
168 }
169
170 /// <summary>
171 /// Duplicate creates a new instance of the selected control scheme and places it in the selected
172 /// control scheme property of the state but doesn't persist anything.
173 /// </summary>
174 public static Command DuplicateSelectedControlScheme()
175 {
176 return (in InputActionsEditorState state) =>
177 {
178 state.m_Analytics?.RegisterControlSchemeEdit();
179
180 return state.With(selectedControlScheme: new InputControlScheme(
181 MakeUniqueControlSchemeName(state, state.selectedControlScheme.name),
182 state.selectedControlScheme.deviceRequirements));
183 };
184 }
185
186 public static Command DeleteSelectedControlScheme()
187 {
188 return (in InputActionsEditorState state) =>
189 {
190 var selectedControlSchemeName = state.selectedControlScheme.name;
191
192 var serializedArray = InputActionSerializationHelpers.GetControlSchemesArray(state.serializedObject);
193 var indexOfArrayElement = InputActionSerializationHelpers.IndexOfControlScheme(serializedArray, selectedControlSchemeName);
194 if (indexOfArrayElement < 0)
195 throw new InvalidOperationException("Control scheme doesn't exist in collection.");
196
197 // Ask for confirmation.
198 if (Dialog.Result.Cancel == Dialog.ControlScheme.ShowDeleteControlScheme(selectedControlSchemeName))
199 return state;
200
201 serializedArray.DeleteArrayElementAtIndex(indexOfArrayElement);
202 state.serializedObject.ApplyModifiedProperties();
203
204 if (serializedArray.arraySize == 0)
205 return state.With(
206 selectedControlSchemeIndex: -1,
207 selectedControlScheme: new InputControlScheme(),
208 selectedDeviceRequirementIndex: -1);
209
210 if (indexOfArrayElement > serializedArray.arraySize - 1)
211 return state.With(
212 selectedControlSchemeIndex: serializedArray.arraySize - 1,
213 selectedControlScheme: new InputControlScheme(serializedArray.GetArrayElementAtIndex(serializedArray.arraySize - 1)), selectedDeviceRequirementIndex: -1);
214
215 state.m_Analytics?.RegisterControlSchemeEdit();
216
217 return state.With(
218 selectedControlSchemeIndex: indexOfArrayElement,
219 selectedControlScheme: new InputControlScheme(serializedArray.GetArrayElementAtIndex(indexOfArrayElement)), selectedDeviceRequirementIndex: -1);
220 };
221 }
222
223 internal static string MakeUniqueControlSchemeName(InputActionsEditorState state, string name)
224 {
225 var controlSchemes = state.serializedObject.FindProperty(nameof(InputActionAsset.m_ControlSchemes));
226
227 IEnumerable<string> controlSchemeNames = Array.Empty<string>();
228 if (controlSchemes != null)
229 controlSchemeNames =
230 controlSchemes.Select(sp => sp.FindPropertyRelative(nameof(InputControlScheme.m_Name)).stringValue);
231
232 return StringHelpers.MakeUniqueName(name, controlSchemeNames.Append(kAllControlSchemesName), x => x);
233 }
234
235 public static Command ChangeDeviceRequirement(int deviceRequirementIndex, bool isRequired)
236 {
237 return (in InputActionsEditorState state) =>
238 {
239 var deviceRequirements = state.selectedControlScheme.deviceRequirements.ToList();
240 var requirement = deviceRequirements[deviceRequirementIndex];
241 requirement.isOptional = !isRequired;
242 deviceRequirements[deviceRequirementIndex] = requirement;
243
244 state.m_Analytics?.RegisterControlSchemeEdit();
245
246 return state.With(selectedControlScheme: new InputControlScheme(
247 state.selectedControlScheme.name,
248 deviceRequirements,
249 state.selectedControlScheme.bindingGroup));
250 };
251 }
252
253 public static Command ReorderDeviceRequirements(int oldPosition, int newPosition)
254 {
255 return (in InputActionsEditorState state) =>
256 {
257 var deviceRequirements = state.selectedControlScheme.deviceRequirements.ToList();
258 var requirement = deviceRequirements[oldPosition];
259 deviceRequirements.RemoveAt(oldPosition);
260 deviceRequirements.Insert(newPosition, requirement);
261
262 state.m_Analytics?.RegisterControlSchemeEdit();
263
264 return state.With(selectedControlScheme: new InputControlScheme(
265 state.selectedControlScheme.name,
266 deviceRequirements,
267 state.selectedControlScheme.bindingGroup));
268 };
269 }
270
271 public static Command ChangeSelectedBindingsControlSchemes(string controlScheme, bool add)
272 {
273 return (in InputActionsEditorState state) =>
274 {
275 var actionMapSO = state.serializedObject
276 ?.FindProperty(nameof(InputActionAsset.m_ActionMaps))
277 ?.GetArrayElementAtIndex(state.selectedActionMapIndex);
278 var serializedProperty = actionMapSO?.FindPropertyRelative(nameof(InputActionMap.m_Bindings))
279 ?.GetArrayElementAtIndex(state.selectedBindingIndex);
280
281 var groupsProperty = serializedProperty.FindPropertyRelative(nameof(InputBinding.m_Groups));
282 var groups = groupsProperty.stringValue;
283
284 if (add)
285 groupsProperty.stringValue = groups
286 .Split(InputBinding.kSeparatorString)
287 .Append(controlScheme)
288 .Distinct()
289 .Join(InputBinding.kSeparatorString);
290 else
291 groupsProperty.stringValue = groups
292 .Split(InputBinding.kSeparatorString)
293 .Where(s => s != controlScheme)
294 .Join(InputBinding.kSeparatorString);
295
296 state.m_Analytics?.RegisterBindingEdit();
297
298 state.serializedObject.ApplyModifiedProperties();
299 return state;
300 };
301 }
302 }
303}
304
305#endif