A game about forced loneliness, made by TACStudios
at master 29 kB view raw
1#if UNITY_EDITOR 2using System; 3using System.Collections.Generic; 4using System.Linq; 5using System.Text; 6using UnityEditor; 7using UnityEngine.InputSystem.Controls; 8using UnityEngine.InputSystem.Layouts; 9using UnityEngine.InputSystem.Utilities; 10 11////TODO: have tooltips on each entry in the picker 12 13////TODO: find better way to present controls when filtering to specific devices 14 15////REVIEW: if there's only a single device in the picker, automatically go into it? 16 17namespace UnityEngine.InputSystem.Editor 18{ 19 internal class InputControlPickerDropdown : AdvancedDropdown, IDisposable 20 { 21 public InputControlPickerDropdown( 22 InputControlPickerState state, 23 Action<string> onPickCallback, 24 InputControlPicker.Mode mode = InputControlPicker.Mode.PickControl) 25 : base(state.advancedDropdownState) 26 { 27 m_Gui = new InputControlPickerGUI(this); 28 29 minimumSize = new Vector2(275, 300); 30 maximumSize = new Vector2(0, 300); 31 32 m_OnPickCallback = onPickCallback; 33 m_Mode = mode; 34 } 35 36 public void SetControlPathsToMatch(string[] controlPathsToMatch) 37 { 38 m_ControlPathsToMatch = controlPathsToMatch; 39 Reload(); 40 } 41 42 public void SetExpectedControlLayout(string expectedControlLayout) 43 { 44 m_ExpectedControlLayout = expectedControlLayout; 45 46 if (string.Equals(expectedControlLayout, "InputDevice", StringComparison.InvariantCultureIgnoreCase)) 47 m_ExpectedControlType = typeof(InputDevice); 48 else 49 m_ExpectedControlType = !string.IsNullOrEmpty(expectedControlLayout) 50 ? InputSystem.s_Manager.m_Layouts.GetControlTypeForLayout(new InternedString(expectedControlLayout)) 51 : null; 52 53 // If the layout is for a device, automatically switch to device 54 // picking mode. 55 if (m_ExpectedControlType != null && typeof(InputDevice).IsAssignableFrom(m_ExpectedControlType)) 56 m_Mode = InputControlPicker.Mode.PickDevice; 57 58 Reload(); 59 } 60 61 public void SetPickedCallback(Action<string> action) 62 { 63 m_OnPickCallback = action; 64 } 65 66 protected override void OnDestroy() 67 { 68 #if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS 69 InputActionsEditorSettingsProvider.SetIMGUIDropdownVisible(false, false); 70 #endif 71 m_RebindingOperation?.Dispose(); 72 m_RebindingOperation = null; 73 } 74 75 public void Dispose() 76 { 77 m_RebindingOperation?.Dispose(); 78 } 79 80 protected override AdvancedDropdownItem BuildRoot() 81 { 82 var root = new AdvancedDropdownItem(string.Empty); 83 84 // Usages. 85 if (m_Mode != InputControlPicker.Mode.PickDevice) 86 { 87 var usages = BuildTreeForControlUsages(); 88 if (usages.children.Any()) 89 { 90 root.AddChild(usages); 91 root.AddSeparator(); 92 } 93 } 94 95 // Devices. 96 AddItemsForDevices(root); 97 98 return root; 99 } 100 101 protected override AdvancedDropdownItem BuildCustomSearch(string searchString, 102 IEnumerable<AdvancedDropdownItem> elements) 103 { 104 if (!isListening) 105 return null; 106 107 var root = new AdvancedDropdownItem(!string.IsNullOrEmpty(m_ExpectedControlLayout) 108 ? $"Listening for {m_ExpectedControlLayout}..." 109 : "Listening for input..."); 110 111 if (searchString == "\u0017") 112 return root; 113 114 var paths = searchString.Substring(1).Split('\u0017'); 115 foreach (var element in elements) 116 { 117 if (element is ControlDropdownItem controlItem && paths.Any(x => controlItem.controlPathWithDevice == x)) 118 root.AddChild(element); 119 } 120 121 return root; 122 } 123 124 protected override void ItemSelected(AdvancedDropdownItem item) 125 { 126 #if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS 127 InputActionsEditorSettingsProvider.SetIMGUIDropdownVisible(false, true); 128 #endif 129 var path = ((InputControlDropdownItem)item).controlPathWithDevice; 130 m_OnPickCallback(path); 131 } 132 133 private AdvancedDropdownItem BuildTreeForControlUsages(string device = "", string usage = "") 134 { 135 var usageRoot = new AdvancedDropdownItem("Usages"); 136 foreach (var usageAndLayouts in EditorInputControlLayoutCache.allUsages) 137 { 138 if (usageAndLayouts.Item2.Any(LayoutMatchesExpectedControlLayoutFilter)) 139 { 140 var child = new ControlUsageDropdownItem(device, usage, usageAndLayouts.Item1); 141 usageRoot.AddChild(child); 142 } 143 } 144 return usageRoot; 145 } 146 147 private void AddItemsForDevices(AdvancedDropdownItem parent) 148 { 149 // Add devices that are marked as generic types of devices directly to the parent. 150 // E.g. adds "Gamepad" and then underneath all the more specific types of gamepads. 151 foreach (var deviceLayout in EditorInputControlLayoutCache.allLayouts 152 .Where(x => x.isDeviceLayout && !x.isOverride && x.isGenericTypeOfDevice && !x.hideInUI) 153 .OrderBy(a => a.displayName)) 154 { 155 AddDeviceTreeItemRecursive(deviceLayout, parent); 156 } 157 158 // We have devices that are based directly on InputDevice but are not marked as generic types 159 // of devices (e.g. Vive Lighthouses). We do not want them to clutter the list at the root so we 160 // put all of them in a group called "Other" at the end of the list. 161 var otherGroup = new AdvancedDropdownItem("Other"); 162 foreach (var deviceLayout in EditorInputControlLayoutCache.allLayouts 163 .Where(x => x.isDeviceLayout && !x.isOverride && !x.isGenericTypeOfDevice && 164 (x.type.BaseType == typeof(InputDevice) || x.type == typeof(InputDevice)) && 165 !x.hideInUI && !x.baseLayouts.Any()).OrderBy(a => a.displayName)) 166 { 167 AddDeviceTreeItemRecursive(deviceLayout, otherGroup); 168 } 169 170 if (otherGroup.children.Any()) 171 parent.AddChild(otherGroup); 172 } 173 174 private void AddDeviceTreeItemRecursive(InputControlLayout layout, AdvancedDropdownItem parent, bool searchable = true) 175 { 176 // Find all layouts directly based on this one (ignoring overrides). 177 var childLayouts = EditorInputControlLayoutCache.allLayouts 178 .Where(x => x.isDeviceLayout && !x.isOverride && !x.hideInUI && x.baseLayouts.Contains(layout.name)).OrderBy(x => x.displayName); 179 180 // See if the entire tree should be excluded. 181 var shouldIncludeDeviceLayout = ShouldIncludeDeviceLayout(layout); 182 var shouldIncludeAtLeastOneChildLayout = childLayouts.Any(ShouldIncludeDeviceLayout); 183 184 if (!shouldIncludeDeviceLayout && !shouldIncludeAtLeastOneChildLayout) 185 return; 186 187 // Add toplevel item for device. 188 var deviceItem = new DeviceDropdownItem(layout, searchable: searchable); 189 190 var defaultControlPickerLayout = new DefaultInputControlPickerLayout(); 191 192 // Add common usage variants of the device 193 if (layout.commonUsages.Count > 0) 194 { 195 foreach (var usage in layout.commonUsages) 196 { 197 var usageItem = new DeviceDropdownItem(layout, usage); 198 199 // Add control usages to the device variants 200 var deviceVariantControlUsages = BuildTreeForControlUsages(layout.name, usage); 201 if (deviceVariantControlUsages.children.Any()) 202 { 203 usageItem.AddChild(deviceVariantControlUsages); 204 usageItem.AddSeparator(); 205 } 206 207 if (m_Mode == InputControlPicker.Mode.PickControl) 208 AddControlTreeItemsRecursive(defaultControlPickerLayout, layout, usageItem, layout.name, usage, searchable); 209 deviceItem.AddChild(usageItem); 210 } 211 deviceItem.AddSeparator(); 212 } 213 214 // Add control usages 215 var deviceControlUsages = BuildTreeForControlUsages(layout.name); 216 if (deviceControlUsages.children.Any()) 217 { 218 deviceItem.AddChild(deviceControlUsages); 219 deviceItem.AddSeparator(); 220 } 221 222 // Add controls. 223 if (m_Mode != InputControlPicker.Mode.PickDevice) 224 { 225 // The keyboard is special in that we want to allow binding by display name (i.e. character 226 // generated by a key) instead of only by physical key location. Also, we want to give an indication 227 // of which specific key an entry refers to by taking the current keyboard layout into account. 228 // 229 // So what we do is add an extra level to the keyboard where key's can be bound by character 230 // according to the current layout. And in the top level of the keyboard we display keys with 231 // both physical and logical names. 232 if (layout.type == typeof(Keyboard) && InputSystem.GetDevice<Keyboard>() != null) 233 { 234 var byLocationGroup = new AdvancedDropdownItem("By Location of Key (Using US Layout)"); 235 var byCharacterGroup = new AdvancedDropdownItem("By Character Mapped to Key"); 236 237 deviceItem.AddChild(byLocationGroup); 238 deviceItem.AddChild(byCharacterGroup); 239 240 var keyboard = InputSystem.GetDevice<Keyboard>(); 241 242 AddCharacterKeyBindingsTo(byCharacterGroup, keyboard); 243 AddPhysicalKeyBindingsTo(byLocationGroup, keyboard, searchable); 244 245 // AnyKey won't appear in either group. Add it explicitly. 246 AddControlItem(defaultControlPickerLayout, deviceItem, null, 247 layout.FindControl(new InternedString("anyKey")).Value, layout.name, null, searchable); 248 } 249 else if (layout.type == typeof(Touchscreen)) 250 { 251 AddControlTreeItemsRecursive(new TouchscreenControlPickerLayout(), layout, deviceItem, layout.name, null, searchable); 252 } 253 else 254 { 255 AddControlTreeItemsRecursive(defaultControlPickerLayout, layout, deviceItem, layout.name, null, searchable); 256 } 257 } 258 259 // Add child items. 260 var isFirstChild = true; 261 foreach (var childLayout in childLayouts) 262 { 263 if (!ShouldIncludeDeviceLayout(childLayout)) 264 continue; 265 266 if (isFirstChild) 267 deviceItem.AddSeparator("More Specific " + deviceItem.name.GetPlural()); 268 isFirstChild = false; 269 270 AddDeviceTreeItemRecursive(childLayout, deviceItem, searchable && !childLayout.isGenericTypeOfDevice); 271 } 272 273 // When picking devices, it must be possible to select a device that itself has more specific types 274 // of devices underneath it. However in the dropdown, such a device will be a foldout and not itself 275 // be selectable. We solve this problem by adding an entry for the device underneath the device 276 // itself (e.g. "Gamepad >> Gamepad"). 277 if (m_Mode == InputControlPicker.Mode.PickDevice && deviceItem.m_Children.Count > 0) 278 { 279 var item = new DeviceDropdownItem(layout); 280 deviceItem.m_Children.Insert(0, item); 281 } 282 283 if (deviceItem.m_Children.Count > 0 || m_Mode == InputControlPicker.Mode.PickDevice) 284 parent.AddChild(deviceItem); 285 } 286 287 private void AddControlTreeItemsRecursive(IInputControlPickerLayout controlPickerLayout, InputControlLayout layout, 288 DeviceDropdownItem parent, string device, string usage, bool searchable, ControlDropdownItem parentControl = null) 289 { 290 foreach (var control in layout.controls.OrderBy(a => a.name)) 291 { 292 if (control.isModifyingExistingControl) 293 continue; 294 295 // Skip variants except the default variant and variants dictated by the layout itself. 296 if (!control.variants.IsEmpty() && control.variants != InputControlLayout.DefaultVariant 297 && (layout.variants.IsEmpty() || !InputControlLayout.VariantsMatch(layout.variants, control.variants))) 298 { 299 continue; 300 } 301 302 controlPickerLayout.AddControlItem(this, parent, parentControl, control, device, usage, searchable); 303 } 304 305 // Add optional controls for devices. 306 var optionalControls = EditorInputControlLayoutCache.GetOptionalControlsForLayout(layout.name); 307 if (optionalControls.Any() && layout.isDeviceLayout) 308 { 309 var optionalGroup = new AdvancedDropdownItem("Optional Controls"); 310 foreach (var optionalControl in optionalControls) 311 { 312 ////FIXME: this should list children, too 313 ////FIXME: this should handle arrays, too 314 if (LayoutMatchesExpectedControlLayoutFilter(optionalControl.layout)) 315 { 316 var child = new OptionalControlDropdownItem(optionalControl, device, usage); 317 child.icon = EditorInputControlLayoutCache.GetIconForLayout(optionalControl.layout); 318 optionalGroup.AddChild(child); 319 } 320 } 321 322 if (optionalGroup.children.Any()) 323 { 324 var deviceName = EditorInputControlLayoutCache.TryGetLayout(device).m_DisplayName ?? 325 ObjectNames.NicifyVariableName(device); 326 parent.AddSeparator("Controls Present on More Specific " + deviceName.GetPlural()); 327 parent.AddChild(optionalGroup); 328 } 329 } 330 } 331 332 internal void AddControlItem(IInputControlPickerLayout controlPickerLayout, 333 DeviceDropdownItem parent, ControlDropdownItem parentControl, 334 InputControlLayout.ControlItem control, string device, string usage, bool searchable, 335 string controlNameOverride = default) 336 { 337 var controlName = controlNameOverride ?? control.name; 338 339 // If it's an array, generate a control entry for each array element. 340 for (var i = 0; i < (control.isArray ? control.arraySize : 1); ++i) 341 { 342 var name = control.isArray ? controlName + i : controlName; 343 var displayName = !string.IsNullOrEmpty(control.displayName) 344 ? (control.isArray ? $"{control.displayName} #{i}" : control.displayName) 345 : name; 346 347 var child = new ControlDropdownItem(parentControl, name, displayName, 348 device, usage, searchable); 349 child.icon = EditorInputControlLayoutCache.GetIconForLayout(control.layout); 350 var controlLayout = EditorInputControlLayoutCache.TryGetLayout(control.layout); 351 352 if (LayoutMatchesExpectedControlLayoutFilter(control.layout)) 353 parent.AddChild(child); 354 else if (controlLayout.controls.Any(x => LayoutMatchesExpectedControlLayoutFilter(x.layout))) 355 { 356 child.enabled = false; 357 parent.AddChild(child); 358 } 359 // Add children. 360 if (controlLayout != null) 361 AddControlTreeItemsRecursive(controlPickerLayout, controlLayout, parent, device, usage, 362 searchable, child); 363 } 364 } 365 366 private static void AddPhysicalKeyBindingsTo(AdvancedDropdownItem parent, Keyboard keyboard, bool searchable) 367 { 368 foreach (var key in keyboard.children.OfType<KeyControl>()) 369 { 370 // If the key has a display name that differs from the key name, show it in the UI. 371 var displayName = key.m_DisplayNameFromLayout; 372 var keyDisplayName = key.displayName; 373 if (keyDisplayName.All(x => x.IsPrintable()) && string.Compare(keyDisplayName, displayName, 374 StringComparison.InvariantCultureIgnoreCase) != 0) 375 displayName = $"{displayName} (Current Layout: {key.displayName})"; 376 377 // For left/right modifier keys, prepend artificial combined version. 378 ButtonControl combinedVersion = null; 379 if (key == keyboard.leftShiftKey) 380 combinedVersion = keyboard.shiftKey; 381 else if (key == keyboard.leftAltKey) 382 combinedVersion = keyboard.altKey; 383 else if (key == keyboard.leftCtrlKey) 384 combinedVersion = keyboard.ctrlKey; 385 if (combinedVersion != null) 386 parent.AddChild(new ControlDropdownItem(null, combinedVersion.name, combinedVersion.displayName, keyboard.layout, 387 "", searchable)); 388 389 var item = new ControlDropdownItem(null, key.name, displayName, 390 keyboard.layout, "", searchable); 391 392 parent.AddChild(item); 393 } 394 } 395 396 private static void AddCharacterKeyBindingsTo(AdvancedDropdownItem parent, Keyboard keyboard) 397 { 398 foreach (var key in keyboard.children.OfType<KeyControl>()) 399 { 400 if (!key.keyCode.IsTextInputKey()) 401 continue; 402 403 // We can only bind to characters that can be printed. 404 var displayName = key.displayName; 405 if (!displayName.All(x => x.IsPrintable())) 406 continue; 407 408 if (displayName.Contains(')')) 409 displayName = string.Join("", displayName.Select(x => "\\" + x)); 410 411 ////TODO: should be searchable; when searching, needs different display name 412 var item = new ControlDropdownItem(null, $"#({displayName})", "", keyboard.layout, "", false); 413 item.name = key.displayName; 414 415 parent.AddChild(item); 416 } 417 } 418 419 private bool LayoutMatchesExpectedControlLayoutFilter(string layout) 420 { 421 if (m_ExpectedControlType == null) 422 return true; 423 424 var layoutType = InputSystem.s_Manager.m_Layouts.GetControlTypeForLayout(new InternedString(layout)); 425 return m_ExpectedControlType.IsAssignableFrom(layoutType); 426 } 427 428 private bool ShouldIncludeDeviceLayout(InputControlLayout layout) 429 { 430 if (layout.hideInUI) 431 return false; 432 433 // By default, if a device has no (usable) controls, we don't want it listed in the control picker 434 // except if we're picking devices. 435 if (!layout.controls.Any(x => LayoutMatchesExpectedControlLayoutFilter(x.layout)) && layout.controls.Any(x => true) && 436 m_Mode != InputControlPicker.Mode.PickDevice) 437 return false; 438 439 // If we have a device filter, see if we should ignore the device. 440 if (m_ControlPathsToMatch != null && m_ControlPathsToMatch.Length > 0) 441 { 442 var matchesAnyInDeviceFilter = false; 443 foreach (var entry in m_ControlPathsToMatch) 444 { 445 // Include the layout if it's in the inheritance hierarchy of the layout we expect (either below 446 // or above it or, well, just right on it). 447 var expectedLayout = InputControlPath.TryGetDeviceLayout(entry); 448 if (!string.IsNullOrEmpty(expectedLayout) && 449 (expectedLayout == layout.name || 450 InputControlLayout.s_Layouts.IsBasedOn(layout.name, new InternedString(expectedLayout)) || 451 InputControlLayout.s_Layouts.IsBasedOn(new InternedString(expectedLayout), layout.name))) 452 { 453 matchesAnyInDeviceFilter = true; 454 break; 455 } 456 } 457 458 if (!matchesAnyInDeviceFilter) 459 return false; 460 } 461 462 return true; 463 } 464 465 private void StartListening() 466 { 467 if (m_RebindingOperation == null) 468 m_RebindingOperation = new InputActionRebindingExtensions.RebindingOperation(); 469 470 ////TODO: for keyboard, generate both possible paths (physical and by display name) 471 472 m_RebindingOperation.Reset(); 473 m_RebindingOperation 474 .WithExpectedControlType(m_ExpectedControlLayout) 475 // Require minimum actuation of 0.15f. This is after deadzoning has been applied. 476 .WithMagnitudeHavingToBeGreaterThan(0.15f) 477 ////REVIEW: should we exclude only the system's active pointing device? 478 // With the mouse operating the UI, its cursor control is too fickle a thing to 479 // bind to. Ignore mouse position and delta and clicks. 480 // NOTE: We go for all types of pointers here, not just mice. 481 .WithControlsExcluding("<Pointer>/position") 482 .WithControlsExcluding("<Pointer>/delta") 483 .WithControlsExcluding("<Pointer>/press") 484 .WithControlsExcluding("<Pointer>/clickCount") 485 .WithControlsExcluding("<Pointer>/{PrimaryAction}") 486 .WithControlsExcluding("<Mouse>/scroll") 487 .OnPotentialMatch( 488 operation => 489 { 490 // We never really complete the pick but keep listening for as long as the "Interactive" 491 // button is toggled on. 492 493 Repaint(); 494 }) 495 .OnCancel( 496 operation => 497 { 498 Repaint(); 499 }) 500 .OnApplyBinding( 501 (operation, newPath) => 502 { 503 // This is never invoked (because we don't complete the pick) but we need it nevertheless 504 // as RebindingOperation requires the callback if we don't supply an action to apply the binding to. 505 }); 506 507 // If we have control paths to match, pass them on. 508 if (m_ControlPathsToMatch.LengthSafe() > 0) 509 m_ControlPathsToMatch.Select(x => m_RebindingOperation.WithControlsHavingToMatchPath(x)); 510 511 m_RebindingOperation.Start(); 512 } 513 514 private void StopListening() 515 { 516 m_RebindingOperation?.Cancel(); 517 } 518 519 // This differs from RebindingOperation.GeneratePathForControl in that it cycles through all 520 // layouts in the inheritance chain and generates a path for each one that contains the given control. 521 private static IEnumerable<string> GeneratePossiblePathsForControl(InputControl control) 522 { 523 var builder = new StringBuilder(); 524 var deviceLayoutName = control.device.m_Layout; 525 do 526 { 527 // Skip layout if it is supposed to be hidden in the UI. 528 var layout = EditorInputControlLayoutCache.TryGetLayout(deviceLayoutName); 529 if (layout.hideInUI) 530 continue; 531 532 builder.Length = 0; 533 yield return control.BuildPath(deviceLayoutName, builder); 534 } 535 while (InputControlLayout.s_Layouts.baseLayoutTable.TryGetValue(deviceLayoutName, out deviceLayoutName)); 536 } 537 538 private Action<string> m_OnPickCallback; 539 private InputControlPicker.Mode m_Mode; 540 private string[] m_ControlPathsToMatch; 541 private string m_ExpectedControlLayout; 542 private Type m_ExpectedControlType; 543 private InputActionRebindingExtensions.RebindingOperation m_RebindingOperation; 544 545 private bool isListening => m_RebindingOperation != null && m_RebindingOperation.started; 546 547 private class InputControlPickerGUI : AdvancedDropdownGUI 548 { 549 private readonly InputControlPickerDropdown m_Owner; 550 551 public InputControlPickerGUI(InputControlPickerDropdown owner) 552 { 553 m_Owner = owner; 554 } 555 556 internal override void BeginDraw(EditorWindow window) 557 { 558 if (Event.current.isKey && Event.current.keyCode == KeyCode.Escape) 559 { 560 window.Close(); 561 return; 562 } 563 564 if (m_Owner.isListening) 565 { 566 // Eat key events to suppress the editor from passing them to the OS 567 // (causing beeps or menu commands being triggered). 568 if (Event.current.isKey) 569 Event.current.Use(); 570 } 571 } 572 573 internal override string DrawSearchFieldControl(string searchString) 574 { 575 using (new EditorGUILayout.HorizontalScope()) 576 { 577 var isListening = false; 578 579 // When picking controls, have a "Listen" button that allows listening for input. 580 if (m_Owner.m_Mode == InputControlPicker.Mode.PickControl) 581 { 582 using (new EditorGUILayout.VerticalScope(GUILayout.MaxWidth(50))) 583 { 584 GUILayout.Space(4); 585 var isListeningOld = m_Owner.isListening; 586 var isListeningNew = GUILayout.Toggle(isListeningOld, "Listen", 587 EditorStyles.miniButton, GUILayout.MaxWidth(50)); 588 589 if (isListeningOld != isListeningNew) 590 { 591 if (isListeningNew) 592 { 593 m_Owner.StartListening(); 594 } 595 else 596 { 597 m_Owner.StopListening(); 598 searchString = string.Empty; 599 } 600 } 601 602 isListening = isListeningNew; 603 } 604 } 605 606 ////FIXME: the search box doesn't clear out when listening; no idea why the new string isn't taking effect 607 EditorGUI.BeginDisabledGroup(isListening); 608 var newSearchString = base.DrawSearchFieldControl(isListening ? string.Empty : searchString); 609 EditorGUI.EndDisabledGroup(); 610 611 if (isListening) 612 { 613 var rebind = m_Owner.m_RebindingOperation; 614 return "\u0017" + string.Join("\u0017", 615 rebind.candidates.SelectMany(x => GeneratePossiblePathsForControl(x).Reverse())); 616 } 617 618 return newSearchString; 619 } 620 } 621 622 internal override void DrawItem(AdvancedDropdownItem item, string name, Texture2D icon, bool enabled, 623 bool drawArrow, bool selected, bool hasSearch, bool richText = false) 624 { 625 if (hasSearch && item is InputControlDropdownItem viewItem) 626 name = viewItem.searchableName; 627 628 base.DrawItem(item, name, icon, enabled, drawArrow, selected, hasSearch); 629 } 630 631 internal override void DrawFooter(AdvancedDropdownItem selectedItem) 632 { 633 //dun work because there is no selection 634 if (selectedItem is ControlDropdownItem controlItem) 635 { 636 var content = new GUIContent(controlItem.controlPath); 637 var rect = GUILayoutUtility.GetRect(content, headerStyle, GUILayout.ExpandWidth(true)); 638 EditorGUI.TextField(rect, controlItem.controlPath, headerStyle); 639 } 640 } 641 } 642 643 private static class Styles 644 { 645 public static readonly GUIStyle waitingForInputLabel = new GUIStyle("WhiteBoldLabel").WithFontSize(22); 646 } 647 } 648} 649#endif // UNITY_EDITOR