diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModColumn.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModColumn.cs index 534e9e0144..01ef4b4b60 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModColumn.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModColumn.cs @@ -132,6 +132,43 @@ namespace osu.Game.Tests.Visual.UserInterface }); } + [Test] + public void TestKeyboardSelection() + { + ModColumn column = null; + AddStep("create content", () => Child = new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding(30), + Child = column = new ModColumn(ModType.DifficultyReduction, true, new Key[] { Key.Q, Key.W, Key.E, Key.R, Key.T, Key.Y, Key.U, Key.I, Key.O, Key.P }) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre + } + }); + + AddStep("press W", () => InputManager.Key(Key.W)); + AddAssert("NF panel selected", () => this.ChildrenOfType().Single(panel => panel.Mod.Acronym == "NF").Active.Value); + + AddStep("press W again", () => InputManager.Key(Key.W)); + AddAssert("NF panel deselected", () => !this.ChildrenOfType().Single(panel => panel.Mod.Acronym == "NF").Active.Value); + + AddStep("set filter to NF", () => column.Filter = mod => mod.Acronym == "NF"); + + AddStep("press W", () => InputManager.Key(Key.W)); + AddAssert("NF panel selected", () => this.ChildrenOfType().Single(panel => panel.Mod.Acronym == "NF").Active.Value); + + AddStep("press W again", () => InputManager.Key(Key.W)); + AddAssert("NF panel deselected", () => !this.ChildrenOfType().Single(panel => panel.Mod.Acronym == "NF").Active.Value); + + AddStep("filter out everything", () => column.Filter = _ => false); + + AddStep("press W", () => InputManager.Key(Key.W)); + AddAssert("NF panel not selected", () => !this.ChildrenOfType().Single(panel => panel.Mod.Acronym == "NF").Active.Value); + + AddStep("clear filter", () => column.Filter = null); + } + private class TestModColumn : ModColumn { public new bool SelectionAnimationRunning => base.SelectionAnimationRunning; diff --git a/osu.Game/Overlays/Mods/ModColumn.cs b/osu.Game/Overlays/Mods/ModColumn.cs index 649f54a96a..dd3b7274fa 100644 --- a/osu.Game/Overlays/Mods/ModColumn.cs +++ b/osu.Game/Overlays/Mods/ModColumn.cs @@ -14,6 +14,7 @@ using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; +using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; @@ -21,6 +22,7 @@ using osu.Game.Rulesets.Mods; using osu.Game.Utils; using osuTK; using osuTK.Graphics; +using osuTK.Input; #nullable enable @@ -41,6 +43,7 @@ namespace osu.Game.Overlays.Mods } private readonly ModType modType; + private readonly Key[]? toggleKeys; private readonly Bindable>> availableMods = new Bindable>>(); @@ -55,9 +58,10 @@ namespace osu.Game.Overlays.Mods private const float header_height = 60; - public ModColumn(ModType modType, bool allowBulkSelection) + public ModColumn(ModType modType, bool allowBulkSelection, Key[]? toggleKeys = null) { this.modType = modType; + this.toggleKeys = toggleKeys; Width = 450; RelativeSizeAxes = Axes.Y; @@ -381,5 +385,24 @@ namespace osu.Game.Overlays.Mods } #endregion + + #region Keyboard selection support + + protected override bool OnKeyDown(KeyDownEvent e) + { + if (e.ControlPressed || e.AltPressed) return false; + if (toggleKeys == null) return false; + + int index = Array.IndexOf(toggleKeys, e.Key); + if (index < 0) return false; + + var panel = panelFlow.ElementAtOrDefault(index); + if (panel == null || panel.Filtered.Value) return false; + + panel.Active.Toggle(); + return true; + } + + #endregion } }