mirror of
https://github.com/ppy/osu.git
synced 2025-01-28 20:32:55 +08: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.
|
/// Displays an interactive ruleset gameplay instance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TObject">The type of HitObject contained by this DrawableRuleset.</typeparam>
|
/// <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
|
where TObject : HitObject
|
||||||
{
|
{
|
||||||
public override event Action<JudgementResult> NewResult;
|
public override event Action<JudgementResult> NewResult;
|
||||||
@ -327,8 +327,8 @@ namespace osu.Game.Rulesets.UI
|
|||||||
/// <returns>The representing <see cref="DrawableHitObject{TObject}"/>.</returns>
|
/// <returns>The representing <see cref="DrawableHitObject{TObject}"/>.</returns>
|
||||||
public abstract DrawableHitObject<TObject> CreateDrawableRepresentation(TObject h);
|
public abstract DrawableHitObject<TObject> CreateDrawableRepresentation(TObject h);
|
||||||
|
|
||||||
public void Attach(IAttachableSkinComponent skinComponent) =>
|
public void Attach(IKeybindingListener skinComponent) =>
|
||||||
(KeyBindingInputManager as ICanAttachHUDPieces)?.Attach(skinComponent);
|
(KeyBindingInputManager as IKeybindingEventsEmitter)?.Attach(skinComponent);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a key conversion input manager. An exception will be thrown if a valid <see cref="RulesetInputManager{T}"/> is not returned.
|
/// 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.Bindings;
|
||||||
using osu.Game.Input.Handlers;
|
using osu.Game.Input.Handlers;
|
||||||
using osu.Game.Rulesets.Scoring;
|
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;
|
using static osu.Game.Input.Handlers.ReplayInputHandler;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.UI
|
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
|
where T : struct
|
||||||
{
|
{
|
||||||
protected override bool AllowRightClickFromLongTouch => false;
|
protected override bool AllowRightClickFromLongTouch => false;
|
||||||
@ -66,6 +64,7 @@ namespace osu.Game.Rulesets.UI
|
|||||||
InternalChild = KeyBindingContainer =
|
InternalChild = KeyBindingContainer =
|
||||||
CreateKeyBindingContainer(ruleset, variant, unique)
|
CreateKeyBindingContainer(ruleset, variant, unique)
|
||||||
.WithChild(content = new Container { RelativeSizeAxes = Axes.Both });
|
.WithChild(content = new Container { RelativeSizeAxes = Axes.Both });
|
||||||
|
KeyBindingContainer.Add(actionListener = new ActionListener());
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader(true)]
|
[BackgroundDependencyLoader(true)]
|
||||||
@ -160,63 +159,47 @@ namespace osu.Game.Rulesets.UI
|
|||||||
|
|
||||||
#region Component attachement
|
#region Component attachement
|
||||||
|
|
||||||
public void Attach(IAttachableSkinComponent skinComponent)
|
private readonly ActionListener actionListener;
|
||||||
|
|
||||||
|
public void Attach(IKeybindingListener skinComponent)
|
||||||
{
|
{
|
||||||
switch (skinComponent)
|
skinComponent.Setup(KeyBindingContainer.DefaultKeyBindings
|
||||||
{
|
|
||||||
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
|
|
||||||
.Select(b => b.GetAction<T>())
|
.Select(b => b.GetAction<T>())
|
||||||
.Distinct()
|
.Distinct()
|
||||||
.OrderBy(action => action)
|
.OrderBy(a => a));
|
||||||
.Select(action => new KeyCounterActionTrigger<T>(action)));
|
|
||||||
|
if (skinComponent.CanHandleKeybindings && skinComponent is Drawable component)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
KeyBindingContainer.Add(component);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
actionListener.OnPressedEvent += skinComponent.OnPressed;
|
||||||
|
actionListener.OnReleasedEvent += skinComponent.OnReleased;
|
||||||
#region Keys per second Counter Attachment
|
|
||||||
|
|
||||||
private void attachClicksPerSecond(ClicksPerSecondCalculator calculator)
|
|
||||||
{
|
|
||||||
var listener = new ActionListener(calculator);
|
|
||||||
|
|
||||||
KeyBindingContainer.Add(listener);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private partial class ActionListener : Component, IKeyBindingHandler<T>
|
private partial class ActionListener : Component, IKeyBindingHandler<T>
|
||||||
{
|
{
|
||||||
private readonly ClicksPerSecondCalculator calculator;
|
public event Action<KeyBindingPressEvent<T>> OnPressedEvent;
|
||||||
|
|
||||||
public ActionListener(ClicksPerSecondCalculator calculator)
|
public event Action<KeyBindingReleaseEvent<T>> OnReleasedEvent;
|
||||||
{
|
|
||||||
this.calculator = calculator;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool OnPressed(KeyBindingPressEvent<T> e)
|
public bool OnPressed(KeyBindingPressEvent<T> e)
|
||||||
{
|
{
|
||||||
calculator.AddInputTimestamp();
|
OnPressedEvent?.Invoke(e);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnReleased(KeyBindingReleaseEvent<T> e)
|
public void OnReleased(KeyBindingReleaseEvent<T> e)
|
||||||
{
|
{
|
||||||
|
OnReleasedEvent?.Invoke(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,16 +240,11 @@ namespace osu.Game.Rulesets.UI
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Supports attaching various HUD pieces.
|
/// Sends <see cref="IKeyBinding"/> events to a <see cref="IKeybindingListener"/>
|
||||||
/// Keys will be populated automatically and a receptor will be injected inside.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface ICanAttachHUDPieces
|
public interface IKeybindingEventsEmitter
|
||||||
{
|
|
||||||
void Attach(IAttachableSkinComponent component);
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface IAttachableSkinComponent
|
|
||||||
{
|
{
|
||||||
|
void Attach(IKeybindingListener component);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class RulesetInputManagerInputState<T> : InputState
|
public class RulesetInputManagerInputState<T> : InputState
|
||||||
|
@ -4,11 +4,12 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Play.HUD.ClicksPerSecond
|
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>();
|
private readonly List<double> timestamps = new List<double>();
|
||||||
|
|
||||||
@ -53,5 +54,21 @@ namespace osu.Game.Screens.Play.HUD.ClicksPerSecond
|
|||||||
|
|
||||||
Value = count;
|
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;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Play.HUD
|
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);
|
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 HandleNonPositionalInput => true;
|
||||||
|
|
||||||
public override bool HandlePositionalInput => 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.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions.EnumExtensions;
|
using osu.Framework.Extensions.EnumExtensions;
|
||||||
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
@ -102,6 +103,8 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
private readonly List<Drawable> hideTargets;
|
private readonly List<Drawable> hideTargets;
|
||||||
|
|
||||||
|
private readonly IEnumerable<IKeybindingListener> actionInjectionCandidates;
|
||||||
|
|
||||||
public HUDOverlay(DrawableRuleset drawableRuleset, IReadOnlyList<Mod> mods, bool alwaysShowLeaderboard = true)
|
public HUDOverlay(DrawableRuleset drawableRuleset, IReadOnlyList<Mod> mods, bool alwaysShowLeaderboard = true)
|
||||||
{
|
{
|
||||||
Drawable rulesetComponents;
|
Drawable rulesetComponents;
|
||||||
@ -163,6 +166,8 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
hideTargets = new List<Drawable> { mainComponents, rulesetComponents, topRightElements };
|
hideTargets = new List<Drawable> { mainComponents, rulesetComponents, topRightElements };
|
||||||
|
|
||||||
|
actionInjectionCandidates = new IKeybindingListener[] { clicksPerSecondCalculator, KeyCounter };
|
||||||
|
|
||||||
if (!alwaysShowLeaderboard)
|
if (!alwaysShowLeaderboard)
|
||||||
hideTargets.Add(LeaderboardFlow);
|
hideTargets.Add(LeaderboardFlow);
|
||||||
}
|
}
|
||||||
@ -319,11 +324,8 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
protected virtual void BindDrawableRuleset(DrawableRuleset drawableRuleset)
|
protected virtual void BindDrawableRuleset(DrawableRuleset drawableRuleset)
|
||||||
{
|
{
|
||||||
if (drawableRuleset is ICanAttachHUDPieces attachTarget)
|
if (drawableRuleset is IKeybindingEventsEmitter attachTarget)
|
||||||
{
|
actionInjectionCandidates.ForEach(attachTarget.Attach);
|
||||||
attachTarget.Attach(KeyCounter);
|
|
||||||
attachTarget.Attach(clicksPerSecondCalculator);
|
|
||||||
}
|
|
||||||
|
|
||||||
replayLoaded.BindTo(drawableRuleset.HasReplayLoaded);
|
replayLoaded.BindTo(drawableRuleset.HasReplayLoaded);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user