A game about forced loneliness, made by TACStudios
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

at master 674 lines 30 kB view raw
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