diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs
index 4fa18f53a7..e0a1533c4b 100644
--- a/osu.Game/Rulesets/UI/DrawableRuleset.cs
+++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs
@@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.UI
/// Displays an interactive ruleset gameplay instance.
///
/// The type of HitObject contained by this DrawableRuleset.
- public abstract partial class DrawableRuleset : DrawableRuleset, IProvideCursor, ICanAttachHUDPieces
+ public abstract partial class DrawableRuleset : DrawableRuleset, IProvideCursor, IKeybindingEventsEmitter
where TObject : HitObject
{
public override event Action NewResult;
@@ -327,8 +327,8 @@ namespace osu.Game.Rulesets.UI
/// The representing .
public abstract DrawableHitObject CreateDrawableRepresentation(TObject h);
- public void Attach(IAttachableSkinComponent skinComponent) =>
- (KeyBindingInputManager as ICanAttachHUDPieces)?.Attach(skinComponent);
+ public void Attach(IKeybindingListener skinComponent) =>
+ (KeyBindingInputManager as IKeybindingEventsEmitter)?.Attach(skinComponent);
///
/// Creates a key conversion input manager. An exception will be thrown if a valid is not returned.
diff --git a/osu.Game/Rulesets/UI/IKeybindingListener.cs b/osu.Game/Rulesets/UI/IKeybindingListener.cs
new file mode 100644
index 0000000000..f38ce8643e
--- /dev/null
+++ b/osu.Game/Rulesets/UI/IKeybindingListener.cs
@@ -0,0 +1,50 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+#nullable disable
+using System.Collections.Generic;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Input.Bindings;
+using osu.Framework.Input.Events;
+
+namespace osu.Game.Rulesets.UI
+{
+ ///
+ /// Listens to events emitted by an .
+ /// Alternative to for classes that need to not depend on type parameters.
+ ///
+ public interface IKeybindingListener
+ {
+ ///
+ /// This class or a member of this class can already handle keybindings.
+ /// Signals to the that and
+ /// don't necessarily need to be called.
+ ///
+ ///
+ /// This is usually true for s and s that need to
+ /// pass s events to children that can already handle them.
+ ///
+ public bool CanHandleKeybindings { get; }
+
+ ///
+ /// Prepares this class to receive events.
+ ///
+ /// The list of possible actions that can occur.
+ /// The type actions, commonly enums.
+ public void Setup(IEnumerable actions) where T : struct;
+
+ ///
+ /// Called when an action is pressed.
+ ///
+ /// The event containing information about the pressed action.
+ /// The type of binding, commonly enums.
+ public void OnPressed(KeyBindingPressEvent action) where T : struct;
+
+ ///
+ /// Called when an action is released.
+ ///
+ /// The event containing information about the released action.
+ /// The type of binding, commonly enums.
+ public void OnReleased(KeyBindingReleaseEvent action) where T : struct;
+ }
+}
diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs
index d3bc381f72..44c1f00cf7 100644
--- a/osu.Game/Rulesets/UI/RulesetInputManager.cs
+++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs
@@ -19,13 +19,11 @@ using osu.Game.Input;
using osu.Game.Input.Bindings;
using osu.Game.Input.Handlers;
using osu.Game.Rulesets.Scoring;
-using osu.Game.Screens.Play.HUD;
-using osu.Game.Screens.Play.HUD.ClicksPerSecond;
using static osu.Game.Input.Handlers.ReplayInputHandler;
namespace osu.Game.Rulesets.UI
{
- public abstract partial class RulesetInputManager : PassThroughInputManager, ICanAttachHUDPieces, IHasReplayHandler, IHasRecordingHandler
+ public abstract partial class RulesetInputManager : PassThroughInputManager, IKeybindingEventsEmitter, IHasReplayHandler, IHasRecordingHandler
where T : struct
{
protected override bool AllowRightClickFromLongTouch => false;
@@ -66,6 +64,7 @@ namespace osu.Game.Rulesets.UI
InternalChild = KeyBindingContainer =
CreateKeyBindingContainer(ruleset, variant, unique)
.WithChild(content = new Container { RelativeSizeAxes = Axes.Both });
+ KeyBindingContainer.Add(actionListener = new ActionListener());
}
[BackgroundDependencyLoader(true)]
@@ -160,63 +159,47 @@ namespace osu.Game.Rulesets.UI
#region Component attachement
- public void Attach(IAttachableSkinComponent skinComponent)
+ private readonly ActionListener actionListener;
+
+ public void Attach(IKeybindingListener skinComponent)
{
- switch (skinComponent)
- {
- case KeyCounterController keyCounterDisplay:
- attachKeyCounter(keyCounterDisplay);
- break;
-
- case ClicksPerSecondCalculator clicksPerSecondCalculator:
- attachClicksPerSecond(clicksPerSecondCalculator);
- break;
- }
- }
-
- #endregion
-
- #region Key Counter Attachment
-
- private void attachKeyCounter(KeyCounterController keyCounter)
- {
- KeyBindingContainer.Add(keyCounter);
-
- keyCounter.AddRange(KeyBindingContainer.DefaultKeyBindings
+ skinComponent.Setup(KeyBindingContainer.DefaultKeyBindings
.Select(b => b.GetAction())
.Distinct()
- .OrderBy(action => action)
- .Select(action => new KeyCounterActionTrigger(action)));
- }
+ .OrderBy(a => a));
- #endregion
+ if (skinComponent.CanHandleKeybindings && skinComponent is Drawable component)
+ {
+ try
+ {
+ KeyBindingContainer.Add(component);
+ return;
+ }
+ catch (Exception)
+ {
+ return;
+ }
+ }
- #region Keys per second Counter Attachment
-
- private void attachClicksPerSecond(ClicksPerSecondCalculator calculator)
- {
- var listener = new ActionListener(calculator);
-
- KeyBindingContainer.Add(listener);
+ actionListener.OnPressedEvent += skinComponent.OnPressed;
+ actionListener.OnReleasedEvent += skinComponent.OnReleased;
}
private partial class ActionListener : Component, IKeyBindingHandler
{
- private readonly ClicksPerSecondCalculator calculator;
+ public event Action> OnPressedEvent;
- public ActionListener(ClicksPerSecondCalculator calculator)
- {
- this.calculator = calculator;
- }
+ public event Action> OnReleasedEvent;
public bool OnPressed(KeyBindingPressEvent e)
{
- calculator.AddInputTimestamp();
+ OnPressedEvent?.Invoke(e);
return false;
}
public void OnReleased(KeyBindingReleaseEvent e)
{
+ OnReleasedEvent?.Invoke(e);
}
}
@@ -257,16 +240,11 @@ namespace osu.Game.Rulesets.UI
}
///
- /// Supports attaching various HUD pieces.
- /// Keys will be populated automatically and a receptor will be injected inside.
+ /// Sends events to a
///
- public interface ICanAttachHUDPieces
- {
- void Attach(IAttachableSkinComponent component);
- }
-
- public interface IAttachableSkinComponent
+ public interface IKeybindingEventsEmitter
{
+ void Attach(IKeybindingListener component);
}
public class RulesetInputManagerInputState : InputState
diff --git a/osu.Game/Screens/Play/HUD/ClicksPerSecond/ClicksPerSecondCalculator.cs b/osu.Game/Screens/Play/HUD/ClicksPerSecond/ClicksPerSecondCalculator.cs
index 3e55e11f1c..e0e93cb66e 100644
--- a/osu.Game/Screens/Play/HUD/ClicksPerSecond/ClicksPerSecondCalculator.cs
+++ b/osu.Game/Screens/Play/HUD/ClicksPerSecond/ClicksPerSecondCalculator.cs
@@ -4,11 +4,12 @@
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
+using osu.Framework.Input.Events;
using osu.Game.Rulesets.UI;
namespace osu.Game.Screens.Play.HUD.ClicksPerSecond
{
- public partial class ClicksPerSecondCalculator : Component, IAttachableSkinComponent
+ public partial class ClicksPerSecondCalculator : Component, IKeybindingListener
{
private readonly List timestamps = new List();
@@ -53,5 +54,21 @@ namespace osu.Game.Screens.Play.HUD.ClicksPerSecond
Value = count;
}
+
+ #region IKeybindingListener
+
+ bool IKeybindingListener.CanHandleKeybindings => false;
+
+ void IKeybindingListener.Setup(IEnumerable actions)
+ {
+ }
+
+ void IKeybindingListener.OnPressed(KeyBindingPressEvent action) => AddInputTimestamp();
+
+ void IKeybindingListener.OnReleased(KeyBindingReleaseEvent action)
+ {
+ }
+
+ #endregion
}
}
diff --git a/osu.Game/Screens/Play/HUD/KeyCounterController.cs b/osu.Game/Screens/Play/HUD/KeyCounterController.cs
index 0fa02afbb4..2e678e55fc 100644
--- a/osu.Game/Screens/Play/HUD/KeyCounterController.cs
+++ b/osu.Game/Screens/Play/HUD/KeyCounterController.cs
@@ -3,14 +3,16 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using osu.Framework.Bindables;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Input.Events;
using osu.Game.Rulesets.UI;
namespace osu.Game.Screens.Play.HUD
{
- public partial class KeyCounterController : CompositeComponent, IAttachableSkinComponent
+ public partial class KeyCounterController : CompositeComponent, IKeybindingListener
{
public readonly Bindable IsCounting = new BindableBool(true);
@@ -36,5 +38,22 @@ namespace osu.Game.Screens.Play.HUD
public override bool HandleNonPositionalInput => true;
public override bool HandlePositionalInput => true;
+
+ #region IKeybindingListener
+
+ bool IKeybindingListener.CanHandleKeybindings => true;
+
+ void IKeybindingListener.Setup(IEnumerable actions)
+ => AddRange(actions.Select(a => new KeyCounterActionTrigger(a)));
+
+ void IKeybindingListener.OnPressed(KeyBindingPressEvent action)
+ {
+ }
+
+ void IKeybindingListener.OnReleased(KeyBindingReleaseEvent action)
+ {
+ }
+
+ #endregion
}
}
diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs
index 21636ac04c..b74b5d835a 100644
--- a/osu.Game/Screens/Play/HUDOverlay.cs
+++ b/osu.Game/Screens/Play/HUDOverlay.cs
@@ -10,6 +10,7 @@ using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.EnumExtensions;
+using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Bindings;
@@ -102,6 +103,8 @@ namespace osu.Game.Screens.Play
private readonly List hideTargets;
+ private readonly IEnumerable actionInjectionCandidates;
+
public HUDOverlay(DrawableRuleset drawableRuleset, IReadOnlyList mods, bool alwaysShowLeaderboard = true)
{
Drawable rulesetComponents;
@@ -163,6 +166,8 @@ namespace osu.Game.Screens.Play
hideTargets = new List { mainComponents, rulesetComponents, topRightElements };
+ actionInjectionCandidates = new IKeybindingListener[] { clicksPerSecondCalculator, KeyCounter };
+
if (!alwaysShowLeaderboard)
hideTargets.Add(LeaderboardFlow);
}
@@ -319,11 +324,8 @@ namespace osu.Game.Screens.Play
protected virtual void BindDrawableRuleset(DrawableRuleset drawableRuleset)
{
- if (drawableRuleset is ICanAttachHUDPieces attachTarget)
- {
- attachTarget.Attach(KeyCounter);
- attachTarget.Attach(clicksPerSecondCalculator);
- }
+ if (drawableRuleset is IKeybindingEventsEmitter attachTarget)
+ actionInjectionCandidates.ForEach(attachTarget.Attach);
replayLoaded.BindTo(drawableRuleset.HasReplayLoaded);
}