No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

TwoModifiersComposite.cs 8.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. using System;
  2. using System.ComponentModel;
  3. using Unity.Collections.LowLevel.Unsafe;
  4. using UnityEngine.InputSystem.Layouts;
  5. using UnityEngine.InputSystem.Utilities;
  6. namespace UnityEngine.InputSystem.Composites
  7. {
  8. /// <summary>
  9. /// A binding with two additional modifiers modifier. The bound controls only trigger when
  10. /// both modifiers are pressed.
  11. /// </summary>
  12. /// <remarks>
  13. /// This composite can be used to require two buttons to be held in order to "activate"
  14. /// another binding. This is most commonly used on keyboards to require two of the
  15. /// modifier keys (shift, ctrl, or alt) to be held in combination with another control,
  16. /// e.g. "SHIFT+CTRL+1".
  17. ///
  18. /// <example>
  19. /// <code>
  20. /// // Create a button action that triggers when SHIFT+CTRL+1
  21. /// // is pressed on the keyboard.
  22. /// var action = new InputAction(type: InputActionType.Button);
  23. /// action.AddCompositeBinding("TwoModifiers")
  24. /// .With("Modifier", "&lt;Keyboard&gt;/ctrl")
  25. /// .With("Modifier", "&lt;Keyboard&gt;/shift")
  26. /// .With("Binding", "&lt;Keyboard&gt;/1")
  27. /// </code>
  28. /// </example>
  29. ///
  30. /// However, this can also be used to "gate" other types of controls. For example, a "look"
  31. /// action could be bound to mouse <see cref="Pointer.delta"/> such that the <see cref="Keyboard.ctrlKey"/> and
  32. /// <see cref="Keyboard.shiftKey"/> on the keyboard have to be pressed in order for the player to be able to
  33. /// look around.
  34. ///
  35. /// <example>
  36. /// <code>
  37. /// var action = new InputAction();
  38. /// action.AddCompositeBinding("TwoModifiers")
  39. /// .With("Modifier1", "&lt;Keyboard&gt;/ctrl")
  40. /// .With("Modifier2", "&lt;Keyboard&gt;/shift")
  41. /// .With("Binding", "&lt;Mouse&gt;/delta");
  42. /// </code>
  43. /// </example>
  44. /// </remarks>
  45. /// <seealso cref="OneModifierComposite"/>
  46. [DisplayStringFormat("{modifier1}+{modifier2}+{binding}")]
  47. [DisplayName("Binding With Two Modifiers")]
  48. public class TwoModifiersComposite : InputBindingComposite
  49. {
  50. /// <summary>
  51. /// Binding for the first button that acts as a modifier, e.g. <c>&lt;Keyboard/leftCtrl</c>.
  52. /// </summary>
  53. /// <value>Part index to use with <see cref="InputBindingCompositeContext.ReadValue{T}(int)"/>.</value>
  54. /// <remarks>
  55. /// This property is automatically assigned by the input system.
  56. /// </remarks>
  57. // ReSharper disable once MemberCanBePrivate.Global
  58. // ReSharper disable once FieldCanBeMadeReadOnly.Global
  59. // ReSharper disable once UnassignedField.Global
  60. [InputControl(layout = "Button")] public int modifier1;
  61. /// <summary>
  62. /// Binding for the second button that acts as a modifier, e.g. <c>&lt;Keyboard/leftCtrl</c>.
  63. /// </summary>
  64. /// <value>Part index to use with <see cref="InputBindingCompositeContext.ReadValue{T}(int)"/>.</value>
  65. /// <remarks>
  66. /// This property is automatically assigned by the input system.
  67. /// </remarks>
  68. // ReSharper disable once MemberCanBePrivate.Global
  69. // ReSharper disable once FieldCanBeMadeReadOnly.Global
  70. // ReSharper disable once UnassignedField.Global
  71. [InputControl(layout = "Button")] public int modifier2;
  72. /// <summary>
  73. /// Binding for the control that is gated by <see cref="modifier1"/> and <see cref="modifier2"/>.
  74. /// The composite will assume the value of this button while both of the modifiers are pressed.
  75. /// </summary>
  76. /// <value>Part index to use with <see cref="InputBindingCompositeContext.ReadValue{T}(int)"/>.</value>
  77. /// <remarks>
  78. /// This property is automatically assigned by the input system.
  79. /// </remarks>
  80. // ReSharper disable once MemberCanBePrivate.Global
  81. // ReSharper disable once FieldCanBeMadeReadOnly.Global
  82. // ReSharper disable once UnassignedField.Global
  83. [InputControl] public int binding;
  84. /// <summary>
  85. /// If set to <c>true</c>, the built-in logic to determine if modifiers need to be pressed first is overridden.
  86. /// Default value is <c>false</c>.
  87. /// </summary>
  88. /// <remarks>
  89. /// By default, if <see cref="binding"/> is bound to only <see cref="Controls.ButtonControl"/>s, then the composite requires
  90. /// both <see cref="modifier1"/> and <see cref="modifier2"/> to be pressed <em>before</em> pressing <see cref="binding"/>.
  91. /// 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
  92. /// before pressing the <c>B</c> key. This is the behavior usually expected with keyboard shortcuts.
  93. ///
  94. /// However, when binding, for example, <c>Ctrl+Shift+MouseDelta</c>, it should be possible to press <c>ctrl</c> and <c>shift</c>
  95. /// at any time and in any order. The default logic will automatically detect the difference between this binding and the button
  96. /// binding in the example above and behave accordingly.
  97. ///
  98. /// This field allows you to explicitly override this default inference and make it so that regardless of what <see cref="binding"/>
  99. /// is bound to, any press sequence is acceptable. For the example binding to <c>Ctrl+Shift+B</c>, it would mean that pressing
  100. /// <c>B</c> and only then pressing <c>Ctrl</c> and <c>Shift</c> will still trigger the binding.
  101. /// </remarks>
  102. public bool overrideModifiersNeedToBePressedFirst;
  103. /// <summary>
  104. /// Type of values read from controls bound to <see cref="binding"/>.
  105. /// </summary>
  106. public override Type valueType => m_ValueType;
  107. /// <summary>
  108. /// Size of the largest value that may be read from the controls bound to <see cref="binding"/>.
  109. /// </summary>
  110. public override int valueSizeInBytes => m_ValueSizeInBytes;
  111. private int m_ValueSizeInBytes;
  112. private Type m_ValueType;
  113. private bool m_BindingIsButton;
  114. public override float EvaluateMagnitude(ref InputBindingCompositeContext context)
  115. {
  116. if (ModifiersArePressed(ref context))
  117. return context.EvaluateMagnitude(binding);
  118. return default;
  119. }
  120. /// <inheritdoc/>
  121. public override unsafe void ReadValue(ref InputBindingCompositeContext context, void* buffer, int bufferSize)
  122. {
  123. if (ModifiersArePressed(ref context))
  124. context.ReadValue(binding, buffer, bufferSize);
  125. else
  126. UnsafeUtility.MemClear(buffer, m_ValueSizeInBytes);
  127. }
  128. private bool ModifiersArePressed(ref InputBindingCompositeContext context)
  129. {
  130. var modifiersDown = context.ReadValueAsButton(modifier1) && context.ReadValueAsButton(modifier2);
  131. // When the modifiers are gating a button, we require the modifiers to be pressed *first*.
  132. if (modifiersDown && m_BindingIsButton && !overrideModifiersNeedToBePressedFirst)
  133. {
  134. var timestamp = context.GetPressTime(binding);
  135. var timestamp1 = context.GetPressTime(modifier1);
  136. var timestamp2 = context.GetPressTime(modifier2);
  137. return timestamp1 <= timestamp && timestamp2 <= timestamp;
  138. }
  139. return modifiersDown;
  140. }
  141. /// <inheritdoc/>
  142. protected override void FinishSetup(ref InputBindingCompositeContext context)
  143. {
  144. OneModifierComposite.DetermineValueTypeAndSize(ref context, binding, out m_ValueType, out m_ValueSizeInBytes, out m_BindingIsButton);
  145. if (!overrideModifiersNeedToBePressedFirst)
  146. overrideModifiersNeedToBePressedFirst = !InputSystem.settings.shortcutKeysConsumeInput;
  147. }
  148. public override object ReadValueAsObject(ref InputBindingCompositeContext context)
  149. {
  150. if (context.ReadValueAsButton(modifier1) && context.ReadValueAsButton(modifier2))
  151. return context.ReadValueAsObject(binding);
  152. return null;
  153. }
  154. }
  155. }