#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEditor.IMGUI.Controls;
using UnityEditorInternal;
using UnityEngine.InputSystem.Utilities;
////TODO: better method for creating display names than InputControlPath.TryGetDeviceLayout
////FIXME: Device requirements list in control scheme popup must mention explicitly that that is what it is
namespace UnityEngine.InputSystem.Editor
{
///
/// Toolbar in input action asset editor.
///
///
/// Allows editing and selecting from the set of control schemes as well as selecting from the
/// set of device requirements within the currently selected control scheme.
///
/// Also controls saving and has the global search text field.
///
///
[Serializable]
internal class InputActionEditorToolbar
{
public void OnGUI()
{
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
DrawSchemeSelection();
DrawDeviceFilterSelection();
if (!InputEditorUserSettings.autoSaveInputActionAssets)
DrawSaveButton();
GUILayout.FlexibleSpace();
DrawAutoSaveToggle();
GUILayout.Space(5);
DrawSearchField();
GUILayout.Space(5);
EditorGUILayout.EndHorizontal();
}
private void DrawSchemeSelection()
{
var buttonGUI = m_ControlSchemes.LengthSafe() > 0
? new GUIContent(selectedControlScheme?.name ?? "All Control Schemes")
: new GUIContent("No Control Schemes");
var buttonRect = GUILayoutUtility.GetRect(buttonGUI, EditorStyles.toolbarPopup, GUILayout.MinWidth(k_MinimumButtonWidth));
if (GUI.Button(buttonRect, buttonGUI, EditorStyles.toolbarPopup))
{
// PopupWindow.Show already takes the current OnGUI EditorWindow context into account for window coordinates.
// However, on macOS, menu commands are performed asynchronously, so we don't have a current OnGUI context.
// So in that case, we need to translate the rect to screen coordinates. Don't do that on windows, as we will
// overcompensate otherwise.
if (Application.platform == RuntimePlatform.OSXEditor)
buttonRect = new Rect(EditorGUIUtility.GUIToScreenPoint(new Vector2(buttonRect.x, buttonRect.y)), Vector2.zero);
var menu = new GenericMenu();
// Add entries to select control scheme, if we have some.
if (m_ControlSchemes.LengthSafe() > 0)
{
menu.AddItem(s_AllControlSchemes, m_SelectedControlSchemeIndex == -1, OnControlSchemeSelected, null);
var selectedControlSchemeName = m_SelectedControlSchemeIndex == -1
? null : m_ControlSchemes[m_SelectedControlSchemeIndex].name;
foreach (var controlScheme in m_ControlSchemes.OrderBy(x => x.name))
menu.AddItem(new GUIContent(controlScheme.name),
controlScheme.name == selectedControlSchemeName, OnControlSchemeSelected,
controlScheme.name);
menu.AddSeparator(string.Empty);
}
// Add entries to add/edit/duplicate/delete control schemes.
menu.AddItem(s_AddControlSchemeLabel, false, OnAddControlScheme, buttonRect);
if (m_SelectedControlSchemeIndex >= 0)
{
menu.AddItem(s_EditControlSchemeLabel, false, OnEditSelectedControlScheme, buttonRect);
menu.AddItem(s_DuplicateControlSchemeLabel, false, OnDuplicateControlScheme, buttonRect);
menu.AddItem(s_DeleteControlSchemeLabel, false, OnDeleteControlScheme);
}
else
{
menu.AddDisabledItem(s_EditControlSchemeLabel, false);
menu.AddDisabledItem(s_DuplicateControlSchemeLabel, false);
menu.AddDisabledItem(s_DeleteControlSchemeLabel, false);
}
menu.ShowAsContext();
}
}
private void DrawDeviceFilterSelection()
{
// Lazy-initialize list of GUIContents that represent each individual device requirement.
if (m_SelectedSchemeDeviceRequirementNames == null && m_ControlSchemes.LengthSafe() > 0 && m_SelectedControlSchemeIndex >= 0)
{
m_SelectedSchemeDeviceRequirementNames = m_ControlSchemes[m_SelectedControlSchemeIndex]
.deviceRequirements.Select(x => new GUIContent(DeviceRequirementToDisplayString(x)))
.ToArray();
}
EditorGUI.BeginDisabledGroup(m_SelectedControlSchemeIndex < 0);
if (m_SelectedSchemeDeviceRequirementNames.LengthSafe() == 0)
{
GUILayout.Button(s_AllDevicesLabel, EditorStyles.toolbarPopup, GUILayout.MinWidth(k_MinimumButtonWidth));
}
else if (GUILayout.Button(m_SelectedDeviceRequirementIndex < 0 ? s_AllDevicesLabel : m_SelectedSchemeDeviceRequirementNames[m_SelectedDeviceRequirementIndex],
EditorStyles.toolbarPopup, GUILayout.MinWidth(k_MinimumButtonWidth)))
{
var menu = new GenericMenu();
menu.AddItem(s_AllDevicesLabel, m_SelectedControlSchemeIndex == -1, OnSelectedDeviceChanged, -1);
for (var i = 0; i < m_SelectedSchemeDeviceRequirementNames.Length; i++)
menu.AddItem(m_SelectedSchemeDeviceRequirementNames[i], m_SelectedDeviceRequirementIndex == i, OnSelectedDeviceChanged, i);
menu.ShowAsContext();
}
EditorGUI.EndDisabledGroup();
}
private void DrawSaveButton()
{
EditorGUI.BeginDisabledGroup(!m_IsDirty);
EditorGUILayout.Space();
if (GUILayout.Button(s_SaveAssetLabel, EditorStyles.toolbarButton))
onSave();
EditorGUI.EndDisabledGroup();
}
private void DrawAutoSaveToggle()
{
////FIXME: Using a normal Toggle style with a miniFont, I can't get the "Auto-Save" label to align properly on the vertical.
//// The workaround here splits it into a toggle with an empty label plus an extra label.
//// Not using EditorStyles.toolbarButton here as it makes it hard to tell that it's a toggle.
if (s_MiniToggleStyle == null)
{
s_MiniToggleStyle = new GUIStyle("Toggle")
{
font = EditorStyles.miniFont,
margin = new RectOffset(0, 0, 1, 0),
padding = new RectOffset(0, 16, 0, 0)
};
s_MiniLabelStyle = new GUIStyle("Label")
{
font = EditorStyles.miniFont,
margin = new RectOffset(0, 0, 3, 0)
};
}
var autoSaveNew = GUILayout.Toggle(InputEditorUserSettings.autoSaveInputActionAssets, "",
s_MiniToggleStyle);
GUILayout.Label(s_AutoSaveLabel, s_MiniLabelStyle);
if (autoSaveNew != InputEditorUserSettings.autoSaveInputActionAssets && autoSaveNew && m_IsDirty)
{
// If it changed from disabled to enabled, perform an initial save.
onSave();
}
InputEditorUserSettings.autoSaveInputActionAssets = autoSaveNew;
GUILayout.Space(5);
}
private void DrawSearchField()
{
if (m_SearchField == null)
m_SearchField = new SearchField();
EditorGUI.BeginChangeCheck();
m_SearchText = m_SearchField.OnToolbarGUI(m_SearchText, GUILayout.MaxWidth(250));
if (EditorGUI.EndChangeCheck())
onSearchChanged?.Invoke();
}
private void OnControlSchemeSelected(object nameObj)
{
var index = -1;
var name = (string)nameObj;
if (name != null)
{
index = ArrayHelpers.IndexOf(m_ControlSchemes,
x => x.name.Equals(name, StringComparison.InvariantCultureIgnoreCase));
Debug.Assert(index != -1, $"Cannot find control scheme {name}");
}
m_SelectedControlSchemeIndex = index;
m_SelectedDeviceRequirementIndex = -1;
m_SelectedSchemeDeviceRequirementNames = null;
onSelectedSchemeChanged?.Invoke();
}
private void OnSelectedDeviceChanged(object indexObj)
{
Debug.Assert(m_SelectedControlSchemeIndex >= 0, "Control scheme must be selected");
m_SelectedDeviceRequirementIndex = (int)indexObj;
onSelectedDeviceChanged?.Invoke();
}
private void OnAddControlScheme(object position)
{
var uniqueName = MakeUniqueControlSchemeName("New control scheme");
ControlSchemePropertiesPopup.Show((Rect)position,
new InputControlScheme(uniqueName),
(s, _) => AddAndSelectControlScheme(s));
}
private void OnDeleteControlScheme()
{
Debug.Assert(m_SelectedControlSchemeIndex >= 0, "Control scheme must be selected");
var name = m_ControlSchemes[m_SelectedControlSchemeIndex].name;
var bindingGroup = m_ControlSchemes[m_SelectedControlSchemeIndex].bindingGroup;
// Ask for confirmation.
if (!EditorUtility.DisplayDialog("Delete scheme?", $"Do you want to delete control scheme '{name}'?",
"Delete", "Cancel"))
return;
ArrayHelpers.EraseAt(ref m_ControlSchemes, m_SelectedControlSchemeIndex);
m_SelectedControlSchemeIndex = -1;
m_SelectedSchemeDeviceRequirementNames = null;
if (m_SelectedDeviceRequirementIndex >= 0)
{
m_SelectedDeviceRequirementIndex = -1;
onSelectedDeviceChanged?.Invoke();
}
onControlSchemesChanged?.Invoke();
onSelectedSchemeChanged?.Invoke();
onControlSchemeDeleted?.Invoke(name, bindingGroup);
}
////REVIEW: this does nothing to bindings; should this ask to duplicate bindings as well?
private void OnDuplicateControlScheme(object position)
{
Debug.Assert(m_SelectedControlSchemeIndex >= 0, "Control scheme must be selected");
var scheme = m_ControlSchemes[m_SelectedControlSchemeIndex];
scheme = new InputControlScheme(MakeUniqueControlSchemeName(scheme.name),
devices: scheme.deviceRequirements);
ControlSchemePropertiesPopup.Show((Rect)position, scheme,
(s, _) => AddAndSelectControlScheme(s));
}
private void OnEditSelectedControlScheme(object position)
{
Debug.Assert(m_SelectedControlSchemeIndex >= 0, "Control scheme must be selected");
ControlSchemePropertiesPopup.Show((Rect)position,
m_ControlSchemes[m_SelectedControlSchemeIndex],
UpdateControlScheme,
m_SelectedControlSchemeIndex);
}
private void AddAndSelectControlScheme(InputControlScheme scheme)
{
// Ensure scheme has a name.
if (string.IsNullOrEmpty(scheme.name))
scheme.m_Name = "New control scheme";
// Make sure name is unique.
scheme.m_Name = MakeUniqueControlSchemeName(scheme.name);
var index = ArrayHelpers.Append(ref m_ControlSchemes, scheme);
onControlSchemesChanged?.Invoke();
SelectControlScheme(index);
}
private void UpdateControlScheme(InputControlScheme scheme, int index)
{
Debug.Assert(index >= 0 && index < m_ControlSchemes.LengthSafe(), "Control scheme index out of range");
var renamed = false;
string oldBindingGroup = null;
string newBindingGroup = null;
// If given scheme has no name, preserve the existing one on the control scheme.
if (string.IsNullOrEmpty(scheme.name))
scheme.m_Name = m_ControlSchemes[index].name;
// If name is changing, make sure it's unique.
else if (scheme.name != m_ControlSchemes[index].name)
{
renamed = true;
oldBindingGroup = m_ControlSchemes[index].bindingGroup;
m_ControlSchemes[index].m_Name = ""; // Don't want this to interfere with finding a unique name.
var newName = MakeUniqueControlSchemeName(scheme.name);
m_ControlSchemes[index].SetNameAndBindingGroup(newName);
newBindingGroup = m_ControlSchemes[index].bindingGroup;
}
m_ControlSchemes[index] = scheme;
onControlSchemesChanged?.Invoke();
if (renamed)
onControlSchemeRenamed?.Invoke(oldBindingGroup, newBindingGroup);
}
private void SelectControlScheme(int index)
{
Debug.Assert(index >= 0 && index < m_ControlSchemes.LengthSafe(), "Control scheme index out of range");
m_SelectedControlSchemeIndex = index;
m_SelectedSchemeDeviceRequirementNames = null;
onSelectedSchemeChanged?.Invoke();
// Reset device selection.
if (m_SelectedDeviceRequirementIndex != -1)
{
m_SelectedDeviceRequirementIndex = -1;
onSelectedDeviceChanged?.Invoke();
}
}
private string MakeUniqueControlSchemeName(string name)
{
const string presetName = "All Control Schemes";
if (m_ControlSchemes == null)
return StringHelpers.MakeUniqueName(name, new[] {presetName}, x => x);
return StringHelpers.MakeUniqueName(name, m_ControlSchemes.Select(x => x.name).Append(presetName), x => x);
}
private static string DeviceRequirementToDisplayString(InputControlScheme.DeviceRequirement requirement)
{
////TODO: need something more flexible to produce correct results for more than the simple string we produce here
var deviceLayout = InputControlPath.TryGetDeviceLayout(requirement.controlPath);
var deviceLayoutText = !string.IsNullOrEmpty(deviceLayout)
? EditorInputControlLayoutCache.GetDisplayName(deviceLayout)
: string.Empty;
var usages = InputControlPath.TryGetDeviceUsages(requirement.controlPath);
if (usages != null && usages.Length > 0)
return $"{deviceLayoutText} {string.Join("}{", usages)}";
return deviceLayoutText;
}
// Notifications.
public Action onSearchChanged;
public Action onSelectedSchemeChanged;
public Action onSelectedDeviceChanged;
public Action onControlSchemesChanged;
public Action onControlSchemeRenamed;
public Action onControlSchemeDeleted;
public Action onSave;
[SerializeField] private bool m_IsDirty;
[SerializeField] private int m_SelectedControlSchemeIndex = -1;
[SerializeField] private int m_SelectedDeviceRequirementIndex = -1;
[SerializeField] private InputControlScheme[] m_ControlSchemes;
[SerializeField] private string m_SearchText;
private GUIContent[] m_SelectedSchemeDeviceRequirementNames;
private SearchField m_SearchField;
private static readonly GUIContent s_AllControlSchemes = EditorGUIUtility.TrTextContent("All Control Schemes");
private static readonly GUIContent s_AddControlSchemeLabel = new GUIContent("Add Control Scheme...");
private static readonly GUIContent s_EditControlSchemeLabel = EditorGUIUtility.TrTextContent("Edit Control Scheme...");
private static readonly GUIContent s_DuplicateControlSchemeLabel = EditorGUIUtility.TrTextContent("Duplicate Control Scheme...");
private static readonly GUIContent s_DeleteControlSchemeLabel = EditorGUIUtility.TrTextContent("Delete Control Scheme...");
private static readonly GUIContent s_SaveAssetLabel = EditorGUIUtility.TrTextContent("Save Asset");
private static readonly GUIContent s_AutoSaveLabel = EditorGUIUtility.TrTextContent("Auto-Save");
private static readonly GUIContent s_AllDevicesLabel = EditorGUIUtility.TrTextContent("All Devices");
private static GUIStyle s_MiniToggleStyle;
private static GUIStyle s_MiniLabelStyle;
private const float k_MinimumButtonWidth = 110f;
public ReadOnlyArray controlSchemes
{
get => m_ControlSchemes;
set
{
m_ControlSchemes = value.ToArray();
m_SelectedSchemeDeviceRequirementNames = null;
}
}
///
/// The control scheme currently selected in the toolbar or null if none is selected.
///
public InputControlScheme? selectedControlScheme => m_SelectedControlSchemeIndex >= 0
? new InputControlScheme ? (m_ControlSchemes[m_SelectedControlSchemeIndex])
: null;
///
/// The device requirement of the currently selected control scheme which is currently selected
/// in the toolbar or null if none is selected.
///
public InputControlScheme.DeviceRequirement? selectedDeviceRequirement => m_SelectedDeviceRequirementIndex >= 0
? new InputControlScheme.DeviceRequirement ? (m_ControlSchemes[m_SelectedControlSchemeIndex]
.deviceRequirements[m_SelectedDeviceRequirementIndex])
: null;
///
/// The search text currently entered in the toolbar or null.
///
public string searchText => m_SearchText;
internal void ResetSearchFilters()
{
m_SearchText = null;
m_SelectedControlSchemeIndex = -1;
m_SelectedDeviceRequirementIndex = -1;
}
public bool isDirty
{
get => m_IsDirty;
set => m_IsDirty = value;
}
///
/// Popup window content for editing control schemes.
///
private class ControlSchemePropertiesPopup : PopupWindowContent
{
public static void Show(Rect position, InputControlScheme controlScheme, Action onApply,
int controlSchemeIndex = -1)
{
var popup = new ControlSchemePropertiesPopup
{
m_ControlSchemeIndex = controlSchemeIndex,
m_ControlScheme = controlScheme,
m_OnApply = onApply,
m_SetFocus = true,
};
// We're calling here from a callback, so we need to manually handle ExitGUIException.
try
{
PopupWindow.Show(position, popup);
}
catch (ExitGUIException) {}
}
public override Vector2 GetWindowSize()
{
return m_ButtonsAndLabelsHeights > 0 ? new Vector2(300, m_ButtonsAndLabelsHeights) : s_DefaultSize;
}
public override void OnOpen()
{
m_DeviceList = m_ControlScheme.deviceRequirements.Select(a => new DeviceEntry(a)).ToList();
m_DeviceView = new ReorderableList(m_DeviceList, typeof(InputControlScheme.DeviceRequirement));
m_DeviceView.headerHeight = 2;
m_DeviceView.onAddCallback += list =>
{
var dropdown = new InputControlPickerDropdown(
new InputControlPickerState(),
path =>
{
var requirement = new InputControlScheme.DeviceRequirement
{
controlPath = path,
isOptional = false
};
AddDeviceRequirement(requirement);
},
mode: InputControlPicker.Mode.PickDevice);
dropdown.Show(new Rect(Event.current.mousePosition, Vector2.zero));
};
m_DeviceView.onRemoveCallback += list =>
{
list.list.RemoveAt(list.index);
list.index = -1;
};
}
public override void OnGUI(Rect rect)
{
if (Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Escape)
{
editorWindow.Close();
Event.current.Use();
}
if (Event.current.type == EventType.Repaint)
m_ButtonsAndLabelsHeights = 0;
GUILayout.BeginArea(rect);
DrawTopBar();
EditorGUILayout.BeginVertical(EditorStyles.label);
DrawSpace();
DrawNameEditTextField();
DrawSpace();
DrawDeviceList();
DrawConfirmationButton();
EditorGUILayout.EndVertical();
GUILayout.EndArea();
}
private void DrawConfirmationButton()
{
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("Cancel", GUILayout.ExpandWidth(true)))
{
editorWindow.Close();
}
if (GUILayout.Button("Save", GUILayout.ExpandWidth(true)))
{
// Don't allow control scheme name to be empty.
if (string.IsNullOrEmpty(m_ControlScheme.name))
{
////FIXME: On 2019.1 this doesn't display properly in the window; check 2019.3
editorWindow.ShowNotification(new GUIContent("Control scheme must have a name"));
}
else
{
m_ControlScheme = new InputControlScheme(m_ControlScheme.name,
devices: m_DeviceList.Select(a => a.deviceRequirement));
editorWindow.Close();
m_OnApply(m_ControlScheme, m_ControlSchemeIndex);
}
}
if (Event.current.type == EventType.Repaint)
m_ButtonsAndLabelsHeights += GUILayoutUtility.GetLastRect().height;
EditorGUILayout.EndHorizontal();
}
private void DrawDeviceList()
{
EditorGUILayout.BeginHorizontal(EditorStyles.label);
var requirementsLabelSize = EditorStyles.label.CalcSize(s_RequirementsLabel);
var deviceListRect = GUILayoutUtility.GetRect(GetWindowSize().x - requirementsLabelSize.x - 20, m_DeviceView.GetHeight());
m_DeviceView.DoList(deviceListRect);
var requirementsHeight = DrawRequirementsCheckboxes();
var listHeight = m_DeviceView.GetHeight() + EditorGUIUtility.singleLineHeight * 3;
if (Event.current.type == EventType.Repaint)
{
if (listHeight < requirementsHeight)
{
m_ButtonsAndLabelsHeights += requirementsHeight;
}
else
{
m_ButtonsAndLabelsHeights += listHeight;
}
}
EditorGUILayout.EndHorizontal();
}
private void DrawSpace()
{
GUILayout.Space(6f);
if (Event.current.type == EventType.Repaint)
m_ButtonsAndLabelsHeights += 6f;
}
private void DrawTopBar()
{
EditorGUILayout.LabelField(s_AddControlSchemeLabel, Styles.headerLabel);
if (Event.current.type == EventType.Repaint)
m_ButtonsAndLabelsHeights += GUILayoutUtility.GetLastRect().height;
}
private void DrawNameEditTextField()
{
EditorGUILayout.BeginHorizontal();
var labelSize = EditorStyles.label.CalcSize(s_ControlSchemeNameLabel);
EditorGUILayout.LabelField(s_ControlSchemeNameLabel, GUILayout.Width(labelSize.x));
GUI.SetNextControlName("ControlSchemeName");
////FIXME: This should be a DelayedTextField but for some reason (maybe because it's in a popup?), this
//// will lead to the text field not working correctly. Hitting enter on the keyboard will apply the
//// change as expected but losing focus will *NOT*. In most cases, this makes the text field seem not
//// to work at all so instead we use a normal text field here and then apply the name change as part
//// of apply the control scheme changes as a whole. The only real downside is that if the name gets
//// adjusted automatically because of a naming conflict, this will only become evident *after* hitting
//// the "Save" button.
m_ControlScheme.m_Name = EditorGUILayout.TextField(m_ControlScheme.m_Name);
if (m_SetFocus)
{
EditorGUI.FocusTextInControl("ControlSchemeName");
m_SetFocus = false;
}
EditorGUILayout.EndHorizontal();
}
private float DrawRequirementsCheckboxes()
{
EditorGUILayout.BeginVertical();
EditorGUILayout.LabelField(s_RequirementsLabel, GUILayout.Width(200));
var requirementHeights = GUILayoutUtility.GetLastRect().y;
EditorGUI.BeginDisabledGroup(m_DeviceView.index == -1);
var requirementsOption = -1;
if (m_DeviceView.index >= 0)
{
var deviceEntryForList = (DeviceEntry)m_DeviceView.list[m_DeviceView.index];
requirementsOption = deviceEntryForList.deviceRequirement.isOptional ? 0 : 1;
}
EditorGUI.BeginChangeCheck();
requirementsOption = GUILayout.SelectionGrid(requirementsOption, s_RequiredOptionalChoices, 1, EditorStyles.radioButton);
requirementHeights += GUILayoutUtility.GetLastRect().y;
if (EditorGUI.EndChangeCheck())
m_DeviceList[m_DeviceView.index].deviceRequirement.isOptional = requirementsOption == 0;
EditorGUI.EndDisabledGroup();
EditorGUILayout.EndVertical();
return requirementHeights;
}
private void AddDeviceRequirement(InputControlScheme.DeviceRequirement requirement)
{
ArrayHelpers.Append(ref m_ControlScheme.m_DeviceRequirements, requirement);
m_DeviceList.Add(new DeviceEntry(requirement));
m_DeviceView.index = m_DeviceView.list.Count - 1;
editorWindow.Repaint();
}
///
/// The control scheme edited by the popup.
///
public InputControlScheme controlScheme => m_ControlScheme;
private int m_ControlSchemeIndex;
private InputControlScheme m_ControlScheme;
private Action m_OnApply;
private ReorderableList m_DeviceView;
private List m_DeviceList = new List();
private int m_RequirementsOptionsChoice;
private bool m_SetFocus;
private float m_ButtonsAndLabelsHeights;
private static Vector2 s_DefaultSize => new Vector2(300, 200);
private static readonly GUIContent s_RequirementsLabel = EditorGUIUtility.TrTextContent("Requirements:");
private static readonly GUIContent s_AddControlSchemeLabel = EditorGUIUtility.TrTextContent("Add control scheme");
private static readonly GUIContent s_ControlSchemeNameLabel = EditorGUIUtility.TrTextContent("Scheme Name");
private static readonly string[] s_RequiredOptionalChoices = { "Optional", "Required" };
private static class Styles
{
public static readonly GUIStyle headerLabel = new GUIStyle(EditorStyles.toolbar)
.WithAlignment(TextAnchor.MiddleCenter)
.WithFontStyle(FontStyle.Bold)
.WithPadding(new RectOffset(10, 6, 0, 0));
}
private class DeviceEntry
{
public string displayText;
public InputControlScheme.DeviceRequirement deviceRequirement;
public DeviceEntry(InputControlScheme.DeviceRequirement requirement)
{
displayText = DeviceRequirementToDisplayString(requirement);
deviceRequirement = requirement;
}
public override string ToString()
{
return displayText;
}
}
}
}
}
#endif // UNITY_EDITOR