Beispiel #1
0
        public static void ApplyBindingOverride(this InputAction action, int bindingIndex, InputBinding bindingOverride)
        {
            if (action == null)
            {
                throw new ArgumentNullException(nameof(action));
            }

            var indexOnMap = action.BindingIndexOnActionToBindingIndexOnMap(bindingIndex);

            bindingOverride.action = action.name;
            ApplyBindingOverride(action.GetOrCreateActionMap(), indexOnMap, bindingOverride);
        }
Beispiel #2
0
        private void SetUpBindingArrayForEachAction()
        {
            // Handle case where we don't have any bindings.
            if (m_Bindings == null)
            {
                return;
            }

            if (m_SingletonAction != null)
            {
                // Dead simple case: map is internally owned by action. The entire
                // list of bindings is specific to the action.

                Debug.Assert(m_Bindings == m_SingletonAction.m_SingletonActionBindings);

                m_BindingsForEachAction = m_Bindings;
                m_ActionForEachBinding  = null; // No point in having this for singleton actions.

                m_SingletonAction.m_BindingsStartIndex = 0;
                m_SingletonAction.m_BindingsCount      = m_Bindings.Length;
            }
            else
            {
                // Go through all bindings and slice them out to individual actions.

                Debug.Assert(m_Actions != null); // Action isn't a singleton so this has to be true.

                // Allocate array to retain resolved actions, if need be.
                var totalBindingsCount = m_Bindings.Length;
                if (m_ActionForEachBinding == null || m_ActionForEachBinding.Length != totalBindingsCount)
                {
                    m_ActionForEachBinding = new InputAction[totalBindingsCount];
                }

                // Reset state on each action. Important we have actions that are no longer
                // referred to by bindings.
                for (var i = 0; i < m_Actions.Length; ++i)
                {
                    m_Actions[i].m_BindingsCount      = 0;
                    m_Actions[i].m_BindingsStartIndex = 0;
                }

                // Collect actions and count bindings.
                // After this loop, we can have one of two situations:
                // 1) The bindings for any action X start at some index N and occupy the next m_BindingsCount slots.
                // 2) The bindings for some or all actions are scattered across non-contiguous chunks of the array.
                for (var i = 0; i < m_Bindings.Length; ++i)
                {
                    // Look up action.
                    var actionForBinding = TryGetAction(m_Bindings[i].action);
                    m_ActionForEachBinding[i] = actionForBinding;
                    if (actionForBinding == null)
                    {
                        continue;
                    }

                    ++actionForBinding.m_BindingsCount;
                }

                // Collect the bindings and bundle them into chunks.
                var            newBindingsArrayIndex = 0;
                InputBinding[] newBindingsArray      = null;
                for (var sourceBindingIndex = 0; sourceBindingIndex < m_Bindings.Length;)
                {
                    var currentAction = m_ActionForEachBinding[sourceBindingIndex];
                    if (currentAction == null || currentAction.m_BindingsStartIndex != 0)
                    {
                        // Skip bindings not targeting an action or bindings whose actions we
                        // have already processed (when gathering bindings for a single actions scattered
                        // across the array we may be skipping ahead).
                        ++sourceBindingIndex;
                        continue;
                    }

                    // Bindings for current action start at current index.
                    currentAction.m_BindingsStartIndex = newBindingsArray != null
                        ? newBindingsArrayIndex
                        : sourceBindingIndex;

                    // Collect all bindings for the action.
                    var bindingCountForCurrentAction = currentAction.m_BindingsCount;
                    var sourceBindingToCopy          = sourceBindingIndex;
                    for (var i = 0; i < bindingCountForCurrentAction; ++i)
                    {
                        if (m_ActionForEachBinding[sourceBindingToCopy] != currentAction)
                        {
                            // If this is the first action that has its bindings scattered around, switch to
                            // having a separate bindings array and copy whatever bindings we already processed
                            // over to it.
                            if (newBindingsArray == null)
                            {
                                newBindingsArray      = new InputBinding[totalBindingsCount];
                                newBindingsArrayIndex = sourceBindingToCopy;
                                Array.Copy(m_Bindings, 0, newBindingsArray, 0, sourceBindingToCopy);
                            }

                            // Find the next binding belonging to the action. We've counted bindings for
                            // the action in the previous pass so we know exactly how many bindings we
                            // can expect.
                            do
                            {
                                ++sourceBindingToCopy;
                                Debug.Assert(sourceBindingToCopy < m_ActionForEachBinding.Length);
                            }while (m_ActionForEachBinding[sourceBindingToCopy] != currentAction);
                        }
                        else if (sourceBindingIndex == sourceBindingToCopy)
                        {
                            ++sourceBindingIndex;
                        }

                        // Copy binding over to new bindings array, if need be.
                        if (newBindingsArray != null)
                        {
                            newBindingsArray[newBindingsArrayIndex++] = m_Bindings[sourceBindingToCopy];
                        }

                        ++sourceBindingToCopy;
                    }
                }

                if (newBindingsArray == null)
                {
                    // Bindings are already clustered by action in m_Bindings
                    // so we can just stick to having one array only.
                    m_BindingsForEachAction = m_Bindings;
                }
                else
                {
                    // Bindings are not clustered by action in m_Bindings so
                    // we had to allocate a separate array where the bindings are sorted.
                    m_BindingsForEachAction = newBindingsArray;
                }
            }
        }
Beispiel #3
0
        /// <summary>
        /// Collect data from <see cref="m_Bindings"/> and <see cref="m_Actions"/> such that we can
        /// we can cleanly expose it from <see cref="InputAction.bindings"/> and <see cref="InputAction.controls"/>.
        /// </summary>
        /// <remarks>
        /// We set up per-action caches the first time their information is requested. Internally, we do not
        /// use those arrays and thus they will not get set up by default.
        ///
        /// Note that it is important to allow to call this method at a point where we have not resolved
        /// controls yet (i.e. <see cref="m_State"/> is <c>null</c>). Otherwise, using <see cref="InputAction.bindings"/>
        /// may trigger a control resolution which would be surprising.
        /// </remarks>
        private void SetUpPerActionCachedBindingData()
        {
            // Handle case where we don't have any bindings.
            if (m_Bindings == null)
            {
                return;
            }

            if (m_SingletonAction != null)
            {
                // Dead simple case: map is internally owned by action. The entire
                // list of bindings is specific to the action.

                Debug.Assert(m_Bindings == m_SingletonAction.m_SingletonActionBindings);

                m_BindingsForEachAction = m_Bindings;
                m_ControlsForEachAction = m_State != null ? m_State.controls : null;

                m_SingletonAction.m_BindingsStartIndex = 0;
                m_SingletonAction.m_BindingsCount      = m_Bindings.Length;
                m_SingletonAction.m_ControlStartIndex  = 0;
                m_SingletonAction.m_ControlCount       = m_State != null ? m_State.totalControlCount : 0;
            }
            else
            {
                // Go through all bindings and slice them out to individual actions.

                Debug.Assert(m_Actions != null); // Action isn't a singleton so this has to be true.
                var mapIndices = m_State != null
                    ? m_State.FetchMapIndices(this)
                    : new InputActionMapState.ActionMapIndices();

                // Reset state on each action. Important if we have actions that are no longer
                // referred to by bindings.
                for (var i = 0; i < m_Actions.Length; ++i)
                {
                    var action = m_Actions[i];
                    action.m_BindingsCount      = 0;
                    action.m_BindingsStartIndex = -1;
                    action.m_ControlCount       = 0;
                    action.m_ControlStartIndex  = -1;
                }

                // Count bindings on each action.
                // After this loop, we can have one of two situations:
                // 1) The bindings for any action X start at some index N and occupy the next m_BindingsCount slots.
                // 2) The bindings for some or all actions are scattered across non-contiguous chunks of the array.
                var bindingCount = m_Bindings.Length;
                for (var i = 0; i < bindingCount; ++i)
                {
                    var action = TryGetAction(m_Bindings[i].action);
                    if (action != null)
                    {
                        ++action.m_BindingsCount;
                    }
                }

                // Collect the bindings and controls and bundle them into chunks.
                var newBindingsArrayIndex = 0;
                if (m_State != null && (m_ControlsForEachAction == null || m_ControlsForEachAction.Length != mapIndices.controlCount))
                {
                    if (mapIndices.controlCount == 0)
                    {
                        m_ControlsForEachAction = null;
                    }
                    else
                    {
                        m_ControlsForEachAction = new InputControl[mapIndices.controlCount];
                    }
                }
                InputBinding[] newBindingsArray    = null;
                var            currentControlIndex = 0;
                for (var currentBindingIndex = 0; currentBindingIndex < m_Bindings.Length;)
                {
                    var currentAction = TryGetAction(m_Bindings[currentBindingIndex].action);
                    if (currentAction == null || currentAction.m_BindingsStartIndex != -1)
                    {
                        // Skip bindings not targeting an action or bindings we have already processed
                        // (when gathering bindings for a single actions scattered across the array we may have
                        // skipping ahead).
                        ++currentBindingIndex;
                        continue;
                    }

                    // Bindings for current action start at current index.
                    currentAction.m_BindingsStartIndex = newBindingsArray != null
                        ? newBindingsArrayIndex
                        : currentBindingIndex;
                    currentAction.m_ControlStartIndex = currentControlIndex;

                    // Collect all bindings for the action. As part of that, also copy the controls
                    // for each binding over to m_ControlsForEachAction.
                    var bindingCountForCurrentAction = currentAction.m_BindingsCount;
                    Debug.Assert(bindingCountForCurrentAction > 0);
                    var sourceBindingToCopy = currentBindingIndex;
                    for (var i = 0; i < bindingCountForCurrentAction; ++i)
                    {
                        // See if we've come across a binding that isn't belong to our currently looked at action.
                        if (TryGetAction(m_Bindings[sourceBindingToCopy].action) != currentAction)
                        {
                            // Yes, we have. Means the bindings for our actions are scattered in m_Bindings and
                            // we need to collect them.

                            // If this is the first action that has its bindings scattered around, switch to
                            // having a separate bindings array and copy whatever bindings we already processed
                            // over to it.
                            if (newBindingsArray == null)
                            {
                                newBindingsArray      = new InputBinding[mapIndices.bindingCount];
                                newBindingsArrayIndex = sourceBindingToCopy;
                                Array.Copy(m_Bindings, 0, newBindingsArray, 0, sourceBindingToCopy);
                            }

                            // Find the next binding belonging to the action. We've counted bindings for
                            // the action in the previous pass so we know exactly how many bindings we
                            // can expect.
                            do
                            {
                                ++sourceBindingToCopy;
                                Debug.Assert(sourceBindingToCopy < mapIndices.bindingCount);
                            }while (TryGetAction(m_Bindings[sourceBindingToCopy].action) != currentAction);
                        }
                        else if (currentBindingIndex == sourceBindingToCopy)
                        {
                            ++currentBindingIndex;
                        }

                        // Copy binding over to new bindings array, if need be.
                        if (newBindingsArray != null)
                        {
                            newBindingsArray[newBindingsArrayIndex++] = m_Bindings[sourceBindingToCopy];
                        }

                        // Copy controls for binding, if we have resolved controls already.
                        if (m_State != null)
                        {
                            var controlCountForBinding = m_State
                                                         .bindingStates[mapIndices.bindingStartIndex + sourceBindingToCopy].controlCount;
                            if (controlCountForBinding > 0)
                            {
                                Array.Copy(m_State.controls,
                                           m_State.bindingStates[mapIndices.bindingStartIndex + sourceBindingToCopy]
                                           .controlStartIndex,
                                           m_ControlsForEachAction, currentControlIndex, controlCountForBinding);

                                currentControlIndex          += controlCountForBinding;
                                currentAction.m_ControlCount += controlCountForBinding;
                            }
                        }

                        ++sourceBindingToCopy;
                    }
                }

                if (newBindingsArray == null)
                {
                    // Bindings are already clustered by action in m_Bindings
                    // so we can just stick to having one array only.
                    m_BindingsForEachAction = m_Bindings;
                }
                else
                {
                    // Bindings are not clustered by action in m_Bindings so
                    // we had to allocate a separate array where the bindings are sorted.
                    m_BindingsForEachAction = newBindingsArray;
                }
            }
        }
Beispiel #4
0
        public static void ApplyBindingOverride(this InputActionMap actionMap, int bindingIndex, InputBinding bindingOverride)
        {
            if (actionMap == null)
            {
                throw new ArgumentNullException(nameof(actionMap));
            }
            var bindingsCount = actionMap.m_Bindings?.Length ?? 0;

            if (bindingIndex < 0 || bindingIndex >= bindingsCount)
            {
                throw new ArgumentOutOfRangeException(
                          $"Cannot apply override to binding at index {bindingIndex} in map '{actionMap}' with only {bindingsCount} bindings", "bindingIndex");
            }

            actionMap.m_Bindings[bindingIndex].overridePath         = bindingOverride.overridePath;
            actionMap.m_Bindings[bindingIndex].overrideInteractions = bindingOverride.overrideInteractions;
            actionMap.LazyResolveBindings();
        }
Beispiel #5
0
        public static void ApplyBindingOverride(this InputActionMap actionMap, int bindingIndex, InputBinding bindingOverride)
        {
            if (actionMap == null)
            {
                throw new ArgumentNullException("actionMap");
            }
            var bindingsCount = actionMap.m_Bindings != null ? actionMap.m_Bindings.Length : 0;

            if (bindingIndex < 0 || bindingIndex >= bindingsCount)
            {
                throw new ArgumentOutOfRangeException(
                          string.Format("Cannot apply override to binding at index {0} in map '{1}' with only {2} bindings",
                                        bindingIndex, actionMap, bindingsCount), "bindingIndex");
            }

            actionMap.m_Bindings[bindingIndex].overridePath         = bindingOverride.overridePath;
            actionMap.m_Bindings[bindingIndex].overrideInteractions = bindingOverride.overrideInteractions;
            actionMap.InvalidateResolvedData();
        }
Beispiel #6
0
        public static void ApplyBindingOverride(this InputAction action, int bindingIndex, InputBinding bindingOverride)
        {
            if (action == null)
            {
                throw new ArgumentNullException("action");
            }

            // We don't want to hit InputAction.bindings here as this requires setting up per-action
            // binding info which we then nuke as part of the override process. Calling ApplyBindingOverride
            // repeatedly with an index would thus cause the same data to be computed and thrown away
            // over and over.
            // Instead we manually search through the map's bindings to find the right binding index
            // in the map.

            var actionMap         = action.GetOrCreateActionMap();
            var bindingsInMap     = actionMap.m_Bindings;
            var bindingCountInMap = bindingsInMap != null ? bindingsInMap.Length : 0;
            var actionName        = action.name;

            var currentBindingIndexOnAction = -1;

            for (var i = 0; i < bindingCountInMap; ++i)
            {
                if (string.Compare(bindingsInMap[i].action, actionName, StringComparison.InvariantCultureIgnoreCase) != 0)
                {
                    continue;
                }

                ++currentBindingIndexOnAction;
                if (currentBindingIndexOnAction == bindingIndex)
                {
                    bindingOverride.action = actionName;
                    ApplyBindingOverride(actionMap, i, bindingOverride);
                    return;
                }
            }

            throw new ArgumentOutOfRangeException(
                      string.Format("Binding index {0} is out of range for action '{1}' with {2} bindings", bindingIndex,
                                    action, currentBindingIndexOnAction), "bindingIndex");
        }
Beispiel #7
0
        static GenericMenu GetMenu <C, T>(InputBinding <C, T> binding, IControlDomainSource domainSource, Action <InputBinding <C, T> > action) where C : InputControl <T>
        {
            GenericMenu menu = new GenericMenu();

            Type[]   derivedTypes = null;
            string[] derivedNames = null;
            Dictionary <Type, int> indicesOfDerivedTypes = null;

            TypeGUI.GetDerivedTypesInfo(typeof(InputBinding <C, T>), out derivedTypes, out derivedNames, out indicesOfDerivedTypes);

            Type bindingType  = typeof(ControlReferenceBinding <C, T>);
            Type existingType = binding == null ? null : binding.GetType();

            var reference = binding as ControlReferenceBinding <C, T>;

            // Add control references for devices.
            bool hasReferences = false;

            if (derivedTypes.Contains(bindingType))
            {
                hasReferences = true;
                List <DomainEntry> domainEntries = domainSource.GetDomainEntries();
                for (int i = 0; i < domainEntries.Count; i++)
                {
                    int domainHash = domainEntries[i].hash;
                    List <DomainEntry> controlEntries = domainSource.GetControlEntriesOfType(domainHash, typeof(C));

                    bool   showFlatList = (domainEntries.Count <= 1 && controlEntries.Count <= 20);
                    string prefix       = showFlatList ? string.Empty : domainEntries[i].name + "/";

                    bool nonStandardizedSectionStart = false;
                    for (int j = 0; j < controlEntries.Count; j++)
                    {
                        bool selected = (reference != null &&
                                         reference.deviceKey == domainHash &&
                                         reference.controlHash == controlEntries[j].hash);

                        if (!nonStandardizedSectionStart && !controlEntries[j].standardized)
                        {
                            nonStandardizedSectionStart = true;
                            menu.AddSeparator(prefix);
                        }

                        GUIContent name  = new GUIContent(prefix + controlEntries[j].name);
                        int        index = j; // See "close over the loop variable".
                        menu.AddItem(name, selected,
                                     () => {
                            var newReference         = new ControlReferenceBinding <C, T>();
                            newReference.deviceKey   = domainHash;
                            newReference.controlHash = controlEntries[index].hash;
                            action(newReference);
                        });
                    }
                }
            }

            if (derivedTypes.Length <= (hasReferences ? 1 : 0))
            {
                return(menu);
            }

            menu.AddSeparator("");

            // Add other control types.
            for (int i = 0; i < derivedTypes.Length; i++)
            {
                if (derivedTypes[i] != bindingType)
                {
                    bool   selected = (existingType == derivedTypes[i]);
                    string name     = NicifyBindingName(derivedNames[i]);
                    int    index    = i; // See "close over the loop variable".
                    menu.AddItem(new GUIContent(name), selected,
                                 () => {
                        var newBinding = Activator.CreateInstance(derivedTypes[index]) as InputBinding <C, T>;
                        action(newBinding);
                    });
                }
            }

            return(menu);
        }
 // For convenience for instances created manually (hardcoded).
 public AxisFromButtonsBinding(InputBinding <ButtonControl, float> negative, InputBinding <ButtonControl, float> positive)
 {
     this.negative = negative;
     this.positive = positive;
 }
Beispiel #9
0
        public static void ControlField <C, T>(Rect position, InputBinding <C, T> binding, GUIContent label, IControlDomainSource domainSource, Action <InputBinding <C, T> > action, SerializedProperty prop = null) where C : InputControl <T>
        {
            if (prop != null)
            {
                label = EditorGUI.BeginProperty(position, label, prop);
            }

            position.height = EditorGUIUtility.singleLineHeight;

            Rect buttonPosition = EditorGUI.PrefixLabel(position, label);
            Rect detectPosition = buttonPosition;

            ControlScheme scheme           = domainSource as ControlScheme;
            bool          detectionSupport = (scheme != null);

            if (detectionSupport)
            {
                detectPosition.xMin  = detectPosition.xMax - 20;
                buttonPosition.xMax -= (20 + 4);
            }

            if (EditorGUI.DropdownButton(buttonPosition, new GUIContent(GetName(binding, domainSource) ?? "None"), FocusType.Keyboard))
            {
                GenericMenu menu = GetMenu(
                    binding, domainSource,
                    a =>
                {
                    if (prop != null)
                    {
                        Undo.RecordObjects(prop.serializedObject.targetObjects, "Control");
                    }

                    action(a);

                    if (prop != null)
                    {
                        // Flushing seems necessaary to have prefab property override status change without lag.
                        Undo.FlushUndoRecordObjects();
                        prop.serializedObject.SetIsDifferentCacheDirty();
                    }
                });
                menu.DropDown(buttonPosition);

                // GenericMenu doesn't modify GUI.changed because it relies on a callback, so let's assume that something was changed, so target object dirtying doesn't go missing
                GUI.changed = true;
            }

            if (detectionSupport)
            {
                EditorGUI.BeginDisabledGroup(s_Binding != null);
                //if (Event.current.type == EventType.repaint)
                //    EditorStyles.miniButton.Draw(detectPosition, "O", false, false, s_Binding == binding, false);
                if (GUI.Toggle(detectPosition, s_Binding == binding, "O", EditorStyles.miniButton) && s_Binding == null)
                {
                    EditorWindow window = EditorWindow.focusedWindow;
                    window.ShowNotification(new GUIContent("Waiting for input."));
                    s_BindingListener = (InputControl control) =>
                    {
                        if (!(control is C))
                        {
                            window.ShowNotification(new GUIContent("Incompatible control type."));
                            window.Repaint();
                            return(false);
                        }

                        DeviceSlot match = null;
                        Type       controlProviderType = control.provider.GetType();
                        for (int slotIndex = 0; slotIndex < scheme.deviceSlots.Count; slotIndex++)
                        {
                            Type deviceType = scheme.deviceSlots[slotIndex].type.value;
                            if (deviceType == null)
                            {
                                continue;
                            }
                            if (deviceType.IsAssignableFrom(controlProviderType))
                            {
                                match = scheme.deviceSlots[slotIndex];
                                break;
                            }
                        }
                        if (match == null)
                        {
                            window.ShowNotification(new GUIContent("Incompatible device type."));
                            window.Repaint();
                            return(false);
                        }

                        var newReference = new ControlReferenceBinding <C, T>();
                        newReference.deviceKey   = match.key;
                        newReference.controlHash = control.provider.GetHashForControlIndex(control.index);
                        action(newReference);

                        StopListening(window);

                        return(true);
                    };
                    InputSystem.ListenForBinding(s_BindingListener);
                    s_Binding = binding;

                    float time = Time.realtimeSinceStartup;
                    s_UpdateCallback = () => {
                        if (Time.realtimeSinceStartup > time + 3)
                        {
                            StopListening(window);
                        }
                    };
                    EditorApplication.update += s_UpdateCallback;
                }
                EditorGUI.EndDisabledGroup();
            }

            if (binding != null && !(binding is ControlReferenceBinding <C, T>))
            {
                EditorGUI.indentLevel++;
                position.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
                binding.OnGUI(position, domainSource);
                EditorGUI.indentLevel--;
            }

            if (prop != null)
            {
                EditorGUI.EndProperty();
            }
        }
Beispiel #10
0
 // For convenience for instances created manually (hardcoded).
 public Vector2FromAxesBinding(InputBinding <AxisControl, float> x, InputBinding <AxisControl, float> y)
 {
     m_X = x;
     m_Y = y;
 }
Beispiel #11
0
        public override void OnGUI(Rect position, IControlDomainSource domainSource)
        {
            position.height = ControlGUIUtility.GetControlHeight(m_X, s_XContent);
            ControlGUIUtility.ControlField(position, m_X, s_XContent, domainSource, b => m_X = b);

            position.y += position.height + EditorGUIUtility.standardVerticalSpacing;

            position.height = ControlGUIUtility.GetControlHeight(m_Y, s_YContent);
            ControlGUIUtility.ControlField(position, m_Y, s_YContent, domainSource, b => m_Y = b);
        }
Beispiel #12
0
 internal bool HaveBindingFilterMatching(ref InputBinding bindingFilter)
 {
     throw new NotImplementedException();
 }
Beispiel #13
0
        public override void OnGUI(Rect position, IControlDomainSource domainSource)
        {
            // Bindings
            for (int i = 0; i < sources.Count; i++)
            {
                InputBinding <C, T> source = sources[i];
                position.height = ControlGUIUtility.GetControlHeight(source, Styles.bindingContent);
                DrawSourceSettings(position, i, domainSource);
                position.y += position.height + EditorGUIUtility.standardVerticalSpacing;
            }

            // Bindings remove and add buttons
            position.height = EditorGUIUtility.singleLineHeight;
            Rect buttonPosition = position;

            buttonPosition.width = Styles.iconToolbarMinus.image.width;
            if (GUI.Button(buttonPosition, Styles.iconToolbarMinus, GUIStyle.none))
            {
                var selected = m_Selected as InputBinding <C, T>;
                if (sources.Contains(selected))
                {
                    sources.Remove(selected);
                }
            }
            buttonPosition.x += buttonPosition.width;
            if (GUI.Button(buttonPosition, Styles.iconToolbarPlus, GUIStyle.none))
            {
                var source = new ControlReferenceBinding <C, T>();
                sources.Add(source);
                m_Selected = source;
            }
            position.y += position.height + Styles.k_Spacing;

            position.height = EditorGUIUtility.singleLineHeight;
            GUI.Label(position, "Processors");
            position.y += position.height + Styles.k_Spacing;

            // Processors
            for (int i = 0; i < processors.Count; i++)
            {
                InputBindingProcessor <C, T> processor = processors[i];
                position.height = processor.GetPropertyHeight();
                DrawProcessorSettings(position, i);
                position.y += position.height + EditorGUIUtility.standardVerticalSpacing;
            }

            // Processors remove and add buttons
            position.height      = EditorGUIUtility.singleLineHeight;
            buttonPosition       = position;
            buttonPosition.width = Styles.iconToolbarMinus.image.width;
            if (GUI.Button(buttonPosition, Styles.iconToolbarMinus, GUIStyle.none))
            {
                var selected = m_Selected as InputBindingProcessor <C, T>;
                if (selected != null && processors.Contains(selected))
                {
                    processors.Remove(selected);
                }
            }
            buttonPosition.x += buttonPosition.width;
            if (GUI.Button(buttonPosition, Styles.iconToolbarPlus, GUIStyle.none))
            {
                InputBindingProcessor <C, T> .ShowAddProcessorDropdown(buttonPosition, p => processors.Add(p));
            }
        }