Maze / Library /PackageCache /com.unity.inputsystem@1.6.1 /InputSystem /Actions /Composites /TwoModifiersComposite.cs
| using System; | |
| using System.ComponentModel; | |
| using Unity.Collections.LowLevel.Unsafe; | |
| using UnityEngine.InputSystem.Layouts; | |
| using UnityEngine.InputSystem.Utilities; | |
| namespace UnityEngine.InputSystem.Composites | |
| { | |
| /// <summary> | |
| /// A binding with two additional modifiers modifier. The bound controls only trigger when | |
| /// both modifiers are pressed. | |
| /// </summary> | |
| /// <remarks> | |
| /// This composite can be used to require two buttons to be held in order to "activate" | |
| /// another binding. This is most commonly used on keyboards to require two of the | |
| /// modifier keys (shift, ctrl, or alt) to be held in combination with another control, | |
| /// e.g. "SHIFT+CTRL+1". | |
| /// | |
| /// <example> | |
| /// <code> | |
| /// // Create a button action that triggers when SHIFT+CTRL+1 | |
| /// // is pressed on the keyboard. | |
| /// var action = new InputAction(type: InputActionType.Button); | |
| /// action.AddCompositeBinding("TwoModifiers") | |
| /// .With("Modifier", "<Keyboard>/ctrl") | |
| /// .With("Modifier", "<Keyboard>/shift") | |
| /// .With("Binding", "<Keyboard>/1") | |
| /// </code> | |
| /// </example> | |
| /// | |
| /// However, this can also be used to "gate" other types of controls. For example, a "look" | |
| /// action could be bound to mouse <see cref="Pointer.delta"/> such that the <see cref="Keyboard.ctrlKey"/> and | |
| /// <see cref="Keyboard.shiftKey"/> on the keyboard have to be pressed in order for the player to be able to | |
| /// look around. | |
| /// | |
| /// <example> | |
| /// <code> | |
| /// var action = new InputAction(); | |
| /// action.AddCompositeBinding("TwoModifiers") | |
| /// .With("Modifier1", "<Keyboard>/ctrl") | |
| /// .With("Modifier2", "<Keyboard>/shift") | |
| /// .With("Binding", "<Mouse>/delta"); | |
| /// </code> | |
| /// </example> | |
| /// </remarks> | |
| /// <seealso cref="OneModifierComposite"/> | |
| [] | |
| [] | |
| public class TwoModifiersComposite : InputBindingComposite | |
| { | |
| /// <summary> | |
| /// Binding for the first button that acts as a modifier, e.g. <c><Keyboard/leftCtrl</c>. | |
| /// </summary> | |
| /// <value>Part index to use with <see cref="InputBindingCompositeContext.ReadValue{T}(int)"/>.</value> | |
| /// <remarks> | |
| /// This property is automatically assigned by the input system. | |
| /// </remarks> | |
| // ReSharper disable once MemberCanBePrivate.Global | |
| // ReSharper disable once FieldCanBeMadeReadOnly.Global | |
| // ReSharper disable once UnassignedField.Global | |
| [] public int modifier1; | |
| /// <summary> | |
| /// Binding for the second button that acts as a modifier, e.g. <c><Keyboard/leftCtrl</c>. | |
| /// </summary> | |
| /// <value>Part index to use with <see cref="InputBindingCompositeContext.ReadValue{T}(int)"/>.</value> | |
| /// <remarks> | |
| /// This property is automatically assigned by the input system. | |
| /// </remarks> | |
| // ReSharper disable once MemberCanBePrivate.Global | |
| // ReSharper disable once FieldCanBeMadeReadOnly.Global | |
| // ReSharper disable once UnassignedField.Global | |
| [] public int modifier2; | |
| /// <summary> | |
| /// Binding for the control that is gated by <see cref="modifier1"/> and <see cref="modifier2"/>. | |
| /// The composite will assume the value of this button while both of the modifiers are pressed. | |
| /// </summary> | |
| /// <value>Part index to use with <see cref="InputBindingCompositeContext.ReadValue{T}(int)"/>.</value> | |
| /// <remarks> | |
| /// This property is automatically assigned by the input system. | |
| /// </remarks> | |
| // ReSharper disable once MemberCanBePrivate.Global | |
| // ReSharper disable once FieldCanBeMadeReadOnly.Global | |
| // ReSharper disable once UnassignedField.Global | |
| [] public int binding; | |
| /// <summary> | |
| /// If set to <c>true</c>, the built-in logic to determine if modifiers need to be pressed first is overridden. | |
| /// Default value is <c>false</c>. | |
| /// </summary> | |
| /// <remarks> | |
| /// By default, if <see cref="binding"/> is bound to only <see cref="Controls.ButtonControl"/>s, then the composite requires | |
| /// both <see cref="modifier1"/> and <see cref="modifier2"/> to be pressed <em>before</em> pressing <see cref="binding"/>. | |
| /// This means that binding to, for example, <c>Ctrl+Shift+B</c>, the <c>ctrl</c> and <c>shift</c> keys have to be pressed | |
| /// before pressing the <c>B</c> key. This is the behavior usually expected with keyboard shortcuts. | |
| /// | |
| /// However, when binding, for example, <c>Ctrl+Shift+MouseDelta</c>, it should be possible to press <c>ctrl</c> and <c>shift</c> | |
| /// at any time and in any order. The default logic will automatically detect the difference between this binding and the button | |
| /// binding in the example above and behave accordingly. | |
| /// | |
| /// This field allows you to explicitly override this default inference and make it so that regardless of what <see cref="binding"/> | |
| /// is bound to, any press sequence is acceptable. For the example binding to <c>Ctrl+Shift+B</c>, it would mean that pressing | |
| /// <c>B</c> and only then pressing <c>Ctrl</c> and <c>Shift</c> will still trigger the binding. | |
| /// </remarks> | |
| public bool overrideModifiersNeedToBePressedFirst; | |
| /// <summary> | |
| /// Type of values read from controls bound to <see cref="binding"/>. | |
| /// </summary> | |
| public override Type valueType => m_ValueType; | |
| /// <summary> | |
| /// Size of the largest value that may be read from the controls bound to <see cref="binding"/>. | |
| /// </summary> | |
| public override int valueSizeInBytes => m_ValueSizeInBytes; | |
| private int m_ValueSizeInBytes; | |
| private Type m_ValueType; | |
| private bool m_BindingIsButton; | |
| public override float EvaluateMagnitude(ref InputBindingCompositeContext context) | |
| { | |
| if (ModifiersArePressed(ref context)) | |
| return context.EvaluateMagnitude(binding); | |
| return default; | |
| } | |
| /// <inheritdoc/> | |
| public override unsafe void ReadValue(ref InputBindingCompositeContext context, void* buffer, int bufferSize) | |
| { | |
| if (ModifiersArePressed(ref context)) | |
| context.ReadValue(binding, buffer, bufferSize); | |
| else | |
| UnsafeUtility.MemClear(buffer, m_ValueSizeInBytes); | |
| } | |
| private bool ModifiersArePressed(ref InputBindingCompositeContext context) | |
| { | |
| var modifiersDown = context.ReadValueAsButton(modifier1) && context.ReadValueAsButton(modifier2); | |
| // When the modifiers are gating a button, we require the modifiers to be pressed *first*. | |
| if (modifiersDown && m_BindingIsButton && !overrideModifiersNeedToBePressedFirst) | |
| { | |
| var timestamp = context.GetPressTime(binding); | |
| var timestamp1 = context.GetPressTime(modifier1); | |
| var timestamp2 = context.GetPressTime(modifier2); | |
| return timestamp1 <= timestamp && timestamp2 <= timestamp; | |
| } | |
| return modifiersDown; | |
| } | |
| /// <inheritdoc/> | |
| protected override void FinishSetup(ref InputBindingCompositeContext context) | |
| { | |
| OneModifierComposite.DetermineValueTypeAndSize(ref context, binding, out m_ValueType, out m_ValueSizeInBytes, out m_BindingIsButton); | |
| if (!overrideModifiersNeedToBePressedFirst) | |
| overrideModifiersNeedToBePressedFirst = !InputSystem.settings.shortcutKeysConsumeInput; | |
| } | |
| public override object ReadValueAsObject(ref InputBindingCompositeContext context) | |
| { | |
| if (context.ReadValueAsButton(modifier1) && context.ReadValueAsButton(modifier2)) | |
| return context.ReadValueAsObject(binding); | |
| return null; | |
| } | |
| } | |
| } | |