From 6e635f124aee13d3d95d26ba10a08c321360ceb7 Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Sun, 23 Mar 2025 13:41:18 -0400 Subject: [PATCH 1/5] Change `SettingDescription` to differentiate between settings and values --- .../Mods/CatchModDifficultyAdjust.cs | 25 ++++++++------- .../Mods/OsuModDifficultyAdjust.cs | 19 ++++++------ .../Mods/TaikoModDifficultyAdjust.cs | 15 +++++---- .../Leaderboards/LeaderboardScoreTooltip.cs | 22 ++++++++----- osu.Game/Rulesets/Mods/Mod.cs | 31 +++---------------- .../Rulesets/Mods/ModAccuracyChallenge.cs | 18 +++++++++-- osu.Game/Rulesets/Mods/ModBarrelRoll.cs | 10 +++++- osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs | 15 ++++----- .../Rulesets/Mods/ModEasyWithExtraLives.cs | 12 ++++++- osu.Game/Rulesets/Mods/ModRateAdjust.cs | 13 ++++++-- osu.Game/Rulesets/Mods/ModTimeRamp.cs | 10 +++++- 11 files changed, 108 insertions(+), 82 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs b/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs index 6efb415880..1312f45cdc 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs @@ -1,8 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Linq; +using System.Collections.Generic; using osu.Framework.Bindables; +using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Rulesets.Catch.Beatmaps; @@ -35,21 +36,21 @@ namespace osu.Game.Rulesets.Catch.Mods [SettingSource("Spicy Patterns", "Adjust the patterns as if Hard Rock is enabled.")] public BindableBool HardRockOffsets { get; } = new BindableBool(); - public override string SettingDescription + public override IEnumerable<(LocalisableString setting, LocalisableString value)> SettingDescription { get { - string circleSize = CircleSize.IsDefault ? string.Empty : $"CS {CircleSize.Value:N1}"; - string approachRate = ApproachRate.IsDefault ? string.Empty : $"AR {ApproachRate.Value:N1}"; - string spicyPatterns = HardRockOffsets.IsDefault ? string.Empty : "Spicy patterns"; + if (!CircleSize.IsDefault) + yield return ("Circle size", $"{CircleSize.Value:N1}"); - return string.Join(", ", new[] - { - circleSize, - base.SettingDescription, - approachRate, - spicyPatterns, - }.Where(s => !string.IsNullOrEmpty(s))); + foreach (var setting in base.SettingDescription) + yield return setting; + + if (!ApproachRate.IsDefault) + yield return ("Approach rate", $"{ApproachRate.Value:N1}"); + + if (!HardRockOffsets.IsDefault) + yield return ("Spicy patterns", "On"); } } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs index 10282ff988..77e9aeb123 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.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.Linq; +using System.Collections.Generic; using osu.Framework.Bindables; using osu.Framework.Localisation; using osu.Game.Beatmaps; @@ -36,19 +36,18 @@ namespace osu.Game.Rulesets.Osu.Mods ReadCurrentFromDifficulty = diff => diff.ApproachRate, }; - public override string SettingDescription + public override IEnumerable<(LocalisableString setting, LocalisableString value)> SettingDescription { get { - string circleSize = CircleSize.IsDefault ? string.Empty : $"CS {CircleSize.Value:N1}"; - string approachRate = ApproachRate.IsDefault ? string.Empty : $"AR {ApproachRate.Value:N1}"; + if (!CircleSize.IsDefault) + yield return ("Circle size", $"{CircleSize.Value:N1}"); - return string.Join(", ", new[] - { - circleSize, - base.SettingDescription, - approachRate - }.Where(s => !string.IsNullOrEmpty(s))); + foreach (var setting in base.SettingDescription) + yield return setting; + + if (!ApproachRate.IsDefault) + yield return ("Approach rate", $"{ApproachRate.Value:N1}"); } } diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs index 99a064d35f..000736e9f7 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs @@ -1,7 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Linq; +using System.Collections.Generic; +using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Rulesets.Mods; @@ -19,17 +20,15 @@ namespace osu.Game.Rulesets.Taiko.Mods ReadCurrentFromDifficulty = _ => 1, }; - public override string SettingDescription + public override IEnumerable<(LocalisableString setting, LocalisableString value)> SettingDescription { get { - string scrollSpeed = ScrollSpeed.IsDefault ? string.Empty : $"Scroll x{ScrollSpeed.Value:N2}"; + foreach (var setting in base.SettingDescription) + yield return setting; - return string.Join(", ", new[] - { - base.SettingDescription, - scrollSpeed - }.Where(s => !string.IsNullOrEmpty(s))); + if (!ScrollSpeed.IsDefault) + yield return ("Scroll speed", $"x{ScrollSpeed.Value:N2}"); } } diff --git a/osu.Game/Online/Leaderboards/LeaderboardScoreTooltip.cs b/osu.Game/Online/Leaderboards/LeaderboardScoreTooltip.cs index e79aff9e81..ee497bf3fd 100644 --- a/osu.Game/Online/Leaderboards/LeaderboardScoreTooltip.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardScoreTooltip.cs @@ -1,6 +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.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Containers; @@ -219,15 +220,20 @@ namespace osu.Game.Online.Leaderboards } }; - container.Add(new OsuSpriteText + string description = string.Join(", ", mod.SettingDescription.Select(svp => $"{svp.setting}: {svp.value}")); + + if (!string.IsNullOrEmpty(description)) { - RelativeSizeAxes = Axes.Y, - Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold), - Text = mod.IconTooltip, - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - Margin = new MarginPadding { Top = 1 }, - }); + container.Add(new OsuSpriteText + { + RelativeSizeAxes = Axes.Y, + Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold), + Text = description, + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + Margin = new MarginPadding { Top = 1 }, + }); + } } } } diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index 1b21216235..f23f16fd44 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -14,7 +14,6 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Game.Configuration; using osu.Game.Extensions; -using osu.Game.Rulesets.UI; using osu.Game.Utils; namespace osu.Game.Rulesets.Mods @@ -43,36 +42,16 @@ namespace osu.Game.Rulesets.Mods public abstract LocalisableString Description { get; } /// - /// The tooltip to display for this mod when used in a . - /// - /// - /// Differs from , as the value of attributes (AR, CS, etc) changeable via the mod - /// are displayed in the tooltip. - /// - [JsonIgnore] - public string IconTooltip - { - get - { - string description = SettingDescription; - - return string.IsNullOrEmpty(description) ? Name : $"{Name} ({description})"; - } - } - - /// - /// The description of editable settings of a mod to use in the . + /// The description of editable settings of a mod. /// /// /// Parentheses are added to the tooltip, surrounding the value of this property. If this property is string.Empty, /// the tooltip will not have parentheses. /// - public virtual string SettingDescription + public virtual IEnumerable<(LocalisableString setting, LocalisableString value)> SettingDescription { get { - var tooltipTexts = new List(); - foreach ((SettingSourceAttribute attr, PropertyInfo property) in this.GetOrderedSettingsSourceProperties()) { var bindable = (IBindable)property.GetValue(this)!; @@ -82,7 +61,7 @@ namespace osu.Game.Rulesets.Mods switch (bindable) { case Bindable b: - valueText = b.Value ? "on" : "off"; + valueText = b.Value ? "On" : "Off"; break; default: @@ -91,10 +70,8 @@ namespace osu.Game.Rulesets.Mods } if (!bindable.IsDefault) - tooltipTexts.Add($"{attr.Label}: {valueText}"); + yield return (attr.Label, valueText); } - - return string.Join(", ", tooltipTexts.Where(s => !string.IsNullOrEmpty(s))); } } diff --git a/osu.Game/Rulesets/Mods/ModAccuracyChallenge.cs b/osu.Game/Rulesets/Mods/ModAccuracyChallenge.cs index 9570cddb0a..db16e771d3 100644 --- a/osu.Game/Rulesets/Mods/ModAccuracyChallenge.cs +++ b/osu.Game/Rulesets/Mods/ModAccuracyChallenge.cs @@ -2,9 +2,10 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Globalization; +using System.Collections.Generic; using System.Linq; using osu.Framework.Bindables; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Localisation; using osu.Game.Configuration; using osu.Game.Localisation.HUD; @@ -33,7 +34,20 @@ namespace osu.Game.Rulesets.Mods public override bool Ranked => true; - public override string SettingDescription => base.SettingDescription.Replace(MinimumAccuracy.ToString(), MinimumAccuracy.Value.ToString("##%", NumberFormatInfo.InvariantInfo)); + public override IEnumerable<(LocalisableString setting, LocalisableString value)> SettingDescription + { + get + { + if (!MinimumAccuracy.IsDefault) + yield return ("Minimum accuracy", $"{MinimumAccuracy.Value:##%}"); + + if (!AccuracyJudgeMode.IsDefault) + yield return ("Accuracy mode", AccuracyJudgeMode.Value.ToLocalisableString()); + + if (!Restart.IsDefault) + yield return ("Restart on fail", "On"); + } + } [SettingSource("Minimum accuracy", "Trigger a failure if your accuracy goes below this value.", SettingControlType = typeof(SettingsPercentageSlider))] public BindableNumber MinimumAccuracy { get; } = new BindableDouble diff --git a/osu.Game/Rulesets/Mods/ModBarrelRoll.cs b/osu.Game/Rulesets/Mods/ModBarrelRoll.cs index 67f9da37be..ceaa9aa6e5 100644 --- a/osu.Game/Rulesets/Mods/ModBarrelRoll.cs +++ b/osu.Game/Rulesets/Mods/ModBarrelRoll.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Graphics; @@ -38,7 +39,14 @@ namespace osu.Game.Rulesets.Mods public override LocalisableString Description => "The whole playfield is on a wheel!"; public override double ScoreMultiplier => 1; - public override string SettingDescription => $"{SpinSpeed.Value:N2} rpm {Direction.Value.GetDescription().ToLowerInvariant()}"; + public override IEnumerable<(LocalisableString setting, LocalisableString value)> SettingDescription + { + get + { + yield return ("Roll speed", $"{SpinSpeed.Value:N2} rpm"); + yield return ("Direction", Direction.Value.GetDescription()); + } + } private PlayfieldAdjustmentContainer playfieldAdjustmentContainer = null!; diff --git a/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs b/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs index f4c6be4f77..cdde1b73b6 100644 --- a/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs +++ b/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs @@ -2,7 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Linq; +using System.Collections.Generic; using osu.Framework.Bindables; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; @@ -65,18 +65,15 @@ namespace osu.Game.Rulesets.Mods } } - public override string SettingDescription + public override IEnumerable<(LocalisableString setting, LocalisableString value)> SettingDescription { get { - string drainRate = DrainRate.IsDefault ? string.Empty : $"HP {DrainRate.Value:N1}"; - string overallDifficulty = OverallDifficulty.IsDefault ? string.Empty : $"OD {OverallDifficulty.Value:N1}"; + if (!DrainRate.IsDefault) + yield return ("HP drain", $"{DrainRate.Value:N1}"); - return string.Join(", ", new[] - { - drainRate, - overallDifficulty - }.Where(s => !string.IsNullOrEmpty(s))); + if (!OverallDifficulty.IsDefault) + yield return ("Accuracy", $"{OverallDifficulty.Value:N1}"); } } diff --git a/osu.Game/Rulesets/Mods/ModEasyWithExtraLives.cs b/osu.Game/Rulesets/Mods/ModEasyWithExtraLives.cs index e101ac440e..1a2cb08a53 100644 --- a/osu.Game/Rulesets/Mods/ModEasyWithExtraLives.cs +++ b/osu.Game/Rulesets/Mods/ModEasyWithExtraLives.cs @@ -2,9 +2,11 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using System.Linq; using Humanizer; using osu.Framework.Bindables; +using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Rulesets.Scoring; @@ -20,7 +22,15 @@ namespace osu.Game.Rulesets.Mods MaxValue = 10 }; - public override string SettingDescription => Retries.IsDefault ? string.Empty : $"{"lives".ToQuantity(Retries.Value)}"; + public override IEnumerable<(LocalisableString setting, LocalisableString value)> SettingDescription + { + get + { + if (!Retries.IsDefault) + yield return ("Extra lives", "lives".ToQuantity(Retries.Value)); + } + } + public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModAccuracyChallenge)).ToArray(); private int retries; diff --git a/osu.Game/Rulesets/Mods/ModRateAdjust.cs b/osu.Game/Rulesets/Mods/ModRateAdjust.cs index e5af758b4f..358034541c 100644 --- a/osu.Game/Rulesets/Mods/ModRateAdjust.cs +++ b/osu.Game/Rulesets/Mods/ModRateAdjust.cs @@ -2,8 +2,10 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using osu.Framework.Audio; using osu.Framework.Bindables; +using osu.Framework.Localisation; namespace osu.Game.Rulesets.Mods { @@ -24,8 +26,13 @@ namespace osu.Game.Rulesets.Mods public override Type[] IncompatibleMods => new[] { typeof(ModTimeRamp), typeof(ModAdaptiveSpeed), typeof(ModRateAdjust) }; - public override string SettingDescription => SpeedChange.IsDefault ? string.Empty : $"{SpeedChange.Value:N2}x"; - - public override string ExtendedIconInformation => SettingDescription; + public override IEnumerable<(LocalisableString setting, LocalisableString value)> SettingDescription + { + get + { + if (!SpeedChange.IsDefault) + yield return ("Speed change", $"{SpeedChange.Value:N2}x"); + } + } } } diff --git a/osu.Game/Rulesets/Mods/ModTimeRamp.cs b/osu.Game/Rulesets/Mods/ModTimeRamp.cs index 36e4522771..fd85709b52 100644 --- a/osu.Game/Rulesets/Mods/ModTimeRamp.cs +++ b/osu.Game/Rulesets/Mods/ModTimeRamp.cs @@ -2,9 +2,11 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using System.Linq; using osu.Framework.Audio; using osu.Framework.Bindables; +using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Overlays.Settings; @@ -34,7 +36,13 @@ namespace osu.Game.Rulesets.Mods public override Type[] IncompatibleMods => new[] { typeof(ModRateAdjust), typeof(ModAdaptiveSpeed) }; - public override string SettingDescription => $"{InitialRate.Value:N2}x to {FinalRate.Value:N2}x"; + public override IEnumerable<(LocalisableString setting, LocalisableString value)> SettingDescription + { + get + { + yield return ("Speed change", $"{InitialRate.Value:N2}x to {FinalRate.Value:N2}x"); + } + } private double finalRateTime; private double beginRampTime; From 9b55325526b862273eb1bde535f70a7892c90914 Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Sun, 23 Mar 2025 13:43:21 -0400 Subject: [PATCH 2/5] Introduce rich mod tooltip --- osu.Game/Rulesets/UI/ModIcon.cs | 12 +- osu.Game/Rulesets/UI/ModTooltip.cs | 141 ++++++++++++++++++ .../SelectV2/Footer/ScreenFooterButtonMods.cs | 6 +- .../Leaderboards/LeaderboardScoreV2.cs | 13 +- 4 files changed, 161 insertions(+), 11 deletions(-) create mode 100644 osu.Game/Rulesets/UI/ModTooltip.cs diff --git a/osu.Game/Rulesets/UI/ModIcon.cs b/osu.Game/Rulesets/UI/ModIcon.cs index 6abc7355d5..ee0103a8e5 100644 --- a/osu.Game/Rulesets/UI/ModIcon.cs +++ b/osu.Game/Rulesets/UI/ModIcon.cs @@ -10,11 +10,11 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; -using osu.Framework.Localisation; using osu.Framework.Utils; using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Overlays; using osu.Game.Rulesets.Mods; using osuTK; using osuTK.Graphics; @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.UI /// /// Display the specified mod at a fixed size. /// - public partial class ModIcon : Container, IHasTooltip + public partial class ModIcon : Container, IHasCustomTooltip { public readonly BindableBool Selected = new BindableBool(); @@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.UI public static readonly Vector2 MOD_ICON_SIZE = new Vector2(80); - public virtual LocalisableString TooltipText => showTooltip ? ((mod as Mod)?.IconTooltip ?? mod.Name) : string.Empty; + public Mod? TooltipContent { get; private set; } private IMod mod; @@ -70,6 +70,9 @@ namespace osu.Game.Rulesets.UI [Resolved] private OsuColour colours { get; set; } = null!; + [Resolved] + private OverlayColourProvider? colourProvider { get; set; } + private Color4 backgroundColour; private Sprite extendedBackground = null!; @@ -188,6 +191,7 @@ namespace osu.Game.Rulesets.UI modAcronym.Text = value.Acronym; modIcon.Icon = value.Icon ?? FontAwesome.Solid.Question; + TooltipContent = showTooltip ? value as Mod : null; if (value.Icon == null) { @@ -227,5 +231,7 @@ namespace osu.Game.Rulesets.UI base.Dispose(isDisposing); modSettingsChangeTracker?.Dispose(); } + + public ITooltip GetCustomTooltip() => new ModTooltip(colourProvider); } } diff --git a/osu.Game/Rulesets/UI/ModTooltip.cs b/osu.Game/Rulesets/UI/ModTooltip.cs new file mode 100644 index 0000000000..07bb30e15a --- /dev/null +++ b/osu.Game/Rulesets/UI/ModTooltip.cs @@ -0,0 +1,141 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Effects; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Overlays; +using osu.Game.Rulesets.Mods; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.UI +{ + public partial class ModTooltip : VisibilityContainer, ITooltip + { + private readonly OverlayColourProvider colourProvider; + + private OsuSpriteText nameText = null!; + private TextFlowContainer settingsLabelsFlow = null!; + private TextFlowContainer settingsValuesFlow = null!; + + public ModTooltip(OverlayColourProvider? colourProvider = null) + { + this.colourProvider = colourProvider ?? new OverlayColourProvider(OverlayColourScheme.Aquamarine); + } + + [BackgroundDependencyLoader] + private void load() + { + AutoSizeAxes = Axes.Both; + CornerRadius = 7; + Masking = true; + + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Black.Opacity(0.2f), + Radius = 10f, + }; + + InternalChildren = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colourProvider.Background6, + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Padding = new MarginPadding(10f), + Spacing = new Vector2(20f, 0f), + Children = new Drawable[] + { + new FillFlowContainer + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0f, 5f), + Children = new Drawable[] + { + nameText = new OsuSpriteText + { + Font = OsuFont.Torus.With(size: 16f, weight: FontWeight.SemiBold), + Colour = colourProvider.Content1, + UseFullGlyphHeight = false, + }, + settingsLabelsFlow = new TextFlowContainer(t => + { + t.Font = OsuFont.Torus.With(size: 12f, weight: FontWeight.SemiBold); + }) + { + AutoSizeAxes = Axes.Both, + Colour = colourProvider.Content2, + }, + }, + }, + settingsValuesFlow = new TextFlowContainer(t => + { + t.Font = OsuFont.Torus.With(size: 12f, weight: FontWeight.SemiBold); + }) + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + AutoSizeAxes = Axes.Both, + Colour = colourProvider.Content1, + TextAnchor = Anchor.TopRight, + }, + }, + } + }; + } + + private Mod? displayedContent; + + public void SetContent(Mod content) + { + if (content == displayedContent) + return; + + displayedContent = content; + nameText.Text = content.Name; + settingsLabelsFlow.Clear(); + settingsValuesFlow.Clear(); + + if (content.SettingDescription.Any()) + { + settingsLabelsFlow.Show(); + settingsValuesFlow.Show(); + + foreach (var part in content.SettingDescription) + { + settingsLabelsFlow.AddText(part.setting); + settingsLabelsFlow.NewLine(); + + settingsValuesFlow.AddText(part.value); + settingsValuesFlow.NewLine(); + } + } + else + { + settingsLabelsFlow.Hide(); + settingsValuesFlow.Hide(); + } + } + + protected override void PopIn() => this.FadeIn(300, Easing.OutQuint); + protected override void PopOut() => this.FadeOut(300, Easing.OutQuint); + public void Move(Vector2 pos) => Position = pos; + } +} diff --git a/osu.Game/Screens/SelectV2/Footer/ScreenFooterButtonMods.cs b/osu.Game/Screens/SelectV2/Footer/ScreenFooterButtonMods.cs index 0992203dbc..869aef1470 100644 --- a/osu.Game/Screens/SelectV2/Footer/ScreenFooterButtonMods.cs +++ b/osu.Game/Screens/SelectV2/Footer/ScreenFooterButtonMods.cs @@ -244,18 +244,18 @@ namespace osu.Game.Screens.SelectV2.Footer Mods.BindValueChanged(v => Text = ModSelectOverlayStrings.Mods(v.NewValue.Count).ToUpper(), true); } - public ITooltip> GetCustomTooltip() => new ModTooltip(colourProvider); + public ITooltip> GetCustomTooltip() => new ModOverflowTooltip(colourProvider); public IReadOnlyList? TooltipContent => Mods.Value; - public partial class ModTooltip : VisibilityContainer, ITooltip> + public partial class ModOverflowTooltip : VisibilityContainer, ITooltip> { private ModDisplay extendedModDisplay = null!; [Cached] private OverlayColourProvider colourProvider; - public ModTooltip(OverlayColourProvider colourProvider) + public ModOverflowTooltip(OverlayColourProvider colourProvider) { this.colourProvider = colourProvider; } diff --git a/osu.Game/Screens/SelectV2/Leaderboards/LeaderboardScoreV2.cs b/osu.Game/Screens/SelectV2/Leaderboards/LeaderboardScoreV2.cs index b54f007f38..16599a2080 100644 --- a/osu.Game/Screens/SelectV2/Leaderboards/LeaderboardScoreV2.cs +++ b/osu.Game/Screens/SelectV2/Leaderboards/LeaderboardScoreV2.cs @@ -715,18 +715,21 @@ namespace osu.Game.Screens.SelectV2.Leaderboards public LocalisableString TooltipText { get; } } - private sealed partial class ColouredModSwitchTiny : ModSwitchTiny, IHasTooltip + private sealed partial class ColouredModSwitchTiny : ModSwitchTiny, IHasCustomTooltip { - private readonly IMod mod; + public Mod? TooltipContent { get; } - public ColouredModSwitchTiny(IMod mod) + [Resolved] + private OverlayColourProvider colourProvider { get; set; } = null!; + + public ColouredModSwitchTiny(Mod mod) : base(mod) { - this.mod = mod; + TooltipContent = mod; Active.Value = true; } - public LocalisableString TooltipText => (mod as Mod)?.IconTooltip ?? mod.Name; + public ITooltip GetCustomTooltip() => new ModTooltip(colourProvider); } private sealed partial class MoreModSwitchTiny : CompositeDrawable From 623e705704148d66ed31cc936366c8ff3cdb6b0c Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Sun, 23 Mar 2025 13:43:38 -0400 Subject: [PATCH 3/5] Update mod preset tooltip design accordingly --- osu.Game/Overlays/Mods/EditPresetPopover.cs | 29 ++++++-- osu.Game/Overlays/Mods/ModPresetRow.cs | 75 ++++++++++++++++----- osu.Game/Overlays/Mods/ModPresetTooltip.cs | 16 ++++- 3 files changed, 94 insertions(+), 26 deletions(-) diff --git a/osu.Game/Overlays/Mods/EditPresetPopover.cs b/osu.Game/Overlays/Mods/EditPresetPopover.cs index 526ab6fc63..8014126942 100644 --- a/osu.Game/Overlays/Mods/EditPresetPopover.cs +++ b/osu.Game/Overlays/Mods/EditPresetPopover.cs @@ -8,6 +8,7 @@ using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Game.Database; using osu.Game.Graphics; @@ -75,17 +76,31 @@ namespace osu.Game.Overlays.Mods TabbableContentContainer = this, Current = { Value = preset.PerformRead(p => p.Description) }, }, - new OsuScrollContainer + new Container { RelativeSizeAxes = Axes.X, Height = 100, - Padding = new MarginPadding(7), - Child = scrollContent = new FillFlowContainer + CornerRadius = 10, + Masking = true, + Children = new Drawable[] { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Padding = new MarginPadding(7), - Spacing = new Vector2(7), + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colourProvider.Background5, + }, + new OsuScrollContainer + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding(7), + Child = scrollContent = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding(7), + Spacing = new Vector2(7), + } + }, } }, new FillFlowContainer diff --git a/osu.Game/Overlays/Mods/ModPresetRow.cs b/osu.Game/Overlays/Mods/ModPresetRow.cs index 4829e93b87..4f001eba9b 100644 --- a/osu.Game/Overlays/Mods/ModPresetRow.cs +++ b/osu.Game/Overlays/Mods/ModPresetRow.cs @@ -1,10 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; -using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.UI; @@ -14,12 +15,20 @@ namespace osu.Game.Overlays.Mods { public partial class ModPresetRow : FillFlowContainer { + private readonly Mod mod; + public ModPresetRow(Mod mod) + { + this.mod = mod; + } + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; Direction = FillDirection.Vertical; - Spacing = new Vector2(4); + Spacing = new Vector2(5); InternalChildren = new Drawable[] { new FillFlowContainer @@ -39,26 +48,58 @@ namespace osu.Game.Overlays.Mods }, new OsuSpriteText { - Text = mod.Name, - Font = OsuFont.Default.With(size: 16, weight: FontWeight.SemiBold), - Origin = Anchor.CentreLeft, Anchor = Anchor.CentreLeft, - Margin = new MarginPadding { Bottom = 2 } - } + Origin = Anchor.CentreLeft, + Font = OsuFont.Torus.With(size: 16f, weight: FontWeight.SemiBold), + Colour = colourProvider.Content1, + UseFullGlyphHeight = false, + Text = mod.Name, + }, + } + }, + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Horizontal = 10f }, + Alpha = mod.SettingDescription.Any() ? 1 : 0, + Children = new Drawable[] + { + new TextFlowContainer(t => + { + t.Font = OsuFont.Torus.With(size: 12f, weight: FontWeight.SemiBold); + }) + { + AutoSizeAxes = Axes.Both, + Colour = colourProvider.Content2, + Text = string.Join('\n', mod.SettingDescription.Select(svp => svp.setting)), + }, + new TextFlowContainer(t => + { + t.Font = OsuFont.Torus.With(size: 12f, weight: FontWeight.SemiBold); + }) + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + AutoSizeAxes = Axes.Both, + Colour = colourProvider.Content1, + TextAnchor = Anchor.TopRight, + Text = string.Join('\n', mod.SettingDescription.Select(svp => svp.value)), + }, } } }; - if (!string.IsNullOrEmpty(mod.SettingDescription)) - { - AddInternal(new OsuTextFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Padding = new MarginPadding { Left = 14 }, - Text = mod.SettingDescription - }); - } + // if (!string.IsNullOrEmpty(mod.SettingDescription)) + // { + // AddInternal(new OsuTextFlowContainer + // { + // RelativeSizeAxes = Axes.X, + // AutoSizeAxes = Axes.Y, + // Padding = new MarginPadding { Left = 14 }, + // // Text = mod.SettingDescription + // }); + // } } } } diff --git a/osu.Game/Overlays/Mods/ModPresetTooltip.cs b/osu.Game/Overlays/Mods/ModPresetTooltip.cs index 6ffcfca1e0..6204d75057 100644 --- a/osu.Game/Overlays/Mods/ModPresetTooltip.cs +++ b/osu.Game/Overlays/Mods/ModPresetTooltip.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Linq; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; @@ -14,6 +15,9 @@ namespace osu.Game.Overlays.Mods { public partial class ModPresetTooltip : VisibilityContainer, ITooltip { + [Cached] + private readonly OverlayColourProvider colourProvider; + protected override Container Content { get; } private const double transition_duration = 200; @@ -22,6 +26,8 @@ namespace osu.Game.Overlays.Mods public ModPresetTooltip(OverlayColourProvider colourProvider) { + this.colourProvider = colourProvider; + Width = 250; AutoSizeAxes = Axes.Y; @@ -39,7 +45,7 @@ namespace osu.Game.Overlays.Mods { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Padding = new MarginPadding { Left = 10, Right = 10, Top = 5, Bottom = 5 }, + Padding = new MarginPadding(10f), Spacing = new Vector2(7), Children = new[] { @@ -64,7 +70,13 @@ namespace osu.Game.Overlays.Mods if (ReferenceEquals(preset, lastPreset)) return; - descriptionText.Text = preset.Description; + if (!string.IsNullOrEmpty(preset.Description)) + { + descriptionText.Show(); + descriptionText.Text = preset.Description; + } + else + descriptionText.Hide(); lastPreset = preset; From 54eb0b33b04ef41c4128362c379b1c23f92c95fe Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Sun, 23 Mar 2025 13:46:19 -0400 Subject: [PATCH 4/5] Add extra margin below description text --- osu.Game/Overlays/Mods/ModPresetTooltip.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Overlays/Mods/ModPresetTooltip.cs b/osu.Game/Overlays/Mods/ModPresetTooltip.cs index 6204d75057..4464ba22f1 100644 --- a/osu.Game/Overlays/Mods/ModPresetTooltip.cs +++ b/osu.Game/Overlays/Mods/ModPresetTooltip.cs @@ -57,6 +57,7 @@ namespace osu.Game.Overlays.Mods { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, + Margin = new MarginPadding { Bottom = 5f }, } } } From 1bb85bab89c89a0e5bb2ce3a7cdce0d6e5d24a04 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 25 Mar 2025 16:34:42 +0900 Subject: [PATCH 5/5] Remove pointless left-over code --- osu.Game/Overlays/Mods/ModPresetRow.cs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModPresetRow.cs b/osu.Game/Overlays/Mods/ModPresetRow.cs index 4f001eba9b..408c541bf5 100644 --- a/osu.Game/Overlays/Mods/ModPresetRow.cs +++ b/osu.Game/Overlays/Mods/ModPresetRow.cs @@ -89,17 +89,6 @@ namespace osu.Game.Overlays.Mods } } }; - - // if (!string.IsNullOrEmpty(mod.SettingDescription)) - // { - // AddInternal(new OsuTextFlowContainer - // { - // RelativeSizeAxes = Axes.X, - // AutoSizeAxes = Axes.Y, - // Padding = new MarginPadding { Left = 14 }, - // // Text = mod.SettingDescription - // }); - // } } } }