From 26f15def70a6c2ccf1f66bdac5aad14097524efe Mon Sep 17 00:00:00 2001 From: Nicholas Chin Date: Wed, 11 Dec 2024 23:15:05 +0800 Subject: [PATCH 1/8] Add missing mania tooltip overlay for 4k and 7k --- osu.Game/Localisation/CommonStrings.cs | 12 ++++- .../Profile/Header/Components/MainDetails.cs | 48 ++++++++++++++++++- osu.Game/Users/UserStatistics.cs | 40 ++++++++++++++++ 3 files changed, 97 insertions(+), 3 deletions(-) diff --git a/osu.Game/Localisation/CommonStrings.cs b/osu.Game/Localisation/CommonStrings.cs index 243a100029..88766a608c 100644 --- a/osu.Game/Localisation/CommonStrings.cs +++ b/osu.Game/Localisation/CommonStrings.cs @@ -179,6 +179,16 @@ namespace osu.Game.Localisation /// public static LocalisableString CopyLink => new TranslatableString(getKey(@"copy_link"), @"Copy link"); + /// + /// "4K" + /// + public static LocalisableString FourKey => new TranslatableString(getKey(@"four_key"), @"4K"); + + /// + /// "7K" + /// + public static LocalisableString SevenKey => new TranslatableString(getKey(@"seven_key"), @"7K"); + private static string getKey(string key) => $@"{prefix}:{key}"; } -} \ No newline at end of file +} diff --git a/osu.Game/Overlays/Profile/Header/Components/MainDetails.cs b/osu.Game/Overlays/Profile/Header/Components/MainDetails.cs index 3d97082230..84919d18bb 100644 --- a/osu.Game/Overlays/Profile/Header/Components/MainDetails.cs +++ b/osu.Game/Overlays/Profile/Header/Components/MainDetails.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.LocalisationExtensions; @@ -164,13 +165,56 @@ namespace osu.Game.Overlays.Profile.Header.Components detailGlobalRank.Content = user?.Statistics?.GlobalRank?.ToLocalisableString("\\##,##0") ?? (LocalisableString)"-"; var rankHighest = user?.RankHighest; + var variants = user?.Statistics.Variants; - detailGlobalRank.ContentTooltipText = rankHighest != null - ? UsersStrings.ShowRankHighest(rankHighest.Rank.ToLocalisableString("\\##,##0"), rankHighest.UpdatedAt.ToLocalisableString(@"d MMM yyyy")) + #region Global rank tooltip + var tooltipParts = new List(); + + if (variants?.Count > 0) + { + foreach (var variant in variants) + { + if (variant.GlobalRank != null) + { + tooltipParts.Add($"{variant.VariantDisplay}: {variant.GlobalRank.ToLocalisableString("\\##,##0")}"); + } + } + } + + if (rankHighest != null) + { + tooltipParts.Add(UsersStrings.ShowRankHighest( + rankHighest.Rank.ToLocalisableString("\\##,##0"), + rankHighest.UpdatedAt.ToLocalisableString(@"d MMM yyyy")) + ); + } + + detailGlobalRank.ContentTooltipText = tooltipParts.Any() + ? string.Join("\n", tooltipParts) : string.Empty; + #endregion detailCountryRank.Content = user?.Statistics?.CountryRank?.ToLocalisableString("\\##,##0") ?? (LocalisableString)"-"; + #region Country rank tooltip + var countryTooltipParts = new List(); + + if (variants?.Count > 0) + { + foreach (var variant in variants) + { + if (variant.CountryRank != null) + { + countryTooltipParts.Add($"{variant.VariantDisplay}: {variant.CountryRank.Value.ToLocalisableString("\\##,##0")}"); + } + } + } + + detailCountryRank.ContentTooltipText = countryTooltipParts.Any() + ? string.Join("\n", countryTooltipParts) + : string.Empty; + #endregion + rankGraph.Statistics.Value = user?.Statistics; } diff --git a/osu.Game/Users/UserStatistics.cs b/osu.Game/Users/UserStatistics.cs index 918a1b6968..d18675198f 100644 --- a/osu.Game/Users/UserStatistics.cs +++ b/osu.Game/Users/UserStatistics.cs @@ -4,8 +4,12 @@ #nullable disable using System; +using System.Collections.Generic; +using System.Runtime.Serialization; using Newtonsoft.Json; +using Newtonsoft.Json.Converters; using osu.Framework.Localisation; +using osu.Game.Localisation; using osu.Game.Online.API.Requests.Responses; using osu.Game.Scoring; using osu.Game.Utils; @@ -74,6 +78,9 @@ namespace osu.Game.Users [JsonProperty(@"grade_counts")] public Grades GradesCount; + [JsonProperty(@"variants")] + public List Variants = null!; + public struct Grades { [JsonProperty(@"ssh")] @@ -118,5 +125,38 @@ namespace osu.Game.Users } } } + public enum GameVariant + { + [EnumMember(Value = "4k")] + FourKey, + [EnumMember(Value = "7k")] + SevenKey + } + + public class Variant + { + [JsonProperty("country_rank")] + public int? CountryRank; + + [JsonProperty("global_rank")] + public int? GlobalRank; + + [JsonProperty("mode")] + public string Mode; + + [JsonProperty("pp")] + public decimal PP; + + [JsonProperty("variant")] + [JsonConverter(typeof(StringEnumConverter))] + public GameVariant? VariantType; + + public LocalisableString VariantDisplay => VariantType switch + { + GameVariant.FourKey => CommonStrings.FourKey, + GameVariant.SevenKey => CommonStrings.SevenKey, + _ => string.Empty + }; + } } } From a22f3416d6f7c0a03f5fbfddb0ec4e47cff0e723 Mon Sep 17 00:00:00 2001 From: Nicholas Chin Date: Thu, 12 Dec 2024 22:39:21 +0800 Subject: [PATCH 2/8] Replace switch expression with LocalisableDescription attribute for variant display Use existing localisation strings from BeatmapsStrings instead of CommonStrings for consistent localisation handling --- osu.Game/Localisation/CommonStrings.cs | 12 +----------- osu.Game/Users/UserStatistics.cs | 12 +++++------- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/osu.Game/Localisation/CommonStrings.cs b/osu.Game/Localisation/CommonStrings.cs index 88766a608c..243a100029 100644 --- a/osu.Game/Localisation/CommonStrings.cs +++ b/osu.Game/Localisation/CommonStrings.cs @@ -179,16 +179,6 @@ namespace osu.Game.Localisation /// public static LocalisableString CopyLink => new TranslatableString(getKey(@"copy_link"), @"Copy link"); - /// - /// "4K" - /// - public static LocalisableString FourKey => new TranslatableString(getKey(@"four_key"), @"4K"); - - /// - /// "7K" - /// - public static LocalisableString SevenKey => new TranslatableString(getKey(@"seven_key"), @"7K"); - private static string getKey(string key) => $@"{prefix}:{key}"; } -} +} \ No newline at end of file diff --git a/osu.Game/Users/UserStatistics.cs b/osu.Game/Users/UserStatistics.cs index d18675198f..b485485d48 100644 --- a/osu.Game/Users/UserStatistics.cs +++ b/osu.Game/Users/UserStatistics.cs @@ -8,9 +8,10 @@ using System.Collections.Generic; using System.Runtime.Serialization; using Newtonsoft.Json; using Newtonsoft.Json.Converters; +using osu.Framework.Extensions; using osu.Framework.Localisation; -using osu.Game.Localisation; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Resources.Localisation.Web; using osu.Game.Scoring; using osu.Game.Utils; @@ -128,8 +129,10 @@ namespace osu.Game.Users public enum GameVariant { [EnumMember(Value = "4k")] + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.VariantMania4k))] FourKey, [EnumMember(Value = "7k")] + [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.VariantMania7k))] SevenKey } @@ -151,12 +154,7 @@ namespace osu.Game.Users [JsonConverter(typeof(StringEnumConverter))] public GameVariant? VariantType; - public LocalisableString VariantDisplay => VariantType switch - { - GameVariant.FourKey => CommonStrings.FourKey, - GameVariant.SevenKey => CommonStrings.SevenKey, - _ => string.Empty - }; + public LocalisableString VariantDisplay => VariantType?.GetLocalisableDescription() ?? string.Empty; } } } From c0b6e784a5076dbaf6addbfdae00bdebd35c3f6f Mon Sep 17 00:00:00 2001 From: Nicholas Chin Date: Fri, 13 Dec 2024 21:58:23 +0800 Subject: [PATCH 3/8] Fix text anchor for mania tooltip --- osu.Game/Graphics/Cursor/OsuTooltipContainer.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs b/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs index 0d36cc1d08..4180825a8d 100644 --- a/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs +++ b/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs @@ -80,6 +80,7 @@ namespace osu.Game.Graphics.Cursor Margin = new MarginPadding(5), AutoSizeAxes = Axes.Both, MaximumSize = new Vector2(max_width, float.PositiveInfinity), + TextAnchor = Anchor.TopCentre, } }; } From 153e6c0c22504fb5b1e8b32068f54ddd0832de48 Mon Sep 17 00:00:00 2001 From: Nicholas Chin Date: Sat, 14 Dec 2024 08:29:32 +0800 Subject: [PATCH 4/8] Use Count comparison instead of Any --- osu.Game/Overlays/Profile/Header/Components/MainDetails.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/MainDetails.cs b/osu.Game/Overlays/Profile/Header/Components/MainDetails.cs index 84919d18bb..a3208bb85d 100644 --- a/osu.Game/Overlays/Profile/Header/Components/MainDetails.cs +++ b/osu.Game/Overlays/Profile/Header/Components/MainDetails.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.LocalisationExtensions; @@ -189,7 +188,7 @@ namespace osu.Game.Overlays.Profile.Header.Components ); } - detailGlobalRank.ContentTooltipText = tooltipParts.Any() + detailGlobalRank.ContentTooltipText = tooltipParts.Count > 0 ? string.Join("\n", tooltipParts) : string.Empty; #endregion @@ -210,7 +209,7 @@ namespace osu.Game.Overlays.Profile.Header.Components } } - detailCountryRank.ContentTooltipText = countryTooltipParts.Any() + detailCountryRank.ContentTooltipText = countryTooltipParts.Count > 0 ? string.Join("\n", countryTooltipParts) : string.Empty; #endregion From e2edd9e0d5351a295468f068d8a643ed57dea3ba Mon Sep 17 00:00:00 2001 From: Nicholas Chin Date: Sun, 15 Dec 2024 13:53:33 +0800 Subject: [PATCH 5/8] Fix code quality issues --- osu.Game/Overlays/Profile/Header/Components/MainDetails.cs | 6 +++++- osu.Game/Users/UserStatistics.cs | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/MainDetails.cs b/osu.Game/Overlays/Profile/Header/Components/MainDetails.cs index a3208bb85d..5df755473d 100644 --- a/osu.Game/Overlays/Profile/Header/Components/MainDetails.cs +++ b/osu.Game/Overlays/Profile/Header/Components/MainDetails.cs @@ -164,9 +164,10 @@ namespace osu.Game.Overlays.Profile.Header.Components detailGlobalRank.Content = user?.Statistics?.GlobalRank?.ToLocalisableString("\\##,##0") ?? (LocalisableString)"-"; var rankHighest = user?.RankHighest; - var variants = user?.Statistics.Variants; + var variants = user?.Statistics?.Variants; #region Global rank tooltip + var tooltipParts = new List(); if (variants?.Count > 0) @@ -191,11 +192,13 @@ namespace osu.Game.Overlays.Profile.Header.Components detailGlobalRank.ContentTooltipText = tooltipParts.Count > 0 ? string.Join("\n", tooltipParts) : string.Empty; + #endregion detailCountryRank.Content = user?.Statistics?.CountryRank?.ToLocalisableString("\\##,##0") ?? (LocalisableString)"-"; #region Country rank tooltip + var countryTooltipParts = new List(); if (variants?.Count > 0) @@ -212,6 +215,7 @@ namespace osu.Game.Overlays.Profile.Header.Components detailCountryRank.ContentTooltipText = countryTooltipParts.Count > 0 ? string.Join("\n", countryTooltipParts) : string.Empty; + #endregion rankGraph.Statistics.Value = user?.Statistics; diff --git a/osu.Game/Users/UserStatistics.cs b/osu.Game/Users/UserStatistics.cs index b485485d48..1effacb36b 100644 --- a/osu.Game/Users/UserStatistics.cs +++ b/osu.Game/Users/UserStatistics.cs @@ -126,11 +126,13 @@ namespace osu.Game.Users } } } + public enum GameVariant { [EnumMember(Value = "4k")] [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.VariantMania4k))] FourKey, + [EnumMember(Value = "7k")] [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.VariantMania7k))] SevenKey From 8d1d026f56bc8bfc0f4ef6eee2c7babce9adcae5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 16 Dec 2024 12:46:25 +0900 Subject: [PATCH 6/8] Clean up model - Properly annotate things as nullable - Remove weird passthrough property (more on that later) --- .../Overlays/Profile/Header/Components/MainDetails.cs | 5 +++-- osu.Game/Users/UserStatistics.cs | 11 +++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/MainDetails.cs b/osu.Game/Overlays/Profile/Header/Components/MainDetails.cs index 5df755473d..6d7eaa4265 100644 --- a/osu.Game/Overlays/Profile/Header/Components/MainDetails.cs +++ b/osu.Game/Overlays/Profile/Header/Components/MainDetails.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions; using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -176,7 +177,7 @@ namespace osu.Game.Overlays.Profile.Header.Components { if (variant.GlobalRank != null) { - tooltipParts.Add($"{variant.VariantDisplay}: {variant.GlobalRank.ToLocalisableString("\\##,##0")}"); + tooltipParts.Add($"{variant.VariantType.GetLocalisableDescription()}: {variant.GlobalRank.ToLocalisableString("\\##,##0")}"); } } } @@ -207,7 +208,7 @@ namespace osu.Game.Overlays.Profile.Header.Components { if (variant.CountryRank != null) { - countryTooltipParts.Add($"{variant.VariantDisplay}: {variant.CountryRank.Value.ToLocalisableString("\\##,##0")}"); + countryTooltipParts.Add($"{variant.VariantType.GetLocalisableDescription()}: {variant.CountryRank.Value.ToLocalisableString("\\##,##0")}"); } } } diff --git a/osu.Game/Users/UserStatistics.cs b/osu.Game/Users/UserStatistics.cs index 1effacb36b..687dd52594 100644 --- a/osu.Game/Users/UserStatistics.cs +++ b/osu.Game/Users/UserStatistics.cs @@ -6,9 +6,9 @@ using System; using System.Collections.Generic; using System.Runtime.Serialization; +using JetBrains.Annotations; using Newtonsoft.Json; using Newtonsoft.Json.Converters; -using osu.Framework.Extensions; using osu.Framework.Localisation; using osu.Game.Online.API.Requests.Responses; using osu.Game.Resources.Localisation.Web; @@ -80,7 +80,8 @@ namespace osu.Game.Users public Grades GradesCount; [JsonProperty(@"variants")] - public List Variants = null!; + [CanBeNull] + public List Variants; public struct Grades { @@ -127,7 +128,7 @@ namespace osu.Game.Users } } - public enum GameVariant + public enum RulesetVariant { [EnumMember(Value = "4k")] [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.VariantMania4k))] @@ -154,9 +155,7 @@ namespace osu.Game.Users [JsonProperty("variant")] [JsonConverter(typeof(StringEnumConverter))] - public GameVariant? VariantType; - - public LocalisableString VariantDisplay => VariantType?.GetLocalisableDescription() ?? string.Empty; + public RulesetVariant VariantType; } } } From cfdb959cf69287a8bed61576103313c27f27a331 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 16 Dec 2024 13:14:07 +0900 Subject: [PATCH 7/8] Split actual methods & fix completely broken localisation Localisable strings cannot be plainly interpolated or joined. That is a lossy operation that loses data. --- .../Profile/Header/Components/MainDetails.cs | 61 +++++++++++-------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/MainDetails.cs b/osu.Game/Overlays/Profile/Header/Components/MainDetails.cs index 6d7eaa4265..4bdd5425c0 100644 --- a/osu.Game/Overlays/Profile/Header/Components/MainDetails.cs +++ b/osu.Game/Overlays/Profile/Header/Components/MainDetails.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Leaderboards; using osu.Game.Resources.Localisation.Web; using osu.Game.Scoring; @@ -163,13 +164,20 @@ namespace osu.Game.Overlays.Profile.Header.Components scoreRankInfo.Value.RankCount = user?.Statistics?.GradesCount[scoreRankInfo.Key] ?? 0; detailGlobalRank.Content = user?.Statistics?.GlobalRank?.ToLocalisableString("\\##,##0") ?? (LocalisableString)"-"; + detailGlobalRank.ContentTooltipText = getGlobalRankTooltipText(user); + detailCountryRank.Content = user?.Statistics?.CountryRank?.ToLocalisableString("\\##,##0") ?? (LocalisableString)"-"; + detailCountryRank.ContentTooltipText = getCountryRankTooltipText(user); + + rankGraph.Statistics.Value = user?.Statistics; + } + + private static LocalisableString getGlobalRankTooltipText(APIUser? user) + { var rankHighest = user?.RankHighest; var variants = user?.Statistics?.Variants; - #region Global rank tooltip - - var tooltipParts = new List(); + LocalisableString? result = null; if (variants?.Count > 0) { @@ -177,30 +185,36 @@ namespace osu.Game.Overlays.Profile.Header.Components { if (variant.GlobalRank != null) { - tooltipParts.Add($"{variant.VariantType.GetLocalisableDescription()}: {variant.GlobalRank.ToLocalisableString("\\##,##0")}"); + var variantText = LocalisableString.Interpolate($"{variant.VariantType.GetLocalisableDescription()}: {variant.GlobalRank.ToLocalisableString("\\##,##0")}"); + + if (result == null) + result = variantText; + else + result = LocalisableString.Interpolate($"{result}\n{variantText}"); } } } if (rankHighest != null) { - tooltipParts.Add(UsersStrings.ShowRankHighest( + var rankHighestText = UsersStrings.ShowRankHighest( rankHighest.Rank.ToLocalisableString("\\##,##0"), - rankHighest.UpdatedAt.ToLocalisableString(@"d MMM yyyy")) - ); + rankHighest.UpdatedAt.ToLocalisableString(@"d MMM yyyy")); + + if (result == null) + result = rankHighestText; + else + result = LocalisableString.Interpolate($"{result}\n{rankHighestText}"); } - detailGlobalRank.ContentTooltipText = tooltipParts.Count > 0 - ? string.Join("\n", tooltipParts) - : string.Empty; + return result ?? default; + } - #endregion + private static LocalisableString getCountryRankTooltipText(APIUser? user) + { + var variants = user?.Statistics?.Variants; - detailCountryRank.Content = user?.Statistics?.CountryRank?.ToLocalisableString("\\##,##0") ?? (LocalisableString)"-"; - - #region Country rank tooltip - - var countryTooltipParts = new List(); + LocalisableString? result = null; if (variants?.Count > 0) { @@ -208,18 +222,17 @@ namespace osu.Game.Overlays.Profile.Header.Components { if (variant.CountryRank != null) { - countryTooltipParts.Add($"{variant.VariantType.GetLocalisableDescription()}: {variant.CountryRank.Value.ToLocalisableString("\\##,##0")}"); + var variantText = LocalisableString.Interpolate($"{variant.VariantType.GetLocalisableDescription()}: {variant.CountryRank.ToLocalisableString("\\##,##0")}"); + + if (result == null) + result = variantText; + else + result = LocalisableString.Interpolate($"{result}\n{variantText}"); } } } - detailCountryRank.ContentTooltipText = countryTooltipParts.Count > 0 - ? string.Join("\n", countryTooltipParts) - : string.Empty; - - #endregion - - rankGraph.Statistics.Value = user?.Statistics; + return result ?? default; } private partial class ScoreRankInfo : CompositeDrawable From ecb7a809f2242ad4f71b1a22f0a1d8cb453fb67a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 16 Dec 2024 13:18:45 +0900 Subject: [PATCH 8/8] Revert "Fix text anchor for mania tooltip" This reverts commit c0b6e784a5076dbaf6addbfdae00bdebd35c3f6f. The change affects editor and other stuff and I'm not sure it's correct. It's not like client needs to match the appearance really. It already doesn't in many places. --- osu.Game/Graphics/Cursor/OsuTooltipContainer.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs b/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs index 4180825a8d..0d36cc1d08 100644 --- a/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs +++ b/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs @@ -80,7 +80,6 @@ namespace osu.Game.Graphics.Cursor Margin = new MarginPadding(5), AutoSizeAxes = Axes.Both, MaximumSize = new Vector2(max_width, float.PositiveInfinity), - TextAnchor = Anchor.TopCentre, } }; }