mirror of
https://github.com/ppy/osu
synced 2024-12-30 02:42:29 +00:00
refactor: tidy up attachement flow
TODO: find better naming and improve XMLDocs
This commit is contained in:
parent
141f9efad5
commit
f83a4f4952
@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.UI
|
||||
/// Displays an interactive ruleset gameplay instance.
|
||||
/// </summary>
|
||||
/// <typeparam name="TObject">The type of HitObject contained by this DrawableRuleset.</typeparam>
|
||||
public abstract partial class DrawableRuleset<TObject> : DrawableRuleset, IProvideCursor, ICanAttachHUDPieces
|
||||
public abstract partial class DrawableRuleset<TObject> : DrawableRuleset, IProvideCursor, IKeybindingEventsEmitter
|
||||
where TObject : HitObject
|
||||
{
|
||||
public override event Action<JudgementResult> NewResult;
|
||||
@ -327,8 +327,8 @@ namespace osu.Game.Rulesets.UI
|
||||
/// <returns>The representing <see cref="DrawableHitObject{TObject}"/>.</returns>
|
||||
public abstract DrawableHitObject<TObject> CreateDrawableRepresentation(TObject h);
|
||||
|
||||
public void Attach(IAttachableSkinComponent skinComponent) =>
|
||||
(KeyBindingInputManager as ICanAttachHUDPieces)?.Attach(skinComponent);
|
||||
public void Attach(IKeybindingListener skinComponent) =>
|
||||
(KeyBindingInputManager as IKeybindingEventsEmitter)?.Attach(skinComponent);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a key conversion input manager. An exception will be thrown if a valid <see cref="RulesetInputManager{T}"/> is not returned.
|
||||
|
50
osu.Game/Rulesets/UI/IKeybindingListener.cs
Normal file
50
osu.Game/Rulesets/UI/IKeybindingListener.cs
Normal file
@ -0,0 +1,50 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. 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
|
||||
{
|
||||
/// <summary>
|
||||
/// Listens to <see cref="IKeyBinding"/> events emitted by an <see cref="IKeybindingEventsEmitter"/>.
|
||||
/// Alternative to <see cref="IKeyBindingHandler{T}"/> for classes that need to not depend on type parameters.
|
||||
/// </summary>
|
||||
public interface IKeybindingListener
|
||||
{
|
||||
/// <summary>
|
||||
/// This class or a member of this class can already handle keybindings.
|
||||
/// Signals to the <see cref="IKeybindingEventsEmitter"/> that <see cref="OnPressed{T}"/> and <see cref="OnReleased{T}"/>
|
||||
/// don't necessarily need to be called.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is usually true for <see cref="CompositeDrawable"/>s and <see cref="CompositeComponent"/>s that need to
|
||||
/// pass <see cref="IKeyBinding"/>s events to children that can already handle them.
|
||||
/// </remarks>
|
||||
public bool CanHandleKeybindings { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Prepares this class to receive events.
|
||||
/// </summary>
|
||||
/// <param name="actions">The list of possible actions that can occur.</param>
|
||||
/// <typeparam name="T">The type actions, commonly enums.</typeparam>
|
||||
public void Setup<T>(IEnumerable<T> actions) where T : struct;
|
||||
|
||||
/// <summary>
|
||||
/// Called when an action is pressed.
|
||||
/// </summary>
|
||||
/// <param name="action">The event containing information about the pressed action.</param>
|
||||
/// <typeparam name="T">The type of binding, commonly enums.</typeparam>
|
||||
public void OnPressed<T>(KeyBindingPressEvent<T> action) where T : struct;
|
||||
|
||||
/// <summary>
|
||||
/// Called when an action is released.
|
||||
/// </summary>
|
||||
/// <param name="action">The event containing information about the released action.</param>
|
||||
/// <typeparam name="T">The type of binding, commonly enums.</typeparam>
|
||||
public void OnReleased<T>(KeyBindingReleaseEvent<T> action) where T : struct;
|
||||
}
|
||||
}
|
@ -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<T> : PassThroughInputManager, ICanAttachHUDPieces, IHasReplayHandler, IHasRecordingHandler
|
||||
public abstract partial class RulesetInputManager<T> : 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<T>())
|
||||
.Distinct()
|
||||
.OrderBy(action => action)
|
||||
.Select(action => new KeyCounterActionTrigger<T>(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<T>
|
||||
{
|
||||
private readonly ClicksPerSecondCalculator calculator;
|
||||
public event Action<KeyBindingPressEvent<T>> OnPressedEvent;
|
||||
|
||||
public ActionListener(ClicksPerSecondCalculator calculator)
|
||||
{
|
||||
this.calculator = calculator;
|
||||
}
|
||||
public event Action<KeyBindingReleaseEvent<T>> OnReleasedEvent;
|
||||
|
||||
public bool OnPressed(KeyBindingPressEvent<T> e)
|
||||
{
|
||||
calculator.AddInputTimestamp();
|
||||
OnPressedEvent?.Invoke(e);
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnReleased(KeyBindingReleaseEvent<T> e)
|
||||
{
|
||||
OnReleasedEvent?.Invoke(e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -257,16 +240,11 @@ namespace osu.Game.Rulesets.UI
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Supports attaching various HUD pieces.
|
||||
/// Keys will be populated automatically and a receptor will be injected inside.
|
||||
/// Sends <see cref="IKeyBinding"/> events to a <see cref="IKeybindingListener"/>
|
||||
/// </summary>
|
||||
public interface ICanAttachHUDPieces
|
||||
{
|
||||
void Attach(IAttachableSkinComponent component);
|
||||
}
|
||||
|
||||
public interface IAttachableSkinComponent
|
||||
public interface IKeybindingEventsEmitter
|
||||
{
|
||||
void Attach(IKeybindingListener component);
|
||||
}
|
||||
|
||||
public class RulesetInputManagerInputState<T> : InputState
|
||||
|
@ -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<double> timestamps = new List<double>();
|
||||
|
||||
@ -53,5 +54,21 @@ namespace osu.Game.Screens.Play.HUD.ClicksPerSecond
|
||||
|
||||
Value = count;
|
||||
}
|
||||
|
||||
#region IKeybindingListener
|
||||
|
||||
bool IKeybindingListener.CanHandleKeybindings => false;
|
||||
|
||||
void IKeybindingListener.Setup<T>(IEnumerable<T> actions)
|
||||
{
|
||||
}
|
||||
|
||||
void IKeybindingListener.OnPressed<T>(KeyBindingPressEvent<T> action) => AddInputTimestamp();
|
||||
|
||||
void IKeybindingListener.OnReleased<T>(KeyBindingReleaseEvent<T> action)
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
@ -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<bool> 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<T>(IEnumerable<T> actions)
|
||||
=> AddRange(actions.Select(a => new KeyCounterActionTrigger<T>(a)));
|
||||
|
||||
void IKeybindingListener.OnPressed<T>(KeyBindingPressEvent<T> action)
|
||||
{
|
||||
}
|
||||
|
||||
void IKeybindingListener.OnReleased<T>(KeyBindingReleaseEvent<T> action)
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
@ -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<Drawable> hideTargets;
|
||||
|
||||
private readonly IEnumerable<IKeybindingListener> actionInjectionCandidates;
|
||||
|
||||
public HUDOverlay(DrawableRuleset drawableRuleset, IReadOnlyList<Mod> mods, bool alwaysShowLeaderboard = true)
|
||||
{
|
||||
Drawable rulesetComponents;
|
||||
@ -163,6 +166,8 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
hideTargets = new List<Drawable> { 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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user