using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; namespace MLEM.Input { /// /// A keybind represents a generic way to trigger input. /// A keybind is made up of multiple key combinations, one of which has to be pressed for the keybind to be triggered. /// Note that this type is serializable using . /// [DataContract] public class Keybind { [DataMember] private Combination[] combinations = Array.Empty(); /// /// Creates a new keybind and adds the given key and modifiers using /// /// The key to be pressed. /// The modifier keys that have to be held down. public Keybind(GenericInput key, params GenericInput[] modifiers) { this.Add(key, modifiers); } /// public Keybind(GenericInput key, ModifierKey modifier) { this.Add(key, modifier); } /// /// Creates a new keybind with no default combinations /// public Keybind() {} /// /// Adds a new key combination to this keybind that can optionally be pressed for the keybind to trigger. /// /// The key to be pressed. /// The modifier keys that have to be held down. /// This keybind, for chaining public Keybind Add(GenericInput key, params GenericInput[] modifiers) { this.combinations = this.combinations.Append(new Combination(key, modifiers)).ToArray(); return this; } /// public Keybind Add(GenericInput key, ModifierKey modifier) { return this.Add(key, modifier.GetKeys().Select(m => (GenericInput) m).ToArray()); } /// /// Clears this keybind, removing all active combinations. /// /// This keybind, for chaining public Keybind Clear() { this.combinations = Array.Empty(); 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. /// /// The keybind to copy from /// This keybind, for chaining public Keybind CopyFrom(Keybind other) { this.combinations = this.combinations.Concat(other.combinations).ToArray(); return this; } /// /// Returns whether this keybind is considered to be 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 this keybind is considered to be down public bool IsDown(InputHandler handler, int gamepadIndex = -1) { foreach (var combination in this.combinations) { if (combination.IsDown(handler, gamepadIndex)) return true; } return false; } /// /// Returns whether this keybind is considered to be pressed. /// 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 this keybind is considered to be pressed public bool IsPressed(InputHandler handler, int gamepadIndex = -1) { foreach (var combination in this.combinations) { if (combination.IsPressed(handler, gamepadIndex)) return true; } return false; } /// /// 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) { foreach (var combination in this.combinations) { if (combination.IsModifierDown(handler, gamepadIndex)) return true; } return false; } /// /// Returns an enumerable of all of the combinations that this keybind currently contains /// /// This keybind's combinations public IEnumerable GetCombinations() { foreach (var combination in this.combinations) yield return combination; } /// /// Converts this keybind into an easily human-readable string. /// When using , this method is used with set to ", ". /// /// The string to use to join combinations /// The string to use for combination-internal joining, see /// The function to use for determining the display name of generic inputs, see /// A human-readable string representing this keybind public string ToString(string joiner, string combinationJoiner = " + ", Func inputName = null) { return string.Join(joiner, this.combinations.Select(c => c.ToString(combinationJoiner, inputName))); } /// /// Converts this keybind into a string, separating every included by a comma /// /// This keybind as a string public override string ToString() { return this.ToString(", "); } /// /// A key combination is a combination of a set of modifier keys and a key. /// All of the keys are instances, so they can be keyboard-, mouse- or gamepad-based. /// [DataContract] public class Combination { /// /// The inputs that have to be held down for this combination to be valid. /// If this collection is empty, there are no required modifier keys. /// [DataMember] public readonly GenericInput[] Modifiers; /// /// The input that has to be down (or pressed) for this combination to be considered down (or pressed). /// Note that needs to be empty, or all of its values need to be down, as well. /// [DataMember] public readonly GenericInput Key; /// /// Creates a new combination with the given settings. /// To add a combination to a , use instead. /// /// The key /// The modifiers public Combination(GenericInput key, GenericInput[] modifiers) { this.Modifiers = modifiers; this.Key = key; } /// /// 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); } /// /// 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); } /// /// 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) { if (this.Modifiers.Length <= 0) return true; foreach (var modifier in this.Modifiers) { if (handler.IsDown(modifier, gamepadIndex)) return true; } return false; } /// /// Converts this combination into an easily human-readable string. /// When using , this method is used with set to " + ". /// /// The string to use to join this combination's and together /// The function to use for determining the display name of a . If this is null, the generic input's default method is used. /// A human-readable string representing this combination public string ToString(string joiner, Func inputName = null) { return string.Join(joiner, this.Modifiers.Append(this.Key).Select(i => inputName?.Invoke(i) ?? i.ToString())); } /// Returns a string that represents the current object. /// A string that represents the current object. public override string ToString() { return this.ToString(" + "); } } } }