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) {
foreach (var mod in modifier.GetKeys())
this.Add(key, mod);
return this;
}
///
/// Inserts a new key combination into the given of this keybind's combinations that can optionally be pressed for the keybind to trigger.
///
/// The index to insert this combination into.
/// The key to be pressed.
/// The modifier keys that have to be held down.
/// This keybind, for chaining.
public Keybind Insert(int index, GenericInput key, params GenericInput[] modifiers) {
this.combinations = this.combinations.Take(index).Append(new Combination(key, modifiers)).Concat(this.combinations.Skip(index)).ToArray();
return this;
}
///
public Keybind Insert(int index, GenericInput key, ModifierKey modifier) {
foreach (var mod in modifier.GetKeys().Reverse())
this.Insert(index, key, mod);
return this;
}
///
/// 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;
}
///
/// Tries to retrieve the combination at the given within this keybind.
///
/// The index of the combination to retrieve.
/// The combination, or default if this method returns false.
/// Whether the combination could be successfully retrieved or the index was out of bounds of this keybind's combination collection.
public bool TryGetCombination(int index, out Combination combination) {
if (index >= 0 && index < this.combinations.Length) {
combination = this.combinations[index];
return true;
} else {
combination = default;
return false;
}
}
///
/// 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(" + ");
}
}
}
}