From 34b12fbfa45c3275c1100e842ae627ded93e3e28 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 9 Aug 2017 17:10:32 +0900 Subject: [PATCH 01/18] Add global actions; improve default assignment --- osu.Game.Rulesets.Catch/CatchInputManager.cs | 19 +++++++------- osu.Game/Input/ActionMappingInputManager.cs | 8 ++++-- .../Input/GlobalActionMappingInputManager.cs | 18 +++++++++++++ osu.Game/Input/OsuAction.cs | 12 +++++++++ osu.Game/OsuGame.cs | 25 ++++++++----------- osu.Game/OsuGameBase.cs | 7 +++--- osu.Game/osu.Game.csproj | 2 ++ 7 files changed, 62 insertions(+), 29 deletions(-) create mode 100644 osu.Game/Input/GlobalActionMappingInputManager.cs create mode 100644 osu.Game/Input/OsuAction.cs diff --git a/osu.Game.Rulesets.Catch/CatchInputManager.cs b/osu.Game.Rulesets.Catch/CatchInputManager.cs index eada5cf532..98d9e7f505 100644 --- a/osu.Game.Rulesets.Catch/CatchInputManager.cs +++ b/osu.Game.Rulesets.Catch/CatchInputManager.cs @@ -11,16 +11,17 @@ namespace osu.Game.Rulesets.Catch { public CatchInputManager(RulesetInfo ruleset) : base(ruleset) { - Mappings = new Dictionary - { - { Key.Z, CatchAction.MoveLeft }, - { Key.Left, CatchAction.MoveLeft }, - { Key.X, CatchAction.MoveRight }, - { Key.Right, CatchAction.MoveRight }, - { Key.LShift, CatchAction.Dash }, - { Key.RShift, CatchAction.Dash }, - }; } + + protected override IDictionary CreateDefaultMappings() => new Dictionary + { + { Key.Z, CatchAction.MoveLeft }, + { Key.Left, CatchAction.MoveLeft }, + { Key.X, CatchAction.MoveRight }, + { Key.Right, CatchAction.MoveRight }, + { Key.LShift, CatchAction.Dash }, + { Key.RShift, CatchAction.Dash }, + }; } public enum CatchAction diff --git a/osu.Game/Input/ActionMappingInputManager.cs b/osu.Game/Input/ActionMappingInputManager.cs index c918982fab..b329d69ccd 100644 --- a/osu.Game/Input/ActionMappingInputManager.cs +++ b/osu.Game/Input/ActionMappingInputManager.cs @@ -14,7 +14,7 @@ namespace osu.Game.Input /// Maps custom action data of type and stores to . /// /// The type of the custom action. - public class ActionMappingInputManager : PassThroughInputManager + public abstract class ActionMappingInputManager : PassThroughInputManager where T : struct { private readonly RulesetInfo ruleset; @@ -30,9 +30,13 @@ namespace osu.Game.Input { this.ruleset = ruleset; this.variant = variant; + + Mappings = CreateDefaultMappings(); } - protected IDictionary Mappings { get; set; } + protected IDictionary Mappings { get; private set; } + + protected abstract IDictionary CreateDefaultMappings(); [BackgroundDependencyLoader] private void load(BindingStore bindings) diff --git a/osu.Game/Input/GlobalActionMappingInputManager.cs b/osu.Game/Input/GlobalActionMappingInputManager.cs new file mode 100644 index 0000000000..782520b3c9 --- /dev/null +++ b/osu.Game/Input/GlobalActionMappingInputManager.cs @@ -0,0 +1,18 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Input; +using OpenTK.Input; +using System.Collections.Generic; + +namespace osu.Game +{ + public class GlobalActionMappingInputManager : ActionMappingInputManager + { + protected override IDictionary CreateDefaultMappings() => new Dictionary() + { + { Key.F8, OsuAction.ToggleChat }, + { Key.F9, OsuAction.ToggleSocial }, + }; + } +} diff --git a/osu.Game/Input/OsuAction.cs b/osu.Game/Input/OsuAction.cs new file mode 100644 index 0000000000..5e25bb26b6 --- /dev/null +++ b/osu.Game/Input/OsuAction.cs @@ -0,0 +1,12 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + + +namespace osu.Game +{ + public enum OsuAction + { + ToggleChat, + ToggleSocial + } +} diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 8d8c5cf26e..8ad2dd96ef 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -256,22 +256,17 @@ namespace osu.Game { if (args.Repeat || intro == null) return false; - switch (args.Key) + if (state.Data is OsuAction) { - case Key.F8: - chat.ToggleVisibility(); - return true; - case Key.F9: - social.ToggleVisibility(); - return true; - case Key.PageUp: - case Key.PageDown: - var swClock = (Clock as ThrottledFrameClock)?.Source as StopwatchClock; - if (swClock == null) return false; - - swClock.Rate *= args.Key == Key.PageUp ? 1.1f : 0.9f; - Logger.Log($@"Adjusting game clock to {swClock.Rate}", LoggingTarget.Debug); - return true; + switch ((OsuAction)state.Data) + { + case OsuAction.ToggleChat: + chat.ToggleVisibility(); + return true; + case OsuAction.ToggleSocial: + social.ToggleVisibility(); + return true; + } } if (state.Keyboard.ControlPressed) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index b76235f3f4..09e4996157 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -187,13 +187,14 @@ namespace osu.Game Children = new Drawable[] { Cursor = new MenuCursor(), - new OsuTooltipContainer(Cursor) + new GlobalActionMappingInputManager { RelativeSizeAxes = Axes.Both, - Child = content = new OsuContextMenuContainer + Child = new OsuTooltipContainer(Cursor) { RelativeSizeAxes = Axes.Both, - }, + Child = content = new OsuContextMenuContainer { RelativeSizeAxes = Axes.Both }, + } } } }); diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index aba4384b59..539e77f142 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -94,6 +94,8 @@ + + From 720bd38d8efa56d100d9d6deedf978fb4b30fb1b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 9 Aug 2017 17:21:44 +0900 Subject: [PATCH 02/18] Fix CI issues --- osu.Game/Input/GlobalActionMappingInputManager.cs | 5 ++--- osu.Game/Input/OsuAction.cs | 3 +-- osu.Game/OsuGame.cs | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/osu.Game/Input/GlobalActionMappingInputManager.cs b/osu.Game/Input/GlobalActionMappingInputManager.cs index 782520b3c9..968d092f8d 100644 --- a/osu.Game/Input/GlobalActionMappingInputManager.cs +++ b/osu.Game/Input/GlobalActionMappingInputManager.cs @@ -1,15 +1,14 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Game.Input; using OpenTK.Input; using System.Collections.Generic; -namespace osu.Game +namespace osu.Game.Input { public class GlobalActionMappingInputManager : ActionMappingInputManager { - protected override IDictionary CreateDefaultMappings() => new Dictionary() + protected override IDictionary CreateDefaultMappings() => new Dictionary { { Key.F8, OsuAction.ToggleChat }, { Key.F9, OsuAction.ToggleSocial }, diff --git a/osu.Game/Input/OsuAction.cs b/osu.Game/Input/OsuAction.cs index 5e25bb26b6..8e956746ed 100644 --- a/osu.Game/Input/OsuAction.cs +++ b/osu.Game/Input/OsuAction.cs @@ -1,8 +1,7 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game +namespace osu.Game.Input { public enum OsuAction { diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 8ad2dd96ef..27d3fb601c 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -13,7 +13,6 @@ using OpenTK.Input; using osu.Framework.Logging; using osu.Game.Graphics.UserInterface.Volume; using osu.Framework.Allocation; -using osu.Framework.Timing; using osu.Game.Overlays.Toolbar; using osu.Game.Screens; using osu.Game.Screens.Menu; @@ -27,6 +26,7 @@ using osu.Game.Rulesets.Scoring; using osu.Game.Overlays.Notifications; using osu.Game.Rulesets; using osu.Game.Screens.Play; +using osu.Game.Input; namespace osu.Game { From 30bd1d70b543fc24281d278c4e63dea388eacbef Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 10 Aug 2017 16:08:43 +0900 Subject: [PATCH 03/18] ActionMapping doesn't support concurrent actions by default But can when required. Also supports key combination bindings now. --- osu.Game.Rulesets.Catch/CatchInputManager.cs | 4 +- osu.Game/Input/ActionMappingInputManager.cs | 90 +++++++++++++------ osu.Game/Input/Binding.cs | 28 +++++- osu.Game/Input/BindingStore.cs | 18 ++++ .../Input/GlobalActionMappingInputManager.cs | 2 +- osu.Game/Input/KeyCombination.cs | 58 ++++++++++++ osu.Game/osu.Game.csproj | 1 + 7 files changed, 167 insertions(+), 34 deletions(-) create mode 100644 osu.Game/Input/KeyCombination.cs diff --git a/osu.Game.Rulesets.Catch/CatchInputManager.cs b/osu.Game.Rulesets.Catch/CatchInputManager.cs index 98d9e7f505..40020e8649 100644 --- a/osu.Game.Rulesets.Catch/CatchInputManager.cs +++ b/osu.Game.Rulesets.Catch/CatchInputManager.cs @@ -9,11 +9,11 @@ namespace osu.Game.Rulesets.Catch { public class CatchInputManager : ActionMappingInputManager { - public CatchInputManager(RulesetInfo ruleset) : base(ruleset) + public CatchInputManager(RulesetInfo ruleset) : base(ruleset, allowConcurrentActions: true) { } - protected override IDictionary CreateDefaultMappings() => new Dictionary + protected override IDictionary CreateDefaultMappings() => new Dictionary { { Key.Z, CatchAction.MoveLeft }, { Key.Left, CatchAction.MoveLeft }, diff --git a/osu.Game/Input/ActionMappingInputManager.cs b/osu.Game/Input/ActionMappingInputManager.cs index b329d69ccd..db6561849c 100644 --- a/osu.Game/Input/ActionMappingInputManager.cs +++ b/osu.Game/Input/ActionMappingInputManager.cs @@ -3,10 +3,10 @@ using System; using System.Collections.Generic; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Input; using osu.Game.Rulesets; -using OpenTK.Input; namespace osu.Game.Input { @@ -21,58 +21,92 @@ namespace osu.Game.Input private readonly int? variant; + private readonly bool allowConcurrentActions; + + 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. - protected ActionMappingInputManager(RulesetInfo ruleset = null, int? variant = null) + /// Allow concurrent actions to be actuated at once. Note that this disables chord bindings. + protected ActionMappingInputManager(RulesetInfo ruleset = null, int? variant = null, bool allowConcurrentActions = false) { this.ruleset = ruleset; this.variant = variant; - - Mappings = CreateDefaultMappings(); + this.allowConcurrentActions = allowConcurrentActions; } - protected IDictionary Mappings { get; private set; } + protected abstract IDictionary CreateDefaultMappings(); - protected abstract IDictionary CreateDefaultMappings(); + private BindingStore store; [BackgroundDependencyLoader] private void load(BindingStore bindings) { - var rulesetId = ruleset?.ID; - foreach (var b in bindings.Query(b => b.RulesetID == rulesetId && b.Variant == variant)) - Mappings[b.Key] = (T)(object)b.Action; + 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); + } + + if (allowConcurrentActions) + { + // ensure we have no overlapping bindings. + foreach (var m in mappings) + foreach (var colliding in mappings.Where(k => !k.Keys.Equals(m.Keys) && k.Keys.CheckValid(m.Keys.Keys))) + throw new InvalidOperationException($"Multiple partially overlapping bindings are not supported ({m} and {colliding} are colliding)!"); + } + } + + private readonly List pressedBindings = new List(); + protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) { - mapKey(state, args.Key); + if (!args.Repeat && (allowConcurrentActions || pressedBindings.Count == 0)) + { + Binding validBinding; + + if ((validBinding = mappings.Except(pressedBindings).LastOrDefault(m => m.Keys.CheckValid(state.Keyboard.Keys))) != null) + { + // store both the pressed combination and the resulting action, just in case the assignments change while we are actuated. + pressedBindings.Add(validBinding); + state.Data = validBinding.GetAction(); + } + } + return base.OnKeyDown(state, args); } protected override bool OnKeyUp(InputState state, KeyUpEventArgs args) { - mapKey(state, args.Key); + foreach (var binding in pressedBindings.ToList()) + { + if (!binding.Keys.CheckValid(state.Keyboard.Keys)) + { + // set data as KeyUp. + state.Data = binding.GetAction(); + + // and clear the no-longer-valid combination/action. + pressedBindings.Remove(binding); + } + } + return base.OnKeyUp(state, args); } - - private void mapKey(InputState state, Key key) - { - T mappedData; - if (Mappings.TryGetValue(key, out mappedData)) - state.Data = mappedData; - } - - private T parseStringRepresentation(string str) - { - T res; - - if (Enum.TryParse(str, out res)) - return res; - - return default(T); - } } } diff --git a/osu.Game/Input/Binding.cs b/osu.Game/Input/Binding.cs index e887d15a65..11db78ec93 100644 --- a/osu.Game/Input/Binding.cs +++ b/osu.Game/Input/Binding.cs @@ -2,7 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Game.Rulesets; -using OpenTK.Input; using SQLite.Net.Attributes; using SQLiteNetExtensions.Attributes; @@ -16,8 +15,31 @@ namespace osu.Game.Input [Indexed] public int? Variant { get; set; } - public Key Key { get; set; } + [Column("Keys")] + public string KeysString + { + get { return Keys.ToString(); } + set { Keys = value; } + } - public int Action { get; set; } + [Ignore] + public KeyCombination Keys { get; private set; } + + public int Action { get; private set; } + + public Binding() + { + + } + + public Binding(KeyCombination keys, object action) + { + Keys = keys; + Action = (int)action; + } + + public virtual T GetAction() => (T)(object)Action; + + public override string ToString() => $"{KeysString}=>{Action}"; } } \ No newline at end of file diff --git a/osu.Game/Input/BindingStore.cs b/osu.Game/Input/BindingStore.cs index aa47bee068..f66d481a74 100644 --- a/osu.Game/Input/BindingStore.cs +++ b/osu.Game/Input/BindingStore.cs @@ -15,6 +15,24 @@ namespace osu.Game.Input { } + protected override int StoreVersion => 2; + + protected override void PerformMigration(int currentVersion, int targetVersion) + { + base.PerformMigration(currentVersion, targetVersion); + + while (currentVersion++ < targetVersion) + { + switch (currentVersion) + { + case 1: + // cannot migrate; breaking underlying changes. + Reset(); + break; + } + } + } + protected override void Prepare(bool reset = false) { Connection.CreateTable(); diff --git a/osu.Game/Input/GlobalActionMappingInputManager.cs b/osu.Game/Input/GlobalActionMappingInputManager.cs index 968d092f8d..cd601ac944 100644 --- a/osu.Game/Input/GlobalActionMappingInputManager.cs +++ b/osu.Game/Input/GlobalActionMappingInputManager.cs @@ -8,7 +8,7 @@ namespace osu.Game.Input { public class GlobalActionMappingInputManager : ActionMappingInputManager { - protected override IDictionary CreateDefaultMappings() => new Dictionary + protected override IDictionary CreateDefaultMappings() => new Dictionary { { Key.F8, OsuAction.ToggleChat }, { Key.F9, OsuAction.ToggleSocial }, diff --git a/osu.Game/Input/KeyCombination.cs b/osu.Game/Input/KeyCombination.cs new file mode 100644 index 0000000000..ef1d578dee --- /dev/null +++ b/osu.Game/Input/KeyCombination.cs @@ -0,0 +1,58 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using OpenTK.Input; + +namespace osu.Game.Input +{ + /// + /// Represent a combination of more than one s. + /// + public class KeyCombination : IEquatable + { + public readonly IEnumerable Keys; + + public KeyCombination(params Key[] keys) + { + Keys = keys; + } + + public KeyCombination(IEnumerable keys) + { + Keys = keys; + } + + public KeyCombination(string stringRepresentation) + { + Keys = stringRepresentation.Split(',').Select(s => (Key)int.Parse(s)); + } + + public bool CheckValid(IEnumerable keys) => !Keys.Except(keys).Any(); + + public bool Equals(KeyCombination other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return Keys.SequenceEqual(other.Keys); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != GetType()) return false; + return Equals((KeyCombination)obj); + } + + public override int GetHashCode() => Keys != null ? Keys.Select(k => k.GetHashCode()).Aggregate((h1, h2) => h1 + h2) : 0; + + public static implicit operator KeyCombination(Key singleKey) => new KeyCombination(singleKey); + + public static implicit operator KeyCombination(string stringRepresentation) => new KeyCombination(stringRepresentation); + + public override string ToString() => Keys.Select(k => ((int)k).ToString()).Aggregate((s1, s2) => $"{s1},{s2}"); + } +} \ No newline at end of file diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 539e77f142..6aff6734e0 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -94,6 +94,7 @@ + From d9e36237c77db26f5a189042af299449400fc9f1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 10 Aug 2017 16:45:10 +0900 Subject: [PATCH 04/18] Move all OsuGame events to OsuAction --- .../Input/GlobalActionMappingInputManager.cs | 4 +++ osu.Game/Input/KeyCombination.cs | 2 ++ osu.Game/Input/OsuAction.cs | 6 +++- osu.Game/OsuGame.cs | 35 ++++++------------- 4 files changed, 21 insertions(+), 26 deletions(-) diff --git a/osu.Game/Input/GlobalActionMappingInputManager.cs b/osu.Game/Input/GlobalActionMappingInputManager.cs index cd601ac944..250640422d 100644 --- a/osu.Game/Input/GlobalActionMappingInputManager.cs +++ b/osu.Game/Input/GlobalActionMappingInputManager.cs @@ -12,6 +12,10 @@ namespace osu.Game.Input { { Key.F8, OsuAction.ToggleChat }, { Key.F9, OsuAction.ToggleSocial }, + { new[] { Key.LControl, Key.LAlt, Key.R }, OsuAction.ResetInputSettings }, + { new[] { Key.LControl, Key.T }, OsuAction.ToggleToolbar }, + { new[] { Key.LControl, Key.O }, OsuAction.ToggleSettings }, + { new[] { Key.LControl, Key.D }, OsuAction.ToggleDirect }, }; } } diff --git a/osu.Game/Input/KeyCombination.cs b/osu.Game/Input/KeyCombination.cs index ef1d578dee..67966b8935 100644 --- a/osu.Game/Input/KeyCombination.cs +++ b/osu.Game/Input/KeyCombination.cs @@ -53,6 +53,8 @@ namespace osu.Game.Input public static implicit operator KeyCombination(string stringRepresentation) => new KeyCombination(stringRepresentation); + public static implicit operator KeyCombination(Key[] keys) => new KeyCombination(keys); + public override string ToString() => Keys.Select(k => ((int)k).ToString()).Aggregate((s1, s2) => $"{s1},{s2}"); } } \ No newline at end of file diff --git a/osu.Game/Input/OsuAction.cs b/osu.Game/Input/OsuAction.cs index 8e956746ed..c80f212df8 100644 --- a/osu.Game/Input/OsuAction.cs +++ b/osu.Game/Input/OsuAction.cs @@ -6,6 +6,10 @@ namespace osu.Game.Input public enum OsuAction { ToggleChat, - ToggleSocial + ToggleSocial, + ResetInputSettings, + ToggleToolbar, + ToggleSettings, + ToggleDirect } } diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 27d3fb601c..d9321654f7 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -9,7 +9,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Overlays; using osu.Framework.Input; -using OpenTK.Input; using osu.Framework.Logging; using osu.Game.Graphics.UserInterface.Volume; using osu.Framework.Allocation; @@ -266,36 +265,22 @@ namespace osu.Game case OsuAction.ToggleSocial: social.ToggleVisibility(); return true; - } - } + case OsuAction.ResetInputSettings: + var sensitivity = frameworkConfig.GetBindable(FrameworkSetting.CursorSensitivity); - if (state.Keyboard.ControlPressed) - { - switch (args.Key) - { - case Key.R: - if (state.Keyboard.AltPressed) - { - var sensitivity = frameworkConfig.GetBindable(FrameworkSetting.CursorSensitivity); + sensitivity.Disabled = false; + sensitivity.Value = 1; + sensitivity.Disabled = true; - sensitivity.Disabled = false; - sensitivity.Value = 1; - sensitivity.Disabled = true; - - frameworkConfig.Set(FrameworkSetting.ActiveInputHandlers, string.Empty); - return true; - } - break; - case Key.T: + frameworkConfig.Set(FrameworkSetting.ActiveInputHandlers, string.Empty); + return true; + case OsuAction.ToggleToolbar: Toolbar.ToggleVisibility(); return true; - case Key.O: + case OsuAction.ToggleSettings: settings.ToggleVisibility(); return true; - case Key.D: - if (state.Keyboard.ShiftPressed || state.Keyboard.AltPressed) - return false; - + case OsuAction.ToggleDirect: direct.ToggleVisibility(); return true; } From da50101c252c1b7bb04b1d920214869f33b48613 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 10 Aug 2017 16:45:33 +0900 Subject: [PATCH 05/18] Don't match on partial key chords when concurrent is disallowed --- osu.Game/Input/ActionMappingInputManager.cs | 4 ++-- osu.Game/Input/KeyCombination.cs | 8 +++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/osu.Game/Input/ActionMappingInputManager.cs b/osu.Game/Input/ActionMappingInputManager.cs index db6561849c..c4489b644c 100644 --- a/osu.Game/Input/ActionMappingInputManager.cs +++ b/osu.Game/Input/ActionMappingInputManager.cs @@ -81,7 +81,7 @@ namespace osu.Game.Input { Binding validBinding; - if ((validBinding = mappings.Except(pressedBindings).LastOrDefault(m => m.Keys.CheckValid(state.Keyboard.Keys))) != null) + if ((validBinding = mappings.Except(pressedBindings).LastOrDefault(m => m.Keys.CheckValid(state.Keyboard.Keys, !allowConcurrentActions))) != null) { // store both the pressed combination and the resulting action, just in case the assignments change while we are actuated. pressedBindings.Add(validBinding); @@ -96,7 +96,7 @@ namespace osu.Game.Input { foreach (var binding in pressedBindings.ToList()) { - if (!binding.Keys.CheckValid(state.Keyboard.Keys)) + if (!binding.Keys.CheckValid(state.Keyboard.Keys, !allowConcurrentActions)) { // set data as KeyUp. state.Data = binding.GetAction(); diff --git a/osu.Game/Input/KeyCombination.cs b/osu.Game/Input/KeyCombination.cs index 67966b8935..1e8dff0a51 100644 --- a/osu.Game/Input/KeyCombination.cs +++ b/osu.Game/Input/KeyCombination.cs @@ -30,7 +30,13 @@ namespace osu.Game.Input Keys = stringRepresentation.Split(',').Select(s => (Key)int.Parse(s)); } - public bool CheckValid(IEnumerable keys) => !Keys.Except(keys).Any(); + public bool CheckValid(IEnumerable keys, bool requireExactMatch = false) + { + if (requireExactMatch) + return Keys.SequenceEqual(keys); + else + return !Keys.Except(keys).Any(); + } public bool Equals(KeyCombination other) { From 6ba5bdf1e6bdae7a40e5fbb3771dab34f29f2a98 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 10 Aug 2017 17:14:30 +0900 Subject: [PATCH 06/18] Add description attributes to all actions --- osu.Game.Rulesets.Catch/CatchInputManager.cs | 4 ++++ osu.Game/Input/OsuAction.cs | 10 +++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/CatchInputManager.cs b/osu.Game.Rulesets.Catch/CatchInputManager.cs index 40020e8649..8e973a70c7 100644 --- a/osu.Game.Rulesets.Catch/CatchInputManager.cs +++ b/osu.Game.Rulesets.Catch/CatchInputManager.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Collections.Generic; +using System.ComponentModel; using osu.Game.Input; using OpenTK.Input; @@ -26,8 +27,11 @@ namespace osu.Game.Rulesets.Catch public enum CatchAction { + [Description("Move left")] MoveLeft, + [Description("Move right")] MoveRight, + [Description("Engage dash")] Dash } } diff --git a/osu.Game/Input/OsuAction.cs b/osu.Game/Input/OsuAction.cs index c80f212df8..a0c203e3ec 100644 --- a/osu.Game/Input/OsuAction.cs +++ b/osu.Game/Input/OsuAction.cs @@ -1,15 +1,23 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System.ComponentModel; + namespace osu.Game.Input { public enum OsuAction { + [Description("Toggle chat overlay")] ToggleChat, + [Description("Toggle social overlay")] ToggleSocial, + [Description("Reset input settings")] ResetInputSettings, + [Description("Toggle toolbar")] ToggleToolbar, + [Description("Toggle settings")] ToggleSettings, - ToggleDirect + [Description("Toggle osu!direct")] + ToggleDirect, } } From c82db54fb5f1cd8313ec894c726d3574686af5cf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 10 Aug 2017 17:22:08 +0900 Subject: [PATCH 07/18] Rename OsuAction to GlobalAction --- osu.Game/Input/{OsuAction.cs => GlobalAction.cs} | 2 +- .../Input/GlobalActionMappingInputManager.cs | 16 ++++++++-------- osu.Game/OsuGame.cs | 16 ++++++++-------- osu.Game/osu.Game.csproj | 2 +- 4 files changed, 18 insertions(+), 18 deletions(-) rename osu.Game/Input/{OsuAction.cs => GlobalAction.cs} (92%) diff --git a/osu.Game/Input/OsuAction.cs b/osu.Game/Input/GlobalAction.cs similarity index 92% rename from osu.Game/Input/OsuAction.cs rename to osu.Game/Input/GlobalAction.cs index a0c203e3ec..c15773b888 100644 --- a/osu.Game/Input/OsuAction.cs +++ b/osu.Game/Input/GlobalAction.cs @@ -5,7 +5,7 @@ using System.ComponentModel; namespace osu.Game.Input { - public enum OsuAction + public enum GlobalAction { [Description("Toggle chat overlay")] ToggleChat, diff --git a/osu.Game/Input/GlobalActionMappingInputManager.cs b/osu.Game/Input/GlobalActionMappingInputManager.cs index 250640422d..890872be04 100644 --- a/osu.Game/Input/GlobalActionMappingInputManager.cs +++ b/osu.Game/Input/GlobalActionMappingInputManager.cs @@ -6,16 +6,16 @@ using System.Collections.Generic; namespace osu.Game.Input { - public class GlobalActionMappingInputManager : ActionMappingInputManager + public class GlobalActionMappingInputManager : ActionMappingInputManager { - protected override IDictionary CreateDefaultMappings() => new Dictionary + protected override IDictionary CreateDefaultMappings() => new Dictionary { - { Key.F8, OsuAction.ToggleChat }, - { Key.F9, OsuAction.ToggleSocial }, - { new[] { Key.LControl, Key.LAlt, Key.R }, OsuAction.ResetInputSettings }, - { new[] { Key.LControl, Key.T }, OsuAction.ToggleToolbar }, - { new[] { Key.LControl, Key.O }, OsuAction.ToggleSettings }, - { new[] { Key.LControl, Key.D }, OsuAction.ToggleDirect }, + { Key.F8, GlobalAction.ToggleChat }, + { Key.F9, GlobalAction.ToggleSocial }, + { new[] { Key.LControl, Key.LAlt, Key.R }, GlobalAction.ResetInputSettings }, + { new[] { Key.LControl, Key.T }, GlobalAction.ToggleToolbar }, + { new[] { Key.LControl, Key.O }, GlobalAction.ToggleSettings }, + { new[] { Key.LControl, Key.D }, GlobalAction.ToggleDirect }, }; } } diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index d9321654f7..f84864596b 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -255,17 +255,17 @@ namespace osu.Game { if (args.Repeat || intro == null) return false; - if (state.Data is OsuAction) + if (state.Data is GlobalAction) { - switch ((OsuAction)state.Data) + switch ((GlobalAction)state.Data) { - case OsuAction.ToggleChat: + case GlobalAction.ToggleChat: chat.ToggleVisibility(); return true; - case OsuAction.ToggleSocial: + case GlobalAction.ToggleSocial: social.ToggleVisibility(); return true; - case OsuAction.ResetInputSettings: + case GlobalAction.ResetInputSettings: var sensitivity = frameworkConfig.GetBindable(FrameworkSetting.CursorSensitivity); sensitivity.Disabled = false; @@ -274,13 +274,13 @@ namespace osu.Game frameworkConfig.Set(FrameworkSetting.ActiveInputHandlers, string.Empty); return true; - case OsuAction.ToggleToolbar: + case GlobalAction.ToggleToolbar: Toolbar.ToggleVisibility(); return true; - case OsuAction.ToggleSettings: + case GlobalAction.ToggleSettings: settings.ToggleVisibility(); return true; - case OsuAction.ToggleDirect: + case GlobalAction.ToggleDirect: direct.ToggleVisibility(); return true; } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 6aff6734e0..9d849b9d67 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -95,7 +95,7 @@ - + From b6bb07c0b5dee5241af0f622fa3d4601ab6a4edf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 10 Aug 2017 18:28:22 +0900 Subject: [PATCH 08/18] Add ConcurrentActionModes to support osu! gameplay more easily --- osu.Game.Rulesets.Catch/CatchInputManager.cs | 2 +- .../UI/CatchRulesetContainer.cs | 2 +- osu.Game/Input/ActionMappingInputManager.cs | 48 ++++++++++++++----- 3 files changed, 37 insertions(+), 15 deletions(-) diff --git a/osu.Game.Rulesets.Catch/CatchInputManager.cs b/osu.Game.Rulesets.Catch/CatchInputManager.cs index 8e973a70c7..2344b41d95 100644 --- a/osu.Game.Rulesets.Catch/CatchInputManager.cs +++ b/osu.Game.Rulesets.Catch/CatchInputManager.cs @@ -10,7 +10,7 @@ namespace osu.Game.Rulesets.Catch { public class CatchInputManager : ActionMappingInputManager { - public CatchInputManager(RulesetInfo ruleset) : base(ruleset, allowConcurrentActions: true) + public CatchInputManager(RulesetInfo ruleset) : base(ruleset, concurrencyMode: ConcurrentActionMode.UniqueActions) { } diff --git a/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs b/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs index 27cc05c47a..8469be24dd 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Catch.UI protected override Playfield CreatePlayfield() => new CatchPlayfield(); - protected override PassThroughInputManager CreateActionMappingInputManager() => new CatchInputManager(Ruleset.RulesetInfo); + protected override PassThroughInputManager CreateActionMappingInputManager() => new CatchInputManager(Ruleset?.RulesetInfo); protected override DrawableHitObject GetVisualRepresentation(CatchBaseHit h) { diff --git a/osu.Game/Input/ActionMappingInputManager.cs b/osu.Game/Input/ActionMappingInputManager.cs index c4489b644c..8c8d274264 100644 --- a/osu.Game/Input/ActionMappingInputManager.cs +++ b/osu.Game/Input/ActionMappingInputManager.cs @@ -10,6 +10,25 @@ using osu.Game.Rulesets; namespace osu.Game.Input { + 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, + } + + /// /// Maps custom action data of type and stores to . /// @@ -21,7 +40,7 @@ namespace osu.Game.Input private readonly int? variant; - private readonly bool allowConcurrentActions; + private readonly ConcurrentActionMode concurrencyMode; private readonly List mappings = new List(); @@ -30,12 +49,12 @@ namespace osu.Game.Input /// /// 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. - /// Allow concurrent actions to be actuated at once. Note that this disables chord bindings. - protected ActionMappingInputManager(RulesetInfo ruleset = null, int? variant = null, bool allowConcurrentActions = false) + /// 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.allowConcurrentActions = allowConcurrentActions; + this.concurrencyMode = concurrencyMode; } protected abstract IDictionary CreateDefaultMappings(); @@ -64,7 +83,7 @@ namespace osu.Game.Input mappings.Add(b); } - if (allowConcurrentActions) + if (concurrencyMode > ConcurrentActionMode.None) { // ensure we have no overlapping bindings. foreach (var m in mappings) @@ -77,15 +96,17 @@ namespace osu.Game.Input protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) { - if (!args.Repeat && (allowConcurrentActions || pressedBindings.Count == 0)) + if (!args.Repeat && (concurrencyMode > ConcurrentActionMode.None || pressedBindings.Count == 0)) { Binding validBinding; - if ((validBinding = mappings.Except(pressedBindings).LastOrDefault(m => m.Keys.CheckValid(state.Keyboard.Keys, !allowConcurrentActions))) != null) + if ((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)) + state.Data = validBinding.GetAction(); + // store both the pressed combination and the resulting action, just in case the assignments change while we are actuated. pressedBindings.Add(validBinding); - state.Data = validBinding.GetAction(); } } @@ -96,13 +117,14 @@ namespace osu.Game.Input { foreach (var binding in pressedBindings.ToList()) { - if (!binding.Keys.CheckValid(state.Keyboard.Keys, !allowConcurrentActions)) + if (!binding.Keys.CheckValid(state.Keyboard.Keys, concurrencyMode == ConcurrentActionMode.None)) { - // set data as KeyUp. - state.Data = binding.GetAction(); - - // and clear the no-longer-valid combination/action. + // 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. + state.Data = binding.GetAction(); } } From e9a11ebc9f3e00f1af1bb98fca768195fe1edd92 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 10 Aug 2017 18:28:43 +0900 Subject: [PATCH 09/18] Use new population methods and implement osu! ruleset actions --- .../Objects/Drawables/Pieces/CirclePiece.cs | 14 +++++- osu.Game.Rulesets.Osu/OsuInputManager.cs | 50 +++++++++++++++++++ .../OsuKeyConversionInputManager.cs | 39 --------------- .../UI/OsuRulesetContainer.cs | 2 +- .../osu.Game.Rulesets.Osu.csproj | 2 +- osu.Game/Input/ActionMappingInputManager.cs | 8 +-- 6 files changed, 66 insertions(+), 49 deletions(-) create mode 100644 osu.Game.Rulesets.Osu/OsuInputManager.cs delete mode 100644 osu.Game.Rulesets.Osu/OsuKeyConversionInputManager.cs diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs index 3004dafda7..75a0c8e957 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs @@ -49,9 +49,19 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces disc.Texture = textures.Get(@"Play/osu/disc"); } - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) { - return Hit?.Invoke() ?? false; + if (state.Data is OsuAction) + { + switch ((OsuAction)state.Data) + { + case OsuAction.LeftButton: + case OsuAction.RightButton: + return IsHovered && (Hit?.Invoke() ?? false); + } + } + + return false; } } } \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu/OsuInputManager.cs b/osu.Game.Rulesets.Osu/OsuInputManager.cs new file mode 100644 index 0000000000..83304a9615 --- /dev/null +++ b/osu.Game.Rulesets.Osu/OsuInputManager.cs @@ -0,0 +1,50 @@ +// 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.Input; +using osu.Game.Input; +using OpenTK.Input; +using KeyboardState = osu.Framework.Input.KeyboardState; +using MouseState = osu.Framework.Input.MouseState; + +namespace osu.Game.Rulesets.Osu +{ + public class OsuInputManager : ActionMappingInputManager + { + public OsuInputManager(RulesetInfo ruleset) : base(ruleset, concurrencyMode: ConcurrentActionMode.UniqueActions) + { + + } + protected override void TransformState(InputState state) + { + base.TransformState(state); + + var mouse = state.Mouse as MouseState; + var keyboard = state.Keyboard as KeyboardState; + + if (mouse != null && keyboard != null) + { + if (mouse.IsPressed(MouseButton.Left)) + keyboard.Keys = keyboard.Keys.Concat(new[] { Key.LastKey + 1 }); + if (mouse.IsPressed(MouseButton.Right)) + keyboard.Keys = keyboard.Keys.Concat(new[] { Key.LastKey + 2 }); + } + } + + protected override IDictionary CreateDefaultMappings() => new Dictionary + { + { Key.Z, OsuAction.LeftButton }, + { Key.X, OsuAction.RightButton }, + { Key.LastKey + 1, OsuAction.LeftButton }, + { Key.LastKey + 2, OsuAction.RightButton }, + }; + } + + public enum OsuAction + { + LeftButton, + RightButton + } +} diff --git a/osu.Game.Rulesets.Osu/OsuKeyConversionInputManager.cs b/osu.Game.Rulesets.Osu/OsuKeyConversionInputManager.cs deleted file mode 100644 index 10adca4e43..0000000000 --- a/osu.Game.Rulesets.Osu/OsuKeyConversionInputManager.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Linq; -using osu.Framework.Input; -using OpenTK.Input; -using KeyboardState = osu.Framework.Input.KeyboardState; -using MouseState = osu.Framework.Input.MouseState; - -namespace osu.Game.Rulesets.Osu -{ - public class OsuKeyConversionInputManager : PassThroughInputManager - { - private bool leftViaKeyboard; - private bool rightViaKeyboard; - - protected override void TransformState(InputState state) - { - base.TransformState(state); - - var mouse = state.Mouse as MouseState; - var keyboard = state.Keyboard as KeyboardState; - - if (keyboard != null) - { - leftViaKeyboard = keyboard.Keys.Contains(Key.Z); - rightViaKeyboard = keyboard.Keys.Contains(Key.X); - } - - if (mouse != null) - { - if (leftViaKeyboard) - mouse.SetPressed(MouseButton.Left, true); - if (rightViaKeyboard) - mouse.SetPressed(MouseButton.Right, true); - } - } - } -} diff --git a/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs b/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs index d029524a32..538b94603c 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Osu.UI protected override Playfield CreatePlayfield() => new OsuPlayfield(); - protected override PassThroughInputManager CreateActionMappingInputManager() => new OsuKeyConversionInputManager(); + protected override PassThroughInputManager CreateActionMappingInputManager() => new OsuInputManager(Ruleset?.RulesetInfo); protected override DrawableHitObject GetVisualRepresentation(OsuHitObject h) { diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index b24d5b3a4a..0b960d6607 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -77,7 +77,7 @@ - + diff --git a/osu.Game/Input/ActionMappingInputManager.cs b/osu.Game/Input/ActionMappingInputManager.cs index 8c8d274264..9d83ab8f50 100644 --- a/osu.Game/Input/ActionMappingInputManager.cs +++ b/osu.Game/Input/ActionMappingInputManager.cs @@ -94,7 +94,7 @@ namespace osu.Game.Input private readonly List pressedBindings = new List(); - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + protected override void PopulateDataKeyDown(InputState state, KeyDownEventArgs args) { if (!args.Repeat && (concurrencyMode > ConcurrentActionMode.None || pressedBindings.Count == 0)) { @@ -109,11 +109,9 @@ namespace osu.Game.Input pressedBindings.Add(validBinding); } } - - return base.OnKeyDown(state, args); } - protected override bool OnKeyUp(InputState state, KeyUpEventArgs args) + protected override void PopulateDataKeyUp(InputState state, KeyUpEventArgs args) { foreach (var binding in pressedBindings.ToList()) { @@ -127,8 +125,6 @@ namespace osu.Game.Input state.Data = binding.GetAction(); } } - - return base.OnKeyUp(state, args); } } } From 1fe273cbc0acab63dd5c94b02c0dcc0188d0e0c6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 10 Aug 2017 18:37:41 +0900 Subject: [PATCH 10/18] Move GameplayCursor to osu! ruleset and make work with OsuActions --- .../UI}/Cursor/CursorTrail.cs | 18 ++++---- .../UI}/Cursor/GameplayCursor.cs | 44 ++++++++++++++----- osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs | 2 +- .../osu.Game.Rulesets.Osu.csproj | 2 + osu.Game/osu.Game.csproj | 2 - 5 files changed, 45 insertions(+), 23 deletions(-) rename {osu.Game/Graphics => osu.Game.Rulesets.Osu/UI}/Cursor/CursorTrail.cs (96%) rename {osu.Game/Graphics => osu.Game.Rulesets.Osu/UI}/Cursor/GameplayCursor.cs (80%) diff --git a/osu.Game/Graphics/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs similarity index 96% rename from osu.Game/Graphics/Cursor/CursorTrail.cs rename to osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs index 035f6f98b0..a5be6a7952 100644 --- a/osu.Game/Graphics/Cursor/CursorTrail.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs @@ -1,22 +1,22 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; +using System.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.OpenGL.Buffers; +using osu.Framework.Graphics.OpenGL.Vertices; +using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Shaders; using osu.Framework.Graphics.Textures; using osu.Framework.Input; -using OpenTK; -using System; -using osu.Framework.Graphics.OpenGL.Buffers; -using OpenTK.Graphics.ES30; -using osu.Framework.Graphics.Primitives; -using osu.Framework.Graphics.Colour; using osu.Framework.Timing; -using System.Diagnostics; -using osu.Framework.Graphics.OpenGL.Vertices; +using OpenTK; +using OpenTK.Graphics.ES30; -namespace osu.Game.Graphics.Cursor +namespace osu.Game.Rulesets.Osu.UI.Cursor { internal class CursorTrail : Drawable { diff --git a/osu.Game/Graphics/Cursor/GameplayCursor.cs b/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs similarity index 80% rename from osu.Game/Graphics/Cursor/GameplayCursor.cs rename to osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs index 9f771ae56d..79331d9414 100644 --- a/osu.Game/Graphics/Cursor/GameplayCursor.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs @@ -1,8 +1,6 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using OpenTK; -using OpenTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Extensions.Color4Extensions; @@ -13,8 +11,10 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Input; using osu.Game.Beatmaps; using osu.Game.Configuration; +using OpenTK; +using OpenTK.Graphics; -namespace osu.Game.Graphics.Cursor +namespace osu.Game.Rulesets.Osu.UI.Cursor { public class GameplayCursor : CursorContainer { @@ -25,18 +25,40 @@ namespace osu.Game.Graphics.Cursor Add(new CursorTrail { Depth = 1 }); } - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + private int downCount; + + protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) { - ActiveCursor.Scale = new Vector2(1); - ActiveCursor.ScaleTo(1.2f, 100, Easing.OutQuad); - return base.OnMouseDown(state, args); + if (state.Data is OsuAction) + { + switch ((OsuAction)state.Data) + { + case OsuAction.LeftButton: + case OsuAction.RightButton: + downCount++; + ActiveCursor.ScaleTo(1).ScaleTo(1.2f, 100, Easing.OutQuad); + break; + } + } + + return false; } - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + protected override bool OnKeyUp(InputState state, KeyUpEventArgs args) { - if (!state.Mouse.HasMainButtonPressed) - ActiveCursor.ScaleTo(1, 200, Easing.OutQuad); - return base.OnMouseUp(state, args); + if (state.Data is OsuAction) + { + switch ((OsuAction)state.Data) + { + case OsuAction.LeftButton: + case OsuAction.RightButton: + if (--downCount == 0) + ActiveCursor.ScaleTo(1, 200, Easing.OutQuad); + break; + } + } + + return false; } public class OsuCursor : Container diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs index 2604b1ee8a..9b88c9d1b3 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs @@ -10,8 +10,8 @@ using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables.Connections; using osu.Game.Rulesets.UI; using System.Linq; -using osu.Game.Graphics.Cursor; using osu.Game.Rulesets.Osu.Judgements; +using osu.Game.Rulesets.Osu.UI.Cursor; namespace osu.Game.Rulesets.Osu.UI { diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index 0b960d6607..1422ded407 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -78,6 +78,8 @@ + + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 9d849b9d67..7c21d64312 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -145,8 +145,6 @@ - - From 798fff00b218a880ffa6637f173fd24ae873546e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 10 Aug 2017 19:28:24 +0900 Subject: [PATCH 11/18] Remove shortcomings, remove InputState.Data usage, make everything amazing No more casting! --- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 60 +++++++--------- .../Objects/Drawables/Pieces/CirclePiece.cs | 19 +++-- .../UI/Cursor/GameplayCursor.cs | 66 ++++++++---------- osu.Game/Input/ActionMappingInputManager.cs | 69 ++++++++++--------- osu.Game/Input/GlobalHotkeys.cs | 24 +++++++ osu.Game/Input/IHandleActions.cs | 12 ++++ osu.Game/OsuGame.cs | 55 +++++++-------- osu.Game/osu.Game.csproj | 2 + 8 files changed, 164 insertions(+), 143 deletions(-) create mode 100644 osu.Game/Input/GlobalHotkeys.cs create mode 100644 osu.Game/Input/IHandleActions.cs diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 776f2119ab..858dfa770f 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -8,8 +8,8 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; -using osu.Framework.Input; using osu.Framework.MathUtils; +using osu.Game.Input; using osu.Game.Rulesets.Catch.Judgements; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects.Drawable; @@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Catch.UI catcher.Size = new Vector2(DrawSize.Y); } - private class Catcher : Container + private class Catcher : Container, IHandleActions { private Texture texture; @@ -104,48 +104,40 @@ namespace osu.Game.Rulesets.Catch.UI OriginPosition = new Vector2(DrawWidth / 2, 10) //temporary until the sprite is aligned correctly. }; - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + public bool OnPressed(CatchAction action) { - if (args.Repeat) return true; - - if (state.Data is CatchAction) + switch (action) { - switch ((CatchAction)state.Data) - { - case CatchAction.MoveLeft: - currentDirection--; - return true; - case CatchAction.MoveRight: - currentDirection++; - return true; - case CatchAction.Dash: - Dashing = true; - return true; - } + case CatchAction.MoveLeft: + currentDirection--; + return true; + case CatchAction.MoveRight: + currentDirection++; + return true; + case CatchAction.Dash: + Dashing = true; + return true; } - return base.OnKeyDown(state, args); + return false; } - protected override bool OnKeyUp(InputState state, KeyUpEventArgs args) + public bool OnReleased(CatchAction action) { - if (state.Data is CatchAction) + switch (action) { - switch ((CatchAction)state.Data) - { - case CatchAction.MoveLeft: - currentDirection++; - return true; - case CatchAction.MoveRight: - currentDirection--; - return true; - case CatchAction.Dash: - Dashing = false; - return true; - } + case CatchAction.MoveLeft: + currentDirection++; + return true; + case CatchAction.MoveRight: + currentDirection--; + return true; + case CatchAction.Dash: + Dashing = false; + return true; } - return base.OnKeyUp(state, args); + return false; } protected override void Update() diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs index 75a0c8e957..313640a6b3 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs @@ -7,12 +7,12 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; -using osu.Framework.Input; +using osu.Game.Input; using OpenTK; namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { - public class CirclePiece : Container + public class CirclePiece : Container, IHandleActions { private readonly Sprite disc; @@ -49,19 +49,18 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces disc.Texture = textures.Get(@"Play/osu/disc"); } - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + public bool OnPressed(OsuAction action) { - if (state.Data is OsuAction) + switch (action) { - switch ((OsuAction)state.Data) - { - case OsuAction.LeftButton: - case OsuAction.RightButton: - return IsHovered && (Hit?.Invoke() ?? false); - } + case OsuAction.LeftButton: + case OsuAction.RightButton: + return IsHovered && (Hit?.Invoke() ?? false); } return false; } + + public bool OnReleased(OsuAction action) => false; } } \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs b/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs index 79331d9414..84ef8f5079 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs @@ -8,15 +8,15 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input; using osu.Game.Beatmaps; using osu.Game.Configuration; +using osu.Game.Input; using OpenTK; using OpenTK.Graphics; namespace osu.Game.Rulesets.Osu.UI.Cursor { - public class GameplayCursor : CursorContainer + public class GameplayCursor : CursorContainer, IHandleActions { protected override Drawable CreateCursor() => new OsuCursor(); @@ -27,40 +27,6 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor private int downCount; - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) - { - if (state.Data is OsuAction) - { - switch ((OsuAction)state.Data) - { - case OsuAction.LeftButton: - case OsuAction.RightButton: - downCount++; - ActiveCursor.ScaleTo(1).ScaleTo(1.2f, 100, Easing.OutQuad); - break; - } - } - - return false; - } - - protected override bool OnKeyUp(InputState state, KeyUpEventArgs args) - { - if (state.Data is OsuAction) - { - switch ((OsuAction)state.Data) - { - case OsuAction.LeftButton: - case OsuAction.RightButton: - if (--downCount == 0) - ActiveCursor.ScaleTo(1, 200, Easing.OutQuad); - break; - } - } - - return false; - } - public class OsuCursor : Container { private Container cursorContainer; @@ -165,5 +131,33 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor cursorContainer.Scale = new Vector2(scale); } } + + public bool OnPressed(OsuAction action) + { + switch (action) + { + case OsuAction.LeftButton: + case OsuAction.RightButton: + downCount++; + ActiveCursor.ScaleTo(1).ScaleTo(1.2f, 100, Easing.OutQuad); + break; + } + + return false; + } + + public bool OnReleased(OsuAction action) + { + switch (action) + { + case OsuAction.LeftButton: + case OsuAction.RightButton: + if (--downCount == 0) + ActiveCursor.ScaleTo(1, 200, Easing.OutQuad); + break; + } + + return false; + } } } diff --git a/osu.Game/Input/ActionMappingInputManager.cs b/osu.Game/Input/ActionMappingInputManager.cs index 9d83ab8f50..21f273cab9 100644 --- a/osu.Game/Input/ActionMappingInputManager.cs +++ b/osu.Game/Input/ActionMappingInputManager.cs @@ -1,36 +1,17 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; 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 { - 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, - } - - /// - /// Maps custom action data of type and stores to . + /// 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 @@ -82,37 +63,35 @@ namespace osu.Game.Input foreach (var b in store.Query(b => b.RulesetID == rulesetId && b.Variant == variant)) mappings.Add(b); } - - if (concurrencyMode > ConcurrentActionMode.None) - { - // ensure we have no overlapping bindings. - foreach (var m in mappings) - foreach (var colliding in mappings.Where(k => !k.Keys.Equals(m.Keys) && k.Keys.CheckValid(m.Keys.Keys))) - throw new InvalidOperationException($"Multiple partially overlapping bindings are not supported ({m} and {colliding} are colliding)!"); - } } private readonly List pressedBindings = new List(); - protected override void PopulateDataKeyDown(InputState state, KeyDownEventArgs args) + protected override bool PropagateKeyDown(IEnumerable drawables, InputState state, KeyDownEventArgs args) { + bool handled = false; + if (!args.Repeat && (concurrencyMode > ConcurrentActionMode.None || pressedBindings.Count == 0)) { Binding validBinding; - if ((validBinding = mappings.Except(pressedBindings).LastOrDefault(m => m.Keys.CheckValid(state.Keyboard.Keys, concurrencyMode == ConcurrentActionMode.None))) != null) + 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)) - state.Data = validBinding.GetAction(); + 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 void PopulateDataKeyUp(InputState state, KeyUpEventArgs 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)) @@ -121,10 +100,32 @@ namespace osu.Game.Input 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. - state.Data = binding.GetAction(); + 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, + } } diff --git a/osu.Game/Input/GlobalHotkeys.cs b/osu.Game/Input/GlobalHotkeys.cs new file mode 100644 index 0000000000..f4c11fc9b4 --- /dev/null +++ b/osu.Game/Input/GlobalHotkeys.cs @@ -0,0 +1,24 @@ +using System; +using osu.Framework.Graphics; + +namespace osu.Game.Input +{ + /// + /// A simple placeholder container which allows handling keyboard input at a higher level than otherwise possible. + /// + public class GlobalHotkeys : Drawable, IHandleActions + { + public Func Handler; + + public override bool HandleInput => true; + + public GlobalHotkeys() + { + RelativeSizeAxes = Axes.Both; + } + + public bool OnPressed(GlobalAction action) => Handler(action); + + public bool OnReleased(GlobalAction action) => false; + } +} diff --git a/osu.Game/Input/IHandleActions.cs b/osu.Game/Input/IHandleActions.cs new file mode 100644 index 0000000000..34720eb1ee --- /dev/null +++ b/osu.Game/Input/IHandleActions.cs @@ -0,0 +1,12 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Input +{ + public interface IHandleActions + where T : struct + { + bool OnPressed(T action); + bool OnReleased(T action); + } +} \ No newline at end of file diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index f84864596b..0a76fe1b38 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -168,7 +168,7 @@ namespace osu.Game volume = new VolumeControl(), overlayContent = new Container { RelativeSizeAxes = Axes.Both }, new OnScreenDisplay(), - new GlobalHotkeys //exists because UserInputManager is at a level below us. + new Input.GlobalHotkeys //exists because UserInputManager is at a level below us. { Handler = globalHotkeyPressed } @@ -251,39 +251,36 @@ namespace osu.Game Cursor.State = Visibility.Hidden; } - private bool globalHotkeyPressed(InputState state, KeyDownEventArgs args) + private bool globalHotkeyPressed(GlobalAction action) { - if (args.Repeat || intro == null) return false; + if (intro == null) return false; - if (state.Data is GlobalAction) + switch (action) { - switch ((GlobalAction)state.Data) - { - case GlobalAction.ToggleChat: - chat.ToggleVisibility(); - return true; - case GlobalAction.ToggleSocial: - social.ToggleVisibility(); - return true; - case GlobalAction.ResetInputSettings: - var sensitivity = frameworkConfig.GetBindable(FrameworkSetting.CursorSensitivity); + case GlobalAction.ToggleChat: + chat.ToggleVisibility(); + return true; + case GlobalAction.ToggleSocial: + social.ToggleVisibility(); + return true; + case GlobalAction.ResetInputSettings: + var sensitivity = frameworkConfig.GetBindable(FrameworkSetting.CursorSensitivity); - sensitivity.Disabled = false; - sensitivity.Value = 1; - sensitivity.Disabled = true; + sensitivity.Disabled = false; + sensitivity.Value = 1; + sensitivity.Disabled = true; - frameworkConfig.Set(FrameworkSetting.ActiveInputHandlers, string.Empty); - return true; - case GlobalAction.ToggleToolbar: - Toolbar.ToggleVisibility(); - return true; - case GlobalAction.ToggleSettings: - settings.ToggleVisibility(); - return true; - case GlobalAction.ToggleDirect: - direct.ToggleVisibility(); - return true; - } + frameworkConfig.Set(FrameworkSetting.ActiveInputHandlers, string.Empty); + return true; + case GlobalAction.ToggleToolbar: + Toolbar.ToggleVisibility(); + return true; + case GlobalAction.ToggleSettings: + settings.ToggleVisibility(); + return true; + case GlobalAction.ToggleDirect: + direct.ToggleVisibility(); + return true; } return false; diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 7c21d64312..74be1d4262 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -94,6 +94,8 @@ + + From 641b3bd27e2985c0e90f432c673201e776e62c4c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 10 Aug 2017 19:36:47 +0900 Subject: [PATCH 12/18] Improve documentation --- osu.Game/Input/ActionMappingInputManager.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/osu.Game/Input/ActionMappingInputManager.cs b/osu.Game/Input/ActionMappingInputManager.cs index 21f273cab9..43d89ef1de 100644 --- a/osu.Game/Input/ActionMappingInputManager.cs +++ b/osu.Game/Input/ActionMappingInputManager.cs @@ -30,7 +30,7 @@ namespace osu.Game.Input /// /// 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. + /// Specify how to deal with multiple matches of s and s. protected ActionMappingInputManager(RulesetInfo ruleset = null, int? variant = null, ConcurrentActionMode concurrencyMode = ConcurrentActionMode.None) { this.ruleset = ruleset; @@ -114,17 +114,19 @@ namespace osu.Game.Input 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. + /// One action can be pressed at once. The first action matching a chord will take precedence and no other action will be pressed until it has first 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. + /// Unique actions are allowed to be pressed 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 trigger an . + /// The last binding to be released will trigger an . /// 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. + /// Unique actions are allowed to be pressed at the same time. There may therefore be more than one action in an actuated state at once (same as ). + /// In addition to this, multiple are fired for single actions, even if has not yet been fired. + /// The same goes for , which is fired for each matching binding that is released. /// UniqueAndSameActions, } From a93a92a2bbf70030ae24bb8d68a35e74209c7bb0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 10 Aug 2017 19:52:45 +0900 Subject: [PATCH 13/18] Remove GlobalHotkeys --- osu.Game/Input/GlobalAction.cs | 23 ---------- .../Input/GlobalActionMappingInputManager.cs | 46 +++++++++++++++++++ osu.Game/Input/GlobalHotkeys.cs | 24 ---------- osu.Game/OsuGame.cs | 10 ++-- osu.Game/OsuGameBase.cs | 2 +- osu.Game/osu.Game.csproj | 2 - 6 files changed, 51 insertions(+), 56 deletions(-) delete mode 100644 osu.Game/Input/GlobalAction.cs delete mode 100644 osu.Game/Input/GlobalHotkeys.cs diff --git a/osu.Game/Input/GlobalAction.cs b/osu.Game/Input/GlobalAction.cs deleted file mode 100644 index c15773b888..0000000000 --- a/osu.Game/Input/GlobalAction.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.ComponentModel; - -namespace osu.Game.Input -{ - public enum GlobalAction - { - [Description("Toggle chat overlay")] - ToggleChat, - [Description("Toggle social overlay")] - ToggleSocial, - [Description("Reset input settings")] - ResetInputSettings, - [Description("Toggle toolbar")] - ToggleToolbar, - [Description("Toggle settings")] - ToggleSettings, - [Description("Toggle osu!direct")] - ToggleDirect, - } -} diff --git a/osu.Game/Input/GlobalActionMappingInputManager.cs b/osu.Game/Input/GlobalActionMappingInputManager.cs index 890872be04..3136fa5be5 100644 --- a/osu.Game/Input/GlobalActionMappingInputManager.cs +++ b/osu.Game/Input/GlobalActionMappingInputManager.cs @@ -3,11 +3,23 @@ using OpenTK.Input; using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using osu.Framework.Graphics; +using osu.Framework.Input; namespace osu.Game.Input { public class GlobalActionMappingInputManager : ActionMappingInputManager { + private readonly Drawable handler; + + public GlobalActionMappingInputManager(OsuGameBase game) + { + if (game is IHandleActions) + handler = game; + } + protected override IDictionary CreateDefaultMappings() => new Dictionary { { Key.F8, GlobalAction.ToggleChat }, @@ -17,5 +29,39 @@ namespace osu.Game.Input { new[] { Key.LControl, Key.O }, GlobalAction.ToggleSettings }, { new[] { Key.LControl, Key.D }, GlobalAction.ToggleDirect }, }; + + protected override bool PropagateKeyDown(IEnumerable drawables, InputState state, KeyDownEventArgs args) + { + if (handler != null) + drawables = new[] { handler }.Concat(drawables); + + // always handle ourselves before all children. + return base.PropagateKeyDown(drawables, state, args); + } + + protected override bool PropagateKeyUp(IEnumerable drawables, InputState state, KeyUpEventArgs args) + { + if (handler != null) + drawables = new[] { handler }.Concat(drawables); + + // always handle ourselves before all children. + return base.PropagateKeyUp(drawables, state, args); + } + } + + public enum GlobalAction + { + [Description("Toggle chat overlay")] + ToggleChat, + [Description("Toggle social overlay")] + ToggleSocial, + [Description("Reset input settings")] + ResetInputSettings, + [Description("Toggle toolbar")] + ToggleToolbar, + [Description("Toggle settings")] + ToggleSettings, + [Description("Toggle osu!direct")] + ToggleDirect, } } diff --git a/osu.Game/Input/GlobalHotkeys.cs b/osu.Game/Input/GlobalHotkeys.cs deleted file mode 100644 index f4c11fc9b4..0000000000 --- a/osu.Game/Input/GlobalHotkeys.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using osu.Framework.Graphics; - -namespace osu.Game.Input -{ - /// - /// A simple placeholder container which allows handling keyboard input at a higher level than otherwise possible. - /// - public class GlobalHotkeys : Drawable, IHandleActions - { - public Func Handler; - - public override bool HandleInput => true; - - public GlobalHotkeys() - { - RelativeSizeAxes = Axes.Both; - } - - public bool OnPressed(GlobalAction action) => Handler(action); - - public bool OnReleased(GlobalAction action) => false; - } -} diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 0a76fe1b38..9d24597b96 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -29,7 +29,7 @@ using osu.Game.Input; namespace osu.Game { - public class OsuGame : OsuGameBase + public class OsuGame : OsuGameBase, IHandleActions { public Toolbar Toolbar; @@ -168,10 +168,6 @@ namespace osu.Game volume = new VolumeControl(), overlayContent = new Container { RelativeSizeAxes = Axes.Both }, new OnScreenDisplay(), - new Input.GlobalHotkeys //exists because UserInputManager is at a level below us. - { - Handler = globalHotkeyPressed - } }); LoadComponentAsync(screenStack = new Loader(), d => @@ -251,7 +247,7 @@ namespace osu.Game Cursor.State = Visibility.Hidden; } - private bool globalHotkeyPressed(GlobalAction action) + public bool OnPressed(GlobalAction action) { if (intro == null) return false; @@ -286,6 +282,8 @@ namespace osu.Game return false; } + public bool OnReleased(GlobalAction action) => false; + public event Action ScreenChanged; private Container mainContent; diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 09e4996157..90d86f5371 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -187,7 +187,7 @@ namespace osu.Game Children = new Drawable[] { Cursor = new MenuCursor(), - new GlobalActionMappingInputManager + new GlobalActionMappingInputManager(this) { RelativeSizeAxes = Axes.Both, Child = new OsuTooltipContainer(Cursor) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 74be1d4262..a240e58895 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -94,10 +94,8 @@ - - From 8ae010f62bf6490f468e1aec4758bcb0dbc1c920 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 11 Aug 2017 11:46:21 +0900 Subject: [PATCH 14/18] Don't pass through repeat events if we've already handled an action --- osu.Game/Input/ActionMappingInputManager.cs | 26 ++++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/osu.Game/Input/ActionMappingInputManager.cs b/osu.Game/Input/ActionMappingInputManager.cs index 43d89ef1de..a4a356d6ad 100644 --- a/osu.Game/Input/ActionMappingInputManager.cs +++ b/osu.Game/Input/ActionMappingInputManager.cs @@ -71,18 +71,26 @@ namespace osu.Game.Input { bool handled = false; - if (!args.Repeat && (concurrencyMode > ConcurrentActionMode.None || pressedBindings.Count == 0)) + if (args.Repeat) { - Binding validBinding; + if (pressedBindings.Count > 0) + return true; - 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())); + return base.PropagateKeyDown(drawables, state, args); + } - // store both the pressed combination and the resulting action, just in case the assignments change while we are actuated. - pressedBindings.Add(validBinding); - } + if (concurrencyMode == ConcurrentActionMode.None && pressedBindings.Count > 0) + return true; + + 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); From 48d4ed55e9e7deebd23880287ea75792fef102bf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 11 Aug 2017 16:11:46 +0900 Subject: [PATCH 15/18] Move a lot of code to framework --- osu.Game.Rulesets.Catch/CatchInputManager.cs | 5 +- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 4 +- .../Objects/Drawables/Pieces/CirclePiece.cs | 4 +- osu.Game.Rulesets.Osu/OsuInputManager.cs | 5 +- .../UI/Cursor/GameplayCursor.cs | 4 +- osu.Game/Input/ActionMappingInputManager.cs | 141 ------------------ osu.Game/Input/Binding.cs | 45 ------ osu.Game/Input/BindingStore.cs | 5 +- .../Input/Bindings/DatabasedKeyBinding.cs | 31 ++++ .../DatabasedKeyBindingInputManager.cs | 54 +++++++ .../GlobalBindingInputManager.cs} | 9 +- osu.Game/Input/IHandleActions.cs | 12 -- osu.Game/Input/KeyCombination.cs | 66 -------- osu.Game/OsuGame.cs | 5 +- osu.Game/OsuGameBase.cs | 3 +- .../Sections/General/LoginSettings.cs | 2 +- osu.Game/Screens/Select/Footer.cs | 2 +- .../Select/Options/BeatmapOptionsOverlay.cs | 2 +- osu.Game/osu.Game.csproj | 8 +- 19 files changed, 116 insertions(+), 291 deletions(-) delete mode 100644 osu.Game/Input/ActionMappingInputManager.cs delete mode 100644 osu.Game/Input/Binding.cs create mode 100644 osu.Game/Input/Bindings/DatabasedKeyBinding.cs create mode 100644 osu.Game/Input/Bindings/DatabasedKeyBindingInputManager.cs rename osu.Game/Input/{GlobalActionMappingInputManager.cs => Bindings/GlobalBindingInputManager.cs} (87%) delete mode 100644 osu.Game/Input/IHandleActions.cs delete mode 100644 osu.Game/Input/KeyCombination.cs diff --git a/osu.Game.Rulesets.Catch/CatchInputManager.cs b/osu.Game.Rulesets.Catch/CatchInputManager.cs index 2344b41d95..1898ea8d04 100644 --- a/osu.Game.Rulesets.Catch/CatchInputManager.cs +++ b/osu.Game.Rulesets.Catch/CatchInputManager.cs @@ -3,12 +3,13 @@ using System.Collections.Generic; using System.ComponentModel; -using osu.Game.Input; +using osu.Framework.Input.Bindings; +using osu.Game.Input.Bindings; using OpenTK.Input; namespace osu.Game.Rulesets.Catch { - public class CatchInputManager : ActionMappingInputManager + public class CatchInputManager : DatabasedKeyBindingInputManager { public CatchInputManager(RulesetInfo ruleset) : base(ruleset, concurrencyMode: ConcurrentActionMode.UniqueActions) { diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 858dfa770f..9cf6bbc36b 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -8,8 +8,8 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; +using osu.Framework.Input.Bindings; using osu.Framework.MathUtils; -using osu.Game.Input; using osu.Game.Rulesets.Catch.Judgements; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects.Drawable; @@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Catch.UI catcher.Size = new Vector2(DrawSize.Y); } - private class Catcher : Container, IHandleActions + private class Catcher : Container, IHandleKeyBindings { private Texture texture; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs index 313640a6b3..ef1d8f9e0e 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs @@ -7,12 +7,12 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; -using osu.Game.Input; +using osu.Framework.Input.Bindings; using OpenTK; namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { - public class CirclePiece : Container, IHandleActions + public class CirclePiece : Container, IHandleKeyBindings { private readonly Sprite disc; diff --git a/osu.Game.Rulesets.Osu/OsuInputManager.cs b/osu.Game.Rulesets.Osu/OsuInputManager.cs index 83304a9615..9e2a268632 100644 --- a/osu.Game.Rulesets.Osu/OsuInputManager.cs +++ b/osu.Game.Rulesets.Osu/OsuInputManager.cs @@ -4,14 +4,15 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Input; -using osu.Game.Input; +using osu.Framework.Input.Bindings; +using osu.Game.Input.Bindings; using OpenTK.Input; using KeyboardState = osu.Framework.Input.KeyboardState; using MouseState = osu.Framework.Input.MouseState; namespace osu.Game.Rulesets.Osu { - public class OsuInputManager : ActionMappingInputManager + public class OsuInputManager : DatabasedKeyBindingInputManager { public OsuInputManager(RulesetInfo ruleset) : base(ruleset, concurrencyMode: ConcurrentActionMode.UniqueActions) { diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs b/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs index 84ef8f5079..b3be6dd610 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs @@ -8,15 +8,15 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Bindings; using osu.Game.Beatmaps; using osu.Game.Configuration; -using osu.Game.Input; using OpenTK; using OpenTK.Graphics; namespace osu.Game.Rulesets.Osu.UI.Cursor { - public class GameplayCursor : CursorContainer, IHandleActions + public class GameplayCursor : CursorContainer, IHandleKeyBindings { protected override Drawable CreateCursor() => new OsuCursor(); diff --git a/osu.Game/Input/ActionMappingInputManager.cs b/osu.Game/Input/ActionMappingInputManager.cs deleted file mode 100644 index a4a356d6ad..0000000000 --- a/osu.Game/Input/ActionMappingInputManager.cs +++ /dev/null @@ -1,141 +0,0 @@ -// 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 s and s. - 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) - { - if (pressedBindings.Count > 0) - return true; - - return base.PropagateKeyDown(drawables, state, args); - } - - if (concurrencyMode == ConcurrentActionMode.None && pressedBindings.Count > 0) - return true; - - 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 pressed at once. The first action matching a chord will take precedence and no other action will be pressed until it has first been released. - /// - None, - /// - /// Unique actions are allowed to be pressed 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 trigger an . - /// The last binding to be released will trigger an . - /// - UniqueActions, - /// - /// Unique actions are allowed to be pressed at the same time. There may therefore be more than one action in an actuated state at once (same as ). - /// In addition to this, multiple are fired for single actions, even if has not yet been fired. - /// The same goes for , which is fired for each matching binding that is released. - /// - UniqueAndSameActions, - } -} diff --git a/osu.Game/Input/Binding.cs b/osu.Game/Input/Binding.cs deleted file mode 100644 index 11db78ec93..0000000000 --- a/osu.Game/Input/Binding.cs +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets; -using SQLite.Net.Attributes; -using SQLiteNetExtensions.Attributes; - -namespace osu.Game.Input -{ - public class Binding - { - [ForeignKey(typeof(RulesetInfo))] - public int? RulesetID { get; set; } - - [Indexed] - public int? Variant { get; set; } - - [Column("Keys")] - public string KeysString - { - get { return Keys.ToString(); } - set { Keys = value; } - } - - [Ignore] - public KeyCombination Keys { get; private set; } - - public int Action { get; private set; } - - public Binding() - { - - } - - public Binding(KeyCombination keys, object action) - { - Keys = keys; - Action = (int)action; - } - - public virtual T GetAction() => (T)(object)Action; - - public override string ToString() => $"{KeysString}=>{Action}"; - } -} \ No newline at end of file diff --git a/osu.Game/Input/BindingStore.cs b/osu.Game/Input/BindingStore.cs index f66d481a74..25a997e29d 100644 --- a/osu.Game/Input/BindingStore.cs +++ b/osu.Game/Input/BindingStore.cs @@ -4,6 +4,7 @@ using System; using osu.Framework.Platform; using osu.Game.Database; +using osu.Game.Input.Bindings; using SQLite.Net; namespace osu.Game.Input @@ -35,12 +36,12 @@ namespace osu.Game.Input protected override void Prepare(bool reset = false) { - Connection.CreateTable(); + Connection.CreateTable(); } protected override Type[] ValidTypes => new[] { - typeof(Binding) + typeof(DatabasedKeyBinding) }; } diff --git a/osu.Game/Input/Bindings/DatabasedKeyBinding.cs b/osu.Game/Input/Bindings/DatabasedKeyBinding.cs new file mode 100644 index 0000000000..cb571cdf04 --- /dev/null +++ b/osu.Game/Input/Bindings/DatabasedKeyBinding.cs @@ -0,0 +1,31 @@ +using osu.Framework.Input.Bindings; +using osu.Game.Rulesets; +using SQLite.Net.Attributes; +using SQLiteNetExtensions.Attributes; + +namespace osu.Game.Input.Bindings +{ + [Table("KeyBinding")] + public class DatabasedKeyBinding : KeyBinding + { + [ForeignKey(typeof(RulesetInfo))] + public int? RulesetID { get; set; } + + [Indexed] + public int? Variant { get; set; } + + [Column("Keys")] + public string KeysString + { + get { return Keys.ToString(); } + private set { Keys = value; } + } + + [Column("Action")] + public new int Action + { + get { return base.Action; } + private set { base.Action = value; } + } + } +} \ No newline at end of file diff --git a/osu.Game/Input/Bindings/DatabasedKeyBindingInputManager.cs b/osu.Game/Input/Bindings/DatabasedKeyBindingInputManager.cs new file mode 100644 index 0000000000..d31c4ab55d --- /dev/null +++ b/osu.Game/Input/Bindings/DatabasedKeyBindingInputManager.cs @@ -0,0 +1,54 @@ +using osu.Framework.Allocation; +using osu.Framework.Input.Bindings; +using osu.Game.Rulesets; + +namespace osu.Game.Input.Bindings +{ + /// + /// A KeyBindingInputManager with a database backing for custom overrides. + /// + /// The type of the custom action. + public abstract class DatabasedKeyBindingInputManager : KeyBindingInputManager + where T : struct + { + private readonly RulesetInfo ruleset; + + private readonly int? variant; + + private BindingStore store; + + /// + /// 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 s and s. + protected DatabasedKeyBindingInputManager(RulesetInfo ruleset = null, int? variant = null, ConcurrentActionMode concurrencyMode = ConcurrentActionMode.None) + : base(concurrencyMode) + { + this.ruleset = ruleset; + this.variant = variant; + } + + [BackgroundDependencyLoader] + private void load(BindingStore bindings) + { + store = bindings; + } + + protected override void ReloadMappings() + { + // load defaults + base.ReloadMappings(); + + var rulesetId = ruleset?.ID; + + // load from database if present. + if (store != null) + { + foreach (var b in store.Query(b => b.RulesetID == rulesetId && b.Variant == variant)) + Mappings.Add(b); + } + } + } +} \ No newline at end of file diff --git a/osu.Game/Input/GlobalActionMappingInputManager.cs b/osu.Game/Input/Bindings/GlobalBindingInputManager.cs similarity index 87% rename from osu.Game/Input/GlobalActionMappingInputManager.cs rename to osu.Game/Input/Bindings/GlobalBindingInputManager.cs index 3136fa5be5..4255bfd657 100644 --- a/osu.Game/Input/GlobalActionMappingInputManager.cs +++ b/osu.Game/Input/Bindings/GlobalBindingInputManager.cs @@ -7,16 +7,17 @@ using System.ComponentModel; using System.Linq; using osu.Framework.Graphics; using osu.Framework.Input; +using osu.Framework.Input.Bindings; -namespace osu.Game.Input +namespace osu.Game.Input.Bindings { - public class GlobalActionMappingInputManager : ActionMappingInputManager + public class GlobalBindingInputManager : DatabasedKeyBindingInputManager { private readonly Drawable handler; - public GlobalActionMappingInputManager(OsuGameBase game) + public GlobalBindingInputManager(OsuGameBase game) { - if (game is IHandleActions) + if (game is IHandleKeyBindings) handler = game; } diff --git a/osu.Game/Input/IHandleActions.cs b/osu.Game/Input/IHandleActions.cs deleted file mode 100644 index 34720eb1ee..0000000000 --- a/osu.Game/Input/IHandleActions.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Input -{ - public interface IHandleActions - where T : struct - { - bool OnPressed(T action); - bool OnReleased(T action); - } -} \ No newline at end of file diff --git a/osu.Game/Input/KeyCombination.cs b/osu.Game/Input/KeyCombination.cs deleted file mode 100644 index 1e8dff0a51..0000000000 --- a/osu.Game/Input/KeyCombination.cs +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.Linq; -using OpenTK.Input; - -namespace osu.Game.Input -{ - /// - /// Represent a combination of more than one s. - /// - public class KeyCombination : IEquatable - { - public readonly IEnumerable Keys; - - public KeyCombination(params Key[] keys) - { - Keys = keys; - } - - public KeyCombination(IEnumerable keys) - { - Keys = keys; - } - - public KeyCombination(string stringRepresentation) - { - Keys = stringRepresentation.Split(',').Select(s => (Key)int.Parse(s)); - } - - public bool CheckValid(IEnumerable keys, bool requireExactMatch = false) - { - if (requireExactMatch) - return Keys.SequenceEqual(keys); - else - return !Keys.Except(keys).Any(); - } - - public bool Equals(KeyCombination other) - { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - return Keys.SequenceEqual(other.Keys); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; - return Equals((KeyCombination)obj); - } - - public override int GetHashCode() => Keys != null ? Keys.Select(k => k.GetHashCode()).Aggregate((h1, h2) => h1 + h2) : 0; - - public static implicit operator KeyCombination(Key singleKey) => new KeyCombination(singleKey); - - public static implicit operator KeyCombination(string stringRepresentation) => new KeyCombination(stringRepresentation); - - public static implicit operator KeyCombination(Key[] keys) => new KeyCombination(keys); - - public override string ToString() => Keys.Select(k => ((int)k).ToString()).Aggregate((s1, s2) => $"{s1},{s2}"); - } -} \ No newline at end of file diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 9d24597b96..7e5c3987b5 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -18,6 +18,7 @@ using osu.Game.Screens.Menu; using OpenTK; using System.Linq; using System.Threading.Tasks; +using osu.Framework.Input.Bindings; using osu.Framework.Platform; using osu.Framework.Threading; using osu.Game.Graphics; @@ -25,11 +26,11 @@ using osu.Game.Rulesets.Scoring; using osu.Game.Overlays.Notifications; using osu.Game.Rulesets; using osu.Game.Screens.Play; -using osu.Game.Input; +using osu.Game.Input.Bindings; namespace osu.Game { - public class OsuGame : OsuGameBase, IHandleActions + public class OsuGame : OsuGameBase, IHandleKeyBindings { public Toolbar Toolbar; diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 90d86f5371..344b23cca4 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -20,6 +20,7 @@ using SQLite.Net; using osu.Framework.Graphics.Performance; using osu.Game.Database; using osu.Game.Input; +using osu.Game.Input.Bindings; using osu.Game.IO; using osu.Game.Rulesets; using osu.Game.Rulesets.Scoring; @@ -187,7 +188,7 @@ namespace osu.Game Children = new Drawable[] { Cursor = new MenuCursor(), - new GlobalActionMappingInputManager(this) + new GlobalBindingInputManager(this) { RelativeSizeAxes = Axes.Both, Child = new OsuTooltipContainer(Cursor) diff --git a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs b/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs index d07f156673..168360ffd1 100644 --- a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs @@ -245,7 +245,7 @@ namespace osu.Game.Overlays.Settings.Sections.General { RelativeSizeAxes = Axes.X, Text = "Register new account", - //Action = registerLink + //Binding = registerLink } }; } diff --git a/osu.Game/Screens/Select/Footer.cs b/osu.Game/Screens/Select/Footer.cs index bb6d16da0f..fc57f5edeb 100644 --- a/osu.Game/Screens/Select/Footer.cs +++ b/osu.Game/Screens/Select/Footer.cs @@ -39,7 +39,7 @@ namespace osu.Game.Screens.Select /// Text on the button. /// Colour of the button. /// Hotkey of the button. - /// Action the button does. + /// Binding the button does. /// /// Higher depth to be put on the left, and lower to be put on the right. /// Notice this is different to ! diff --git a/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs b/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs index 0a410f0e0f..c66cc7beff 100644 --- a/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs +++ b/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs @@ -86,7 +86,7 @@ namespace osu.Game.Screens.Select.Options /// Colour of the button. /// Icon of the button. /// Hotkey of the button. - /// Action the button does. + /// Binding the button does. /// /// Lower depth to be put on the left, and higher to be put on the right. /// Notice this is different to ! diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index a240e58895..7690a56378 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -92,11 +92,10 @@ - + + - - - + @@ -123,7 +122,6 @@ - From 99458aab48fc36ce7b3022bd3d1dd6ac0cdd0b40 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 12 Aug 2017 19:54:07 +0900 Subject: [PATCH 16/18] Propagate framework updates --- osu.Game.Rulesets.Catch/CatchInputManager.cs | 16 ++++++++-------- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 2 +- .../Objects/Drawables/Pieces/CirclePiece.cs | 2 +- osu.Game.Rulesets.Osu/OsuInputManager.cs | 12 ++++++------ .../UI/Cursor/GameplayCursor.cs | 2 +- .../Bindings/DatabasedKeyBindingInputManager.cs | 6 +++--- .../Input/Bindings/GlobalBindingInputManager.cs | 16 ++++++++-------- osu.Game/OsuGame.cs | 2 +- 8 files changed, 29 insertions(+), 29 deletions(-) diff --git a/osu.Game.Rulesets.Catch/CatchInputManager.cs b/osu.Game.Rulesets.Catch/CatchInputManager.cs index 1898ea8d04..3e292ac08a 100644 --- a/osu.Game.Rulesets.Catch/CatchInputManager.cs +++ b/osu.Game.Rulesets.Catch/CatchInputManager.cs @@ -11,18 +11,18 @@ namespace osu.Game.Rulesets.Catch { public class CatchInputManager : DatabasedKeyBindingInputManager { - public CatchInputManager(RulesetInfo ruleset) : base(ruleset, concurrencyMode: ConcurrentActionMode.UniqueActions) + public CatchInputManager(RulesetInfo ruleset) : base(ruleset, simultaneousMode: SimultaneousBindingMode.Unique) { } - protected override IDictionary CreateDefaultMappings() => new Dictionary + protected override IEnumerable CreateDefaultMappings() => new[] { - { Key.Z, CatchAction.MoveLeft }, - { Key.Left, CatchAction.MoveLeft }, - { Key.X, CatchAction.MoveRight }, - { Key.Right, CatchAction.MoveRight }, - { Key.LShift, CatchAction.Dash }, - { Key.RShift, CatchAction.Dash }, + new KeyBinding( Key.Z, CatchAction.MoveLeft), + new KeyBinding( Key.Left, CatchAction.MoveLeft), + new KeyBinding( Key.X, CatchAction.MoveRight), + new KeyBinding( Key.Right, CatchAction.MoveRight), + new KeyBinding( Key.LShift, CatchAction.Dash), + new KeyBinding( Key.RShift, CatchAction.Dash), }; } diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 9cf6bbc36b..f416f6acfb 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Catch.UI catcher.Size = new Vector2(DrawSize.Y); } - private class Catcher : Container, IHandleKeyBindings + private class Catcher : Container, IKeyBindingHandler { private Texture texture; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs index ef1d8f9e0e..0c3c4f0a6d 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs @@ -12,7 +12,7 @@ using OpenTK; namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { - public class CirclePiece : Container, IHandleKeyBindings + public class CirclePiece : Container, IKeyBindingHandler { private readonly Sprite disc; diff --git a/osu.Game.Rulesets.Osu/OsuInputManager.cs b/osu.Game.Rulesets.Osu/OsuInputManager.cs index 9e2a268632..c1ee19d8c5 100644 --- a/osu.Game.Rulesets.Osu/OsuInputManager.cs +++ b/osu.Game.Rulesets.Osu/OsuInputManager.cs @@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Osu { public class OsuInputManager : DatabasedKeyBindingInputManager { - public OsuInputManager(RulesetInfo ruleset) : base(ruleset, concurrencyMode: ConcurrentActionMode.UniqueActions) + public OsuInputManager(RulesetInfo ruleset) : base(ruleset, simultaneousMode: SimultaneousBindingMode.Unique) { } @@ -34,12 +34,12 @@ namespace osu.Game.Rulesets.Osu } } - protected override IDictionary CreateDefaultMappings() => new Dictionary + protected override IEnumerable CreateDefaultMappings() => new[] { - { Key.Z, OsuAction.LeftButton }, - { Key.X, OsuAction.RightButton }, - { Key.LastKey + 1, OsuAction.LeftButton }, - { Key.LastKey + 2, OsuAction.RightButton }, + new KeyBinding(Key.Z, OsuAction.LeftButton), + new KeyBinding(Key.X, OsuAction.RightButton), + new KeyBinding(Key.LastKey + 1, OsuAction.LeftButton), + new KeyBinding(Key.LastKey + 2, OsuAction.RightButton), }; } diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs b/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs index b3be6dd610..adfc946f86 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs @@ -16,7 +16,7 @@ using OpenTK.Graphics; namespace osu.Game.Rulesets.Osu.UI.Cursor { - public class GameplayCursor : CursorContainer, IHandleKeyBindings + public class GameplayCursor : CursorContainer, IKeyBindingHandler { protected override Drawable CreateCursor() => new OsuCursor(); diff --git a/osu.Game/Input/Bindings/DatabasedKeyBindingInputManager.cs b/osu.Game/Input/Bindings/DatabasedKeyBindingInputManager.cs index d31c4ab55d..ab744c8037 100644 --- a/osu.Game/Input/Bindings/DatabasedKeyBindingInputManager.cs +++ b/osu.Game/Input/Bindings/DatabasedKeyBindingInputManager.cs @@ -22,9 +22,9 @@ namespace osu.Game.Input.Bindings /// /// 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 s and s. - protected DatabasedKeyBindingInputManager(RulesetInfo ruleset = null, int? variant = null, ConcurrentActionMode concurrencyMode = ConcurrentActionMode.None) - : base(concurrencyMode) + /// Specify how to deal with multiple matches of s and s. + protected DatabasedKeyBindingInputManager(RulesetInfo ruleset = null, int? variant = null, SimultaneousBindingMode simultaneousMode = SimultaneousBindingMode.None) + : base(simultaneousMode) { this.ruleset = ruleset; this.variant = variant; diff --git a/osu.Game/Input/Bindings/GlobalBindingInputManager.cs b/osu.Game/Input/Bindings/GlobalBindingInputManager.cs index 4255bfd657..60a4faa8cd 100644 --- a/osu.Game/Input/Bindings/GlobalBindingInputManager.cs +++ b/osu.Game/Input/Bindings/GlobalBindingInputManager.cs @@ -17,18 +17,18 @@ namespace osu.Game.Input.Bindings public GlobalBindingInputManager(OsuGameBase game) { - if (game is IHandleKeyBindings) + if (game is IKeyBindingHandler) handler = game; } - protected override IDictionary CreateDefaultMappings() => new Dictionary + protected override IEnumerable CreateDefaultMappings() => new[] { - { Key.F8, GlobalAction.ToggleChat }, - { Key.F9, GlobalAction.ToggleSocial }, - { new[] { Key.LControl, Key.LAlt, Key.R }, GlobalAction.ResetInputSettings }, - { new[] { Key.LControl, Key.T }, GlobalAction.ToggleToolbar }, - { new[] { Key.LControl, Key.O }, GlobalAction.ToggleSettings }, - { new[] { Key.LControl, Key.D }, GlobalAction.ToggleDirect }, + new KeyBinding(Key.F8, GlobalAction.ToggleChat), + new KeyBinding(Key.F9, GlobalAction.ToggleSocial), + new KeyBinding(new[] { Key.LControl, Key.LAlt, Key.R }, GlobalAction.ResetInputSettings), + new KeyBinding(new[] { Key.LControl, Key.T }, GlobalAction.ToggleToolbar), + new KeyBinding(new[] { Key.LControl, Key.O }, GlobalAction.ToggleSettings), + new KeyBinding(new[] { Key.LControl, Key.D }, GlobalAction.ToggleDirect), }; protected override bool PropagateKeyDown(IEnumerable drawables, InputState state, KeyDownEventArgs args) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 7e5c3987b5..f54fba4a0b 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -30,7 +30,7 @@ using osu.Game.Input.Bindings; namespace osu.Game { - public class OsuGame : OsuGameBase, IHandleKeyBindings + public class OsuGame : OsuGameBase, IKeyBindingHandler { public Toolbar Toolbar; From b6fb68c6e2ea6cf45d5cf7e61f91596b0ac373e1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 12 Aug 2017 22:10:57 +0900 Subject: [PATCH 17/18] Update framework --- osu-framework | 2 +- osu.Game/Input/Bindings/DatabasedKeyBinding.cs | 7 +++++-- osu.Game/Input/Bindings/DatabasedKeyBindingInputManager.cs | 5 ++++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/osu-framework b/osu-framework index 67d89a3601..2a56eb0619 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 67d89a36016f98c0ede576b859a2ccafe114fce8 +Subproject commit 2a56eb0619adf654ed4927af1a4b227596c87494 diff --git a/osu.Game/Input/Bindings/DatabasedKeyBinding.cs b/osu.Game/Input/Bindings/DatabasedKeyBinding.cs index cb571cdf04..278033899c 100644 --- a/osu.Game/Input/Bindings/DatabasedKeyBinding.cs +++ b/osu.Game/Input/Bindings/DatabasedKeyBinding.cs @@ -1,3 +1,6 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + using osu.Framework.Input.Bindings; using osu.Game.Rulesets; using SQLite.Net.Attributes; @@ -17,8 +20,8 @@ namespace osu.Game.Input.Bindings [Column("Keys")] public string KeysString { - get { return Keys.ToString(); } - private set { Keys = value; } + get { return KeyCombination.ToString(); } + private set { KeyCombination = value; } } [Column("Action")] diff --git a/osu.Game/Input/Bindings/DatabasedKeyBindingInputManager.cs b/osu.Game/Input/Bindings/DatabasedKeyBindingInputManager.cs index ab744c8037..5c62f1ddc8 100644 --- a/osu.Game/Input/Bindings/DatabasedKeyBindingInputManager.cs +++ b/osu.Game/Input/Bindings/DatabasedKeyBindingInputManager.cs @@ -1,3 +1,6 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + using osu.Framework.Allocation; using osu.Framework.Input.Bindings; using osu.Game.Rulesets; @@ -47,7 +50,7 @@ namespace osu.Game.Input.Bindings if (store != null) { foreach (var b in store.Query(b => b.RulesetID == rulesetId && b.Variant == variant)) - Mappings.Add(b); + KeyBindings.Add(b); } } } From fe2911f165a90b00fcd5ac8b1b9011c16e082a27 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 13 Aug 2017 10:36:57 +0900 Subject: [PATCH 18/18] Fix some formatting issues --- osu.Game.Rulesets.Catch/CatchInputManager.cs | 15 ++++++++------- .../Settings/Sections/General/LoginSettings.cs | 2 +- osu.Game/Screens/Select/Footer.cs | 2 +- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Catch/CatchInputManager.cs b/osu.Game.Rulesets.Catch/CatchInputManager.cs index 3e292ac08a..446f9b2787 100644 --- a/osu.Game.Rulesets.Catch/CatchInputManager.cs +++ b/osu.Game.Rulesets.Catch/CatchInputManager.cs @@ -11,18 +11,19 @@ namespace osu.Game.Rulesets.Catch { public class CatchInputManager : DatabasedKeyBindingInputManager { - public CatchInputManager(RulesetInfo ruleset) : base(ruleset, simultaneousMode: SimultaneousBindingMode.Unique) + public CatchInputManager(RulesetInfo ruleset) + : base(ruleset, simultaneousMode: SimultaneousBindingMode.Unique) { } protected override IEnumerable CreateDefaultMappings() => new[] { - new KeyBinding( Key.Z, CatchAction.MoveLeft), - new KeyBinding( Key.Left, CatchAction.MoveLeft), - new KeyBinding( Key.X, CatchAction.MoveRight), - new KeyBinding( Key.Right, CatchAction.MoveRight), - new KeyBinding( Key.LShift, CatchAction.Dash), - new KeyBinding( Key.RShift, CatchAction.Dash), + new KeyBinding(Key.Z, CatchAction.MoveLeft), + new KeyBinding(Key.Left, CatchAction.MoveLeft), + new KeyBinding(Key.X, CatchAction.MoveRight), + new KeyBinding(Key.Right, CatchAction.MoveRight), + new KeyBinding(Key.LShift, CatchAction.Dash), + new KeyBinding(Key.RShift, CatchAction.Dash), }; } diff --git a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs b/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs index 168360ffd1..d07f156673 100644 --- a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs @@ -245,7 +245,7 @@ namespace osu.Game.Overlays.Settings.Sections.General { RelativeSizeAxes = Axes.X, Text = "Register new account", - //Binding = registerLink + //Action = registerLink } }; } diff --git a/osu.Game/Screens/Select/Footer.cs b/osu.Game/Screens/Select/Footer.cs index fc57f5edeb..bb6d16da0f 100644 --- a/osu.Game/Screens/Select/Footer.cs +++ b/osu.Game/Screens/Select/Footer.cs @@ -39,7 +39,7 @@ namespace osu.Game.Screens.Select /// Text on the button. /// Colour of the button. /// Hotkey of the button. - /// Binding the button does. + /// Action the button does. /// /// Higher depth to be put on the left, and lower to be put on the right. /// Notice this is different to !