Skip to content

Commit b1163ee

Browse files
committed
Refactor leaderboard statistics component to use text flows
This is done to avoid any baseline alignment issues that occur when the name/label are laid out horizontally, as mentioned in ppy#37056. Also extracts it to a separate component, to be reused on the playlists leaderboard. Supersedes ppy#37056.
1 parent 95648a3 commit b1163ee

File tree

3 files changed

+209
-51
lines changed

3 files changed

+209
-51
lines changed
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
2+
// See the LICENCE file in the repository root for full licence text.
3+
4+
using NUnit.Framework;
5+
using osu.Framework.Allocation;
6+
using osu.Framework.Graphics;
7+
using osu.Framework.Graphics.Containers;
8+
using osu.Framework.Graphics.Shapes;
9+
using osu.Framework.Testing;
10+
using osu.Game.Graphics;
11+
using osu.Game.Online.Leaderboards;
12+
using osu.Game.Overlays;
13+
using osuTK;
14+
15+
namespace osu.Game.Tests.Visual.Online
16+
{
17+
public partial class TestSceneLeaderboardStatistic : OsuTestScene
18+
{
19+
[Cached]
20+
private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Aquamarine);
21+
22+
[Cached]
23+
private OsuColour colours = new OsuColour();
24+
25+
private FillFlowContainer container = null!;
26+
private LeaderboardStatistic[] statistics = [];
27+
28+
[SetUpSteps]
29+
public void SetUpSteps()
30+
{
31+
AddStep("create container", () =>
32+
{
33+
Child = new Container
34+
{
35+
Masking = true,
36+
Anchor = Anchor.Centre,
37+
Origin = Anchor.Centre,
38+
AutoSizeAxes = Axes.X,
39+
Height = 50,
40+
Children = new Drawable[]
41+
{
42+
new Box
43+
{
44+
RelativeSizeAxes = Axes.Both,
45+
Colour = colours.Gray3,
46+
},
47+
container = new FillFlowContainer
48+
{
49+
Anchor = Anchor.CentreLeft,
50+
Origin = Anchor.CentreLeft,
51+
Direction = FillDirection.Horizontal,
52+
AutoSizeAxes = Axes.Both,
53+
Margin = new MarginPadding { Horizontal = 20 },
54+
Spacing = new Vector2(20, 0)
55+
},
56+
},
57+
};
58+
});
59+
}
60+
61+
[Test]
62+
public void TestTwoElements()
63+
{
64+
AddStep("create content", () =>
65+
{
66+
container.AddRange(statistics = new[]
67+
{
68+
new LeaderboardStatistic("COMBO", "123x", false),
69+
new LeaderboardStatistic("ACCURACY", "100.00%", true),
70+
});
71+
});
72+
AddStep("change to vertical", () =>
73+
{
74+
container.Direction = FillDirection.Vertical;
75+
container.ScaleTo(0.8f, 200, Easing.OutQuint);
76+
});
77+
AddStep("change to horizontal", () =>
78+
{
79+
container.Direction = FillDirection.Horizontal;
80+
container.ScaleTo(1, 200, Easing.OutQuint);
81+
});
82+
}
83+
84+
[Test]
85+
public void TestThreeElements()
86+
{
87+
AddStep("create content", () =>
88+
{
89+
container.AddRange(statistics = new[]
90+
{
91+
new LeaderboardStatistic("COMBO", "123x", false),
92+
new LeaderboardStatistic("ACCURACY", "100.00%", true),
93+
new LeaderboardStatistic("TEST", "12345", false),
94+
});
95+
});
96+
AddStep("change to vertical", () =>
97+
{
98+
container.Direction = FillDirection.Vertical;
99+
container.ScaleTo(0.8f, 200, Easing.OutQuint);
100+
101+
foreach (var statistic in statistics)
102+
statistic.Direction = Direction.Vertical;
103+
});
104+
AddStep("change to horizontal", () =>
105+
{
106+
container.Direction = FillDirection.Horizontal;
107+
container.ScaleTo(1, 200, Easing.OutQuint);
108+
109+
foreach (var statistic in statistics)
110+
statistic.Direction = Direction.Horizontal;
111+
});
112+
}
113+
}
114+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
2+
// See the LICENCE file in the repository root for full licence text.
3+
4+
using osu.Framework.Allocation;
5+
using osu.Framework.Graphics;
6+
using osu.Framework.Graphics.Containers;
7+
using osu.Framework.Localisation;
8+
using osu.Game.Graphics;
9+
using osu.Game.Graphics.Containers;
10+
using osu.Game.Overlays;
11+
using osuTK;
12+
using osuTK.Graphics;
13+
14+
namespace osu.Game.Online.Leaderboards
15+
{
16+
public partial class LeaderboardStatistic : Container
17+
{
18+
private readonly LocalisableString name;
19+
private readonly LocalisableString value;
20+
21+
private readonly bool perfect;
22+
23+
private Direction direction;
24+
25+
public Direction Direction
26+
{
27+
get => direction;
28+
set
29+
{
30+
direction = value;
31+
32+
if (IsLoaded)
33+
recreateText();
34+
}
35+
}
36+
37+
[Resolved]
38+
private OsuColour colours { get; set; } = null!;
39+
40+
[Resolved]
41+
private OverlayColourProvider colourProvider { get; set; } = null!;
42+
43+
private readonly Container content;
44+
private readonly OsuTextFlowContainer textFlow;
45+
46+
public override bool Contains(Vector2 screenSpacePos) => content.Contains(screenSpacePos);
47+
48+
public LeaderboardStatistic(LocalisableString name, LocalisableString value, bool perfect, float? minWidth = null)
49+
{
50+
this.name = name;
51+
this.value = value;
52+
this.perfect = perfect;
53+
54+
AutoSizeAxes = Axes.Both;
55+
Child = content = new Container
56+
{
57+
AutoSizeAxes = Axes.Both,
58+
Child = textFlow = new OsuTextFlowContainer
59+
{
60+
AutoSizeAxes = Axes.Both,
61+
ParagraphSpacing = 0.1f,
62+
},
63+
};
64+
65+
if (minWidth != null)
66+
Add(Empty().With(d => d.Width = minWidth.Value));
67+
}
68+
69+
protected override void LoadComplete()
70+
{
71+
base.LoadComplete();
72+
recreateText();
73+
}
74+
75+
private void recreateText()
76+
{
77+
textFlow.Clear();
78+
79+
textFlow.AddText($"{name}{(direction == Direction.Horizontal ? "\n" : "")}", t =>
80+
{
81+
t.Font = OsuFont.Style.Caption2.With(weight: FontWeight.SemiBold);
82+
t.Colour = colourProvider.Content2;
83+
});
84+
85+
textFlow.AddText(value, t =>
86+
{
87+
t.Font = OsuFont.Style.Body;
88+
t.Colour = perfect ? colours.Lime1 : Color4.White;
89+
t.Padding = new MarginPadding { Left = direction == Direction.Horizontal ? 0 : 5 };
90+
});
91+
}
92+
}
93+
}

osu.Game/Screens/Select/BeatmapLeaderboardScore.cs

Lines changed: 2 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -324,9 +324,9 @@ private void load()
324324
Direction = FillDirection.Horizontal,
325325
Children = new Drawable[]
326326
{
327-
new ScoreComponentLabel(BeatmapsetsStrings.ShowScoreboardHeadersCombo.ToUpper(), $"{Score.MaxCombo.ToString()}x",
327+
new LeaderboardStatistic(BeatmapsetsStrings.ShowScoreboardHeadersCombo.ToUpper(), $"{Score.MaxCombo.ToString()}x",
328328
Score.MaxCombo == Score.GetMaximumAchievableCombo(), 60),
329-
new ScoreComponentLabel(BeatmapsetsStrings.ShowScoreboardHeadersAccuracy.ToUpper(), Score.DisplayAccuracy, Score.Accuracy == 1,
329+
new LeaderboardStatistic(BeatmapsetsStrings.ShowScoreboardHeadersAccuracy.ToUpper(), Score.DisplayAccuracy, Score.Accuracy == 1,
330330
55),
331331
},
332332
Alpha = 0,
@@ -659,55 +659,6 @@ public DateLabel(DateTimeOffset date)
659659
protected override LocalisableString Format() => Date.ToShortRelativeTime(TimeSpan.FromSeconds(30));
660660
}
661661

662-
private partial class ScoreComponentLabel : Container
663-
{
664-
private readonly LocalisableString name;
665-
private readonly LocalisableString value;
666-
private readonly bool perfect;
667-
private readonly float minWidth;
668-
669-
private FillFlowContainer content = null!;
670-
public override bool Contains(Vector2 screenSpacePos) => content.Contains(screenSpacePos);
671-
672-
public ScoreComponentLabel(LocalisableString name, LocalisableString value, bool perfect, float minWidth)
673-
{
674-
this.name = name;
675-
this.value = value;
676-
this.perfect = perfect;
677-
this.minWidth = minWidth;
678-
}
679-
680-
[BackgroundDependencyLoader]
681-
private void load(OsuColour colours, OverlayColourProvider colourProvider)
682-
{
683-
AutoSizeAxes = Axes.Both;
684-
Child = content = new FillFlowContainer
685-
{
686-
AutoSizeAxes = Axes.Both,
687-
Direction = FillDirection.Vertical,
688-
Children = new[]
689-
{
690-
new OsuSpriteText
691-
{
692-
Colour = colourProvider.Content2,
693-
Text = name,
694-
Font = OsuFont.Style.Caption2.With(weight: FontWeight.SemiBold),
695-
},
696-
new OsuSpriteText
697-
{
698-
// We don't want the value setting the horizontal size, since it leads to wonky accuracy container length,
699-
// since the accuracy is sometimes longer than its name.
700-
BypassAutoSizeAxes = Axes.X,
701-
Text = value,
702-
Font = OsuFont.Style.Body,
703-
Colour = perfect ? colours.Lime1 : Color4.White,
704-
},
705-
Empty().With(d => d.Width = minWidth),
706-
}
707-
};
708-
}
709-
}
710-
711662
private partial class RankLabel : Container, IHasTooltip
712663
{
713664
private readonly bool darkText;

0 commit comments

Comments
 (0)