From 1a0193001d5b42ff5ee08c0a4d65d99cc1f8e196 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Jan 2026 14:42:41 +0900 Subject: [PATCH 1/5] Fix "ALL MODS" display not displaying in new playlist song select Closes https://github.com/ppy/osu/issues/36411. --- osu.Game/Screens/OnlinePlay/FooterButtonFreeModsV2.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/FooterButtonFreeModsV2.cs b/osu.Game/Screens/OnlinePlay/FooterButtonFreeModsV2.cs index a2194a5d2f..5371415692 100644 --- a/osu.Game/Screens/OnlinePlay/FooterButtonFreeModsV2.cs +++ b/osu.Game/Screens/OnlinePlay/FooterButtonFreeModsV2.cs @@ -122,7 +122,7 @@ namespace osu.Game.Screens.OnlinePlay Freestyle.BindValueChanged(f => Enabled.Value = !f.NewValue, true); FreeMods.BindValueChanged(m => { - if (m.NewValue.Count == 0) + if (m.NewValue.Count == 0 && !Freestyle.Value) modsWedge.FadeOut(200); else modsWedge.FadeIn(200); @@ -133,7 +133,7 @@ namespace osu.Game.Screens.OnlinePlay { base.Update(); - if (modDisplay.DrawWidth * modDisplay.Scale.X > modContainer.DrawWidth) + if (Freestyle.Value || modDisplay.DrawWidth * modDisplay.Scale.X > modContainer.DrawWidth) overflowModCountDisplay.Show(); else overflowModCountDisplay.Hide(); From 0c90cf3cef170b673713942d9372b2d1549f42b9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Jan 2026 15:01:23 +0900 Subject: [PATCH 2/5] Fix mod count overlay flashing when switching freestyle on and off --- .../OnlinePlay/FooterButtonFreeModsV2.cs | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/FooterButtonFreeModsV2.cs b/osu.Game/Screens/OnlinePlay/FooterButtonFreeModsV2.cs index 5371415692..4b94e8c36c 100644 --- a/osu.Game/Screens/OnlinePlay/FooterButtonFreeModsV2.cs +++ b/osu.Game/Screens/OnlinePlay/FooterButtonFreeModsV2.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; +using osu.Framework.Utils; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Localisation; @@ -90,11 +91,6 @@ namespace osu.Game.Screens.OnlinePlay Masking = true, Children = new Drawable[] { - new Box - { - Colour = colourProvider.Background3, - RelativeSizeAxes = Axes.Both, - }, modDisplay = new ModDisplay(showExtendedInformation: true) { Anchor = Anchor.Centre, @@ -123,9 +119,9 @@ namespace osu.Game.Screens.OnlinePlay FreeMods.BindValueChanged(m => { if (m.NewValue.Count == 0 && !Freestyle.Value) - modsWedge.FadeOut(200); + modsWedge.FadeOut(300, Easing.OutExpo); else - modsWedge.FadeIn(200); + modsWedge.FadeIn(300, Easing.OutExpo); }, true); } @@ -133,13 +129,24 @@ namespace osu.Game.Screens.OnlinePlay { base.Update(); - if (Freestyle.Value || modDisplay.DrawWidth * modDisplay.Scale.X > modContainer.DrawWidth) + // If there are freemods selected but the display has no width, it's still loading. + // Don't update visibility in this state or we will cause an awkward flash. + if (FreeMods.Value.Count > 0 && Precision.AlmostEquals(modDisplay.DrawWidth, 0)) + return; + + bool showCountText = + // When freestyle is enabled this text shows "ALL MODS" + Freestyle.Value + // Standard flow where mods are overflowing so we show count text. + || modDisplay.DrawWidth * modDisplay.Scale.X > modContainer.DrawWidth; + + if (showCountText) overflowModCountDisplay.Show(); else overflowModCountDisplay.Hide(); } - private partial class ModCountText : CompositeDrawable + private partial class ModCountText : VisibilityContainer { public readonly Bindable> Mods = new Bindable>(); public readonly Bindable Freestyle = new Bindable(); @@ -178,6 +185,9 @@ namespace osu.Game.Screens.OnlinePlay updateText(); } + protected override void PopIn() => this.FadeIn(300, Easing.OutExpo); + protected override void PopOut() => this.FadeOut(300, Easing.OutExpo); + private void updateText() { if (Freestyle.Value) From c8c91cedfa96852375ac612d689f9554e6d5c575 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Jan 2026 15:58:55 +0900 Subject: [PATCH 3/5] Combine implementation of mods text drawable I don't want to have to update things in multiple places with different code in each place. This also closes https://github.com/ppy/osu/issues/36412. --- .../OnlinePlay/FooterButtonFreeModsV2.cs | 62 ++----------------- osu.Game/Screens/SelectV2/FooterButtonMods.cs | 24 +++++-- 2 files changed, 23 insertions(+), 63 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/FooterButtonFreeModsV2.cs b/osu.Game/Screens/OnlinePlay/FooterButtonFreeModsV2.cs index 4b94e8c36c..822af03c5f 100644 --- a/osu.Game/Screens/OnlinePlay/FooterButtonFreeModsV2.cs +++ b/osu.Game/Screens/OnlinePlay/FooterButtonFreeModsV2.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; @@ -13,21 +12,19 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Utils; using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; using osu.Game.Localisation; using osu.Game.Overlays; using osu.Game.Overlays.Mods; using osu.Game.Rulesets.Mods; using osu.Game.Screens.Footer; using osu.Game.Screens.Play.HUD; +using osu.Game.Screens.SelectV2; using osuTK; namespace osu.Game.Screens.OnlinePlay { public partial class FooterButtonFreeModsV2 : ScreenFooterButton { - private const float bar_height = 30f; - public readonly Bindable> FreeMods = new Bindable>([]); public readonly Bindable Freestyle = new Bindable(); @@ -45,7 +42,7 @@ namespace osu.Game.Screens.OnlinePlay private Container modsWedge = null!; private ModDisplay modDisplay = null!; private Container modContainer = null!; - private ModCountText overflowModCountDisplay = null!; + private FooterButtonMods.ModCountText overflowModCountDisplay = null!; public FooterButtonFreeModsV2(ModSelectOverlay overlay) : base(overlay) @@ -66,7 +63,7 @@ namespace osu.Game.Screens.OnlinePlay Origin = Anchor.BottomLeft, Shear = OsuGame.SHEAR, CornerRadius = CORNER_RADIUS, - Size = new Vector2(BUTTON_WIDTH, bar_height), + Size = new Vector2(BUTTON_WIDTH, FooterButtonMods.BAR_HEIGHT), Masking = true, EdgeEffect = new EdgeEffectParameters { @@ -100,7 +97,7 @@ namespace osu.Game.Screens.OnlinePlay Current = { BindTarget = FreeMods }, ExpansionMode = ExpansionMode.AlwaysContracted, }, - overflowModCountDisplay = new ModCountText + overflowModCountDisplay = new FooterButtonMods.ModCountText { Mods = { BindTarget = FreeMods }, Freestyle = { BindTarget = Freestyle } @@ -145,56 +142,5 @@ namespace osu.Game.Screens.OnlinePlay else overflowModCountDisplay.Hide(); } - - private partial class ModCountText : VisibilityContainer - { - public readonly Bindable> Mods = new Bindable>(); - public readonly Bindable Freestyle = new Bindable(); - - private OsuSpriteText text = null!; - - [Resolved] - private OverlayColourProvider colourProvider { get; set; } = null!; - - protected override void LoadComplete() - { - base.LoadComplete(); - - RelativeSizeAxes = Axes.Both; - - InternalChildren = new Drawable[] - { - new Box - { - Colour = colourProvider.Background3, - Alpha = 0.8f, - RelativeSizeAxes = Axes.Both, - }, - text = new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Font = OsuFont.Torus.With(size: 14f, weight: FontWeight.Bold), - Shear = -OsuGame.SHEAR, - } - }; - - Mods.BindValueChanged(_ => updateText()); - Freestyle.BindValueChanged(_ => updateText()); - - updateText(); - } - - protected override void PopIn() => this.FadeIn(300, Easing.OutExpo); - protected override void PopOut() => this.FadeOut(300, Easing.OutExpo); - - private void updateText() - { - if (Freestyle.Value) - text.Text = ModSelectOverlayStrings.AllMods.ToUpper(); - else - text.Text = ModSelectOverlayStrings.Mods(Mods.Value.Count).ToUpper(); - } - } } } diff --git a/osu.Game/Screens/SelectV2/FooterButtonMods.cs b/osu.Game/Screens/SelectV2/FooterButtonMods.cs index 112f53a53e..f9acf6cf10 100644 --- a/osu.Game/Screens/SelectV2/FooterButtonMods.cs +++ b/osu.Game/Screens/SelectV2/FooterButtonMods.cs @@ -36,7 +36,8 @@ namespace osu.Game.Screens.SelectV2 { public Action? RequestDeselectAllMods { get; init; } - private const float bar_height = 30f; + public const float BAR_HEIGHT = 30f; + private const float mod_display_portion = 0.65f; private readonly BindableWithCurrent> current = new BindableWithCurrent>(Array.Empty()); @@ -92,7 +93,7 @@ namespace osu.Game.Screens.SelectV2 Origin = Anchor.BottomLeft, Shear = OsuGame.SHEAR, CornerRadius = CORNER_RADIUS, - Size = new Vector2(BUTTON_WIDTH, bar_height), + Size = new Vector2(BUTTON_WIDTH, BAR_HEIGHT), Masking = true, EdgeEffect = new EdgeEffectParameters { @@ -257,9 +258,10 @@ namespace osu.Game.Screens.SelectV2 overflowModCountDisplay.Hide(); } - private partial class ModCountText : CompositeDrawable, IHasCustomTooltip> + public partial class ModCountText : VisibilityContainer, IHasCustomTooltip> { public readonly Bindable> Mods = new Bindable>(); + public readonly Bindable Freestyle = new Bindable(); private OsuSpriteText text = null!; @@ -289,13 +291,25 @@ namespace osu.Game.Screens.SelectV2 } }; - Mods.BindValueChanged(v => text.Text = ModSelectOverlayStrings.Mods(v.NewValue.Count).ToUpper(), true); + Freestyle.BindValueChanged(_ => updateText()); + Mods.BindValueChanged(_ => updateText(), true); } public ITooltip> GetCustomTooltip() => new ModOverflowTooltip(colourProvider); public IReadOnlyList? TooltipContent => Mods.Value; + protected override void PopIn() => this.FadeIn(300, Easing.OutExpo); + protected override void PopOut() => this.FadeOut(300, Easing.OutExpo); + + private void updateText() + { + if (Freestyle.Value) + text.Text = ModSelectOverlayStrings.AllMods.ToUpper(); + else + text.Text = ModSelectOverlayStrings.Mods(Mods.Value.Count).ToUpper(); + } + public partial class ModOverflowTooltip : VisibilityContainer, ITooltip> { private ModDisplay extendedModDisplay = null!; @@ -356,7 +370,7 @@ namespace osu.Game.Screens.SelectV2 Shear = OsuGame.SHEAR; CornerRadius = CORNER_RADIUS; AutoSizeAxes = Axes.X; - Height = bar_height; + Height = BAR_HEIGHT; Masking = true; BorderColour = Color4.White; BorderThickness = 2f; From b9d5606fd46682d5a680c8e1e5ef6849b2ff8edd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Jan 2026 16:08:02 +0900 Subject: [PATCH 4/5] Limit maximum size of mod tooltip to avoid it going offscreen --- osu.Game/Screens/SelectV2/FooterButtonMods.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/SelectV2/FooterButtonMods.cs b/osu.Game/Screens/SelectV2/FooterButtonMods.cs index f9acf6cf10..55f3b6fb05 100644 --- a/osu.Game/Screens/SelectV2/FooterButtonMods.cs +++ b/osu.Game/Screens/SelectV2/FooterButtonMods.cs @@ -312,7 +312,7 @@ namespace osu.Game.Screens.SelectV2 public partial class ModOverflowTooltip : VisibilityContainer, ITooltip> { - private ModDisplay extendedModDisplay = null!; + private ModFlowDisplay extendedModDisplay = null!; [Cached] private OverlayColourProvider colourProvider; @@ -336,11 +336,12 @@ namespace osu.Game.Screens.SelectV2 RelativeSizeAxes = Axes.Both, Colour = colourProvider.Background5, }, - extendedModDisplay = new ModDisplay + extendedModDisplay = new ModFlowDisplay { + AutoSizeAxes = Axes.Both, + MaximumSize = new Vector2(400, 0), Margin = new MarginPadding { Vertical = 2f, Horizontal = 10f }, Scale = new Vector2(0.6f), - ExpansionMode = ExpansionMode.AlwaysExpanded, }, }; } From ca7b850c8c75da7a67c48fee2ec4a7747b68d803 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 28 Jan 2026 16:37:39 +0900 Subject: [PATCH 5/5] Remove leaking knowledge of freestyle in normal song select component --- .../OnlinePlay/FooterButtonFreeModsV2.cs | 9 ++++++-- osu.Game/Screens/SelectV2/FooterButtonMods.cs | 22 +++++++++++++++---- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/FooterButtonFreeModsV2.cs b/osu.Game/Screens/OnlinePlay/FooterButtonFreeModsV2.cs index 822af03c5f..612eca9474 100644 --- a/osu.Game/Screens/OnlinePlay/FooterButtonFreeModsV2.cs +++ b/osu.Game/Screens/OnlinePlay/FooterButtonFreeModsV2.cs @@ -5,11 +5,13 @@ using System; using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; +using osu.Framework.Localisation; using osu.Framework.Utils; using osu.Game.Graphics; using osu.Game.Localisation; @@ -100,7 +102,6 @@ namespace osu.Game.Screens.OnlinePlay overflowModCountDisplay = new FooterButtonMods.ModCountText { Mods = { BindTarget = FreeMods }, - Freestyle = { BindTarget = Freestyle } }, } }, @@ -112,7 +113,11 @@ namespace osu.Game.Screens.OnlinePlay { base.LoadComplete(); - Freestyle.BindValueChanged(f => Enabled.Value = !f.NewValue, true); + Freestyle.BindValueChanged(f => + { + Enabled.Value = !f.NewValue; + overflowModCountDisplay.CustomText = f.NewValue ? ModSelectOverlayStrings.AllMods.ToUpper() : (LocalisableString?)null; + }, true); FreeMods.BindValueChanged(m => { if (m.NewValue.Count == 0 && !Freestyle.Value) diff --git a/osu.Game/Screens/SelectV2/FooterButtonMods.cs b/osu.Game/Screens/SelectV2/FooterButtonMods.cs index 55f3b6fb05..27e0ac68f9 100644 --- a/osu.Game/Screens/SelectV2/FooterButtonMods.cs +++ b/osu.Game/Screens/SelectV2/FooterButtonMods.cs @@ -261,7 +261,22 @@ namespace osu.Game.Screens.SelectV2 public partial class ModCountText : VisibilityContainer, IHasCustomTooltip> { public readonly Bindable> Mods = new Bindable>(); - public readonly Bindable Freestyle = new Bindable(); + + private LocalisableString? customText; + + /// + /// When set, this will be shown instead of a mod count. + /// + public LocalisableString? CustomText + { + get => customText; + set + { + customText = value; + if (IsLoaded) + updateText(); + } + } private OsuSpriteText text = null!; @@ -291,7 +306,6 @@ namespace osu.Game.Screens.SelectV2 } }; - Freestyle.BindValueChanged(_ => updateText()); Mods.BindValueChanged(_ => updateText(), true); } @@ -304,8 +318,8 @@ namespace osu.Game.Screens.SelectV2 private void updateText() { - if (Freestyle.Value) - text.Text = ModSelectOverlayStrings.AllMods.ToUpper(); + if (CustomText != null) + text.Text = CustomText.Value; else text.Text = ModSelectOverlayStrings.Mods(Mods.Value.Count).ToUpper(); }