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); }
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; } } }
/// <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; } } }
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(); }
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(); }
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"); }
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; }
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(); } }
// For convenience for instances created manually (hardcoded). public Vector2FromAxesBinding(InputBinding <AxisControl, float> x, InputBinding <AxisControl, float> y) { m_X = x; m_Y = y; }
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); }
internal bool HaveBindingFilterMatching(ref InputBinding bindingFilter) { throw new NotImplementedException(); }
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)); } }