diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboardScore.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboardScore.cs index 856a4be67dcc..95c4de842171 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboardScore.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboardScore.cs @@ -14,6 +14,7 @@ using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Online.Leaderboards; using osu.Game.Overlays; using osu.Game.Rulesets.Mania; using osu.Game.Rulesets.Mods; @@ -67,16 +68,16 @@ public void TestSheared() foreach (var scoreInfo in getTestScores()) { - BeatmapLeaderboardScore.HighlightType? highlightType = null; + LeaderboardRankDisplay.HighlightType? highlightType = null; switch (scoreInfo.User.Id) { case 2: - highlightType = BeatmapLeaderboardScore.HighlightType.Own; + highlightType = LeaderboardRankDisplay.HighlightType.Own; break; case 1541390: - highlightType = BeatmapLeaderboardScore.HighlightType.Friend; + highlightType = LeaderboardRankDisplay.HighlightType.Friend; break; } @@ -118,16 +119,16 @@ public void TestNonSheared() foreach (var scoreInfo in getTestScores()) { - BeatmapLeaderboardScore.HighlightType? highlightType = null; + LeaderboardRankDisplay.HighlightType? highlightType = null; switch (scoreInfo.User.Id) { case 2: - highlightType = BeatmapLeaderboardScore.HighlightType.Own; + highlightType = LeaderboardRankDisplay.HighlightType.Own; break; case 1541390: - highlightType = BeatmapLeaderboardScore.HighlightType.Friend; + highlightType = LeaderboardRankDisplay.HighlightType.Friend; break; } diff --git a/osu.Game/Online/Leaderboards/LeaderboardRankDisplay.cs b/osu.Game/Online/Leaderboards/LeaderboardRankDisplay.cs new file mode 100644 index 000000000000..4d83589edece --- /dev/null +++ b/osu.Game/Online/Leaderboards/LeaderboardRankDisplay.cs @@ -0,0 +1,98 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osuTK.Graphics; + +namespace osu.Game.Online.Leaderboards +{ + public partial class LeaderboardRankDisplay : Container + { + public const int WIDTH = 40; + + public readonly int? Rank; + public readonly HighlightType? Highlight; + + [Resolved] + private OsuColour colours { get; set; } = null!; + + private static readonly Color4 personal_best_gradient_left = Color4Extensions.FromHex("#66FFCC"); + private static readonly Color4 personal_best_gradient_right = Color4Extensions.FromHex("#51A388"); + + private Container highlightGradient = null!; + + private readonly bool sheared; + + public LeaderboardRankDisplay(int? rank, bool sheared = false, HighlightType? highlight = null) + { + Rank = rank; + Highlight = highlight; + + this.sheared = sheared; + } + + [BackgroundDependencyLoader] + private void load() + { + Width = WIDTH; + RelativeSizeAxes = Axes.Y; + Children = new Drawable[] + { + highlightGradient = new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Right = -10f }, + Alpha = Highlight != null ? 1 : 0, + Colour = getHighlightColour(Highlight), + Child = new Box { RelativeSizeAxes = Axes.Both }, + }, + new LeaderboardRankLabel(Rank, sheared, darkText: Highlight == HighlightType.Own) + { + RelativeSizeAxes = Axes.Both, + }, + }; + } + + private ColourInfo getHighlightColour(HighlightType? highlightType, float lightenAmount = 0) + { + switch (highlightType) + { + case HighlightType.Own: + return ColourInfo.GradientHorizontal(personal_best_gradient_left.Lighten(lightenAmount), personal_best_gradient_right.Lighten(lightenAmount)); + + case HighlightType.Friend: + return ColourInfo.GradientHorizontal(colours.Pink1.Lighten(lightenAmount), colours.Pink3.Lighten(lightenAmount)); + + default: + return Colour4.White; + } + } + + public void UpdateHighlightState(bool isHovered, double duration) + { + highlightGradient.FadeColour(getHighlightColour(Highlight, isHovered ? 0.2f : 0), duration, Easing.OutQuint); + } + + public void Appear(double duration) + { + this.FadeIn(duration, Easing.OutQuint).ResizeWidthTo(WIDTH, duration, Easing.OutQuint); + } + + public void Disappear(double duration) + { + this.FadeOut(duration, Easing.OutQuint).ResizeWidthTo(0, duration, Easing.OutQuint); + } + + public enum HighlightType + { + Own, + Friend, + } + } +} diff --git a/osu.Game/Online/Leaderboards/LeaderboardRankLabel.cs b/osu.Game/Online/Leaderboards/LeaderboardRankLabel.cs new file mode 100644 index 000000000000..67c6081fdcdb --- /dev/null +++ b/osu.Game/Online/Leaderboards/LeaderboardRankLabel.cs @@ -0,0 +1,47 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Localisation; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Overlays; +using osu.Game.Utils; +using osuTK; + +namespace osu.Game.Online.Leaderboards +{ + public partial class LeaderboardRankLabel : Container, IHasTooltip + { + private readonly bool darkText; + private readonly OsuSpriteText text; + + public LeaderboardRankLabel(int? rank, bool sheared, bool darkText) + { + this.darkText = darkText; + if (rank >= 1000) + TooltipText = $"#{rank:N0}"; + + Child = text = new OsuSpriteText + { + Shear = sheared ? -OsuGame.SHEAR : Vector2.Zero, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Font = OsuFont.Style.Heading2, + Text = rank?.FormatRank().Insert(0, "#") ?? "-", + Shadow = !darkText, + }; + } + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + text.Colour = darkText ? colourProvider.Background3 : colourProvider.Content1; + } + + public LocalisableString TooltipText { get; } + } +} diff --git a/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeLeaderboard.cs b/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeLeaderboard.cs index 62c5c0c8dfb5..400a4b440a88 100644 --- a/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeLeaderboard.cs +++ b/osu.Game/Screens/OnlinePlay/DailyChallenge/DailyChallengeLeaderboard.cs @@ -13,6 +13,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; +using osu.Game.Online.Leaderboards; using osu.Game.Online.Rooms; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; @@ -160,12 +161,12 @@ public void RefetchScores() { LoadComponentsAsync(best.Select((s, index) => { - BeatmapLeaderboardScore.HighlightType? highlightType = null; + LeaderboardRankDisplay.HighlightType? highlightType = null; if (s.UserID == api.LocalUser.Value.Id) - highlightType = BeatmapLeaderboardScore.HighlightType.Own; + highlightType = LeaderboardRankDisplay.HighlightType.Own; else if (api.LocalUserState.Friends.Any(r => r.TargetID == s.UserID)) - highlightType = BeatmapLeaderboardScore.HighlightType.Friend; + highlightType = LeaderboardRankDisplay.HighlightType.Friend; return new BeatmapLeaderboardScore(s, sheared: false) { @@ -191,7 +192,7 @@ public void RefetchScores() userBestContainer.Add(new BeatmapLeaderboardScore(userBest, sheared: false) { Rank = userBest.Position, - Highlight = BeatmapLeaderboardScore.HighlightType.Own, + Highlight = LeaderboardRankDisplay.HighlightType.Own, Action = () => PresentScore?.Invoke(userBest.OnlineID), SelectedMods = { BindTarget = SelectedMods }, IsValidMod = IsValidMod, diff --git a/osu.Game/Screens/Select/BeatmapLeaderboardScore.cs b/osu.Game/Screens/Select/BeatmapLeaderboardScore.cs index bb96c97d520a..a6889445617e 100644 --- a/osu.Game/Screens/Select/BeatmapLeaderboardScore.cs +++ b/osu.Game/Screens/Select/BeatmapLeaderboardScore.cs @@ -34,7 +34,6 @@ using osu.Game.Scoring; using osu.Game.Users; using osu.Game.Users.Drawables; -using osu.Game.Utils; using osuTK; using osuTK.Graphics; using CommonStrings = osu.Game.Localisation.CommonStrings; @@ -57,15 +56,12 @@ public sealed partial class BeatmapLeaderboardScore : OsuClickableContainer, IHa public Func IsValidMod { get; set; } = _ => true; public int? Rank { get; init; } - public HighlightType? Highlight { get; init; } + public LeaderboardRankDisplay.HighlightType? Highlight { get; init; } public Action? ShowReplay { get; init; } [Resolved] private OverlayColourProvider colourProvider { get; set; } = null!; - [Resolved] - private OsuColour colours { get; set; } = null!; - [Resolved] private IDialogOverlay? dialogOverlay { get; set; } @@ -86,14 +82,10 @@ public sealed partial class BeatmapLeaderboardScore : OsuClickableContainer, IHa private const float username_min_width = 120; private const float statistics_regular_min_width = 165; private const float statistics_compact_min_width = 90; - private const float rank_label_width = 40; private const int corner_radius = 10; private const int transition_duration = 200; - private static readonly Color4 personal_best_gradient_left = Color4Extensions.FromHex("#66FFCC"); - private static readonly Color4 personal_best_gradient_right = Color4Extensions.FromHex("#51A388"); - private Colour4 foregroundColour; private Colour4 backgroundColour; private ColourInfo totalScoreBackgroundGradient; @@ -113,9 +105,8 @@ public sealed partial class BeatmapLeaderboardScore : OsuClickableContainer, IHa private Box totalScoreBackground = null!; private FillFlowContainer statisticsContainer = null!; - private Container highlightGradient = null!; - private Container rankLabelStandalone = null!; - private Container rankLabelOverlay = null!; + private LeaderboardRankDisplay rankDisplay = null!; + private Container rankOverlay = null!; private readonly bool sheared; @@ -159,26 +150,7 @@ private void load() RelativeSizeAxes = Axes.Both, Colour = backgroundColour }, - rankLabelStandalone = new Container - { - Width = rank_label_width, - RelativeSizeAxes = Axes.Y, - Children = new Drawable[] - { - highlightGradient = new Container - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Right = -10f }, - Alpha = Highlight != null ? 1 : 0, - Colour = getHighlightColour(Highlight), - Child = new Box { RelativeSizeAxes = Axes.Both }, - }, - new RankLabel(Rank, sheared, darkText: Highlight == HighlightType.Own) - { - RelativeSizeAxes = Axes.Both, - } - }, - }, + rankDisplay = new LeaderboardRankDisplay(Rank, sheared, Highlight), centreContent = new Container { Name = @"Centre container", @@ -237,7 +209,7 @@ private void load() RelativeSizeAxes = Axes.None, Size = new Vector2(HEIGHT) }, - rankLabelOverlay = new Container + rankOverlay = new Container { RelativeSizeAxes = Axes.Both, Alpha = 0, @@ -248,7 +220,7 @@ private void load() RelativeSizeAxes = Axes.Both, Colour = Colour4.Black.Opacity(0.5f), }, - new RankLabel(Rank, sheared, false) + new LeaderboardRankLabel(Rank, sheared, false) { AutoSizeAxes = Axes.Both, Anchor = Anchor.Centre, @@ -465,21 +437,6 @@ private void load() innerAvatar.OnLoadComplete += d => d.FadeInFromZero(200); } - private ColourInfo getHighlightColour(HighlightType? highlightType, float lightenAmount = 0) - { - switch (highlightType) - { - case HighlightType.Own: - return ColourInfo.GradientHorizontal(personal_best_gradient_left.Lighten(lightenAmount), personal_best_gradient_right.Lighten(lightenAmount)); - - case HighlightType.Friend: - return ColourInfo.GradientHorizontal(colours.Pink1.Lighten(lightenAmount), colours.Pink3.Lighten(lightenAmount)); - - default: - return Colour4.White; - } - } - protected override void LoadComplete() { base.LoadComplete(); @@ -541,12 +498,12 @@ private void updateState() foreground.FadeColour(IsHovered ? foregroundColour.Lighten(0.2f) : foregroundColour, transition_duration, Easing.OutQuint); background.FadeColour(IsHovered ? backgroundColour.Lighten(0.2f) : backgroundColour, transition_duration, Easing.OutQuint); totalScoreBackground.FadeColour(IsHovered ? lightenedGradient : totalScoreBackgroundGradient, transition_duration, Easing.OutQuint); - highlightGradient.FadeColour(getHighlightColour(Highlight, IsHovered ? 0.2f : 0), transition_duration, Easing.OutQuint); + rankDisplay.UpdateHighlightState(IsHovered, transition_duration); if (IsHovered && currentMode != DisplayMode.Full) - rankLabelOverlay.FadeIn(transition_duration, Easing.OutQuint); + rankOverlay.FadeIn(transition_duration, Easing.OutQuint); else - rankLabelOverlay.FadeOut(transition_duration, Easing.OutQuint); + rankOverlay.FadeOut(transition_duration, Easing.OutQuint); } private DisplayMode? currentMode; @@ -562,7 +519,7 @@ protected override void Update() centreContent.Padding = new MarginPadding { - Left = rankLabelStandalone.DrawWidth, + Left = rankDisplay.DrawWidth, Right = rightContent.DrawWidth, }; } @@ -570,10 +527,11 @@ protected override void Update() private void updateDisplayMode(DisplayMode mode) { double duration = currentMode == null ? 0 : transition_duration; + if (mode >= DisplayMode.Full) - rankLabelStandalone.FadeIn(duration, Easing.OutQuint).ResizeWidthTo(rank_label_width, duration, Easing.OutQuint); + rankDisplay.Appear(duration); else - rankLabelStandalone.FadeOut(duration, Easing.OutQuint).ResizeWidthTo(0, duration, Easing.OutQuint); + rankDisplay.Disappear(duration); if (mode >= DisplayMode.Regular) { @@ -595,7 +553,7 @@ private void updateDisplayMode(DisplayMode mode) private DisplayMode getCurrentDisplayMode() { - if (DrawWidth >= username_min_width + statistics_regular_min_width + expanded_right_content_width + rank_label_width) + if (DrawWidth >= username_min_width + statistics_regular_min_width + expanded_right_content_width + LeaderboardRankDisplay.WIDTH) return DisplayMode.Full; if (DrawWidth >= username_min_width + statistics_regular_min_width + expanded_right_content_width) @@ -707,42 +665,5 @@ private void load(OsuColour colours, OverlayColourProvider colourProvider) }; } } - - private partial class RankLabel : Container, IHasTooltip - { - private readonly bool darkText; - private readonly OsuSpriteText text; - - public RankLabel(int? rank, bool sheared, bool darkText) - { - this.darkText = darkText; - if (rank >= 1000) - TooltipText = $"#{rank:N0}"; - - Child = text = new OsuSpriteText - { - Shear = sheared ? -OsuGame.SHEAR : Vector2.Zero, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Font = OsuFont.Style.Heading2, - Text = rank?.FormatRank().Insert(0, "#") ?? "-", - Shadow = !darkText, - }; - } - - [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider) - { - text.Colour = darkText ? colourProvider.Background3 : colourProvider.Content1; - } - - public LocalisableString TooltipText { get; } - } - - public enum HighlightType - { - Own, - Friend, - } } } diff --git a/osu.Game/Screens/Select/BeatmapLeaderboardWedge.cs b/osu.Game/Screens/Select/BeatmapLeaderboardWedge.cs index 4373b7b0736c..cc1bc772668b 100644 --- a/osu.Game/Screens/Select/BeatmapLeaderboardWedge.cs +++ b/osu.Game/Screens/Select/BeatmapLeaderboardWedge.cs @@ -298,12 +298,12 @@ protected void SetScores(IEnumerable scores, ScoreInfo? userScore = n LoadComponentsAsync(scores.Select((s, i) => { - BeatmapLeaderboardScore.HighlightType? highlightType = null; + LeaderboardRankDisplay.HighlightType? highlightType = null; if (s.OnlineID == userScore?.OnlineID) - highlightType = BeatmapLeaderboardScore.HighlightType.Own; + highlightType = LeaderboardRankDisplay.HighlightType.Own; else if (api.LocalUserState.Friends.Any(r => r.TargetID == s.UserID) && Scope.Value != BeatmapLeaderboardScope.Friend) - highlightType = BeatmapLeaderboardScore.HighlightType.Friend; + highlightType = LeaderboardRankDisplay.HighlightType.Friend; return new BeatmapLeaderboardScore(s) { @@ -366,7 +366,7 @@ protected void SetScores(IEnumerable scores, ScoreInfo? userScore = n personalBestDisplay.FadeIn(600, Easing.OutQuint); personalBestScoreContainer.Child = new BeatmapLeaderboardScore(userScore) { - Highlight = BeatmapLeaderboardScore.HighlightType.Own, + Highlight = LeaderboardRankDisplay.HighlightType.Own, Rank = userScore.Position, SelectedMods = { BindTarget = mods }, Action = () => onLeaderboardScoreClicked(userScore),