diff --git a/osu.Game.Tests/Visual/SongSelect/TestCaseBeatmapScoresContainer.cs b/osu.Game.Tests/Visual/SongSelect/TestCaseBeatmapScoresContainer.cs index b30d6b1546..0457e1469a 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestCaseBeatmapScoresContainer.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestCaseBeatmapScoresContainer.cs @@ -25,6 +25,8 @@ public class TestCaseBeatmapScoresContainer : OsuTestCase public override IReadOnlyList RequiredTypes => new[] { typeof(DrawableTopScore), + typeof(TopScoreUserSection), + typeof(TopScoreStatisticsSection), typeof(ScoresContainer), typeof(ScoreTable), typeof(ScoreTableRow), diff --git a/osu.Game/Overlays/BeatmapSet/Scores/DrawableTopScore.cs b/osu.Game/Overlays/BeatmapSet/Scores/DrawableTopScore.cs index f22c6b3529..3667fc3f9f 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/DrawableTopScore.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/DrawableTopScore.cs @@ -1,58 +1,29 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using Humanizer; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; using osu.Game.Graphics; -using osu.Game.Online.Leaderboards; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.UI; using osu.Game.Scoring; -using osu.Game.Users; using osuTK; using osuTK.Graphics; -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Extensions; -using osu.Framework.Localisation; -using osu.Game.Graphics.Sprites; namespace osu.Game.Overlays.BeatmapSet.Scores { public class DrawableTopScore : Container { private const float fade_duration = 100; - private const float avatar_size = 80; - private const float margin = 10; private Color4 backgroundIdleColour; private Color4 backgroundHoveredColour; - private readonly FontUsage smallStatisticsFont = OsuFont.GetFont(size: 20); - private readonly FontUsage largeStatisticsFont = OsuFont.GetFont(size: 25); - private readonly Box background; - private readonly UpdateableAvatar avatar; - private readonly DrawableFlag flag; - private readonly ClickableTopScoreUsername username; - private readonly SpriteText rankText; - private readonly SpriteText date; - private readonly DrawableRank rank; - - private readonly FillFlowContainer statisticsContainer; - - private readonly TextColumn totalScoreColumn; - private readonly TextColumn accuracyColumn; - private readonly TextColumn maxComboColumn; - private readonly TextColumn ppColumn; - - private readonly ModsInfoColumn modsInfo; + private readonly TopScoreUserSection userSection; + private readonly TopScoreStatisticsSection statisticsSection; public DrawableTopScore() { @@ -79,128 +50,30 @@ public DrawableTopScore() { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Padding = new MarginPadding(margin), + Padding = new MarginPadding(10), Children = new Drawable[] { - new FillFlowContainer + new AutoSizingGrid { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(margin, 0), - Children = new Drawable[] - { - rankText = new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Text = "#1", - Font = OsuFont.GetFont(size: 30, weight: FontWeight.Bold, italics: true) - }, - rank = new DrawableRank(ScoreRank.F) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(40), - FillMode = FillMode.Fit, - }, - avatar = new UpdateableAvatar - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(avatar_size), - Masking = true, - CornerRadius = 5, - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Colour = Color4.Black.Opacity(0.25f), - Offset = new Vector2(0, 2), - Radius = 1, - }, - }, - new FillFlowContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 3), - Children = new Drawable[] - { - username = new ClickableTopScoreUsername - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - }, - date = new SpriteText - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Font = OsuFont.GetFont(size: 15, weight: FontWeight.Bold) - }, - flag = new DrawableFlag - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Size = new Vector2(20, 13), - }, - } - } - } - }, - new Container - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Width = 0.65f, - Child = new FillFlowContainer + Content = new[] { - AutoSizeAxes = Axes.Y, - RelativeSizeAxes = Axes.X, - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Spacing = new Vector2(margin), - Children = new Drawable[] + new Drawable[] { - new FillFlowContainer + userSection = new TopScoreUserSection { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(margin, 0), - Children = new Drawable[] - { - statisticsContainer = new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(margin, 0), - }, - ppColumn = new TextColumn("pp", smallStatisticsFont), - modsInfo = new ModsInfoColumn(), - } + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, }, - new FillFlowContainer + statisticsSection = new TopScoreStatisticsSection { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(margin, 0), - Children = new Drawable[] - { - totalScoreColumn = new TextColumn("total score", largeStatisticsFont), - accuracyColumn = new TextColumn("accuracy", largeStatisticsFont), - maxComboColumn = new TextColumn("max combo", largeStatisticsFont) - } - }, - } - } + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + } + }, + }, + ColumnDimensions = new[] { new Dimension(GridSizeMode.AutoSize) }, + RowDimensions = new[] { new Dimension(GridSizeMode.AutoSize) }, } } } @@ -212,7 +85,6 @@ private void load(OsuColour colours) { backgroundIdleColour = colours.Gray3; backgroundHoveredColour = colours.Gray4; - rankText.Colour = colours.Yellow; background.Colour = backgroundIdleColour; } @@ -224,19 +96,8 @@ public ScoreInfo Score { set { - avatar.User = username.User = value.User; - flag.Country = value.User.Country; - date.Text = $@"achieved {value.Date.Humanize()}"; - rank.UpdateRank(value.Rank); - - totalScoreColumn.Text = $@"{value.TotalScore:N0}"; - accuracyColumn.Text = $@"{value.Accuracy:P2}"; - maxComboColumn.Text = $@"{value.MaxCombo:N0}x"; - ppColumn.Text = $@"{value.PP:N0}"; - - statisticsContainer.ChildrenEnumerable = value.Statistics.Select(kvp => new TextColumn(kvp.Key.GetDescription(), smallStatisticsFont) { Text = kvp.Value.ToString() }); - - modsInfo.Mods = value.Mods; + userSection.Score = value; + statisticsSection.Score = value; } } @@ -252,157 +113,11 @@ protected override void OnHoverLost(HoverLostEvent e) base.OnHoverLost(e); } - private class ClickableTopScoreUsername : ClickableUserContainer + private class AutoSizingGrid : GridContainer { - private const float username_fade_duration = 150; - - private readonly FillFlowContainer hoverContainer; - - private readonly SpriteText normalText; - private readonly SpriteText hoveredText; - - public ClickableTopScoreUsername() + public AutoSizingGrid() { - var font = OsuFont.GetFont(size: 20, weight: FontWeight.Bold, italics: true); - - Children = new Drawable[] - { - normalText = new OsuSpriteText { Font = font }, - hoverContainer = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Alpha = 0, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 1), - Children = new Drawable[] - { - hoveredText = new OsuSpriteText { Font = font }, - new Box - { - BypassAutoSizeAxes = Axes.Both, - RelativeSizeAxes = Axes.X, - Height = 1 - } - } - } - }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - hoverContainer.Colour = colours.Blue; - } - - protected override void OnUserChanged(User user) => normalText.Text = hoveredText.Text = user.Username; - - protected override bool OnHover(HoverEvent e) - { - hoverContainer.FadeIn(username_fade_duration, Easing.OutQuint); - return base.OnHover(e); - } - - protected override void OnHoverLost(HoverLostEvent e) - { - hoverContainer.FadeOut(username_fade_duration, Easing.OutQuint); - base.OnHoverLost(e); - } - } - - private class InfoColumn : CompositeDrawable - { - private readonly Box separator; - - public InfoColumn(string title, Drawable content) - { - AutoSizeAxes = Axes.Both; - - InternalChild = new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 2), - Children = new[] - { - new OsuSpriteText - { - Font = OsuFont.GetFont(size: 12, weight: FontWeight.Black), - Text = title.ToUpper() - }, - separator = new Box - { - RelativeSizeAxes = Axes.X, - Height = 2 - }, - content - } - }; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - separator.Colour = colours.Gray5; - } - } - - private class TextColumn : InfoColumn - { - private readonly SpriteText text; - - public TextColumn(string title, FontUsage font) - : this(title, new OsuSpriteText { Font = font }) - { - } - - private TextColumn(string title, SpriteText text) - : base(title, text) - { - this.text = text; - } - - public LocalisedString Text - { - get => text.Text; - set => text.Text = value; - } - } - - private class ModsInfoColumn : InfoColumn - { - private readonly FillFlowContainer modsContainer; - - public ModsInfoColumn() - : this(new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal - }) - { - } - - private ModsInfoColumn(FillFlowContainer modsContainer) - : base("mods", modsContainer) - { - this.modsContainer = modsContainer; - } - - public IEnumerable Mods - { - set - { - modsContainer.Clear(); - - foreach (Mod mod in value) - { - modsContainer.Add(new ModIcon(mod) - { - AutoSizeAxes = Axes.Both, - Scale = new Vector2(0.3f), - }); - } - } + AutoSizeAxes = Axes.Y; } } } diff --git a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs new file mode 100644 index 0000000000..6761d0f710 --- /dev/null +++ b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs @@ -0,0 +1,204 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// 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.Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Localisation; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.UI; +using osu.Game.Scoring; +using osuTK; + +namespace osu.Game.Overlays.BeatmapSet.Scores +{ + public class TopScoreStatisticsSection : CompositeDrawable + { + private const float margin = 10; + + private readonly FontUsage smallFont = OsuFont.GetFont(size: 20); + private readonly FontUsage largeFont = OsuFont.GetFont(size: 25); + + private readonly TextColumn totalScoreColumn; + private readonly TextColumn accuracyColumn; + private readonly TextColumn maxComboColumn; + private readonly TextColumn ppColumn; + + private readonly FillFlowContainer statisticsColumns; + private readonly ModsInfoColumn modsColumn; + + public TopScoreStatisticsSection() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + InternalChild = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Spacing = new Vector2(10, 0), + Children = new Drawable[] + { + new FillFlowContainer + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(margin, 0), + Children = new Drawable[] + { + statisticsColumns = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(margin, 0), + }, + ppColumn = new TextColumn("pp", smallFont), + modsColumn = new ModsInfoColumn(), + } + }, + new FillFlowContainer + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(margin, 0), + Children = new Drawable[] + { + totalScoreColumn = new TextColumn("total score", largeFont), + accuracyColumn = new TextColumn("accuracy", largeFont), + maxComboColumn = new TextColumn("max combo", largeFont) + } + }, + } + }; + } + + /// + /// Sets the score to be displayed. + /// + public ScoreInfo Score + { + set + { + totalScoreColumn.Text = $@"{value.TotalScore:N0}"; + accuracyColumn.Text = $@"{value.Accuracy:P2}"; + maxComboColumn.Text = $@"{value.MaxCombo:N0}x"; + ppColumn.Text = $@"{value.PP:N0}"; + + statisticsColumns.ChildrenEnumerable = value.Statistics.Select(kvp => createStatisticsColumn(kvp.Key, kvp.Value)); + modsColumn.Mods = value.Mods; + } + } + + private TextColumn createStatisticsColumn(HitResult hitResult, int count) => new TextColumn(hitResult.GetDescription(), smallFont) + { + Text = count.ToString() + }; + + private class InfoColumn : CompositeDrawable + { + private readonly Box separator; + + public InfoColumn(string title, Drawable content) + { + AutoSizeAxes = Axes.Both; + + InternalChild = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 2), + Children = new[] + { + new OsuSpriteText + { + Font = OsuFont.GetFont(size: 12, weight: FontWeight.Black), + Text = title.ToUpper() + }, + separator = new Box + { + RelativeSizeAxes = Axes.X, + Height = 2 + }, + content + } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + separator.Colour = colours.Gray5; + } + } + + private class TextColumn : InfoColumn + { + private readonly SpriteText text; + + public TextColumn(string title, FontUsage font) + : this(title, new OsuSpriteText { Font = font }) + { + } + + private TextColumn(string title, SpriteText text) + : base(title, text) + { + this.text = text; + } + + public LocalisedString Text + { + set => text.Text = value; + } + } + + private class ModsInfoColumn : InfoColumn + { + private readonly FillFlowContainer modsContainer; + + public ModsInfoColumn() + : this(new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal + }) + { + } + + private ModsInfoColumn(FillFlowContainer modsContainer) + : base("mods", modsContainer) + { + this.modsContainer = modsContainer; + } + + public IEnumerable Mods + { + set + { + modsContainer.Clear(); + + foreach (Mod mod in value) + { + modsContainer.Add(new ModIcon(mod) + { + AutoSizeAxes = Axes.Both, + Scale = new Vector2(0.3f), + }); + } + } + } + } + } +} diff --git a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs new file mode 100644 index 0000000000..1ec4b7197d --- /dev/null +++ b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs @@ -0,0 +1,181 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using Humanizer; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Input.Events; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Online.Leaderboards; +using osu.Game.Scoring; +using osu.Game.Users; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Overlays.BeatmapSet.Scores +{ + public class TopScoreUserSection : CompositeDrawable + { + private readonly SpriteText rankText; + private readonly DrawableRank rank; + private readonly UpdateableAvatar avatar; + private readonly UsernameText usernameText; + private readonly SpriteText date; + private readonly DrawableFlag flag; + + public TopScoreUserSection() + { + AutoSizeAxes = Axes.Both; + + InternalChild = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(10, 0), + Children = new Drawable[] + { + rankText = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = "#1", + Font = OsuFont.GetFont(size: 30, weight: FontWeight.Bold, italics: true) + }, + rank = new DrawableRank(ScoreRank.F) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(40), + FillMode = FillMode.Fit, + }, + avatar = new UpdateableAvatar + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(80), + Masking = true, + CornerRadius = 5, + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Black.Opacity(0.25f), + Offset = new Vector2(0, 2), + Radius = 1, + }, + }, + new FillFlowContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 3), + Children = new Drawable[] + { + usernameText = new UsernameText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + }, + date = new SpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Font = OsuFont.GetFont(size: 15, weight: FontWeight.Bold) + }, + flag = new DrawableFlag + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Size = new Vector2(20, 13), + }, + } + } + } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + rankText.Colour = colours.Yellow; + } + + /// + /// Sets the score to be displayed. + /// + public ScoreInfo Score + { + set + { + avatar.User = usernameText.User = value.User; + flag.Country = value.User.Country; + date.Text = $@"achieved {value.Date.Humanize()}"; + rank.UpdateRank(value.Rank); + } + } + + private class UsernameText : ClickableUserContainer + { + private const float username_fade_duration = 150; + + private readonly FillFlowContainer hoverContainer; + + private readonly SpriteText normalText; + private readonly SpriteText hoveredText; + + public UsernameText() + { + var font = OsuFont.GetFont(size: 20, weight: FontWeight.Bold, italics: true); + + Children = new Drawable[] + { + normalText = new OsuSpriteText { Font = font }, + hoverContainer = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Alpha = 0, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 1), + Children = new Drawable[] + { + hoveredText = new OsuSpriteText { Font = font }, + new Box + { + BypassAutoSizeAxes = Axes.Both, + RelativeSizeAxes = Axes.X, + Height = 1 + } + } + } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + hoverContainer.Colour = colours.Blue; + } + + protected override void OnUserChanged(User user) => normalText.Text = hoveredText.Text = user.Username; + + protected override bool OnHover(HoverEvent e) + { + hoverContainer.FadeIn(username_fade_duration, Easing.OutQuint); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + hoverContainer.FadeOut(username_fade_duration, Easing.OutQuint); + base.OnHoverLost(e); + } + } + } +}