private void AddControlLayouts(TreeViewItem parent, ref int id) { // Split root into three different groups: // 1) Control layouts // 2) Device layouts that don't match specific products // 3) Device layouts that match specific products var controls = AddChild(parent, "Controls", ref id); var devices = AddChild(parent, "Abstract Devices", ref id); var products = AddChild(parent, "Specific Devices", ref id); foreach (var layout in EditorInputControlLayoutCache.allControlLayouts) { AddControlLayoutItem(layout, controls, ref id); } foreach (var layout in EditorInputControlLayoutCache.allDeviceLayouts) { AddControlLayoutItem(layout, devices, ref id); } foreach (var layout in EditorInputControlLayoutCache.allProductLayouts) { var rootBaseLayoutName = InputControlLayout.s_Layouts.GetRootLayoutName(layout.name).ToString(); var groupName = string.IsNullOrEmpty(rootBaseLayoutName) ? "Other" : rootBaseLayoutName + "s"; var group = products.children?.FirstOrDefault(x => x.displayName == groupName); if (group == null) { group = AddChild(products, groupName, ref id); if (!string.IsNullOrEmpty(rootBaseLayoutName)) { group.icon = EditorInputControlLayoutCache.GetIconForLayout(rootBaseLayoutName); } } AddControlLayoutItem(layout, group, ref id); } controls.children?.Sort((a, b) => string.Compare(a.displayName, b.displayName)); devices.children?.Sort((a, b) => string.Compare(a.displayName, b.displayName)); if (products.children != null) { products.children.Sort((a, b) => string.Compare(a.displayName, b.displayName)); foreach (var productGroup in products.children) { productGroup.children.Sort((a, b) => string.Compare(a.displayName, b.displayName)); } } }
public ControlTreeViewItem(InputControlLayout.ControlItem control, string prefix, string deviceId, string usage) : base("") { m_Device = deviceId; m_Usage = usage; if (!string.IsNullOrEmpty(prefix)) { m_ControlPath = prefix + "/"; name = prefix + "/"; } m_ControlPath += control.name; name += control.name; id = controlPathWithDevice.GetHashCode(); m_SearchableName = InputControlPath.ToHumanReadableString(controlPathWithDevice); icon = EditorInputControlLayoutCache.GetIconForLayout(control.layout); }
private void AddUser(TreeViewItem parent, InputUser user, ref int id) { ////REVIEW: can we get better identification? allow associating GameObject with user? var userItem = AddChild(parent, "User #" + user.index, ref id); // Control scheme. var controlScheme = user.controlScheme; if (controlScheme != null) { AddChild(userItem, "Control Scheme: " + controlScheme, ref id); } // Paired devices. var pairedDevices = user.pairedDevices; if (pairedDevices.Count > 0) { var devicesItem = AddChild(userItem, "Paired Devices", ref id); foreach (var device in user.pairedDevices) { var item = new DeviceItem { id = id++, depth = devicesItem.depth + 1, displayName = device.ToString(), device = device, icon = EditorInputControlLayoutCache.GetIconForLayout(device.layout), }; devicesItem.AddChild(item); } } // Actions. var actions = user.actions; if (actions != null) { var actionsItem = AddChild(userItem, "Actions", ref id); foreach (var action in actions) { AddActionItem(actionsItem, action, ref id); } parent.children?.Sort((a, b) => string.Compare(a.displayName, b.displayName, StringComparison.CurrentCultureIgnoreCase)); } }
// This differs from RebindingOperation.GeneratePathForControl in that it cycles through all // layouts in the inheritance chain and generates a path for each one that contains the given control. private static IEnumerable <string> GeneratePossiblePathsForControl(InputControl control) { var builder = new StringBuilder(); var deviceLayoutName = control.device.m_Layout; do { // Skip layout if it is supposed to be hidden in the UI. var layout = EditorInputControlLayoutCache.TryGetLayout(deviceLayoutName); if (layout.hideInUI) { continue; } builder.Length = 0; yield return(control.BuildPath(deviceLayoutName, builder)); }while (InputControlLayout.s_Layouts.baseLayoutTable.TryGetValue(deviceLayoutName, out deviceLayoutName)); }
private void BuildControlsRecursive(Item parent, InputControlLayout layout, string prefix, ref int id) { foreach (var control in layout.controls) { if (control.isModifyingChildControlByPath) { continue; } // Skip variants. if (!string.IsNullOrEmpty(control.variants) && control.variants.ToLower() != "default") { continue; } var controlPath = prefix + control.name; var child = new Item { id = id++, depth = parent.depth + 1, displayName = controlPath, device = parent.layout.name, controlPath = controlPath, layout = layout }; var childLayout = EditorInputControlLayoutCache.TryGetLayout(control.layout); if (childLayout != null) { BuildControlsRecursive(parent, childLayout, controlPath + "/", ref id); } parent.AddChild(child); } if (parent.children != null) { parent.children.Sort((a, b) => string.Compare(a.displayName, b.displayName, StringComparison.Ordinal)); } }
private void AddDevices(TreeViewItem parent, IEnumerable <InputDevice> devices, ref int id, int participantId = InputDevice.kLocalParticipantId) { foreach (var device in devices) { if (device.m_ParticipantId != participantId) { continue; } var item = new DeviceItem { id = id++, depth = parent.depth + 1, displayName = device.name, device = device, icon = EditorInputControlLayoutCache.GetIconForLayout(device.layout), }; parent.AddChild(item); } parent.children?.Sort((a, b) => string.Compare(a.displayName, b.displayName)); }
/// <summary> /// Grab <see cref="InputSystem.settings"/> and set it up for editing. /// </summary> private void InitializeWithCurrentSettings() { // Find the set of available assets in the project. m_AvailableInputSettingsAssets = FindInputSettingsInProject(); // See which is the active one. m_Settings = InputSystem.settings; var currentSettingsPath = AssetDatabase.GetAssetPath(m_Settings); if (string.IsNullOrEmpty(currentSettingsPath)) { // The current settings aren't coming from an asset. These won't be editable // in the UI but we still have to show something. m_CurrentSelectedInputSettingsAsset = ArrayHelpers.Append(ref m_AvailableInputSettingsAssets, "<No Asset>"); EditorBuildSettings.RemoveConfigObject(kEditorBuildSettingsConfigKey); } else { m_CurrentSelectedInputSettingsAsset = ArrayHelpers.IndexOf(m_AvailableInputSettingsAssets, currentSettingsPath); if (m_CurrentSelectedInputSettingsAsset == -1) { // This is odd and shouldn't happen. Solve by just adding the path to the list. m_CurrentSelectedInputSettingsAsset = ArrayHelpers.Append(ref m_AvailableInputSettingsAssets, currentSettingsPath); } ////REVIEW: should we store this by platform? EditorBuildSettings.AddConfigObject(kEditorBuildSettingsConfigKey, m_Settings, true); } // Refresh the list of assets we display in the UI. m_AvailableSettingsAssetsOptions = new GUIContent[m_AvailableInputSettingsAssets.Length]; for (var i = 0; i < m_AvailableInputSettingsAssets.Length; ++i) { var name = m_AvailableInputSettingsAssets[i]; if (name.StartsWith("Assets/")) { name = name.Substring("Assets/".Length); } if (name.EndsWith(".asset")) { name = name.Substring(0, name.Length - ".asset".Length); } if (name.EndsWith(".inputsettings")) { name = name.Substring(0, name.Length - ".inputsettings".Length); } m_AvailableSettingsAssetsOptions[i] = new GUIContent(name); } // Look up properties. m_SettingsObject = new SerializedObject(m_Settings); m_UpdateMode = m_SettingsObject.FindProperty("m_UpdateMode"); m_ActionUpdateMode = m_SettingsObject.FindProperty("m_ActionUpdateMode"); m_TimesliceEvents = m_SettingsObject.FindProperty("m_TimesliceEvents"); m_RunInBackground = m_SettingsObject.FindProperty("m_RunInBackground"); m_CompensateForScreenOrientation = m_SettingsObject.FindProperty("m_CompensateForScreenOrientation"); m_FilterNoiseOnCurrent = m_SettingsObject.FindProperty("m_FilterNoiseOnCurrent"); m_DefaultDeadzoneMin = m_SettingsObject.FindProperty("m_DefaultDeadzoneMin"); m_DefaultDeadzoneMax = m_SettingsObject.FindProperty("m_DefaultDeadzoneMax"); m_DefaultButtonPressPoint = m_SettingsObject.FindProperty("m_DefaultButtonPressPoint"); m_DefaultTapTime = m_SettingsObject.FindProperty("m_DefaultTapTime"); m_DefaultSlowTapTime = m_SettingsObject.FindProperty("m_DefaultSlowTapTime"); m_DefaultHoldTime = m_SettingsObject.FindProperty("m_DefaultHoldTime"); // Initialize ReorderableList for list of supported devices. var supportedDevicesProperty = m_SettingsObject.FindProperty("m_SupportedDevices"); m_SupportedDevices = new ReorderableList(m_SettingsObject, supportedDevicesProperty) { drawHeaderCallback = rect => { EditorGUI.LabelField(rect, m_SupportedDevicesText); }, onChangedCallback = list => { Apply(); }, onAddDropdownCallback = (rect, list) => { var state = new AdvancedDropdownState(); var dropdown = new InputControlPickerDropdown(state, path => { var layoutName = InputControlPath.TryGetDeviceLayout(path) ?? path; var numDevices = supportedDevicesProperty.arraySize; supportedDevicesProperty.InsertArrayElementAtIndex(numDevices); supportedDevicesProperty.GetArrayElementAtIndex(numDevices) .stringValue = layoutName; Apply(); }, InputControlPickerDropdown.Mode.PickDevice); dropdown.Show(rect); }, drawElementCallback = (rect, index, isActive, isFocused) => { var layoutName = m_Settings.supportedDevices[index]; var icon = EditorInputControlLayoutCache.GetIconForLayout(layoutName); if (icon != null) { var iconRect = rect; iconRect.width = 20; rect.x += 20; rect.width -= 20; GUI.Label(iconRect, icon); } EditorGUI.LabelField(rect, m_Settings.supportedDevices[index]); } }; }
private void AddControlItem(InputControlLayout.ControlItem control, TreeViewItem parent, ref int id) { var item = AddChild(parent, control.variants.IsEmpty() ? control.name : string.Format("{0} ({1})", control.name, control.variants), ref id); if (!control.layout.IsEmpty()) { item.icon = EditorInputControlLayoutCache.GetIconForLayout(control.layout); } ////TODO: fully merge TreeViewItems from isModifyingChildControlByPath control layouts into the control they modify ////TODO: allow clicking this field to jump to the layout if (!control.layout.IsEmpty()) { AddChild(item, string.Format("Layout: {0}", control.layout), ref id); } if (!control.variants.IsEmpty()) { AddChild(item, string.Format("Variant: {0}", control.variants), ref id); } if (!string.IsNullOrEmpty(control.displayName)) { AddChild(item, string.Format("Display Name: {0}", control.displayName), ref id); } if (!string.IsNullOrEmpty(control.shortDisplayName)) { AddChild(item, string.Format("Short Display Name: {0}", control.shortDisplayName), ref id); } if (control.format != 0) { AddChild(item, string.Format("Format: {0}", control.format), ref id); } if (control.offset != InputStateBlock.kInvalidOffset) { AddChild(item, string.Format("Offset: {0}", control.offset), ref id); } if (control.bit != InputStateBlock.kInvalidOffset) { AddChild(item, string.Format("Bit: {0}", control.bit), ref id); } if (control.sizeInBits != 0) { AddChild(item, string.Format("Size In Bits: {0}", control.sizeInBits), ref id); } if (control.isArray) { AddChild(item, string.Format("Array Size: {0}", control.arraySize), ref id); } if (!string.IsNullOrEmpty(control.useStateFrom)) { AddChild(item, string.Format("Use State From: {0}", control.useStateFrom), ref id); } if (!control.defaultState.isEmpty) { AddChild(item, string.Format("Default State: {0}", control.defaultState.ToString()), ref id); } if (!control.minValue.isEmpty) { AddChild(item, string.Format("Min Value: {0}", control.minValue.ToString()), ref id); } if (!control.maxValue.isEmpty) { AddChild(item, string.Format("Max Value: {0}", control.maxValue.ToString()), ref id); } if (control.usages.Count > 0) { AddChild(item, "Usages: " + string.Join(", ", control.usages.Select(x => x.ToString()).ToArray()), ref id); } if (control.aliases.Count > 0) { AddChild(item, "Aliases: " + string.Join(", ", control.aliases.Select(x => x.ToString()).ToArray()), ref id); } if (control.isNoisy || control.isSynthetic) { var flags = "Flags: "; if (control.isNoisy) { flags += "Noisy"; } if (control.isSynthetic) { if (control.isNoisy) { flags += ", Synthetic"; } else { flags += "Synthetic"; } } AddChild(item, flags, ref id); } if (control.parameters.Count > 0) { var parameters = AddChild(item, "Parameters", ref id); foreach (var parameter in control.parameters) { AddChild(parameters, parameter.ToString(), ref id); } } if (control.processors.Count > 0) { var processors = AddChild(item, "Processors", ref id); foreach (var processor in control.processors) { var processorItem = AddChild(processors, processor.name, ref id); foreach (var parameter in processor.parameters) { AddChild(processorItem, parameter.ToString(), ref id); } } } }
protected override TreeViewItem BuildRoot() { var id = 0; var root = new TreeViewItem { id = id++, depth = -1 }; // Actions. m_EnabledActions.Clear(); InputSystem.ListEnabledActions(m_EnabledActions); if (m_EnabledActions.Count > 0) { actionsItem = AddChild(root, string.Format("Actions ({0})", m_EnabledActions.Count), ref id); AddEnabledActions(actionsItem, ref id); } // Users. ////TODO // Devices. var devices = InputSystem.devices; devicesItem = AddChild(root, string.Format("Devices ({0})", devices.Count), ref id); var haveRemotes = devices.Any(x => x.remote); TreeViewItem localDevicesNode = null; if (haveRemotes) { // Split local and remote devices into groups. localDevicesNode = AddChild(devicesItem, "Local", ref id); AddDevices(localDevicesNode, devices, ref id); var remoteDevicesNode = AddChild(devicesItem, "Remote", ref id); foreach (var player in EditorConnection.instance.ConnectedPlayers) { var playerNode = AddChild(remoteDevicesNode, player.name, ref id); AddDevices(playerNode, devices, ref id, "Remote" + player.playerId + InputControlLayout.kNamespaceQualifier); } } else { // We don't have remote devices so don't add an extra group for local devices. // Put them all directly underneath the "Devices" node. AddDevices(devicesItem, devices, ref id); } ////TDO: unsupported and disconnected devices should also be shown for remotes if (m_UnsupportedDevices == null) { m_UnsupportedDevices = new List <InputDeviceDescription>(); } m_UnsupportedDevices.Clear(); InputSystem.GetUnsupportedDevices(m_UnsupportedDevices); if (m_UnsupportedDevices.Count > 0) { var parent = haveRemotes ? localDevicesNode : devicesItem; var unsupportedDevicesNode = AddChild(parent, string.Format("Unsupported ({0})", m_UnsupportedDevices.Count), ref id); foreach (var device in m_UnsupportedDevices) { AddChild(unsupportedDevicesNode, device.ToString(), ref id); } unsupportedDevicesNode.children.Sort((a, b) => string.Compare(a.displayName, b.displayName)); } var disconnectedDevices = InputSystem.disconnectedDevices; if (disconnectedDevices.Count > 0) { var parent = haveRemotes ? localDevicesNode : devicesItem; var disconnectedDevicesNode = AddChild(parent, string.Format("Disconnected ({0})", disconnectedDevices.Count), ref id); foreach (var device in disconnectedDevices) { AddChild(disconnectedDevicesNode, device.ToString(), ref id); } disconnectedDevicesNode.children.Sort((a, b) => string.Compare(a.displayName, b.displayName)); } // Layouts. layoutsItem = AddChild(root, "Layouts", ref id); AddControlLayouts(layoutsItem, ref id); ////FIXME: this shows local configuration only // Settings. var settings = InputSystem.settings; var settingsAssetPath = AssetDatabase.GetAssetPath(settings); var settingsLabel = "Settings"; if (settingsAssetPath != null) { settingsLabel = string.Format("Settings ({0})", Path.GetFileName(settingsAssetPath)); } settingsItem = AddChild(root, settingsLabel, ref id); AddValueItem(settingsItem, "Update Mode", settings.updateMode, ref id); AddValueItem(settingsItem, "Timeslice Events", settings.timesliceEvents, ref id); AddValueItem(settingsItem, "Run In Background", settings.runInBackground, ref id); AddValueItem(settingsItem, "Compensate For Screen Orientation", settings.compensateForScreenOrientation, ref id); AddValueItem(settingsItem, "Filter Noise On .current", settings.filterNoiseOnCurrent, ref id); AddValueItem(settingsItem, "Default Button Press Point", settings.defaultButtonPressPoint, ref id); AddValueItem(settingsItem, "Default Deadzone Min", settings.defaultDeadzoneMin, ref id); AddValueItem(settingsItem, "Default Deadzone Max", settings.defaultDeadzoneMax, ref id); AddValueItem(settingsItem, "Default Tap Time", settings.defaultTapTime, ref id); AddValueItem(settingsItem, "Default Slow Tap Time", settings.defaultSlowTapTime, ref id); AddValueItem(settingsItem, "Default Hold Time", settings.defaultHoldTime, ref id); AddValueItem(settingsItem, "Default Sensitivity", settings.defaultSensitivity, ref id); AddValueItem(settingsItem, "Lock Input To Game View", InputEditorUserSettings.lockInputToGameView, ref id); if (settings.supportedDevices.Count > 0) { var supportedDevices = AddChild(settingsItem, "Supported Devices", ref id); foreach (var item in settings.supportedDevices) { var icon = EditorInputControlLayoutCache.GetIconForLayout(item); AddChild(supportedDevices, item, ref id, icon); } } settingsItem.children.Sort((a, b) => string.Compare(a.displayName, b.displayName)); // Metrics. var metrics = InputSystem.GetMetrics(); metricsItem = AddChild(root, "Metrics", ref id); AddChild(metricsItem, "Current State Size in Bytes: " + StringHelpers.NicifyMemorySize(metrics.currentStateSizeInBytes), ref id); AddValueItem(metricsItem, "Current Control Count", metrics.currentControlCount, ref id); AddValueItem(metricsItem, "Current Layout Count", metrics.currentLayoutCount, ref id); return(root); }
private void DrawInteractivePickButton(Rect rect) { if (s_PickButtonIcon == null) { s_PickButtonIcon = EditorInputControlLayoutCache.GetIconForLayout("Button"); } var toggleRebind = GUI.Toggle(rect, m_RebindingOperation != null && m_RebindingOperation.started, s_PickButtonIcon, EditorStyles.miniButton); if (toggleRebind && (m_RebindingOperation == null || !m_RebindingOperation.started)) { // Start rebind. if (m_RebindingOperation == null) { m_RebindingOperation = new InputActionRebindingExtensions.RebindingOperation(); } ////TODO: if we have multiple candidates that we can't trivially decide between, let user choose m_RebindingOperation .WithExpectedControlLayout(m_ExpectedControlLayout) // Require minimum actuation of 0.15f. This is after deadzoning has been applied. .WithMagnitudeHavingToBeGreaterThan(0.15f) // After 4 seconds, cancel the operation. .WithTimeout(4) ////REVIEW: the delay makes it more robust but doesn't feel good // Give us a buffer of 0.25 seconds to see if a better match comes along. .OnMatchWaitForAnother(0.25f) ////REVIEW: should we exclude only the system's active pointing device? // With the mouse operating the UI, its cursor control is too fickle a thing to // bind to. Ignore mouse position and delta. // NOTE: We go for all types of pointers here, not just mice. .WithControlsExcluding("<Pointer>/position") .WithControlsExcluding("<Pointer>/delta") .OnCancel( operation => { ////REVIEW: Is there a better way to do this? All we want is for the *current* UI to repaint but Unity's //// editor API seems to have no way to retrieve the current EditorWindow from inside an OnGUI callback. //// So we'd have to pass the EditorWindow in here all the way from the EditorWindow.OnGUI() callback //// itself. InternalEditorUtility.RepaintAllViews(); if (m_NeedToClearProgressBar) { EditorUtility.ClearProgressBar(); } }) .OnComplete( operation => { if (m_NeedToClearProgressBar) { EditorUtility.ClearProgressBar(); } }) .OnApplyBinding( (operation, newPath) => { pathProperty.stringValue = newPath; pathProperty.serializedObject.ApplyModifiedProperties(); onModified(); }); // If we have control paths to match, pass them on. m_RebindingOperation.WithoutControlsHavingToMatchPath(); if (m_ControlPathsToMatch.LengthSafe() > 0) { m_ControlPathsToMatch.Select(x => m_RebindingOperation.WithControlsHavingToMatchPath(x)); } m_RebindingOperation.Start(); } else if (!toggleRebind && m_RebindingOperation != null && m_RebindingOperation.started) { m_RebindingOperation.Cancel(); } }