From 183ccbf792001f720166dccf3dfe3b5b15c1fa8e Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 24 Sep 2025 19:17:27 +0900 Subject: [PATCH 1/4] Add left/right keybinds to pool selector --- .../Matchmaking/MatchmakingPoolSelector.cs | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Matchmaking/MatchmakingPoolSelector.cs b/osu.Game/Screens/OnlinePlay/Matchmaking/MatchmakingPoolSelector.cs index 43e6acfaf7..8e4d1e8d2b 100644 --- a/osu.Game/Screens/OnlinePlay/Matchmaking/MatchmakingPoolSelector.cs +++ b/osu.Game/Screens/OnlinePlay/Matchmaking/MatchmakingPoolSelector.cs @@ -1,11 +1,13 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; @@ -13,6 +15,7 @@ using osu.Game.Online.Matchmaking; using osu.Game.Rulesets; using osuTK; using osuTK.Graphics; +using osuTK.Input; namespace osu.Game.Screens.OnlinePlay.Matchmaking { @@ -53,6 +56,35 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking }, true); } + protected override bool OnKeyDown(KeyDownEvent e) + { + if (e.Key != Key.Left && e.Key != Key.Right) + return false; + + if (SelectedPool.Value == null) + { + SelectedPool.Value = AvailablePools.Value[0]; + return true; + } + + int currentPoolIndex = Array.IndexOf(AvailablePools.Value, SelectedPool.Value); + + switch (e.Key) + { + case Key.Left: + SelectedPool.Value = currentPoolIndex == 0 + ? AvailablePools.Value[^1] + : AvailablePools.Value[(currentPoolIndex - 1) % AvailablePools.Value.Length]; + break; + + case Key.Right: + SelectedPool.Value = AvailablePools.Value[(currentPoolIndex + 1) % AvailablePools.Value.Length]; + break; + } + + return true; + } + private partial class SelectorButton : CompositeDrawable { public readonly Bindable SelectedPool = new Bindable(); From 3176006510de5ec57a4f9416578e1b6b5c794142 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 24 Sep 2025 19:25:12 +0900 Subject: [PATCH 2/4] Add select keybind to queue screen buttons --- .../Screens/MatchmakingQueueScreen.cs | 31 +++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Matchmaking/Screens/MatchmakingQueueScreen.cs b/osu.Game/Screens/OnlinePlay/Matchmaking/Screens/MatchmakingQueueScreen.cs index d23ff9bf84..d90943fdb4 100644 --- a/osu.Game/Screens/OnlinePlay/Matchmaking/Screens/MatchmakingQueueScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Matchmaking/Screens/MatchmakingQueueScreen.cs @@ -15,11 +15,14 @@ using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Framework.Screens; using osu.Game.Database; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Input.Bindings; using osu.Game.Online.API; using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Matchmaking; @@ -346,7 +349,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Screens Text = "Found a match!", Font = OsuFont.GetFont(size: 32, weight: FontWeight.Regular, typeface: Typeface.TorusAlternate), }, - new ShearedButton(200) + new SelectionButton(200) { DarkerColour = colours.YellowDark, LighterColour = colours.YellowLight, @@ -436,7 +439,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Screens InRoom } - private partial class BeginQueueingButton : ShearedButton + private partial class BeginQueueingButton : SelectionButton { public readonly IBindable SelectedPool = new Bindable(); @@ -452,5 +455,29 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Screens SelectedPool.BindValueChanged(p => Enabled.Value = p.NewValue != null, true); } } + + private partial class SelectionButton : ShearedButton, IKeyBindingHandler + { + public SelectionButton(float? width = null, float height = DEFAULT_HEIGHT) + : base(width, height) + { + } + + public bool OnPressed(KeyBindingPressEvent e) + { + if (e.Action != GlobalAction.Select) + return false; + + if (e.Repeat) + return true; + + Action(); + return true; + } + + public void OnReleased(KeyBindingReleaseEvent e) + { + } + } } } From 3c37fb11be03ea3bf52975e9dbfe83ad9173a32c Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 24 Sep 2025 19:47:13 +0900 Subject: [PATCH 3/4] Add sounds --- .../UserInterface/HoverClickSounds.cs | 33 ++++++++++--------- .../Matchmaking/MatchmakingPoolSelector.cs | 19 ++++++++--- .../Screens/MatchmakingQueueScreen.cs | 5 +++ 3 files changed, 38 insertions(+), 19 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/HoverClickSounds.cs b/osu.Game/Graphics/UserInterface/HoverClickSounds.cs index 884834ebe8..fea33bfa9d 100644 --- a/osu.Game/Graphics/UserInterface/HoverClickSounds.cs +++ b/osu.Game/Graphics/UserInterface/HoverClickSounds.cs @@ -42,18 +42,20 @@ namespace osu.Game.Graphics.UserInterface this.buttons = buttons ?? new[] { MouseButton.Left }; } + [BackgroundDependencyLoader] + private void load(AudioManager audio) + { + sampleClick = audio.Samples.Get($@"UI/{SampleSet.GetDescription()}-select") + ?? audio.Samples.Get($@"UI/{HoverSampleSet.Default.GetDescription()}-select"); + + sampleClickDisabled = audio.Samples.Get($@"UI/{SampleSet.GetDescription()}-select-disabled") + ?? audio.Samples.Get($@"UI/{HoverSampleSet.Default.GetDescription()}-select-disabled"); + } + protected override bool OnClick(ClickEvent e) { if (buttons.Contains(e.Button)) - { - var channel = Enabled.Value ? sampleClick?.GetChannel() : sampleClickDisabled?.GetChannel(); - - if (channel != null) - { - channel.Frequency.Value = 0.99 + RNG.NextDouble(0.02); - channel.Play(); - } - } + PlayClickSample(); return base.OnClick(e); } @@ -66,14 +68,15 @@ namespace osu.Game.Graphics.UserInterface base.PlayHoverSample(); } - [BackgroundDependencyLoader] - private void load(AudioManager audio) + public void PlayClickSample() { - sampleClick = audio.Samples.Get($@"UI/{SampleSet.GetDescription()}-select") - ?? audio.Samples.Get($@"UI/{HoverSampleSet.Default.GetDescription()}-select"); + var channel = Enabled.Value ? sampleClick?.GetChannel() : sampleClickDisabled?.GetChannel(); - sampleClickDisabled = audio.Samples.Get($@"UI/{SampleSet.GetDescription()}-select-disabled") - ?? audio.Samples.Get($@"UI/{HoverSampleSet.Default.GetDescription()}-select-disabled"); + if (channel != null) + { + channel.Frequency.Value = 0.99 + RNG.NextDouble(0.02); + channel.Play(); + } } } } diff --git a/osu.Game/Screens/OnlinePlay/Matchmaking/MatchmakingPoolSelector.cs b/osu.Game/Screens/OnlinePlay/Matchmaking/MatchmakingPoolSelector.cs index 8e4d1e8d2b..3a7f57eb40 100644 --- a/osu.Game/Screens/OnlinePlay/Matchmaking/MatchmakingPoolSelector.cs +++ b/osu.Game/Screens/OnlinePlay/Matchmaking/MatchmakingPoolSelector.cs @@ -27,6 +27,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking public readonly Bindable SelectedPool = new Bindable(); private FillFlowContainer poolFlow = null!; + private HoverClickSounds clickSounds = null!; public MatchmakingPoolSelector() { @@ -36,11 +37,19 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking [BackgroundDependencyLoader] private void load() { - InternalChild = poolFlow = new FillFlowContainer + InternalChildren = new Drawable[] { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(3) + poolFlow = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(3) + }, + clickSounds = new HoverClickSounds(HoverSampleSet.TabSelect) + { + // Click samples are played manually + Alpha = 0 + } }; } @@ -61,6 +70,8 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking if (e.Key != Key.Left && e.Key != Key.Right) return false; + clickSounds.PlayClickSample(); + if (SelectedPool.Value == null) { SelectedPool.Value = AvailablePools.Value[0]; diff --git a/osu.Game/Screens/OnlinePlay/Matchmaking/Screens/MatchmakingQueueScreen.cs b/osu.Game/Screens/OnlinePlay/Matchmaking/Screens/MatchmakingQueueScreen.cs index d90943fdb4..a52528bf93 100644 --- a/osu.Game/Screens/OnlinePlay/Matchmaking/Screens/MatchmakingQueueScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Matchmaking/Screens/MatchmakingQueueScreen.cs @@ -458,6 +458,8 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Screens private partial class SelectionButton : ShearedButton, IKeyBindingHandler { + private HoverClickSounds clickSounds = null!; + public SelectionButton(float? width = null, float height = DEFAULT_HEIGHT) : base(width, height) { @@ -471,6 +473,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Screens if (e.Repeat) return true; + clickSounds.PlayClickSample(); Action(); return true; } @@ -478,6 +481,8 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Screens public void OnReleased(KeyBindingReleaseEvent e) { } + + protected override HoverSounds CreateHoverSounds(HoverSampleSet sampleSet) => clickSounds = (HoverClickSounds)base.CreateHoverSounds(sampleSet); } } } From 6e39e714e1892ab7852539aa8963e2887e0f9ecb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 30 Sep 2025 19:22:16 +0900 Subject: [PATCH 4/4] Refactor to simplify sample handling --- .../Containers/OsuClickableContainer.cs | 12 +++- .../Matchmaking/Queue/PoolSelector.cs | 63 +++++++------------ .../Matchmaking/Queue/ScreenQueue.cs | 16 ++--- 3 files changed, 40 insertions(+), 51 deletions(-) diff --git a/osu.Game/Graphics/Containers/OsuClickableContainer.cs b/osu.Game/Graphics/Containers/OsuClickableContainer.cs index fceee90d06..dbc354ae07 100644 --- a/osu.Game/Graphics/Containers/OsuClickableContainer.cs +++ b/osu.Game/Graphics/Containers/OsuClickableContainer.cs @@ -18,6 +18,8 @@ namespace osu.Game.Graphics.Containers private readonly Container content = new Container { RelativeSizeAxes = Axes.Both }; + private HoverSounds samples = null!; + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => // base call is checked for cases when `OsuClickableContainer` has masking applied to it directly (ie. externally in object initialisation). base.ReceivePositionalInputAt(screenSpacePos) @@ -33,6 +35,14 @@ namespace osu.Game.Graphics.Containers this.sampleSet = sampleSet; } + public void TriggerClickWithSound() + { + TriggerClick(); + + // TriggerClick doesn't recursively fire the event so we need to manually do this. + (samples as HoverClickSounds)?.PlayClickSample(); + } + public virtual LocalisableString TooltipText { get; set; } [BackgroundDependencyLoader] @@ -46,7 +56,7 @@ namespace osu.Game.Graphics.Containers AddRangeInternal(new Drawable[] { - CreateHoverSounds(sampleSet), + samples = CreateHoverSounds(sampleSet), content, }); } diff --git a/osu.Game/Screens/OnlinePlay/Matchmaking/Queue/PoolSelector.cs b/osu.Game/Screens/OnlinePlay/Matchmaking/Queue/PoolSelector.cs index d71390cfa8..71f976329f 100644 --- a/osu.Game/Screens/OnlinePlay/Matchmaking/Queue/PoolSelector.cs +++ b/osu.Game/Screens/OnlinePlay/Matchmaking/Queue/PoolSelector.cs @@ -1,7 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -28,7 +28,6 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Queue public readonly Bindable SelectedPool = new Bindable(); private FillFlowContainer poolFlow = null!; - private HoverClickSounds clickSounds = null!; public PoolSelector() { @@ -38,20 +37,12 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Queue [BackgroundDependencyLoader] private void load() { - InternalChildren = new Drawable[] + InternalChild = poolFlow = new FillFlowContainer { - poolFlow = new FillFlowContainer - { - AutoSizeAxes = Axes.X, - Height = icon_size * 1.2f, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(5), - }, - clickSounds = new HoverClickSounds(HoverSampleSet.TabSelect) - { - // Click samples are played manually - Alpha = 0 - } + AutoSizeAxes = Axes.X, + Height = icon_size * 1.2f, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(5), }; } @@ -77,37 +68,32 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Queue protected override bool OnKeyDown(KeyDownEvent e) { - if (e.Key != Key.Left && e.Key != Key.Right) - return false; - - clickSounds.PlayClickSample(); - - if (SelectedPool.Value == null) - { - SelectedPool.Value = AvailablePools.Value[0]; - return true; - } - - int currentPoolIndex = Array.IndexOf(AvailablePools.Value, SelectedPool.Value); + var currentSelection = poolFlow.SingleOrDefault(b => b.IsSelected); switch (e.Key) { case Key.Left: - SelectedPool.Value = currentPoolIndex == 0 - ? AvailablePools.Value[^1] - : AvailablePools.Value[(currentPoolIndex - 1) % AvailablePools.Value.Length]; - break; + { + var next = poolFlow.Reverse().SkipWhile(b => b != currentSelection).Skip(1).FirstOrDefault(); + (next ?? poolFlow.Last()).TriggerClickWithSound(); + return true; + } case Key.Right: - SelectedPool.Value = AvailablePools.Value[(currentPoolIndex + 1) % AvailablePools.Value.Length]; - break; + { + var next = poolFlow.SkipWhile(b => b != currentSelection).Skip(1).FirstOrDefault(); + (next ?? poolFlow.First()).TriggerClickWithSound(); + return true; + } } - return true; + return false; } private partial class SelectorButton : OsuAnimatedButton { + public bool IsSelected => SelectedPool.Value?.Equals(pool) == true; + public readonly Bindable SelectedPool = new Bindable(); [Resolved] @@ -119,6 +105,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Queue private Box flashLayer = null!; public SelectorButton(MatchmakingPool pool) + : base(HoverSampleSet.ButtonSidebar) { this.pool = pool; @@ -171,23 +158,21 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Queue protected override bool OnHover(HoverEvent e) { - if (!isSelected) + if (!IsSelected) flashLayer.FadeTo(0.05f, 200, Easing.OutQuint); return base.OnHover(e); } protected override void OnHoverLost(HoverLostEvent e) { - if (!isSelected) + if (!IsSelected) flashLayer.FadeTo(0f, 200, Easing.OutQuint); base.OnHoverLost(e); } - private bool isSelected => SelectedPool.Value?.Equals(pool) == true; - private void onSelectionChanged(ValueChangedEvent selection) { - if (isSelected) + if (IsSelected) { this.ScaleTo(1.2f, 200, Easing.OutQuint); iconSprite.FadeColour(Color4.Gold, 100, Easing.OutQuint); diff --git a/osu.Game/Screens/OnlinePlay/Matchmaking/Queue/ScreenQueue.cs b/osu.Game/Screens/OnlinePlay/Matchmaking/Queue/ScreenQueue.cs index 7f291f27c0..501a46d4c4 100644 --- a/osu.Game/Screens/OnlinePlay/Matchmaking/Queue/ScreenQueue.cs +++ b/osu.Game/Screens/OnlinePlay/Matchmaking/Queue/ScreenQueue.cs @@ -462,8 +462,6 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Queue private partial class SelectionButton : ShearedButton, IKeyBindingHandler { - private HoverClickSounds clickSounds = null!; - public SelectionButton(float? width = null, float height = DEFAULT_HEIGHT) : base(width, height) { @@ -471,22 +469,18 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Queue public bool OnPressed(KeyBindingPressEvent e) { - if (e.Action != GlobalAction.Select) - return false; - - if (e.Repeat) + if (e.Action == GlobalAction.Select && !e.Repeat) + { + TriggerClickWithSound(); return true; + } - clickSounds.PlayClickSample(); - Action(); - return true; + return false; } public void OnReleased(KeyBindingReleaseEvent e) { } - - protected override HoverSounds CreateHoverSounds(HoverSampleSet sampleSet) => clickSounds = (HoverClickSounds)base.CreateHoverSounds(sampleSet); } } }