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 243a10002980..88766a608c4e 100644 --- a/osu.Game/Localisation/CommonStrings.cs +++ b/osu.Game/Localisation/CommonStrings.cs @@ -179,6 +179,16 @@ public static class CommonStrings /// 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 3d97082230f5..84919d18bbdd 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 @@ private void updateDisplay(UserProfileData? data) 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 918a1b696848..d18675198f75 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 @@ public struct LevelInfo [JsonProperty(@"grade_counts")] public Grades GradesCount; + [JsonProperty(@"variants")] + public List Variants = null!; + public struct Grades { [JsonProperty(@"ssh")] @@ -118,5 +125,38 @@ public int this[ScoreRank rank] } } } + 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 88766a608c4e..243a10002980 100644 --- a/osu.Game/Localisation/CommonStrings.cs +++ b/osu.Game/Localisation/CommonStrings.cs @@ -179,16 +179,6 @@ public static class CommonStrings /// 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 d18675198f75..b485485d486d 100644 --- a/osu.Game/Users/UserStatistics.cs +++ b/osu.Game/Users/UserStatistics.cs @@ -8,9 +8,10 @@ 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 @@ public int this[ScoreRank rank] 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 @@ public class Variant [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 0d36cc1d0818..4180825a8d21 100644 --- a/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs +++ b/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs @@ -80,6 +80,7 @@ public OsuTooltip() 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 84919d18bbdd..a3208bb85d31 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 @@ private void updateDisplay(UserProfileData? data) ); } - detailGlobalRank.ContentTooltipText = tooltipParts.Any() + detailGlobalRank.ContentTooltipText = tooltipParts.Count > 0 ? string.Join("\n", tooltipParts) : string.Empty; #endregion @@ -210,7 +209,7 @@ private void updateDisplay(UserProfileData? data) } } - 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 a3208bb85d31..5df755473d21 100644 --- a/osu.Game/Overlays/Profile/Header/Components/MainDetails.cs +++ b/osu.Game/Overlays/Profile/Header/Components/MainDetails.cs @@ -164,9 +164,10 @@ private void updateDisplay(UserProfileData? data) 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 @@ private void updateDisplay(UserProfileData? data) 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 @@ private void updateDisplay(UserProfileData? data) 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 b485485d486d..1effacb36b42 100644 --- a/osu.Game/Users/UserStatistics.cs +++ b/osu.Game/Users/UserStatistics.cs @@ -126,11 +126,13 @@ public int this[ScoreRank rank] } } } + 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 5df755473d21..6d7eaa4265fc 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 @@ private void updateDisplay(UserProfileData? data) { 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 @@ private void updateDisplay(UserProfileData? data) { 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 1effacb36b42..687dd52594d9 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 @@ public struct LevelInfo public Grades GradesCount; [JsonProperty(@"variants")] - public List Variants = null!; + [CanBeNull] + public List Variants; public struct Grades { @@ -127,7 +128,7 @@ public int this[ScoreRank rank] } } - public enum GameVariant + public enum RulesetVariant { [EnumMember(Value = "4k")] [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.VariantMania4k))] @@ -154,9 +155,7 @@ public class Variant [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 6d7eaa4265fc..4bdd5425c034 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.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 @@ private void updateDisplay(UserProfileData? data) 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 @@ private void updateDisplay(UserProfileData? data) { 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")) - ); - } - - detailGlobalRank.ContentTooltipText = tooltipParts.Count > 0 - ? string.Join("\n", tooltipParts) - : string.Empty; + rankHighest.UpdatedAt.ToLocalisableString(@"d MMM yyyy")); - #endregion + if (result == null) + result = rankHighestText; + else + result = LocalisableString.Interpolate($"{result}\n{rankHighestText}"); + } - detailCountryRank.Content = user?.Statistics?.CountryRank?.ToLocalisableString("\\##,##0") ?? (LocalisableString)"-"; + return result ?? default; + } - #region Country rank tooltip + private static LocalisableString getCountryRankTooltipText(APIUser? user) + { + var variants = user?.Statistics?.Variants; - var countryTooltipParts = new List(); + LocalisableString? result = null; if (variants?.Count > 0) { @@ -208,18 +222,17 @@ private void updateDisplay(UserProfileData? data) { 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 4180825a8d21..0d36cc1d0818 100644 --- a/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs +++ b/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs @@ -80,7 +80,6 @@ public OsuTooltip() Margin = new MarginPadding(5), AutoSizeAxes = Axes.Both, MaximumSize = new Vector2(max_width, float.PositiveInfinity), - TextAnchor = Anchor.TopCentre, } }; }