예제 #1
0
        private void AddDeviceTreeItem(InputControlLayout layout, AdvancedDropdownItem parent)
        {
            // Ignore devices that have no controls. We're looking at fully merged layouts here so
            // we're also taking inherited controls into account.
            // EXCEPTION: We're okay with empty devices if we're picking devices and not controls.
            if (layout.controls.Count == 0 && m_Mode != Mode.PickDevice)
            {
                return;
            }

            var deviceItem = new DeviceTreeViewItem(layout);

            // If we have a device filter, see if we should ignore the device.
            if (m_DeviceFilter != null && m_DeviceFilter.Length > 0)
            {
                var matchesAnyInDeviceFilter = false;
                foreach (var entry in m_DeviceFilter)
                {
                    if (entry == layout.name ||
                        InputControlLayout.s_Layouts.IsBasedOn(new InternedString(entry), layout.name))
                    {
                        matchesAnyInDeviceFilter = true;
                    }
                    else
                    {
                        ////FIXME: this also needs to work for full control paths and not just stuff like "<Gamepad>"
                        var expectedLayout = InputControlPath.TryGetDeviceLayout(entry);
                        if (!string.IsNullOrEmpty(expectedLayout) &&
                            (expectedLayout == layout.name ||
                             InputControlLayout.s_Layouts.IsBasedOn(new InternedString(expectedLayout), layout.name)))
                        {
                            matchesAnyInDeviceFilter = true;
                        }
                    }
                }

                if (!matchesAnyInDeviceFilter)
                {
                    return;
                }
            }

            if (m_Mode != Mode.PickDevice)
            {
                AddControlTreeItemsRecursive(layout, deviceItem, "", layout.name, null);
            }

            if (deviceItem.children.Any() || m_Mode == Mode.PickDevice)
            {
                parent.AddChild(deviceItem);
            }

            if (m_Mode != Mode.PickDevice)
            {
                foreach (var commonUsage in layout.commonUsages)
                {
                    var commonUsageGroup = new DeviceTreeViewItem(layout, commonUsage);
                    AddControlTreeItemsRecursive(layout, commonUsageGroup, "", layout.name, commonUsage);
                    if (commonUsageGroup.children.Any())
                    {
                        parent.AddChild(commonUsageGroup);
                    }
                }
            }
        }
예제 #2
0
        /// <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))
            {
                if (m_AvailableInputSettingsAssets.Length != 0)
                {
                    m_CurrentSelectedInputSettingsAsset = 0;
                    m_Settings           = AssetDatabase.LoadAssetAtPath <InputSettings>(m_AvailableInputSettingsAssets[0]);
                    InputSystem.settings = m_Settings;
                }
            }
            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);
                }

                // Ugly hack: GenericMenu iterprets "/" as a submenu path. But luckily, "/" is not the only slash we have in Unicode.
                m_AvailableSettingsAssetsOptions[i] = new GUIContent(name.Replace("/", "\u29f8"));
            }

            // Look up properties.
            m_SettingsObject = new SerializedObject(m_Settings);
            m_UpdateMode     = m_SettingsObject.FindProperty("m_UpdateMode");
            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");
            m_TapRadius          = m_SettingsObject.FindProperty("m_TapRadius");
            m_MultiTapDelayTime  = m_SettingsObject.FindProperty("m_MultiTapDelayTime");

            m_UpdateModeContent                     = new GUIContent("Update Mode", "When should the Input System be updated?");
            m_FilterNoiseOnCurrentContent           = new GUIContent("Filter Noise on current", "If enabled, input from noisy controls will not cause a device to become '.current'.");
            m_CompensateForScreenOrientationContent = new GUIContent("Compensate Orientation", "Whether sensor input on mobile devices should be transformed to be relative to the current device orientation.");
            m_DefaultDeadzoneMinContent             = new GUIContent("Default Deadzone Min", "Default 'min' value for Stick Deadzone and Axis Deadzone processors.");
            m_DefaultDeadzoneMaxContent             = new GUIContent("Default Deadzone Max", "Default 'max' value for Stick Deadzone and Axis Deadzone processors.");
            m_DefaultButtonPressPointContent        = new GUIContent("Default Button Press Point", "The default press point used for Button controls as well as for various interactions. For button controls which have analog physical inputs, this configures how far they need to   be held down to be considered 'pressed'.");
            m_DefaultTapTimeContent                 = new GUIContent("Default Tap Time", "Default duration to be used for Tap and MultiTap interactions. Also used by by Touch screen devices to distinguish taps from to new touches.");
            m_DefaultSlowTapTimeContent             = new GUIContent("Default Slow Tap Time", "Default duration to be used for SlowTap interactions.");
            m_DefaultHoldTimeContent                = new GUIContent("Default Hold Time", "Default duration to be used for Hold interactions.");
            m_TapRadiusContent         = new GUIContent("Tap Radius", "Maximum distance between two finger taps on a touch screen device allowed for the system to consider this a tap of the same touch (as opposed to a new touch).");
            m_MultiTapDelayTimeContent = new GUIContent("MultiTap Delay Time", "Default delay to be allowed between taps for MultiTap interactions. Also used by by touch devices to count multi taps.");

            // 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 dropdown = new InputControlPickerDropdown(
                        new InputControlPickerState(),
                        path =>
                    {
                        var layoutName    = InputControlPath.TryGetDeviceLayout(path) ?? path;
                        var existingIndex = m_Settings.supportedDevices.IndexOf(x => x == layoutName);
                        if (existingIndex != -1)
                        {
                            m_SupportedDevices.index = existingIndex;
                            return;
                        }
                        var numDevices = supportedDevicesProperty.arraySize;
                        supportedDevicesProperty.InsertArrayElementAtIndex(numDevices);
                        supportedDevicesProperty.GetArrayElementAtIndex(numDevices)
                        .stringValue             = layoutName;
                        m_SupportedDevices.index = numDevices;
                        Apply();
                    },
                        mode: InputControlPicker.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]);
                }
            };
        }
        public void DrawBindingGUI(ref bool manualPathEditMode)
        {
            EditorGUILayout.BeginHorizontal();

            var lineRect  = GUILayoutUtility.GetRect(0, EditorGUIUtility.singleLineHeight);
            var labelRect = lineRect;

            labelRect.width = 60;
            EditorGUI.LabelField(labelRect, s_BindingGui);
            lineRect.x     += 65;
            lineRect.width -= 65;

            var bindingTextRect           = lineRect;
            var editButtonRect            = lineRect;
            var interactivePickButtonRect = lineRect;

            bindingTextRect.width           -= 42;
            editButtonRect.x                += bindingTextRect.width + 21;
            editButtonRect.width             = 21;
            editButtonRect.height            = 15;
            interactivePickButtonRect.x     += bindingTextRect.width;
            interactivePickButtonRect.width  = 21;
            interactivePickButtonRect.height = 15;

            var path = m_PathProperty.stringValue;
            ////TODO: this should be cached; generates needless GC churn
            var displayName = InputControlPath.ToHumanReadableString(path);

            if (manualPathEditMode || (!string.IsNullOrEmpty(path) && string.IsNullOrEmpty(displayName)))
            {
                EditorGUI.BeginChangeCheck();
                path = EditorGUI.DelayedTextField(bindingTextRect, path);
                if (EditorGUI.EndChangeCheck())
                {
                    m_PathProperty.stringValue = path;
                    m_PathProperty.serializedObject.ApplyModifiedProperties();
                    m_OnModified();
                }

                m_DrawInteractivePickButton?.Invoke(interactivePickButtonRect, m_PathProperty, m_OnModified);
                if (GUI.Button(editButtonRect, "˅"))
                {
                    bindingTextRect.x += editButtonRect.width;
                    ShowInputControlPicker(bindingTextRect);
                }
            }
            else
            {
                // Dropdown that shows binding text and allows opening control picker.
                if (EditorGUI.DropdownButton(bindingTextRect, new GUIContent(displayName), FocusType.Keyboard))
                {
                    ////TODO: pass expectedControlLayout filter on to control picker
                    ////TODO: for bindings that are part of composites, use the layout information from the [InputControl] attribute on the field
                    ShowInputControlPicker(bindingTextRect);
                }

                // Button to bind interactively.
                m_DrawInteractivePickButton?.Invoke(interactivePickButtonRect, m_PathProperty, m_OnModified);

                // Button that switches binding into text edit mode.
                if (GUI.Button(editButtonRect, "...", EditorStyles.miniButton))
                {
                    manualPathEditMode = true;
                }
            }

            EditorGUILayout.EndHorizontal();
        }
예제 #4
0
 private void UpdateUI()
 {
     bindingDisplayNameText.text = InputControlPath.ToHumanReadableString(action[0].action.bindings[index].effectivePath, InputControlPath.HumanReadableStringOptions.OmitDevice);
     button.animator.SetTrigger("Disabled");
 }
예제 #5
0
 private void SetBindingText()
 {
     bindingText.text = InputControlPath.ToHumanReadableString(actionReference.action.bindings[bindingsIndex].effectivePath);
 }
예제 #6
0
 public void GetCurrentKey()
 {
     receiver.text = InputControlPath.ToHumanReadableString(action.bindings[0].effectivePath);
 }
예제 #7
0
        /// <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;
            m_SettingsDirtyCount = EditorUtility.GetDirtyCount(m_Settings);
            var currentSettingsPath = AssetDatabase.GetAssetPath(m_Settings);

            if (string.IsNullOrEmpty(currentSettingsPath))
            {
                if (m_AvailableInputSettingsAssets.Length != 0)
                {
                    m_CurrentSelectedInputSettingsAsset = 0;
                    m_Settings           = AssetDatabase.LoadAssetAtPath <InputSettings>(m_AvailableInputSettingsAssets[0]);
                    InputSystem.settings = m_Settings;
                }
            }
            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);
                }

                // Ugly hack: GenericMenu interprets "/" as a submenu path. But luckily, "/" is not the only slash we have in Unicode.
                m_AvailableSettingsAssetsOptions[i] = new GUIContent(name.Replace("/", "\u29f8"));
            }

            // Look up properties.
            m_SettingsObject = new SerializedObject(m_Settings);
            m_UpdateMode     = m_SettingsObject.FindProperty("m_UpdateMode");
            m_CompensateForScreenOrientation = m_SettingsObject.FindProperty("m_CompensateForScreenOrientation");
            m_BackgroundBehavior             = m_SettingsObject.FindProperty("m_BackgroundBehavior");
            m_EditorInputBehaviorInPlayMode  = m_SettingsObject.FindProperty("m_EditorInputBehaviorInPlayMode");
            m_DefaultDeadzoneMin             = m_SettingsObject.FindProperty("m_DefaultDeadzoneMin");
            m_DefaultDeadzoneMax             = m_SettingsObject.FindProperty("m_DefaultDeadzoneMax");
            m_DefaultButtonPressPoint        = m_SettingsObject.FindProperty("m_DefaultButtonPressPoint");
            m_ButtonReleaseThreshold         = m_SettingsObject.FindProperty("m_ButtonReleaseThreshold");
            m_DefaultTapTime     = m_SettingsObject.FindProperty("m_DefaultTapTime");
            m_DefaultSlowTapTime = m_SettingsObject.FindProperty("m_DefaultSlowTapTime");
            m_DefaultHoldTime    = m_SettingsObject.FindProperty("m_DefaultHoldTime");
            m_TapRadius          = m_SettingsObject.FindProperty("m_TapRadius");
            m_MultiTapDelayTime  = m_SettingsObject.FindProperty("m_MultiTapDelayTime");

            m_UpdateModeContent = new GUIContent("Update Mode", "When should the Input System be updated?");
            m_CompensateForScreenOrientationContent = new GUIContent("Compensate Orientation", "Whether sensor input on mobile devices should be transformed to be relative to the current device orientation.");
            m_BackgroundBehaviorContent             = new GUIContent("Background Behavior", "If runInBackground is true (and in standalone *development* players and the editor), "
                                                                     + "determines what happens to InputDevices and events when the application moves in and out of running in the foreground.\n\n"
                                                                     + "'Reset And Disable Non-Background Devices' soft-resets and disables devices that cannot run in the background while the application does not have focus. Devices "
                                                                     + "that can run in the background remain enabled and will keep receiving input.\n"
                                                                     + "'Reset And Disable All Devices' soft-resets and disables *all* devices while the application does not have focus. No device will receive input while the application "
                                                                     + "is running in the background.\n"
                                                                     + "'Ignore Focus' leaves all devices untouched when application focus changes. While running in the background, all input that is received is processed as if "
                                                                     + "running in the foreground.");
            m_EditorInputBehaviorInPlayModeContent = new GUIContent("Play Mode Input Behavior", "When in play mode, determines how focus of the Game View is handled with respect to input.\n\n"
                                                                    + "'Pointers And Keyboards Respect Game View Focus' requires Game View focus only for pointers (mice, touch, etc.) and keyboards. Other devices will feed input to the game regardless "
                                                                    + "of whether the Game View is focused or not. Note that this means that input on these devices is not visible in other EditorWindows.\n"
                                                                    + "'All Devices Respect Game View Focus' requires Game View focus for all input devices. While focus is not on the Game View, all input on InputDevices will go to the editor and not "
                                                                    + "the game.\n"
                                                                    + "'All Device Input Always Goes To Game View' causes input to treat 'Background Behavior' exactly as in the player including devices potentially being disabled entirely while the Game View "
                                                                    + "does not have focus. In this setting, no input from the Input System will be visible to EditorWindows.");
            m_DefaultDeadzoneMinContent      = new GUIContent("Default Deadzone Min", "Default 'min' value for Stick Deadzone and Axis Deadzone processors.");
            m_DefaultDeadzoneMaxContent      = new GUIContent("Default Deadzone Max", "Default 'max' value for Stick Deadzone and Axis Deadzone processors.");
            m_DefaultButtonPressPointContent = new GUIContent("Default Button Press Point", "The default press point used for Button controls as well as for various interactions. For button controls which have analog physical inputs, this configures how far they need to   be held down to be considered 'pressed'.");
            m_ButtonReleaseThresholdContent  = new GUIContent("Button Release Threshold", "Percent of press point at which a Button is considered released again. At 1, release points are identical to press points. At 0, a Button must be fully released before it can be pressed again.");
            m_DefaultTapTimeContent          = new GUIContent("Default Tap Time", "Default duration to be used for Tap and MultiTap interactions. Also used by by Touch screen devices to distinguish taps from to new touches.");
            m_DefaultSlowTapTimeContent      = new GUIContent("Default Slow Tap Time", "Default duration to be used for SlowTap interactions.");
            m_DefaultHoldTimeContent         = new GUIContent("Default Hold Time", "Default duration to be used for Hold interactions.");
            m_TapRadiusContent         = new GUIContent("Tap Radius", "Maximum distance between two finger taps on a touch screen device allowed for the system to consider this a tap of the same touch (as opposed to a new touch).");
            m_MultiTapDelayTimeContent = new GUIContent("MultiTap Delay Time", "Default delay to be allowed between taps for MultiTap interactions. Also used by by touch devices to count multi taps.");

            // 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 dropdown = new InputControlPickerDropdown(
                        new InputControlPickerState(),
                        path =>
                    {
                        ////REVIEW: Why are we converting from a layout into a plain string here instead of just using path strings in supportedDevices?
                        ////        Why not just have InputSettings.supportedDevices be a list of paths?
                        var layoutName    = InputControlPath.TryGetDeviceLayout(path) ?? path;
                        var existingIndex = m_Settings.supportedDevices.IndexOf(x => x == layoutName);
                        if (existingIndex != -1)
                        {
                            m_SupportedDevices.index = existingIndex;
                            return;
                        }
                        var numDevices = supportedDevicesProperty.arraySize;
                        supportedDevicesProperty.InsertArrayElementAtIndex(numDevices);
                        supportedDevicesProperty.GetArrayElementAtIndex(numDevices)
                        .stringValue             = layoutName;
                        m_SupportedDevices.index = numDevices;
                        Apply();
                    },
                        mode: InputControlPicker.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, layoutName);
                }
            };

            m_iOSProvider = new InputSettingsiOSProvider(m_SettingsObject);
        }
        private void SetupInputControl()
        {
            Debug.Assert(m_Control == null);
            Debug.Assert(m_NextControlOnDevice == null);
            Debug.Assert(!m_InputEventPtr.valid);

            // Nothing to do if we don't have a control path.
            if (string.IsNullOrEmpty(m_ControlPath))
            {
                return;
            }

            // Determine what type of device to work with.
            var layoutName = InputControlPath.TryGetDeviceLayout(m_ControlPath);

            if (layoutName == null)
            {
                Debug.LogError(
                    string.Format(
                        "Cannot determine device layout to use based on control path '{0}' used in {1} component",
                        m_ControlPath, GetType().Name), this);
                return;
            }

            // Try to find existing on-screen device that matches.
            var internedLayoutName = new InternedString(layoutName);
            var deviceInfoIndex    = -1;

            for (var i = 0; i < s_OnScreenDevices.length; ++i)
            {
                if (s_OnScreenDevices[i].device.m_Layout == internedLayoutName)
                {
                    deviceInfoIndex = i;
                    break;
                }
            }

            // If we don't have a matching one, create a new one.
            InputDevice device;

            if (deviceInfoIndex == -1)
            {
                // Try to create device.
                try
                {
                    device = InputSystem.AddDevice(layoutName);
                }
                catch (Exception exception)
                {
                    Debug.LogError(string.Format("Could not create device with layout '{0}' used in '{1}' component", layoutName,
                                                 GetType().Name));
                    Debug.LogException(exception);
                    return;
                }

                // Create event buffer.
                InputEventPtr eventPtr;
                var           buffer = StateEvent.From(device, out eventPtr, Allocator.Persistent);

                // Add to list.
                deviceInfoIndex = s_OnScreenDevices.Append(new OnScreenDeviceInfo
                {
                    eventPtr = eventPtr,
                    buffer   = buffer,
                    device   = device,
                });
            }
            else
            {
                device = s_OnScreenDevices[deviceInfoIndex].device;
            }

            // Try to find control on device.
            m_Control = InputControlPath.TryFindControl(device, m_ControlPath);
            if (m_Control == null)
            {
                Debug.LogError(
                    string.Format(
                        "Cannot find control with path '{0}' on device of type '{1}' referenced by component '{2}'",
                        m_ControlPath, layoutName, GetType().Name), this);

                // Remove the device, if we just created one.
                if (s_OnScreenDevices[deviceInfoIndex].firstControl == null)
                {
                    s_OnScreenDevices[deviceInfoIndex].Destroy();
                    s_OnScreenDevices.RemoveAt(deviceInfoIndex);
                }

                return;
            }
            m_InputEventPtr = s_OnScreenDevices[deviceInfoIndex].eventPtr;

            // We have all we need. Permanently add us.
            s_OnScreenDevices[deviceInfoIndex] =
                s_OnScreenDevices[deviceInfoIndex].AddControl(this);
        }
예제 #9
0
        /// <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))
            {
                if (m_AvailableInputSettingsAssets.Length != 0)
                {
                    m_CurrentSelectedInputSettingsAsset = 0;
                    m_Settings           = AssetDatabase.LoadAssetAtPath <InputSettings>(m_AvailableInputSettingsAssets[0]);
                    InputSystem.settings = m_Settings;
                }
            }
            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);
                }

                // Ugly hack: GenericMenu iterprets "/" as a submenu path. But luckily, "/" is not the only slash we have in Unicode.
                m_AvailableSettingsAssetsOptions[i] = new GUIContent(name.Replace("/", "\u29f8"));
            }

            // Look up properties.
            m_SettingsObject = new SerializedObject(m_Settings);
            m_UpdateMode     = m_SettingsObject.FindProperty("m_UpdateMode");
            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");
            m_TapRadius          = m_SettingsObject.FindProperty("m_TapRadius");
            m_MultiTapDelayTime  = m_SettingsObject.FindProperty("m_MultiTapDelayTime");

            // 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 dropdown = new InputControlPickerDropdown(
                        new InputControlPickerState(),
                        path =>
                    {
                        var layoutName    = InputControlPath.TryGetDeviceLayout(path) ?? path;
                        var existingIndex = m_Settings.supportedDevices.IndexOf(x => x == layoutName);
                        if (existingIndex != -1)
                        {
                            m_SupportedDevices.index = existingIndex;
                            return;
                        }
                        var numDevices = supportedDevicesProperty.arraySize;
                        supportedDevicesProperty.InsertArrayElementAtIndex(numDevices);
                        supportedDevicesProperty.GetArrayElementAtIndex(numDevices)
                        .stringValue             = layoutName;
                        m_SupportedDevices.index = numDevices;
                        Apply();
                    },
                        mode: InputControlPicker.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]);
                }
            };
        }
예제 #10
0
        /// <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");
            m_DefaultSensitivity = m_SettingsObject.FindProperty("m_DefaultSensitivity");

            // 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]);
                }
            };
        }
        public void OnGUI(Rect rect)
        {
            var lineRect  = rect;
            var labelRect = lineRect;

            labelRect.width = EditorGUIUtility.labelWidth;
            EditorGUI.LabelField(labelRect, s_PathLabel);
            lineRect.x     += labelRect.width;
            lineRect.width -= labelRect.width;

            var bindingTextRect           = lineRect;
            var editButtonRect            = lineRect;
            var interactivePickButtonRect = lineRect;

            bindingTextRect.width           -= 42;
            editButtonRect.x                += bindingTextRect.width + 21;
            editButtonRect.width             = 21;
            editButtonRect.height            = 15;
            interactivePickButtonRect.x     += bindingTextRect.width;
            interactivePickButtonRect.width  = 21;
            interactivePickButtonRect.height = 15;

            var path = pathProperty.stringValue;
            ////TODO: this should be cached; generates needless GC churn
            var displayName = InputControlPath.ToHumanReadableString(path);

            // Either show dropdown control that opens path picker or show path directly as
            // text, if manual path editing is toggled on.
            if (m_PickerState.manualPathEditMode)
            {
                ////FIXME: for some reason the text field does not fill all the rect but rather adds large padding on the left
                EditorGUI.BeginChangeCheck();
                path = EditorGUI.DelayedTextField(bindingTextRect, path);
                if (EditorGUI.EndChangeCheck())
                {
                    pathProperty.stringValue = path;
                    pathProperty.serializedObject.ApplyModifiedProperties();
                    onModified();
                }
            }
            else
            {
                // Dropdown that shows binding text and allows opening control picker.
                if (EditorGUI.DropdownButton(bindingTextRect, new GUIContent(displayName), FocusType.Keyboard))
                {
                    ////TODO: for bindings that are part of composites, use the layout information from the [InputControl] attribute on the field
                    ShowDropdown(bindingTextRect);
                }
            }

            // Button to bind interactively.
            DrawInteractivePickButton(interactivePickButtonRect);

            // Button to toggle between text edit mode.
            m_PickerState.manualPathEditMode = GUI.Toggle(editButtonRect, m_PickerState.manualPathEditMode, "T",
                                                          EditorStyles.miniButton);

            if (m_RebindingOperation != null && m_RebindingOperation.started)
            {
                DrawInteractivePickingProgressBar();
            }
        }
예제 #12
0
 public override string ToString()
 {
     return(string.Format("{0} {1}", InputControlPath.ToHumanReadableString(name), commonUsage));
 }
예제 #13
0
        public CompositeTreeItem(string actionMapName, SerializedProperty bindingProperty, int index)
            : base(actionMapName, bindingProperty, index)
        {
            var path = elementProperty.FindPropertyRelative("m_Path").stringValue;

            displayName = elementProperty.FindPropertyRelative("m_Name").stringValue + ": " + InputControlPath.ToHumanReadableString(path);
        }
예제 #14
0
    private void Start()
    {
        int bindingIndex = RebindingAction.action.GetBindingIndexForControl(RebindingAction.action.controls[0]);

        bindingDisplayNameText.text = InputControlPath.ToHumanReadableString(RebindingAction.action.bindings[bindingIndex].effectivePath, InputControlPath.HumanReadableStringOptions.OmitDevice);
    }
예제 #15
0
        private InputControl InstantiateLayout(InputControlLayout layout, InternedString variants, InternedString name, InputControl parent)
        {
            Debug.Assert(layout.type != null);

            // No, so create a new control.
            var controlObject = Activator.CreateInstance(layout.type);

            if (!(controlObject is InputControl control))
            {
                throw new InvalidOperationException(
                          $"Type '{layout.type.Name}' referenced by layout '{layout.name}' is not an InputControl");
            }

            // If it's a device, perform some extra work specific to the control
            // hierarchy root.
            if (control is InputDevice controlAsDevice)
            {
                if (parent != null)
                {
                    throw new InvalidOperationException(
                              $"Cannot instantiate device layout '{layout.name}' as child of '{parent.path}'; devices must be added at root");
                }

                m_Device = controlAsDevice;
                m_Device.m_StateBlock.byteOffset = 0;
                m_Device.m_StateBlock.bitOffset  = 0;
                m_Device.m_StateBlock.format     = layout.stateFormat;

                // If we have an existing device, we'll start the various control arrays
                // from scratch. Note that all the controls still refer to the existing
                // arrays and so we can iterate children, for example, just fine while
                // we are rebuilding the control hierarchy.
                m_Device.m_AliasesForEachControl  = null;
                m_Device.m_ChildrenForEachControl = null;
                m_Device.m_UsagesForEachControl   = null;
                m_Device.m_UsageToControl         = null;

                if (layout.m_UpdateBeforeRender == true)
                {
                    m_Device.m_DeviceFlags |= InputDevice.DeviceFlags.UpdateBeforeRender;
                }
            }
            else if (parent == null)
            {
                // Someone did "new InputDeviceBuilder(...)" with a control layout.
                // We don't support creating control hierarchies without a device at the root.
                throw new InvalidOperationException(
                          $"Toplevel layout used with InputDeviceBuilder must be a device layout; '{layout.name}' is a control layout");
            }

            // Name defaults to name of layout.
            if (name.IsEmpty())
            {
                name = layout.name;

                // If there's a namespace in the layout name, snip it out.
                var indexOfLastColon = name.ToString().LastIndexOf(':');
                if (indexOfLastColon != -1)
                {
                    name = new InternedString(name.ToString().Substring(indexOfLastColon + 1));
                }
            }

            // Variant defaults to variants of layout.
            if (variants.IsEmpty())
            {
                variants = layout.variants;

                if (variants.IsEmpty())
                {
                    variants = InputControlLayout.DefaultVariant;
                }
            }

            control.m_Name = name;
            control.m_DisplayNameFromLayout = layout.m_DisplayName; // No short display names at layout roots.
            control.m_Layout   = layout.name;
            control.m_Variants = variants;
            control.m_Parent   = parent;
            control.m_Device   = m_Device;

            // Create children and configure their settings from our
            // layout values.
            var haveChildrenUsingStateFromOtherControl = false;

            try
            {
                // Pass list of existing control on to function as we may have decided to not
                // actually reuse the existing control (and thus control.m_ChildrenReadOnly will
                // now be blank) but still want crawling down the hierarchy to preserve existing
                // controls where possible.
                AddChildControls(layout, variants, control,
                                 ref haveChildrenUsingStateFromOtherControl);
            }
            catch
            {
                ////TODO: remove control from collection and rethrow
                throw;
            }

            // Come up with a layout for our state.
            ComputeStateLayout(control);

            // Finally, if we have child controls that take their state blocks from other
            // controls, assign them their blocks now.
            if (haveChildrenUsingStateFromOtherControl)
            {
                foreach (var controlLayout in layout.controls)
                {
                    if (string.IsNullOrEmpty(controlLayout.useStateFrom))
                    {
                        continue;
                    }

                    var child = InputControlPath.TryFindChild(control, controlLayout.name);
                    Debug.Assert(child != null, "Could not find child control which should be present at this point");

                    // Find the referenced control.
                    var referencedControl = InputControlPath.TryFindChild(control, controlLayout.useStateFrom);
                    if (referencedControl == null)
                    {
                        throw new InvalidOperationException(
                                  $"Cannot find control '{controlLayout.useStateFrom}' referenced in 'useStateFrom' of control '{controlLayout.name}' in layout '{layout.name}'");
                    }

                    // Copy its state settings.
                    child.m_StateBlock = referencedControl.m_StateBlock;

                    // At this point, all byteOffsets are relative to parents so we need to
                    // walk up the referenced control's parent chain and add offsets until
                    // we are at the same level that we are at.
                    for (var parentInChain = referencedControl.parent; parentInChain != control; parentInChain = parentInChain.parent)
                    {
                        child.m_StateBlock.byteOffset += parentInChain.m_StateBlock.byteOffset;
                    }
                }
            }

            return(control);
        }
예제 #16
0
    string GetBindingString(int player, int index)
    {
        var binding = DomacinInputManager.e.inputActionsAsset.actionMaps[player].actions[index].bindings[0];

        return(InputControlPath.ToHumanReadableString(binding.effectivePath, InputControlPath.HumanReadableStringOptions.OmitDevice));
    }
예제 #17
0
        private void ModifyChildControl(InputControlLayout layout, InternedString variants, InputControl parent,
                                        ref bool haveChildrenUsingStateFromOtherControls,
                                        ref InputControlLayout.ControlItem controlItem)
        {
            ////TODO: support arrays (we may modify an entire array in bulk)

            // Controls layout themselves as we come back up the hierarchy. However, when we
            // apply layout modifications reaching *into* the hierarchy, we need to retrigger
            // layouting on their parents.
            var haveChangedLayoutOfParent = false;

            // Find the child control.
            var child = InputControlPath.TryFindChild(parent, controlItem.name);

            if (child == null)
            {
                // We're adding a child somewhere in the existing hierarchy. This is a tricky
                // case as we have to potentially shift indices around in the hierarchy to make
                // room for the new control.

                ////TODO: this path does not support recovering existing controls? does it matter?

                child = InsertChildControl(layout, variants, parent,
                                           ref haveChildrenUsingStateFromOtherControls, ref controlItem);
                haveChangedLayoutOfParent = true;
            }
            else
            {
                // Apply modifications.
                if (controlItem.sizeInBits != 0 &&
                    child.m_StateBlock.sizeInBits != controlItem.sizeInBits)
                {
                    child.m_StateBlock.sizeInBits = controlItem.sizeInBits;
                }
                if (controlItem.format != 0 && child.m_StateBlock.format != controlItem.format)
                {
                    SetFormat(child, controlItem);
                    haveChangedLayoutOfParent = true;
                }
                ////REVIEW: ATM, when you move a child with a fixed offset, we only move the child
                ////        and don't move the parent or siblings. What this means is that if you move
                ////        leftStick/x, for example, leftStick stays put. ATM you have to move *all*
                ////        controls that are part of a chain manually. Not sure what the best behavior
                ////        is. If we opt to move parents along with children, we have to make sure we
                ////        are not colliding with any other relocations of children (e.g. if you move
                ////        both leftStick/x and leftStick/y, leftStick itself should move only once and
                ////        not at all if there indeed is a leftStick control layout with an offset;
                ////        so, it'd get quite complicated)
                if (controlItem.offset != InputStateBlock.InvalidOffset)
                {
                    child.m_StateBlock.byteOffset = controlItem.offset;
                }
                if (controlItem.bit != InputStateBlock.InvalidOffset)
                {
                    child.m_StateBlock.bitOffset = controlItem.bit;
                }
                if (controlItem.processors.Count > 0)
                {
                    AddProcessors(child, ref controlItem, layout.name);
                }
                ////REVIEW: ATM parameters applied using this path add on top instead of just overriding existing parameters
                if (controlItem.parameters.Count > 0)
                {
                    NamedValue.ApplyAllToObject(child, controlItem.parameters);
                }
                if (!string.IsNullOrEmpty(controlItem.displayName))
                {
                    child.m_DisplayNameFromLayout = controlItem.displayName;
                }
                if (!controlItem.defaultState.isEmpty)
                {
                    child.m_DefaultValue = controlItem.defaultState;
                    m_Device.hasControlsWithDefaultState = true;
                }
                if (!controlItem.minValue.isEmpty)
                {
                    child.m_MinValue = controlItem.minValue;
                }
                if (!controlItem.maxValue.isEmpty)
                {
                    child.m_MaxValue = controlItem.maxValue;
                }

                ////TODO: other modifications
            }

            // Apply layout change.
            ////REVIEW: not sure what's better here; trigger this immediately means we may trigger
            ////        it a number of times on the same parent but doing it as a final pass would
            ////        require either collecting the necessary parents or doing another pass through
            ////        the list of control layouts
            if (haveChangedLayoutOfParent && !ReferenceEquals(child.parent, parent))
            {
                ComputeStateLayout(child.parent);
            }
        }
예제 #18
0
 private void InitializeButtonText()
 {
     actionButtonText.text = InputControlPath.ToHumanReadableString(
         actionReference.action.bindings[GetBindindIndex()].effectivePath,
         InputControlPath.HumanReadableStringOptions.OmitDevice);
 }