mirror of
https://github.com/ppy/osu.git
synced 2025-01-15 00:02:54 +08:00
Encapsulate mod hotkey selection logic in strategy pattern
This commit is contained in:
parent
b7b7de115f
commit
73124d2b1f
@ -139,7 +139,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding(30),
|
||||
Child = column = new ModColumn(ModType.DifficultyReduction, true, new[] { Key.Q, Key.W, Key.E, Key.R, Key.T, Key.Y, Key.U, Key.I, Key.O, Key.P })
|
||||
Child = column = new ModColumn(ModType.DifficultyReduction, true)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
|
22
osu.Game/Overlays/Mods/Input/IModHotkeyHandler.cs
Normal file
22
osu.Game/Overlays/Mods/Input/IModHotkeyHandler.cs
Normal file
@ -0,0 +1,22 @@
|
||||
// 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.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Overlays.Mods.Input
|
||||
{
|
||||
/// <summary>
|
||||
/// Encapsulates strategies of handling mod hotkeys on the <see cref="ModSelectOverlay"/>.
|
||||
/// </summary>
|
||||
public interface IModHotkeyHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// Attempt to handle a press of the supplied <paramref name="hotkey"/> as a selection of one of the mods in <paramref name="availableMods"/>.
|
||||
/// </summary>
|
||||
/// <param name="hotkey">The key that was pressed by the user.</param>
|
||||
/// <param name="availableMods">The list of currently available mods.</param>
|
||||
/// <returns>Whether the <paramref name="hotkey"/> was handled as a mod selection/deselection.</returns>
|
||||
bool HandleHotkeyPressed(Key hotkey, IEnumerable<ModState> availableMods);
|
||||
}
|
||||
}
|
30
osu.Game/Overlays/Mods/Input/ModHotkeyHandler.cs
Normal file
30
osu.Game/Overlays/Mods/Input/ModHotkeyHandler.cs
Normal file
@ -0,0 +1,30 @@
|
||||
// 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.
|
||||
|
||||
using osu.Game.Rulesets.Mods;
|
||||
|
||||
namespace osu.Game.Overlays.Mods.Input
|
||||
{
|
||||
/// <summary>
|
||||
/// Static factory class for <see cref="IModHotkeyHandler"/>s.
|
||||
/// </summary>
|
||||
public static class ModHotkeyHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates an appropriate <see cref="IModHotkeyHandler"/> for the given <paramref name="modType"/>.
|
||||
/// </summary>
|
||||
public static IModHotkeyHandler Create(ModType modType)
|
||||
{
|
||||
switch (modType)
|
||||
{
|
||||
case ModType.DifficultyReduction:
|
||||
case ModType.DifficultyIncrease:
|
||||
case ModType.Automation:
|
||||
return SequentialModHotkeyHandler.Create(modType);
|
||||
|
||||
default:
|
||||
return new NoopModHotkeyHandler();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
17
osu.Game/Overlays/Mods/Input/NoopModHotkeyHandler.cs
Normal file
17
osu.Game/Overlays/Mods/Input/NoopModHotkeyHandler.cs
Normal file
@ -0,0 +1,17 @@
|
||||
// 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.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Overlays.Mods.Input
|
||||
{
|
||||
/// <summary>
|
||||
/// A no-op implementation of <see cref="IModHotkeyHandler"/>.
|
||||
/// Used when a column is not handling any hotkeys at all.
|
||||
/// </summary>
|
||||
public class NoopModHotkeyHandler : IModHotkeyHandler
|
||||
{
|
||||
public bool HandleHotkeyPressed(Key hotkey, IEnumerable<ModState> availableMods) => false;
|
||||
}
|
||||
}
|
58
osu.Game/Overlays/Mods/Input/SequentialModHotkeyHandler.cs
Normal file
58
osu.Game/Overlays/Mods/Input/SequentialModHotkeyHandler.cs
Normal file
@ -0,0 +1,58 @@
|
||||
// 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.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Overlays.Mods.Input
|
||||
{
|
||||
/// <summary>
|
||||
/// This implementation of <see cref="IModHotkeyHandler"/> receives a sequence of <see cref="Key"/>s,
|
||||
/// and maps the sequence of keys onto the items it is provided in <see cref="HandleHotkeyPressed"/>.
|
||||
/// In this case, particular mods are not bound to particular keys, the hotkeys are a byproduct of mod ordering.
|
||||
/// </summary>
|
||||
public class SequentialModHotkeyHandler : IModHotkeyHandler
|
||||
{
|
||||
public static SequentialModHotkeyHandler Create(ModType modType)
|
||||
{
|
||||
switch (modType)
|
||||
{
|
||||
case ModType.DifficultyReduction:
|
||||
return new SequentialModHotkeyHandler(new[] { Key.Q, Key.W, Key.E, Key.R, Key.T, Key.Y, Key.U, Key.I, Key.O, Key.P });
|
||||
|
||||
case ModType.DifficultyIncrease:
|
||||
return new SequentialModHotkeyHandler(new[] { Key.A, Key.S, Key.D, Key.F, Key.G, Key.H, Key.J, Key.K, Key.L });
|
||||
|
||||
case ModType.Automation:
|
||||
return new SequentialModHotkeyHandler(new[] { Key.Z, Key.X, Key.C, Key.V, Key.B, Key.N, Key.M });
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(modType), modType, $"Cannot create {nameof(SequentialModHotkeyHandler)} for provided mod type");
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Key[] toggleKeys;
|
||||
|
||||
private SequentialModHotkeyHandler(Key[] keys)
|
||||
{
|
||||
toggleKeys = keys;
|
||||
}
|
||||
|
||||
public bool HandleHotkeyPressed(Key hotkey, IEnumerable<ModState> availableMods)
|
||||
{
|
||||
int index = Array.IndexOf(toggleKeys, hotkey);
|
||||
if (index < 0)
|
||||
return false;
|
||||
|
||||
var modState = availableMods.ElementAtOrDefault(index);
|
||||
if (modState == null || modState.Filtered.Value)
|
||||
return false;
|
||||
|
||||
modState.Active.Toggle();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@ -21,10 +21,10 @@ using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Localisation;
|
||||
using osu.Game.Overlays.Mods.Input;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Overlays.Mods
|
||||
{
|
||||
@ -70,7 +70,7 @@ namespace osu.Game.Overlays.Mods
|
||||
|
||||
protected virtual ModPanel CreateModPanel(ModState mod) => new ModPanel(mod);
|
||||
|
||||
private readonly Key[]? toggleKeys;
|
||||
private readonly IModHotkeyHandler hotkeyHandler;
|
||||
|
||||
private readonly TextFlowContainer headerText;
|
||||
private readonly Box headerBackground;
|
||||
@ -86,10 +86,10 @@ namespace osu.Game.Overlays.Mods
|
||||
|
||||
private const float header_height = 42;
|
||||
|
||||
public ModColumn(ModType modType, bool allowBulkSelection, Key[]? toggleKeys = null)
|
||||
public ModColumn(ModType modType, bool allowBulkSelection)
|
||||
{
|
||||
ModType = modType;
|
||||
this.toggleKeys = toggleKeys;
|
||||
hotkeyHandler = ModHotkeyHandler.Create(modType);
|
||||
|
||||
Width = 320;
|
||||
RelativeSizeAxes = Axes.Y;
|
||||
@ -425,17 +425,10 @@ namespace osu.Game.Overlays.Mods
|
||||
|
||||
protected override bool OnKeyDown(KeyDownEvent e)
|
||||
{
|
||||
if (e.ControlPressed || e.AltPressed || e.SuperPressed) return false;
|
||||
if (toggleKeys == null) return false;
|
||||
if (e.ControlPressed || e.AltPressed || e.SuperPressed || e.Repeat)
|
||||
return false;
|
||||
|
||||
int index = Array.IndexOf(toggleKeys, e.Key);
|
||||
if (index < 0) return false;
|
||||
|
||||
var modState = availableMods.ElementAtOrDefault(index);
|
||||
if (modState == null || modState.Filtered.Value) return false;
|
||||
|
||||
modState.Active.Toggle();
|
||||
return true;
|
||||
return hotkeyHandler.HandleHotkeyPressed(e.Key, availableMods);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
@ -21,7 +21,6 @@ using osu.Game.Localisation;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Utils;
|
||||
using osuTK;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Overlays.Mods
|
||||
{
|
||||
@ -68,7 +67,7 @@ namespace osu.Game.Overlays.Mods
|
||||
/// </summary>
|
||||
protected virtual bool AllowCustomisation => true;
|
||||
|
||||
protected virtual ModColumn CreateModColumn(ModType modType, Key[]? toggleKeys = null) => new ModColumn(modType, false, toggleKeys);
|
||||
protected virtual ModColumn CreateModColumn(ModType modType) => new ModColumn(modType, false);
|
||||
|
||||
protected virtual IReadOnlyList<Mod> ComputeNewModsFromSelection(IReadOnlyList<Mod> oldSelection, IReadOnlyList<Mod> newSelection) => newSelection;
|
||||
|
||||
@ -160,9 +159,9 @@ namespace osu.Game.Overlays.Mods
|
||||
Padding = new MarginPadding { Bottom = 10 },
|
||||
Children = new[]
|
||||
{
|
||||
createModColumnContent(ModType.DifficultyReduction, new[] { Key.Q, Key.W, Key.E, Key.R, Key.T, Key.Y, Key.U, Key.I, Key.O, Key.P }),
|
||||
createModColumnContent(ModType.DifficultyIncrease, new[] { Key.A, Key.S, Key.D, Key.F, Key.G, Key.H, Key.J, Key.K, Key.L }),
|
||||
createModColumnContent(ModType.Automation, new[] { Key.Z, Key.X, Key.C, Key.V, Key.B, Key.N, Key.M }),
|
||||
createModColumnContent(ModType.DifficultyReduction),
|
||||
createModColumnContent(ModType.DifficultyIncrease),
|
||||
createModColumnContent(ModType.Automation),
|
||||
createModColumnContent(ModType.Conversion),
|
||||
createModColumnContent(ModType.Fun)
|
||||
}
|
||||
@ -264,9 +263,9 @@ namespace osu.Game.Overlays.Mods
|
||||
column.DeselectAll();
|
||||
}
|
||||
|
||||
private ColumnDimContainer createModColumnContent(ModType modType, Key[]? toggleKeys = null)
|
||||
private ColumnDimContainer createModColumnContent(ModType modType)
|
||||
{
|
||||
var column = CreateModColumn(modType, toggleKeys).With(column =>
|
||||
var column = CreateModColumn(modType).With(column =>
|
||||
{
|
||||
// spacing applied here rather than via `columnFlow.Spacing` to avoid uneven gaps when some of the columns are hidden.
|
||||
column.Margin = new MarginPadding { Right = 10 };
|
||||
|
@ -1,14 +1,10 @@
|
||||
// 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 System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Utils;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Overlays.Mods
|
||||
{
|
||||
@ -19,7 +15,7 @@ namespace osu.Game.Overlays.Mods
|
||||
{
|
||||
}
|
||||
|
||||
protected override ModColumn CreateModColumn(ModType modType, Key[] toggleKeys = null) => new UserModColumn(modType, false, toggleKeys);
|
||||
protected override ModColumn CreateModColumn(ModType modType) => new UserModColumn(modType, false);
|
||||
|
||||
protected override IReadOnlyList<Mod> ComputeNewModsFromSelection(IReadOnlyList<Mod> oldSelection, IReadOnlyList<Mod> newSelection)
|
||||
{
|
||||
@ -44,8 +40,8 @@ namespace osu.Game.Overlays.Mods
|
||||
|
||||
private class UserModColumn : ModColumn
|
||||
{
|
||||
public UserModColumn(ModType modType, bool allowBulkSelection, [CanBeNull] Key[] toggleKeys = null)
|
||||
: base(modType, allowBulkSelection, toggleKeys)
|
||||
public UserModColumn(ModType modType, bool allowBulkSelection)
|
||||
: base(modType, allowBulkSelection)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,6 @@ using osu.Framework.Graphics;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Overlays.Mods;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay
|
||||
{
|
||||
@ -33,7 +32,7 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
IsValidMod = _ => true;
|
||||
}
|
||||
|
||||
protected override ModColumn CreateModColumn(ModType modType, Key[] toggleKeys = null) => new ModColumn(modType, true, toggleKeys);
|
||||
protected override ModColumn CreateModColumn(ModType modType) => new ModColumn(modType, true);
|
||||
|
||||
protected override IEnumerable<ShearedButton> CreateFooterButtons() => base.CreateFooterButtons().Prepend(
|
||||
new SelectAllModsButton(this)
|
||||
|
Loading…
Reference in New Issue
Block a user