// Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Input; using osu.Game.Rulesets; namespace osu.Game.Input { /// /// Maps input actions to custom action data of type . Use in conjunction with s implementing . /// /// The type of the custom action. public abstract class ActionMappingInputManager : PassThroughInputManager where T : struct { private readonly RulesetInfo ruleset; private readonly int? variant; private readonly ConcurrentActionMode concurrencyMode; private readonly List mappings = new List(); /// /// Create a new instance. /// /// A reference to identify the current . Used to lookup mappings. Null for global mappings. /// An optional variant for the specified . Used when a ruleset has more than one possible keyboard layouts. /// Specify how to deal with multiple matches of combinations and actions. protected ActionMappingInputManager(RulesetInfo ruleset = null, int? variant = null, ConcurrentActionMode concurrencyMode = ConcurrentActionMode.None) { this.ruleset = ruleset; this.variant = variant; this.concurrencyMode = concurrencyMode; } protected abstract IDictionary CreateDefaultMappings(); private BindingStore store; [BackgroundDependencyLoader] private void load(BindingStore bindings) { store = bindings; ReloadMappings(); } protected void ReloadMappings() { var rulesetId = ruleset?.ID; mappings.Clear(); foreach (var kvp in CreateDefaultMappings()) mappings.Add(new Binding(kvp.Key, kvp.Value)); if (store != null) { foreach (var b in store.Query(b => b.RulesetID == rulesetId && b.Variant == variant)) mappings.Add(b); } } private readonly List pressedBindings = new List(); protected override bool PropagateKeyDown(IEnumerable drawables, InputState state, KeyDownEventArgs args) { bool handled = false; if (!args.Repeat && (concurrencyMode > ConcurrentActionMode.None || pressedBindings.Count == 0)) { Binding validBinding; while ((validBinding = mappings.Except(pressedBindings).LastOrDefault(m => m.Keys.CheckValid(state.Keyboard.Keys, concurrencyMode == ConcurrentActionMode.None))) != null) { if (concurrencyMode == ConcurrentActionMode.UniqueAndSameActions || pressedBindings.All(p => p.Action != validBinding.Action)) handled = drawables.OfType>().Any(d => d.OnPressed(validBinding.GetAction())); // store both the pressed combination and the resulting action, just in case the assignments change while we are actuated. pressedBindings.Add(validBinding); } } return handled || base.PropagateKeyDown(drawables, state, args); } protected override bool PropagateKeyUp(IEnumerable drawables, InputState state, KeyUpEventArgs args) { bool handled = false; foreach (var binding in pressedBindings.ToList()) { if (!binding.Keys.CheckValid(state.Keyboard.Keys, concurrencyMode == ConcurrentActionMode.None)) { // clear the no-longer-valid combination/action. pressedBindings.Remove(binding); if (concurrencyMode == ConcurrentActionMode.UniqueAndSameActions || pressedBindings.All(p => p.Action != binding.Action)) { // set data as KeyUp if we're all done with this action. handled = drawables.OfType>().Any(d => d.OnReleased(binding.GetAction())); } } } return handled || base.PropagateKeyUp(drawables, state, args); } } public enum ConcurrentActionMode { /// /// One action can be actuated at once. The first action matching a chord will take precedence and no other action will be actuated until it has been released. /// None, /// /// Unique actions are allowed to be fired at the same time. There may therefore be more than one action in an actuated state at once. /// If one action has multiple bindings, only the first will add actuation data, and the last to be released will add de-actuation data. /// UniqueActions, /// /// Both unique actions and the same action can be concurrently actuated. /// Same as , but multiple bindings for the same action will individually add actuation and de-actuation data to events. /// UniqueAndSameActions, } }