A game about forced loneliness, made by TACStudios
1#if UNITY_EDITOR
2using System;
3using System.Collections.Generic;
4using System.Linq;
5using UnityEditor;
6using UnityEditor.IMGUI.Controls;
7using UnityEditorInternal;
8using UnityEngine.InputSystem.Utilities;
9
10////TODO: better method for creating display names than InputControlPath.TryGetDeviceLayout
11
12////FIXME: Device requirements list in control scheme popup must mention explicitly that that is what it is
13
14namespace UnityEngine.InputSystem.Editor
15{
16 /// <summary>
17 /// Toolbar in input action asset editor.
18 /// </summary>
19 /// <remarks>
20 /// Allows editing and selecting from the set of control schemes as well as selecting from the
21 /// set of device requirements within the currently selected control scheme.
22 ///
23 /// Also controls saving and has the global search text field.
24 /// </remarks>
25 /// <seealso cref="InputActionEditorWindow"/>
26 [Serializable]
27 internal class InputActionEditorToolbar
28 {
29 public void OnGUI()
30 {
31 EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
32 DrawSchemeSelection();
33 DrawDeviceFilterSelection();
34 if (!InputEditorUserSettings.autoSaveInputActionAssets)
35 DrawSaveButton();
36 GUILayout.FlexibleSpace();
37 DrawAutoSaveToggle();
38 GUILayout.Space(5);
39 DrawSearchField();
40 GUILayout.Space(5);
41 EditorGUILayout.EndHorizontal();
42 }
43
44 private void DrawSchemeSelection()
45 {
46 var buttonGUI = m_ControlSchemes.LengthSafe() > 0
47 ? new GUIContent(selectedControlScheme?.name ?? "All Control Schemes")
48 : new GUIContent("No Control Schemes");
49
50 var buttonRect = GUILayoutUtility.GetRect(buttonGUI, EditorStyles.toolbarPopup, GUILayout.MinWidth(k_MinimumButtonWidth));
51
52 if (GUI.Button(buttonRect, buttonGUI, EditorStyles.toolbarPopup))
53 {
54 // PopupWindow.Show already takes the current OnGUI EditorWindow context into account for window coordinates.
55 // However, on macOS, menu commands are performed asynchronously, so we don't have a current OnGUI context.
56 // So in that case, we need to translate the rect to screen coordinates. Don't do that on windows, as we will
57 // overcompensate otherwise.
58 if (Application.platform == RuntimePlatform.OSXEditor)
59 buttonRect = new Rect(EditorGUIUtility.GUIToScreenPoint(new Vector2(buttonRect.x, buttonRect.y)), Vector2.zero);
60
61 var menu = new GenericMenu();
62
63 // Add entries to select control scheme, if we have some.
64 if (m_ControlSchemes.LengthSafe() > 0)
65 {
66 menu.AddItem(s_AllControlSchemes, m_SelectedControlSchemeIndex == -1, OnControlSchemeSelected, null);
67 var selectedControlSchemeName = m_SelectedControlSchemeIndex == -1
68 ? null : m_ControlSchemes[m_SelectedControlSchemeIndex].name;
69 foreach (var controlScheme in m_ControlSchemes.OrderBy(x => x.name))
70 menu.AddItem(new GUIContent(controlScheme.name),
71 controlScheme.name == selectedControlSchemeName, OnControlSchemeSelected,
72 controlScheme.name);
73
74 menu.AddSeparator(string.Empty);
75 }
76
77 // Add entries to add/edit/duplicate/delete control schemes.
78 menu.AddItem(s_AddControlSchemeLabel, false, OnAddControlScheme, buttonRect);
79 if (m_SelectedControlSchemeIndex >= 0)
80 {
81 menu.AddItem(s_EditControlSchemeLabel, false, OnEditSelectedControlScheme, buttonRect);
82 menu.AddItem(s_DuplicateControlSchemeLabel, false, OnDuplicateControlScheme, buttonRect);
83 menu.AddItem(s_DeleteControlSchemeLabel, false, OnDeleteControlScheme);
84 }
85 else
86 {
87 menu.AddDisabledItem(s_EditControlSchemeLabel, false);
88 menu.AddDisabledItem(s_DuplicateControlSchemeLabel, false);
89 menu.AddDisabledItem(s_DeleteControlSchemeLabel, false);
90 }
91
92 menu.ShowAsContext();
93 }
94 }
95
96 private void DrawDeviceFilterSelection()
97 {
98 // Lazy-initialize list of GUIContents that represent each individual device requirement.
99 if (m_SelectedSchemeDeviceRequirementNames == null && m_ControlSchemes.LengthSafe() > 0 && m_SelectedControlSchemeIndex >= 0)
100 {
101 m_SelectedSchemeDeviceRequirementNames = m_ControlSchemes[m_SelectedControlSchemeIndex]
102 .deviceRequirements.Select(x => new GUIContent(DeviceRequirementToDisplayString(x)))
103 .ToArray();
104 }
105
106 EditorGUI.BeginDisabledGroup(m_SelectedControlSchemeIndex < 0);
107 if (m_SelectedSchemeDeviceRequirementNames.LengthSafe() == 0)
108 {
109 GUILayout.Button(s_AllDevicesLabel, EditorStyles.toolbarPopup, GUILayout.MinWidth(k_MinimumButtonWidth));
110 }
111 else if (GUILayout.Button(m_SelectedDeviceRequirementIndex < 0 ? s_AllDevicesLabel : m_SelectedSchemeDeviceRequirementNames[m_SelectedDeviceRequirementIndex],
112 EditorStyles.toolbarPopup, GUILayout.MinWidth(k_MinimumButtonWidth)))
113 {
114 var menu = new GenericMenu();
115 menu.AddItem(s_AllDevicesLabel, m_SelectedControlSchemeIndex == -1, OnSelectedDeviceChanged, -1);
116 for (var i = 0; i < m_SelectedSchemeDeviceRequirementNames.Length; i++)
117 menu.AddItem(m_SelectedSchemeDeviceRequirementNames[i], m_SelectedDeviceRequirementIndex == i, OnSelectedDeviceChanged, i);
118 menu.ShowAsContext();
119 }
120 EditorGUI.EndDisabledGroup();
121 }
122
123 private void DrawSaveButton()
124 {
125 EditorGUI.BeginDisabledGroup(!m_IsDirty);
126 EditorGUILayout.Space();
127 if (GUILayout.Button(s_SaveAssetLabel, EditorStyles.toolbarButton))
128 onSave();
129 EditorGUI.EndDisabledGroup();
130 }
131
132 private void DrawAutoSaveToggle()
133 {
134 ////FIXME: Using a normal Toggle style with a miniFont, I can't get the "Auto-Save" label to align properly on the vertical.
135 //// The workaround here splits it into a toggle with an empty label plus an extra label.
136 //// Not using EditorStyles.toolbarButton here as it makes it hard to tell that it's a toggle.
137 if (s_MiniToggleStyle == null)
138 {
139 s_MiniToggleStyle = new GUIStyle("Toggle")
140 {
141 font = EditorStyles.miniFont,
142 margin = new RectOffset(0, 0, 1, 0),
143 padding = new RectOffset(0, 16, 0, 0)
144 };
145 s_MiniLabelStyle = new GUIStyle("Label")
146 {
147 font = EditorStyles.miniFont,
148 margin = new RectOffset(0, 0, 3, 0)
149 };
150 }
151
152 var autoSaveNew = GUILayout.Toggle(InputEditorUserSettings.autoSaveInputActionAssets, "",
153 s_MiniToggleStyle);
154 GUILayout.Label(s_AutoSaveLabel, s_MiniLabelStyle);
155 if (autoSaveNew != InputEditorUserSettings.autoSaveInputActionAssets && autoSaveNew && m_IsDirty)
156 {
157 // If it changed from disabled to enabled, perform an initial save.
158 onSave();
159 }
160
161 InputEditorUserSettings.autoSaveInputActionAssets = autoSaveNew;
162
163 GUILayout.Space(5);
164 }
165
166 private void DrawSearchField()
167 {
168 if (m_SearchField == null)
169 m_SearchField = new SearchField();
170
171 EditorGUI.BeginChangeCheck();
172 m_SearchText = m_SearchField.OnToolbarGUI(m_SearchText, GUILayout.MaxWidth(250));
173 if (EditorGUI.EndChangeCheck())
174 onSearchChanged?.Invoke();
175 }
176
177 private void OnControlSchemeSelected(object nameObj)
178 {
179 var index = -1;
180 var name = (string)nameObj;
181 if (name != null)
182 {
183 index = ArrayHelpers.IndexOf(m_ControlSchemes,
184 x => x.name.Equals(name, StringComparison.InvariantCultureIgnoreCase));
185 Debug.Assert(index != -1, $"Cannot find control scheme {name}");
186 }
187
188 m_SelectedControlSchemeIndex = index;
189 m_SelectedDeviceRequirementIndex = -1;
190 m_SelectedSchemeDeviceRequirementNames = null;
191
192 onSelectedSchemeChanged?.Invoke();
193 }
194
195 private void OnSelectedDeviceChanged(object indexObj)
196 {
197 Debug.Assert(m_SelectedControlSchemeIndex >= 0, "Control scheme must be selected");
198
199 m_SelectedDeviceRequirementIndex = (int)indexObj;
200 onSelectedDeviceChanged?.Invoke();
201 }
202
203 private void OnAddControlScheme(object position)
204 {
205 var uniqueName = MakeUniqueControlSchemeName("New control scheme");
206 ControlSchemePropertiesPopup.Show((Rect)position,
207 new InputControlScheme(uniqueName),
208 (s, _) => AddAndSelectControlScheme(s));
209 }
210
211 private void OnDeleteControlScheme()
212 {
213 Debug.Assert(m_SelectedControlSchemeIndex >= 0, "Control scheme must be selected");
214
215 var name = m_ControlSchemes[m_SelectedControlSchemeIndex].name;
216 var bindingGroup = m_ControlSchemes[m_SelectedControlSchemeIndex].bindingGroup;
217
218 // Ask for confirmation.
219 if (!EditorUtility.DisplayDialog("Delete scheme?", $"Do you want to delete control scheme '{name}'?",
220 "Delete", "Cancel"))
221 return;
222
223 ArrayHelpers.EraseAt(ref m_ControlSchemes, m_SelectedControlSchemeIndex);
224 m_SelectedControlSchemeIndex = -1;
225 m_SelectedSchemeDeviceRequirementNames = null;
226
227 if (m_SelectedDeviceRequirementIndex >= 0)
228 {
229 m_SelectedDeviceRequirementIndex = -1;
230 onSelectedDeviceChanged?.Invoke();
231 }
232
233 onControlSchemesChanged?.Invoke();
234 onSelectedSchemeChanged?.Invoke();
235 onControlSchemeDeleted?.Invoke(name, bindingGroup);
236 }
237
238 ////REVIEW: this does nothing to bindings; should this ask to duplicate bindings as well?
239 private void OnDuplicateControlScheme(object position)
240 {
241 Debug.Assert(m_SelectedControlSchemeIndex >= 0, "Control scheme must be selected");
242
243 var scheme = m_ControlSchemes[m_SelectedControlSchemeIndex];
244 scheme = new InputControlScheme(MakeUniqueControlSchemeName(scheme.name),
245 devices: scheme.deviceRequirements);
246
247 ControlSchemePropertiesPopup.Show((Rect)position, scheme,
248 (s, _) => AddAndSelectControlScheme(s));
249 }
250
251 private void OnEditSelectedControlScheme(object position)
252 {
253 Debug.Assert(m_SelectedControlSchemeIndex >= 0, "Control scheme must be selected");
254
255 ControlSchemePropertiesPopup.Show((Rect)position,
256 m_ControlSchemes[m_SelectedControlSchemeIndex],
257 UpdateControlScheme,
258 m_SelectedControlSchemeIndex);
259 }
260
261 private void AddAndSelectControlScheme(InputControlScheme scheme)
262 {
263 // Ensure scheme has a name.
264 if (string.IsNullOrEmpty(scheme.name))
265 scheme.m_Name = "New control scheme";
266
267 // Make sure name is unique.
268 scheme.m_Name = MakeUniqueControlSchemeName(scheme.name);
269
270 var index = ArrayHelpers.Append(ref m_ControlSchemes, scheme);
271 onControlSchemesChanged?.Invoke();
272
273 SelectControlScheme(index);
274 }
275
276 private void UpdateControlScheme(InputControlScheme scheme, int index)
277 {
278 Debug.Assert(index >= 0 && index < m_ControlSchemes.LengthSafe(), "Control scheme index out of range");
279
280 var renamed = false;
281 string oldBindingGroup = null;
282 string newBindingGroup = null;
283
284 // If given scheme has no name, preserve the existing one on the control scheme.
285 if (string.IsNullOrEmpty(scheme.name))
286 scheme.m_Name = m_ControlSchemes[index].name;
287
288 // If name is changing, make sure it's unique.
289 else if (scheme.name != m_ControlSchemes[index].name)
290 {
291 renamed = true;
292 oldBindingGroup = m_ControlSchemes[index].bindingGroup;
293 m_ControlSchemes[index].m_Name = ""; // Don't want this to interfere with finding a unique name.
294 var newName = MakeUniqueControlSchemeName(scheme.name);
295 m_ControlSchemes[index].SetNameAndBindingGroup(newName);
296 newBindingGroup = m_ControlSchemes[index].bindingGroup;
297 }
298
299 m_ControlSchemes[index] = scheme;
300 onControlSchemesChanged?.Invoke();
301
302 if (renamed)
303 onControlSchemeRenamed?.Invoke(oldBindingGroup, newBindingGroup);
304 }
305
306 private void SelectControlScheme(int index)
307 {
308 Debug.Assert(index >= 0 && index < m_ControlSchemes.LengthSafe(), "Control scheme index out of range");
309
310 m_SelectedControlSchemeIndex = index;
311 m_SelectedSchemeDeviceRequirementNames = null;
312 onSelectedSchemeChanged?.Invoke();
313
314 // Reset device selection.
315 if (m_SelectedDeviceRequirementIndex != -1)
316 {
317 m_SelectedDeviceRequirementIndex = -1;
318 onSelectedDeviceChanged?.Invoke();
319 }
320 }
321
322 private string MakeUniqueControlSchemeName(string name)
323 {
324 const string presetName = "All Control Schemes";
325 if (m_ControlSchemes == null)
326 return StringHelpers.MakeUniqueName(name, new[] {presetName}, x => x);
327 return StringHelpers.MakeUniqueName(name, m_ControlSchemes.Select(x => x.name).Append(presetName), x => x);
328 }
329
330 private static string DeviceRequirementToDisplayString(InputControlScheme.DeviceRequirement requirement)
331 {
332 ////TODO: need something more flexible to produce correct results for more than the simple string we produce here
333 var deviceLayout = InputControlPath.TryGetDeviceLayout(requirement.controlPath);
334 var deviceLayoutText = !string.IsNullOrEmpty(deviceLayout)
335 ? EditorInputControlLayoutCache.GetDisplayName(deviceLayout)
336 : string.Empty;
337 var usages = InputControlPath.TryGetDeviceUsages(requirement.controlPath);
338
339 if (usages != null && usages.Length > 0)
340 return $"{deviceLayoutText} {string.Join("}{", usages)}";
341
342 return deviceLayoutText;
343 }
344
345 // Notifications.
346 public Action onSearchChanged;
347 public Action onSelectedSchemeChanged;
348 public Action onSelectedDeviceChanged;
349 public Action onControlSchemesChanged;
350 public Action<string, string> onControlSchemeRenamed;
351 public Action<string, string> onControlSchemeDeleted;
352 public Action onSave;
353
354 [SerializeField] private bool m_IsDirty;
355 [SerializeField] private int m_SelectedControlSchemeIndex = -1;
356 [SerializeField] private int m_SelectedDeviceRequirementIndex = -1;
357 [SerializeField] private InputControlScheme[] m_ControlSchemes;
358 [SerializeField] private string m_SearchText;
359
360 private GUIContent[] m_SelectedSchemeDeviceRequirementNames;
361 private SearchField m_SearchField;
362
363 private static readonly GUIContent s_AllControlSchemes = EditorGUIUtility.TrTextContent("All Control Schemes");
364 private static readonly GUIContent s_AddControlSchemeLabel = new GUIContent("Add Control Scheme...");
365 private static readonly GUIContent s_EditControlSchemeLabel = EditorGUIUtility.TrTextContent("Edit Control Scheme...");
366 private static readonly GUIContent s_DuplicateControlSchemeLabel = EditorGUIUtility.TrTextContent("Duplicate Control Scheme...");
367 private static readonly GUIContent s_DeleteControlSchemeLabel = EditorGUIUtility.TrTextContent("Delete Control Scheme...");
368 private static readonly GUIContent s_SaveAssetLabel = EditorGUIUtility.TrTextContent("Save Asset");
369 private static readonly GUIContent s_AutoSaveLabel = EditorGUIUtility.TrTextContent("Auto-Save");
370 private static readonly GUIContent s_AllDevicesLabel = EditorGUIUtility.TrTextContent("All Devices");
371
372 private static GUIStyle s_MiniToggleStyle;
373 private static GUIStyle s_MiniLabelStyle;
374
375 private const float k_MinimumButtonWidth = 110f;
376
377 public ReadOnlyArray<InputControlScheme> controlSchemes
378 {
379 get => m_ControlSchemes;
380 set
381 {
382 m_ControlSchemes = value.ToArray();
383 m_SelectedSchemeDeviceRequirementNames = null;
384 }
385 }
386
387 /// <summary>
388 /// The control scheme currently selected in the toolbar or null if none is selected.
389 /// </summary>
390 public InputControlScheme? selectedControlScheme => m_SelectedControlSchemeIndex >= 0
391 ? new InputControlScheme ? (m_ControlSchemes[m_SelectedControlSchemeIndex])
392 : null;
393
394 /// <summary>
395 /// The device requirement of the currently selected control scheme which is currently selected
396 /// in the toolbar or null if none is selected.
397 /// </summary>
398 public InputControlScheme.DeviceRequirement? selectedDeviceRequirement => m_SelectedDeviceRequirementIndex >= 0
399 ? new InputControlScheme.DeviceRequirement ? (m_ControlSchemes[m_SelectedControlSchemeIndex]
400 .deviceRequirements[m_SelectedDeviceRequirementIndex])
401 : null;
402
403 /// <summary>
404 /// The search text currently entered in the toolbar or null.
405 /// </summary>
406 public string searchText => m_SearchText;
407
408 internal void ResetSearchFilters()
409 {
410 m_SearchText = null;
411 m_SelectedControlSchemeIndex = -1;
412 m_SelectedDeviceRequirementIndex = -1;
413 }
414
415 public bool isDirty
416 {
417 get => m_IsDirty;
418 set => m_IsDirty = value;
419 }
420
421 /// <summary>
422 /// Popup window content for editing control schemes.
423 /// </summary>
424 private class ControlSchemePropertiesPopup : PopupWindowContent
425 {
426 public static void Show(Rect position, InputControlScheme controlScheme, Action<InputControlScheme, int> onApply,
427 int controlSchemeIndex = -1)
428 {
429 var popup = new ControlSchemePropertiesPopup
430 {
431 m_ControlSchemeIndex = controlSchemeIndex,
432 m_ControlScheme = controlScheme,
433 m_OnApply = onApply,
434 m_SetFocus = true,
435 };
436
437 // We're calling here from a callback, so we need to manually handle ExitGUIException.
438 try
439 {
440 PopupWindow.Show(position, popup);
441 }
442 catch (ExitGUIException) {}
443 }
444
445 public override Vector2 GetWindowSize()
446 {
447 return m_ButtonsAndLabelsHeights > 0 ? new Vector2(300, m_ButtonsAndLabelsHeights) : s_DefaultSize;
448 }
449
450 public override void OnOpen()
451 {
452 m_DeviceList = m_ControlScheme.deviceRequirements.Select(a => new DeviceEntry(a)).ToList();
453 m_DeviceView = new ReorderableList(m_DeviceList, typeof(InputControlScheme.DeviceRequirement));
454 m_DeviceView.headerHeight = 2;
455 m_DeviceView.onAddCallback += list =>
456 {
457 var dropdown = new InputControlPickerDropdown(
458 new InputControlPickerState(),
459 path =>
460 {
461 var requirement = new InputControlScheme.DeviceRequirement
462 {
463 controlPath = path,
464 isOptional = false
465 };
466
467 AddDeviceRequirement(requirement);
468 },
469 mode: InputControlPicker.Mode.PickDevice);
470 dropdown.Show(new Rect(Event.current.mousePosition, Vector2.zero));
471 };
472 m_DeviceView.onRemoveCallback += list =>
473 {
474 list.list.RemoveAt(list.index);
475 list.index = -1;
476 };
477 }
478
479 public override void OnGUI(Rect rect)
480 {
481 if (Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Escape)
482 {
483 editorWindow.Close();
484 Event.current.Use();
485 }
486
487 if (Event.current.type == EventType.Repaint)
488 m_ButtonsAndLabelsHeights = 0;
489
490 GUILayout.BeginArea(rect);
491 DrawTopBar();
492 EditorGUILayout.BeginVertical(EditorStyles.label);
493 DrawSpace();
494 DrawNameEditTextField();
495 DrawSpace();
496 DrawDeviceList();
497 DrawConfirmationButton();
498 EditorGUILayout.EndVertical();
499 GUILayout.EndArea();
500 }
501
502 private void DrawConfirmationButton()
503 {
504 EditorGUILayout.BeginHorizontal();
505 if (GUILayout.Button("Cancel", GUILayout.ExpandWidth(true)))
506 {
507 editorWindow.Close();
508 }
509 if (GUILayout.Button("Save", GUILayout.ExpandWidth(true)))
510 {
511 // Don't allow control scheme name to be empty.
512 if (string.IsNullOrEmpty(m_ControlScheme.name))
513 {
514 ////FIXME: On 2019.1 this doesn't display properly in the window; check 2019.3
515 editorWindow.ShowNotification(new GUIContent("Control scheme must have a name"));
516 }
517 else
518 {
519 m_ControlScheme = new InputControlScheme(m_ControlScheme.name,
520 devices: m_DeviceList.Select(a => a.deviceRequirement));
521
522 editorWindow.Close();
523 m_OnApply(m_ControlScheme, m_ControlSchemeIndex);
524 }
525 }
526 if (Event.current.type == EventType.Repaint)
527 m_ButtonsAndLabelsHeights += GUILayoutUtility.GetLastRect().height;
528 EditorGUILayout.EndHorizontal();
529 }
530
531 private void DrawDeviceList()
532 {
533 EditorGUILayout.BeginHorizontal(EditorStyles.label);
534 var requirementsLabelSize = EditorStyles.label.CalcSize(s_RequirementsLabel);
535 var deviceListRect = GUILayoutUtility.GetRect(GetWindowSize().x - requirementsLabelSize.x - 20, m_DeviceView.GetHeight());
536 m_DeviceView.DoList(deviceListRect);
537 var requirementsHeight = DrawRequirementsCheckboxes();
538 var listHeight = m_DeviceView.GetHeight() + EditorGUIUtility.singleLineHeight * 3;
539 if (Event.current.type == EventType.Repaint)
540 {
541 if (listHeight < requirementsHeight)
542 {
543 m_ButtonsAndLabelsHeights += requirementsHeight;
544 }
545 else
546 {
547 m_ButtonsAndLabelsHeights += listHeight;
548 }
549 }
550
551 EditorGUILayout.EndHorizontal();
552 }
553
554 private void DrawSpace()
555 {
556 GUILayout.Space(6f);
557 if (Event.current.type == EventType.Repaint)
558 m_ButtonsAndLabelsHeights += 6f;
559 }
560
561 private void DrawTopBar()
562 {
563 EditorGUILayout.LabelField(s_AddControlSchemeLabel, Styles.headerLabel);
564
565 if (Event.current.type == EventType.Repaint)
566 m_ButtonsAndLabelsHeights += GUILayoutUtility.GetLastRect().height;
567 }
568
569 private void DrawNameEditTextField()
570 {
571 EditorGUILayout.BeginHorizontal();
572 var labelSize = EditorStyles.label.CalcSize(s_ControlSchemeNameLabel);
573 EditorGUILayout.LabelField(s_ControlSchemeNameLabel, GUILayout.Width(labelSize.x));
574
575 GUI.SetNextControlName("ControlSchemeName");
576 ////FIXME: This should be a DelayedTextField but for some reason (maybe because it's in a popup?), this
577 //// will lead to the text field not working correctly. Hitting enter on the keyboard will apply the
578 //// change as expected but losing focus will *NOT*. In most cases, this makes the text field seem not
579 //// to work at all so instead we use a normal text field here and then apply the name change as part
580 //// of apply the control scheme changes as a whole. The only real downside is that if the name gets
581 //// adjusted automatically because of a naming conflict, this will only become evident *after* hitting
582 //// the "Save" button.
583 m_ControlScheme.m_Name = EditorGUILayout.TextField(m_ControlScheme.m_Name);
584
585 if (m_SetFocus)
586 {
587 EditorGUI.FocusTextInControl("ControlSchemeName");
588 m_SetFocus = false;
589 }
590
591 EditorGUILayout.EndHorizontal();
592 }
593
594 private float DrawRequirementsCheckboxes()
595 {
596 EditorGUILayout.BeginVertical();
597 EditorGUILayout.LabelField(s_RequirementsLabel, GUILayout.Width(200));
598 var requirementHeights = GUILayoutUtility.GetLastRect().y;
599 EditorGUI.BeginDisabledGroup(m_DeviceView.index == -1);
600 var requirementsOption = -1;
601 if (m_DeviceView.index >= 0)
602 {
603 var deviceEntryForList = (DeviceEntry)m_DeviceView.list[m_DeviceView.index];
604 requirementsOption = deviceEntryForList.deviceRequirement.isOptional ? 0 : 1;
605 }
606 EditorGUI.BeginChangeCheck();
607 requirementsOption = GUILayout.SelectionGrid(requirementsOption, s_RequiredOptionalChoices, 1, EditorStyles.radioButton);
608 requirementHeights += GUILayoutUtility.GetLastRect().y;
609 if (EditorGUI.EndChangeCheck())
610 m_DeviceList[m_DeviceView.index].deviceRequirement.isOptional = requirementsOption == 0;
611 EditorGUI.EndDisabledGroup();
612 EditorGUILayout.EndVertical();
613 return requirementHeights;
614 }
615
616 private void AddDeviceRequirement(InputControlScheme.DeviceRequirement requirement)
617 {
618 ArrayHelpers.Append(ref m_ControlScheme.m_DeviceRequirements, requirement);
619 m_DeviceList.Add(new DeviceEntry(requirement));
620 m_DeviceView.index = m_DeviceView.list.Count - 1;
621
622 editorWindow.Repaint();
623 }
624
625 /// <summary>
626 /// The control scheme edited by the popup.
627 /// </summary>
628 public InputControlScheme controlScheme => m_ControlScheme;
629
630 private int m_ControlSchemeIndex;
631 private InputControlScheme m_ControlScheme;
632 private Action<InputControlScheme, int> m_OnApply;
633
634 private ReorderableList m_DeviceView;
635 private List<DeviceEntry> m_DeviceList = new List<DeviceEntry>();
636 private int m_RequirementsOptionsChoice;
637
638 private bool m_SetFocus;
639 private float m_ButtonsAndLabelsHeights;
640
641 private static Vector2 s_DefaultSize => new Vector2(300, 200);
642 private static readonly GUIContent s_RequirementsLabel = EditorGUIUtility.TrTextContent("Requirements:");
643 private static readonly GUIContent s_AddControlSchemeLabel = EditorGUIUtility.TrTextContent("Add control scheme");
644 private static readonly GUIContent s_ControlSchemeNameLabel = EditorGUIUtility.TrTextContent("Scheme Name");
645 private static readonly string[] s_RequiredOptionalChoices = { "Optional", "Required" };
646
647 private static class Styles
648 {
649 public static readonly GUIStyle headerLabel = new GUIStyle(EditorStyles.toolbar)
650 .WithAlignment(TextAnchor.MiddleCenter)
651 .WithFontStyle(FontStyle.Bold)
652 .WithPadding(new RectOffset(10, 6, 0, 0));
653 }
654
655 private class DeviceEntry
656 {
657 public string displayText;
658 public InputControlScheme.DeviceRequirement deviceRequirement;
659
660 public DeviceEntry(InputControlScheme.DeviceRequirement requirement)
661 {
662 displayText = DeviceRequirementToDisplayString(requirement);
663 deviceRequirement = requirement;
664 }
665
666 public override string ToString()
667 {
668 return displayText;
669 }
670 }
671 }
672 }
673}
674#endif // UNITY_EDITOR