From 798fff00b218a880ffa6637f173fd24ae873546e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 10 Aug 2017 19:28:24 +0900 Subject: [PATCH] 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 @@ + +