diff --git a/MLEM.Ui/Elements/ElementHelper.cs b/MLEM.Ui/Elements/ElementHelper.cs index 1ab92db..66b6bb9 100644 --- a/MLEM.Ui/Elements/ElementHelper.cs +++ b/MLEM.Ui/Elements/ElementHelper.cs @@ -1,4 +1,7 @@ +using System; +using System.Linq; using Microsoft.Xna.Framework; +using MLEM.Input; using MLEM.Textures; namespace MLEM.Ui.Elements { @@ -111,6 +114,60 @@ namespace MLEM.Ui.Elements { return group; } + /// + /// Creates a that acts as a way to input a custom value for a . + /// Note that only the first of the given keybind is displayed and edited, all others are ignored. The exception is that, if is set, unbinding the keybind clears all combinations. + /// Inputting custom keybinds using this element supports -based modifiers and any -based keys. + /// + /// The button's anchor + /// The button's size + /// The keybind that this button should represent + /// The input handler to query inputs with + /// A placeholder text that is displayed while the keybind is being edited + /// An optional generic input that allows the keybind value to be unbound, clearing all combinations + /// A placeholder text that is displayed if the keybind is unbound + /// An optional function to give each input a display name that is easier to read. If this is null, is used. + /// A keybind button with the given settings + public static Button KeybindButton(Anchor anchor, Vector2 size, Keybind keybind, InputHandler inputHandler, string activePlaceholder, GenericInput unbindKey = default, string unboundPlaceholder = "", Func inputName = null) { + string GetCurrentName() { + var combination = keybind.GetCombinations().FirstOrDefault(); + if (combination == null) + return unboundPlaceholder; + return string.Join(" + ", combination.Modifiers + .Append(combination.Key) + .Select(i => inputName?.Invoke(i) ?? i.ToString())); + } + + var button = new Button(anchor, size, GetCurrentName()); + var active = false; + var activeNext = false; + button.OnPressed = e => { + button.Text.Text = activePlaceholder; + activeNext = true; + }; + button.OnUpdated = (e, time) => { + if (activeNext) { + active = true; + activeNext = false; + } else if (active) { + if (unbindKey != default && inputHandler.IsPressed(unbindKey)) { + keybind.Clear(); + button.Text.Text = unboundPlaceholder; + active = false; + } else if (inputHandler.InputsPressed.Length > 0) { + var key = inputHandler.InputsPressed.FirstOrDefault(i => !i.IsModifier()); + if (key != default) { + var mods = inputHandler.InputsDown.Where(i => i.IsModifier()); + keybind.Remove((c, i) => i == 0).Add(key, mods.ToArray()); + button.Text.Text = GetCurrentName(); + active = false; + } + } + } + }; + return button; + } + } /// diff --git a/MLEM/Input/GenericInput.cs b/MLEM/Input/GenericInput.cs index 4905f83..6427c6a 100644 --- a/MLEM/Input/GenericInput.cs +++ b/MLEM/Input/GenericInput.cs @@ -29,13 +29,13 @@ namespace MLEM.Input { public override string ToString() { switch (this.Type) { case InputType.Mouse: - return ((MouseButton) this).ToString(); + return this.Type.ToString() + (MouseButton) this; case InputType.Keyboard: - return ((Keys) this).ToString(); + return this.Type.ToString() + (Keys) this; case InputType.Gamepad: - return ((Buttons) this).ToString(); + return this.Type.ToString() + (Buttons) this; default: - throw new ArgumentOutOfRangeException(nameof(this.Type)); + return this.Type.ToString(); } } @@ -101,11 +101,11 @@ namespace MLEM.Input { /// /// The input to convert /// The resulting keys - /// If the given generic input's is not + /// If the given generic input's is not or public static implicit operator Keys(GenericInput input) { - if (input.Type != InputType.Keyboard) - throw new ArgumentException(); - return (Keys) input.value; + if (input.Type == InputType.None) + return Keys.None; + return input.Type == InputType.Keyboard ? (Keys) input.value : throw new ArgumentException(); } /// @@ -115,9 +115,7 @@ namespace MLEM.Input { /// The resulting button /// If the given generic input's is not public static implicit operator MouseButton(GenericInput input) { - if (input.Type != InputType.Mouse) - throw new ArgumentException(); - return (MouseButton) input.value; + return input.Type == InputType.Mouse ? (MouseButton) input.value : throw new ArgumentException(); } /// @@ -127,9 +125,7 @@ namespace MLEM.Input { /// The resulting buttons /// If the given generic input's is not public static implicit operator Buttons(GenericInput input) { - if (input.Type != InputType.Gamepad) - throw new ArgumentException(); - return (Buttons) input.value; + return input.Type == InputType.Gamepad ? (Buttons) input.value : throw new ArgumentException(); } /// @@ -138,6 +134,11 @@ namespace MLEM.Input { [DataContract] public enum InputType { + /// + /// A type representing no value + /// + [EnumMember] + None, /// /// A type representing /// diff --git a/MLEM/Input/InputHandler.cs b/MLEM/Input/InputHandler.cs index d21373e..a19c63b 100644 --- a/MLEM/Input/InputHandler.cs +++ b/MLEM/Input/InputHandler.cs @@ -523,7 +523,7 @@ namespace MLEM.Input { case GenericInput.InputType.Mouse: return this.IsMouseButtonDown(control); default: - throw new ArgumentException(nameof(control)); + return false; } } @@ -544,7 +544,7 @@ namespace MLEM.Input { case GenericInput.InputType.Mouse: return this.IsMouseButtonUp(control); default: - throw new ArgumentException(nameof(control)); + return false; } } @@ -565,7 +565,7 @@ namespace MLEM.Input { case GenericInput.InputType.Mouse: return this.IsMouseButtonPressed(control); default: - throw new ArgumentException(nameof(control)); + return false; } } diff --git a/MLEM/Input/Keybind.cs b/MLEM/Input/Keybind.cs index 8fdca99..c9888f2 100644 --- a/MLEM/Input/Keybind.cs +++ b/MLEM/Input/Keybind.cs @@ -60,6 +60,16 @@ namespace MLEM.Input { return this; } + /// + /// Removes all combinations that match the given predicate + /// + /// The predicate to match against + /// This keybind, for chaining + public Keybind Remove(Func predicate) { + this.combinations = this.combinations.Where((c, i) => !predicate(c, i)).ToArray(); + return this; + } + /// /// Copies all of the combinations from the given keybind into this keybind. /// Note that this doesn't this keybind, so combinations will be merged rather than replaced. @@ -93,6 +103,17 @@ namespace MLEM.Input { return this.combinations.Any(c => c.IsPressed(handler, gamepadIndex)); } + /// + /// Returns whether any of this keybind's modifier keys are currently down. + /// See for more information. + /// + /// The input handler to query the keys with + /// The index of the gamepad to query, or -1 to query all gamepads + /// Whether any of this keyboard's modifier keys are down + public bool IsModifierDown(InputHandler handler, int gamepadIndex = -1) { + return this.combinations.Any(c => c.IsModifierDown(handler, gamepadIndex)); + } + /// /// Returns an enumerable of all of the combinations that this keybind currently contains /// @@ -133,15 +154,33 @@ namespace MLEM.Input { this.Key = key; } - internal bool IsDown(InputHandler handler, int gamepadIndex = -1) { + /// + /// Returns whether this combination is currently down + /// + /// The input handler to query the keys with + /// The index of the gamepad to query, or -1 to query all gamepads + /// Whether this combination is down + public bool IsDown(InputHandler handler, int gamepadIndex = -1) { return this.IsModifierDown(handler, gamepadIndex) && handler.IsDown(this.Key, gamepadIndex); } - internal bool IsPressed(InputHandler handler, int gamepadIndex = -1) { + /// + /// Returns whether this combination is currently pressed + /// + /// The input handler to query the keys with + /// The index of the gamepad to query, or -1 to query all gamepads + /// Whether this combination is pressed + public bool IsPressed(InputHandler handler, int gamepadIndex = -1) { return this.IsModifierDown(handler, gamepadIndex) && handler.IsPressed(this.Key, gamepadIndex); } - private bool IsModifierDown(InputHandler handler, int gamepadIndex = -1) { + /// + /// Returns whether this combination's modifier keys are currently down + /// + /// The input handler to query the keys with + /// The index of the gamepad to query, or -1 to query all gamepads + /// Whether this combination's modifiers are down + public bool IsModifierDown(InputHandler handler, int gamepadIndex = -1) { return this.Modifiers.Length <= 0 || this.Modifiers.Any(m => handler.IsDown(m, gamepadIndex)); } diff --git a/MLEM/Input/KeysExtensions.cs b/MLEM/Input/KeysExtensions.cs index 4400aca..ce12613 100644 --- a/MLEM/Input/KeysExtensions.cs +++ b/MLEM/Input/KeysExtensions.cs @@ -50,6 +50,11 @@ namespace MLEM.Input { return ModifierKey.None; } + /// + public static ModifierKey GetModifier(this GenericInput input) { + return input.Type == GenericInput.InputType.Keyboard ? GetModifier(input) : ModifierKey.None; + } + /// /// Returns whether the given key is a modifier key or not. /// @@ -59,6 +64,11 @@ namespace MLEM.Input { return GetModifier(key) != ModifierKey.None; } + /// + public static bool IsModifier(this GenericInput input) { + return GetModifier(input) != ModifierKey.None; + } + } /// @@ -68,7 +78,7 @@ namespace MLEM.Input { public enum ModifierKey { /// - /// No modifier key. Only used for + /// No modifier key. Only used for /// None, ///