osu
osu copied to clipboard
New leaderboard score card design implementation
- Part of #22204
- Should resolve https://github.com/ppy/osu/issues/9411
- [x] Depends on https://github.com/ppy/osu/pull/25093
Figma reference: https://www.figma.com/file/EjS27IBaZhc2qX97lFBUxN/Song-Select-5?type=design&node-id=1-1010&mode=design&t=WQyZkwmDC4JWz6An-0
More song select stuff.
Couple of things to note when reviewing this:
Something to note while reviewing this, due to the amount of restructuring that will be required to fit these into song select they have been PRed in isolation, a follow up PR, most likely by me if im allowed will go through the necessary refactor to fit these into a new leaderboard, with the appropriate height, shear, etc.
-
I do not recommend comparing the implementation to the old one, since ive split the commits to be reviewed as if this was an entirely new component, since i found it to be probably the easiest way to understand it.
-
As for the overlapping mods when the count exceeds the amount that can be housed inside the right side of the component they will collapse and overlap as per @peppy 's suggestion.
-
And final major thing to note, i opted to specify the widths of the statistics manually, as to avoid them differing in length across different scores, which looked horrible when placed in a leaderboard together.
Header should be coming some time in the next few days as well as the new tooltip for this component.
So what happens for a mania score? Can you add one to the test scene please? I don't think information is going to fit due to more judgements.
When designing tests, please make sure to not just consider the scenario which works well, but intentionally look for the edge cases that need to be supported. Without this, the tests are not that useful.
added to the test, fwiw i had already guarded against this by making the container for the judgements autosize to the size of the judgement names line . although i feel perhaps it would look better if the judgements were centered underneath?
it doesnt look good side by side with osu ruleset but thats not a case that can happen outside of the test scene
@arflyte are you sure this is the best way to display this? it feels a bit.. half-assed? especially when there's more judgements, it's very hard to visually parse the mapping of judgement names to counts.
@arflyte are you sure this is the best way to display this? it feels a bit.. half-assed? especially when there's more judgements, it's very hard to visually parse the mapping of judgement names to counts.
I'm for moving this to the tooltip. It's not information you really need at a first glance ( at least in my experience) and that way the tooltip isn't just an expanded mod panel? As well as displaying them in a much more comprehensible way
Keep in mind that we already have this in the tooltip on master
, but it looks bad. I think we've asked @arflyte for a design for that in the past but haven't gotten anything yet. I think it's supposed to popup a larger view of the score.
Keep in mind that we already have this in the tooltip on
master
, but it looks bad. I think we've asked @arflyte for a design for that in the past but haven't gotten anything yet. I think it's supposed to popup a larger view of the score.
just to clarify im kind of unsure how i should take this, am i meant to pull something out of somewhere and see if it works or await a design from @arflyte
I've been looking into this for the past few weeks; it's part of the song select revision and mod display fix. I still have no final solution to this yet, but it's one of the higher priorities I'm working on right now.
I've been looking into this for the past few weeks; it's part of the song select revision and mod display fix. I still have no final solution to this yet, but it's one of the higher priorities I'm working on right now.
yeah no hurries on my part, and thanks for keeping me informed
Have updated the design with little tweaks and experimented with the layout to prevent overflow:
https://github.com/ppy/osu/assets/35318437/d930e00d-857e-4017-ba40-1ae11e100d7e
The max combo and accuracy are not in the tooltip yet, but there is no finalized design for that yet.
Here's a branch with the commit required to see in-game: https://github.com/ppy/osu/compare/master...Joehuu:osu:leaderboard-score-redesign-ingame
- What's with the gradient on the text? I don't see that on design.
- What happens if you use non-standard rate on DT / HT? I imagine the answer is "the rate doesn't show", which is a regression from the current UX. The tiny switches need to be able to show DT / HT rate / other extended info if they're to be used in this way.
- The indicator of when the score was set is not vertically centred with the flag.
- That was an alternative design, see right on figma for more content. Removed for now for simplicity and can be revisited when doing the skinning portion
- Have implemented, but needs design consideration when the mod icons are contracted (i.e. should it not display?)
- Fixed by using
UseFullGlyphHeight
I kinda like the gradients, think we should probably push them out and see what the feedback is like?
I dunno what's happening here but I don't like it. It's like the box is getting thinner as it goes further to the right?
I'm also not too keen on this dark border thing here. I don't think it fits well with the rest of the card:
@arflyte please check on these.
Added more alternative designs
https://www.figma.com/file/9bpXaCQNLuYQlbtyzcs115/Song-Select-6?type=design&node-id=0%3A1&mode=design&t=lEv5wCZC7VRK0dYH-1
I've followed this design and removed the dark border. Didn't address the right score area yet.
The right needs a drop shadow, but will need rearranging grid containers / drawables. @peppy does the drop shadow address your first problem in https://github.com/ppy/osu/pull/22237#issuecomment-1758908569?
@arflyte seems like the grade pill issue wasn't addressed in the new figma. Do you think having triangles fill the entire coloured background in https://github.com/ppy/osu/pull/22237#issuecomment-1756838482 works?
Hmm, I think having the entire background filled with triangles would work fine too. Haven't tried it but I think it would work.
@Joehuu to clarify my issue, it looks like the left element is trapezoiding horizontally:
I'd hope this wasn't a thing and it took up the full height.
That was an oversight on my part in implementing that, the gradient in figma doesn't disappear that quickly.
I'd hope this wasn't a thing and it took up the full height.
That would be easier, code/layout-wise. Might do that to keep things moving.
That was an oversight on my part in implementing that, the gradient in figma doesn't disappear that quickly.
I'm not sure that matters? Whatever gradient is put on there, it will have a similar effect, no?
When you see the figma design at a normal zoom, it does exhibit the same effect yes. Been looking at things too zoomed in.
Here's what it looks like now:
@Joehuu i'm not against the fluid layout, but cases like this can't exist IMO:
Basically, the display mode should be based on constant (widths?) rather than if certain information can fit or not. I'm not sure how practical this is to fix (I haven't checked the code yet), open to discussion.
Have fixed some of the issues mentioned here and discord.
Here's my v2 of the fluidity layout (haven't committed as it may be turned down):
Diff
diff --git a/osu.Game/Online/Leaderboards/LeaderboardScoreV2.cs b/osu.Game/Online/Leaderboards/LeaderboardScoreV2.cs
index dea134b4d6..e68156a7ed 100644
--- a/osu.Game/Online/Leaderboards/LeaderboardScoreV2.cs
+++ b/osu.Game/Online/Leaderboards/LeaderboardScoreV2.cs
@@ -52,7 +52,11 @@ public partial class LeaderboardScoreV2 : OsuClickableContainer, IHasContextMenu
private const float right_content_width = 180;
private const float grade_width = 40;
- private const float username_min_width = 100;
+ private const float username_min_width = 125;
+ private const float statistics_regular_min_width = 175;
+ private const float statistics_compact_min_width = 100;
+ private const float rank_label_width = 65;
+ private const float rank_label_visibility_width_cutoff = rank_label_width + height + username_min_width + statistics_regular_min_width + right_content_width;
private readonly ScoreInfo score;
@@ -101,9 +105,9 @@ public partial class LeaderboardScoreV2 : OsuClickableContainer, IHasContextMenu
private Drawable scoreRank = null!;
private Box totalScoreBackground = null!;
- private Container centreContent = null!;
- private FillFlowContainer usernameAndFlagContainer = null!;
private FillFlowContainer statisticsContainer = null!;
+ private RankLabel rankLabel = null!;
+ private Container rankLabelOverlay = null!;
public ITooltip<ScoreInfo> GetCustomTooltip() => new LeaderboardScoreTooltip();
public virtual ScoreInfo TooltipContent => score;
@@ -151,7 +155,7 @@ private void load()
RelativeSizeAxes = Axes.Both,
ColumnDimensions = new[]
{
- new Dimension(GridSizeMode.Absolute, 65),
+ new Dimension(GridSizeMode.AutoSize),
new Dimension(),
new Dimension(GridSizeMode.Absolute, right_content_width),
},
@@ -159,8 +163,17 @@ private void load()
{
new Drawable[]
{
- new RankLabel(rank) { Shear = -shear },
- centreContent = createCentreContent(user),
+ new Container
+ {
+ AutoSizeAxes = Axes.X,
+ RelativeSizeAxes = Axes.Y,
+ Child = rankLabel = new RankLabel(rank)
+ {
+ Width = rank_label_width,
+ RelativeSizeAxes = Axes.Y,
+ },
+ },
+ createCentreContent(user),
createRightContent()
}
}
@@ -214,21 +227,43 @@ private void load()
AutoSizeAxes = Axes.Both,
CornerRadius = corner_radius,
Masking = true,
- Child = avatar = new DelayedLoadWrapper(
- innerAvatar = new ClickableAvatar(user)
+ Children = new[]
+ {
+ avatar = new DelayedLoadWrapper(
+ innerAvatar = new ClickableAvatar(user)
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Scale = new Vector2(1.1f),
+ Shear = -shear,
+ RelativeSizeAxes = Axes.Both,
+ })
+ {
+ RelativeSizeAxes = Axes.None,
+ Size = new Vector2(height)
+ },
+ rankLabelOverlay = new Container
{
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- Scale = new Vector2(1.1f),
- Shear = -shear,
RelativeSizeAxes = Axes.Both,
- })
- {
- RelativeSizeAxes = Axes.None,
- Size = new Vector2(height)
+ Alpha = 0,
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Colour4.Black.Opacity(0.5f),
+ },
+ new RankLabel(rank)
+ {
+ AutoSizeAxes = Axes.Both,
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ },
+ }
+ }
},
},
- usernameAndFlagContainer = new FillFlowContainer
+ new FillFlowContainer
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
@@ -279,7 +314,7 @@ private void load()
{
Name = @"Statistics container",
Padding = new MarginPadding { Right = 40 },
- Spacing = new Vector2(25),
+ Spacing = new Vector2(25, 0),
Shear = -shear,
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
@@ -287,6 +322,8 @@ private void load()
Direction = FillDirection.Horizontal,
Children = statisticsLabels,
Alpha = 0,
+ LayoutEasing = Easing.OutQuint,
+ LayoutDuration = transition_duration,
}
}
}
@@ -483,6 +520,11 @@ 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);
+
+ if (DrawWidth < rank_label_visibility_width_cutoff && IsHovered)
+ rankLabelOverlay.FadeIn(transition_duration, Easing.OutQuint);
+ else
+ rankLabelOverlay.FadeOut(transition_duration, Easing.OutQuint);
}
protected override bool OnInvalidate(Invalidation invalidation, InvalidationSource source)
@@ -490,22 +532,27 @@ protected override bool OnInvalidate(Invalidation invalidation, InvalidationSour
Scheduler.AddOnce(() =>
{
// when width decreases
- // - hide statistics, then
- // - hide avatar, then
- // - hide user and flag and show avatar again
-
- if (centreContent.DrawWidth >= height + username_min_width || centreContent.DrawWidth < username_min_width)
- avatar.FadeIn(transition_duration, Easing.OutQuint).MoveToX(0, transition_duration, Easing.OutQuint);
- else
- avatar.FadeOut(transition_duration, Easing.OutQuint).MoveToX(-avatar.DrawWidth, transition_duration, Easing.OutQuint);
+ // - hide rank and show rank overlay on avatar when hovered
+ // - compact statistics, then
+ // - hide statistics
- if (centreContent.DrawWidth >= username_min_width)
- usernameAndFlagContainer.FadeIn(transition_duration, Easing.OutQuint).MoveToX(0, transition_duration, Easing.OutQuint);
+ if (DrawWidth >= rank_label_visibility_width_cutoff)
+ rankLabel.FadeIn(transition_duration, Easing.OutQuint).MoveToX(0, transition_duration, Easing.OutQuint);
else
- usernameAndFlagContainer.FadeOut(transition_duration, Easing.OutQuint).MoveToX(usernameAndFlagContainer.DrawWidth, transition_duration, Easing.OutQuint);
+ rankLabel.FadeOut(transition_duration, Easing.OutQuint).MoveToX(-rankLabel.DrawWidth, transition_duration, Easing.OutQuint);
- if (centreContent.DrawWidth >= height + statisticsContainer.DrawWidth + username_min_width)
+ if (DrawWidth >= height + username_min_width + statistics_regular_min_width + right_content_width)
+ {
statisticsContainer.FadeIn(transition_duration, Easing.OutQuint).MoveToX(0, transition_duration, Easing.OutQuint);
+ statisticsContainer.Direction = FillDirection.Horizontal;
+ statisticsContainer.ScaleTo(1, transition_duration, Easing.OutQuint);
+ }
+ else if (DrawWidth >= height + username_min_width + statistics_compact_min_width + right_content_width)
+ {
+ statisticsContainer.FadeIn(transition_duration, Easing.OutQuint).MoveToX(0, transition_duration, Easing.OutQuint);
+ statisticsContainer.Direction = FillDirection.Vertical;
+ statisticsContainer.ScaleTo(0.8f, transition_duration, Easing.OutQuint);
+ }
else
statisticsContainer.FadeOut(transition_duration, Easing.OutQuint).MoveToX(statisticsContainer.DrawWidth, transition_duration, Easing.OutQuint);
});
@@ -577,15 +624,14 @@ private partial class RankLabel : Container, IHasTooltip
{
public RankLabel(int? rank)
{
- AutoSizeAxes = Axes.Both;
- Anchor = Anchor.Centre;
- Origin = Anchor.Centre;
-
if (rank >= 1000)
TooltipText = $"#{rank:N0}";
Child = new OsuSpriteText
{
+ Shear = -shear,
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
Font = OsuFont.GetFont(size: 20, weight: FontWeight.SemiBold, italics: true),
Text = rank == null ? "-" : rank.Value.FormatRank().Insert(0, "#")
};
https://github.com/ppy/osu/assets/35318437/e90666d6-bee4-4996-b3ac-2f414e1673ea
- Increased minimum username width
- Considered rank to be not as important, so it gets hidden first (and overlaid on the avatar when hovering score)
- leaderboard always show 1-50 for now, can be reconsidered if needed/wanted for future relative rankings
- user rank can replace the username in personal best like stable
- avatar should be always shown now (for rank and profile viewing)
- Statistics have a compacted state before it gets hidden
Haven't done dynamic font size changing as suggested as I feel it's counterintuitive (at least when increasing ui scale). Dynamic padding can be done last to squeeze every pixel before something gets hidden. Just want to get the layout / fluidity states right first.
Seems better. Maybe too many different view stages/modes but hard to say without seeing this in its final usage.
pp seems like a more important metric than combo, not even talking about judgements people are usually not interested in exact amount of 100s and 50s (let alone 300s), they're only looking on accuracy and misscount
pp is not relevant for a score leaderboard. Save that for a different mode or something.
I dunno about that. Looking at this now it is weird that pp isn't shown, especially since we went out of our way to start showing it on web beatmap leaderboards to stop majority of users from installing third party browser extensions to add it.
Feels like we should be showing it at least in the most expanded mode. Or in the tooltip.
Is it only me who really dislikes having mods display both in contracted and expanded state depends on mods count?
What would everyone's opinion be about these two proposals:
-
Compute spacing based on number of mods:
-
Use constant contracted spacing and cut off at 7+ mods:
diff for number 1
diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboardScoreV2.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboardScoreV2.cs
index c5f96d1568..aca61c4d9a 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboardScoreV2.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboardScoreV2.cs
@@ -154,19 +154,20 @@ private static ScoreInfo[] getTestScores()
}
};
- for (int i = 0; i < LeaderboardScoreV2.MAX_MODS_EXPANDED; i++)
+ for (int i = 0; i < 3; i++)
scores[0].Mods = scores[0].Mods.Concat(new Mod[] { i % 2 == 0 ? new OsuModHidden() : halfTime }).ToArray();
- for (int i = 0; i < LeaderboardScoreV2.MAX_MODS_EXPANDED + 1; i++)
+ for (int i = 0; i < 4; i++)
scores[1].Mods = scores[1].Mods.Concat(new Mod[] { i % 2 == 0 ? new OsuModHidden() : new OsuModHalfTime() }).ToArray();
- for (int i = 0; i < LeaderboardScoreV2.MAX_MODS_CONTRACTED; i++)
+ for (int i = 0; i < 5; i++)
scores[2].Mods = scores[2].Mods.Concat(new Mod[] { i % 2 == 0 ? new OsuModHidden() : halfTime }).ToArray();
- for (int i = 0; i < LeaderboardScoreV2.MAX_MODS_CONTRACTED + 1; i++)
+ for (int i = 0; i < 6; i++)
scores[3].Mods = scores[3].Mods.Concat(new Mod[] { i % 2 == 0 ? new OsuModHidden() : new OsuModHalfTime() }).ToArray();
- scores[4].Mods = scores[4].BeatmapInfo!.Ruleset.CreateInstance().CreateAllMods().ToArray();
+ for (int i = 0; i < 5; i++)
+ scores[4].Mods = scores[4].Mods.Concat(new Mod[] { i % 2 == 0 ? new OsuModHidden() : new OsuModHalfTime() }).ToArray();
return scores;
}
diff --git a/osu.Game/Online/Leaderboards/LeaderboardScoreV2.cs b/osu.Game/Online/Leaderboards/LeaderboardScoreV2.cs
index b9ae3bb20e..153f000836 100644
--- a/osu.Game/Online/Leaderboards/LeaderboardScoreV2.cs
+++ b/osu.Game/Online/Leaderboards/LeaderboardScoreV2.cs
@@ -182,8 +182,21 @@ private void load()
};
innerAvatar.OnLoadComplete += d => d.FadeInFromZero(200);
+ }
+
+ private const float mod_scale = 0.375f;
+
+ protected override void Update()
+ {
+ base.Update();
+
+ float modsContainerWidth = ModSwitchTiny.WIDTH * mod_scale * modsContainer.Children.Count;
+ float requiredSpacing = Math.Min((modsContainer.DrawWidth - modsContainerWidth) / (modsContainer.Children.Count - 1), 1f);
- modsContainer.Spacing = new Vector2(modsContainer.Children.Count > MAX_MODS_EXPANDED ? -20 : 2, 0);
+ if (modsContainer.Children.Count == 0)
+ requiredSpacing = 0;
+
+ modsContainer.Spacing = new Vector2(requiredSpacing, 0);
modsContainer.Padding = new MarginPadding { Top = modsContainer.Children.Count > 0 ? 4 : 0 };
}
@@ -432,14 +445,20 @@ private void load()
Current = scoreManager.GetBindableTotalScoreString(score),
Font = OsuFont.GetFont(size: 30, weight: FontWeight.Light),
},
- modsContainer = new FillFlowContainer<ColouredModSwitchTiny>
+ modsContainer = new ReverseChildIDFillFlowContainer<ColouredModSwitchTiny>
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Shear = -shear,
- AutoSizeAxes = Axes.Both,
+ AutoSizeAxes = Axes.Y,
+ Width = ModSwitchTiny.WIDTH * mod_scale * 4 + 4 * 3,
Direction = FillDirection.Horizontal,
- ChildrenEnumerable = score.Mods.AsOrdered().Select(mod => new ColouredModSwitchTiny(mod) { Scale = new Vector2(0.375f) })
+ ChildrenEnumerable = score.Mods.AsOrdered().Select(mod => new ColouredModSwitchTiny(mod)
+ {
+ Anchor = Anchor.TopRight,
+ Origin = Anchor.TopRight,
+ Scale = new Vector2(mod_scale)
+ })
},
modsCounter = new OsuSpriteText
{
diff --git a/osu.Game/Rulesets/UI/ModSwitchTiny.cs b/osu.Game/Rulesets/UI/ModSwitchTiny.cs
index 4d50e702af..4a3bc9e31b 100644
--- a/osu.Game/Rulesets/UI/ModSwitchTiny.cs
+++ b/osu.Game/Rulesets/UI/ModSwitchTiny.cs
@@ -22,7 +22,7 @@ public partial class ModSwitchTiny : CompositeDrawable
public BindableBool Active { get; } = new BindableBool();
public const float DEFAULT_HEIGHT = 30;
- private const float width = 73;
+ public const float WIDTH = 73;
protected readonly IMod Mod;
private readonly bool showExtendedInformation;
@@ -56,7 +56,7 @@ public ModSwitchTiny(IMod mod, bool showExtendedInformation = false)
Width = 100 + DEFAULT_HEIGHT / 2,
RelativeSizeAxes = Axes.Y,
Masking = true,
- X = width,
+ X = WIDTH,
Margin = new MarginPadding { Left = -DEFAULT_HEIGHT },
Children = new Drawable[]
{
@@ -77,7 +77,7 @@ public ModSwitchTiny(IMod mod, bool showExtendedInformation = false)
},
new CircularContainer
{
- Width = width,
+ Width = WIDTH,
RelativeSizeAxes = Axes.Y,
Masking = true,
Children = new Drawable[]
diff for number 2
diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboardScoreV2.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboardScoreV2.cs
index c5f96d1568..aca61c4d9a 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboardScoreV2.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboardScoreV2.cs
@@ -154,19 +154,20 @@ private static ScoreInfo[] getTestScores()
}
};
- for (int i = 0; i < LeaderboardScoreV2.MAX_MODS_EXPANDED; i++)
+ for (int i = 0; i < 3; i++)
scores[0].Mods = scores[0].Mods.Concat(new Mod[] { i % 2 == 0 ? new OsuModHidden() : halfTime }).ToArray();
- for (int i = 0; i < LeaderboardScoreV2.MAX_MODS_EXPANDED + 1; i++)
+ for (int i = 0; i < 4; i++)
scores[1].Mods = scores[1].Mods.Concat(new Mod[] { i % 2 == 0 ? new OsuModHidden() : new OsuModHalfTime() }).ToArray();
- for (int i = 0; i < LeaderboardScoreV2.MAX_MODS_CONTRACTED; i++)
+ for (int i = 0; i < 5; i++)
scores[2].Mods = scores[2].Mods.Concat(new Mod[] { i % 2 == 0 ? new OsuModHidden() : halfTime }).ToArray();
- for (int i = 0; i < LeaderboardScoreV2.MAX_MODS_CONTRACTED + 1; i++)
+ for (int i = 0; i < 6; i++)
scores[3].Mods = scores[3].Mods.Concat(new Mod[] { i % 2 == 0 ? new OsuModHidden() : new OsuModHalfTime() }).ToArray();
- scores[4].Mods = scores[4].BeatmapInfo!.Ruleset.CreateInstance().CreateAllMods().ToArray();
+ for (int i = 0; i < 5; i++)
+ scores[4].Mods = scores[4].Mods.Concat(new Mod[] { i % 2 == 0 ? new OsuModHidden() : new OsuModHalfTime() }).ToArray();
return scores;
}
diff --git a/osu.Game/Online/Leaderboards/LeaderboardScoreV2.cs b/osu.Game/Online/Leaderboards/LeaderboardScoreV2.cs
index b9ae3bb20e..d789d96151 100644
--- a/osu.Game/Online/Leaderboards/LeaderboardScoreV2.cs
+++ b/osu.Game/Online/Leaderboards/LeaderboardScoreV2.cs
@@ -41,14 +41,9 @@ namespace osu.Game.Online.Leaderboards
public partial class LeaderboardScoreV2 : OsuClickableContainer, IHasContextMenu, IHasCustomTooltip<ScoreInfo>
{
/// <summary>
- /// The maximum number of mods when contracted until the mods display width exceeds the <see cref="right_content_width"/>.
+ /// The maximum number of mods until the mods display width exceeds the <see cref="right_content_width"/>.
/// </summary>
- public const int MAX_MODS_CONTRACTED = 13;
-
- /// <summary>
- /// The maximum number of mods when expanded until the mods display width exceeds the <see cref="right_content_width"/>.
- /// </summary>
- public const int MAX_MODS_EXPANDED = 4;
+ public const int MAX_MODS = 6;
private const float right_content_width = 180;
private const float grade_width = 40;
@@ -183,7 +178,6 @@ private void load()
innerAvatar.OnLoadComplete += d => d.FadeInFromZero(200);
- modsContainer.Spacing = new Vector2(modsContainer.Children.Count > MAX_MODS_EXPANDED ? -20 : 2, 0);
modsContainer.Padding = new MarginPadding { Top = modsContainer.Children.Count > 0 ? 4 : 0 };
}
@@ -439,6 +433,7 @@ private void load()
Shear = -shear,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
+ Spacing = new Vector2(-10, 0f),
ChildrenEnumerable = score.Mods.AsOrdered().Select(mod => new ColouredModSwitchTiny(mod) { Scale = new Vector2(0.375f) })
},
modsCounter = new OsuSpriteText
@@ -492,7 +487,7 @@ public override void Show()
using (BeginDelayedSequence(50))
{
- Drawable modsDrawable = score.Mods.Length > MAX_MODS_CONTRACTED ? modsCounter : modsContainer;
+ Drawable modsDrawable = score.Mods.Length > MAX_MODS ? modsCounter : modsContainer;
var drawables = new[] { flagBadgeAndDateContainer, modsDrawable }.Concat(statisticsLabels).ToArray();
for (int i = 0; i < drawables.Length; i++)
drawables[i].FadeIn(100 + i * 50);
If you ask me, once the acronyms are not clearly readable, you might as well not bother showing the pills and start showing the count alone. So I'd probably just cap at 4 mods with no overlap and call it a day.
That is also a viable option as long as we agree we don't need to show more than 4 mods, I thought the point of going up to 7 mods is to resolve some recurring issues we faced with regards to overflow of mods, but I don't know if that's really the case or just an arbitrary change in this PR.
Upon taking a peek at some of the leaderboards, I think we should at least support showing up to 5 mods for the initial push of the screen, to handle the famous mod combination DT FL HR HD combined with the Classic mod. This is definitely going to show up in a lot of scores and just displaying a text saying "5 mods" feels like it misrepresents the score (as these combinations are quite common), let alone showing them in a contracted state.
Final proposal for the time being:
Keeps design consistent and avoids mods overlapping each other with the "contracted" state (I think contraction is best suited for icon-based mods, which we have seemingly moved away from).
diff
diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboardScoreV2.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboardScoreV2.cs
index c5f96d1568..321f443a97 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboardScoreV2.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboardScoreV2.cs
@@ -154,19 +154,11 @@ private static ScoreInfo[] getTestScores()
}
};
- for (int i = 0; i < LeaderboardScoreV2.MAX_MODS_EXPANDED; i++)
- scores[0].Mods = scores[0].Mods.Concat(new Mod[] { i % 2 == 0 ? new OsuModHidden() : halfTime }).ToArray();
-
- for (int i = 0; i < LeaderboardScoreV2.MAX_MODS_EXPANDED + 1; i++)
- scores[1].Mods = scores[1].Mods.Concat(new Mod[] { i % 2 == 0 ? new OsuModHidden() : new OsuModHalfTime() }).ToArray();
-
- for (int i = 0; i < LeaderboardScoreV2.MAX_MODS_CONTRACTED; i++)
- scores[2].Mods = scores[2].Mods.Concat(new Mod[] { i % 2 == 0 ? new OsuModHidden() : halfTime }).ToArray();
-
- for (int i = 0; i < LeaderboardScoreV2.MAX_MODS_CONTRACTED + 1; i++)
- scores[3].Mods = scores[3].Mods.Concat(new Mod[] { i % 2 == 0 ? new OsuModHidden() : new OsuModHalfTime() }).ToArray();
-
- scores[4].Mods = scores[4].BeatmapInfo!.Ruleset.CreateInstance().CreateAllMods().ToArray();
+ scores[0].Mods = new Mod[] { new OsuModDoubleTime(), new OsuModHidden() };
+ scores[1].Mods = new Mod[] { new OsuModDoubleTime(), new OsuModHidden(), new OsuModFlashlight() };
+ scores[2].Mods = new Mod[] { new OsuModDoubleTime(), new OsuModHidden(), new OsuModFlashlight(), new OsuModHardRock() };
+ scores[3].Mods = new Mod[] { new OsuModDoubleTime(), new OsuModHidden(), new OsuModFlashlight(), new OsuModHardRock(), new OsuModClassic() };
+ scores[4].Mods = new Mod[] { new OsuModDoubleTime(), new OsuModHidden(), new OsuModFlashlight(), new OsuModHardRock(), new OsuModClassic(), new OsuModDifficultyAdjust() };
return scores;
}
diff --git a/osu.Game/Online/Leaderboards/LeaderboardScoreV2.cs b/osu.Game/Online/Leaderboards/LeaderboardScoreV2.cs
index b9ae3bb20e..862eae10e7 100644
--- a/osu.Game/Online/Leaderboards/LeaderboardScoreV2.cs
+++ b/osu.Game/Online/Leaderboards/LeaderboardScoreV2.cs
@@ -41,16 +41,11 @@ namespace osu.Game.Online.Leaderboards
public partial class LeaderboardScoreV2 : OsuClickableContainer, IHasContextMenu, IHasCustomTooltip<ScoreInfo>
{
/// <summary>
- /// The maximum number of mods when contracted until the mods display width exceeds the <see cref="right_content_width"/>.
+ /// The maximum number of mods until the mods display width exceeds the <see cref="right_content_width"/>.
/// </summary>
- public const int MAX_MODS_CONTRACTED = 13;
+ public const int MAX_MODS = 5;
- /// <summary>
- /// The maximum number of mods when expanded until the mods display width exceeds the <see cref="right_content_width"/>.
- /// </summary>
- public const int MAX_MODS_EXPANDED = 4;
-
- private const float right_content_width = 180;
+ private const float right_content_width = 210;
private const float grade_width = 40;
private const float username_min_width = 125;
private const float statistics_regular_min_width = 175;
@@ -182,8 +177,6 @@ private void load()
};
innerAvatar.OnLoadComplete += d => d.FadeInFromZero(200);
-
- modsContainer.Spacing = new Vector2(modsContainer.Children.Count > MAX_MODS_EXPANDED ? -20 : 2, 0);
modsContainer.Padding = new MarginPadding { Top = modsContainer.Children.Count > 0 ? 4 : 0 };
}
@@ -439,6 +432,7 @@ private void load()
Shear = -shear,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
+ Spacing = new Vector2(2f, 0f),
ChildrenEnumerable = score.Mods.AsOrdered().Select(mod => new ColouredModSwitchTiny(mod) { Scale = new Vector2(0.375f) })
},
modsCounter = new OsuSpriteText
@@ -492,7 +486,7 @@ public override void Show()
using (BeginDelayedSequence(50))
{
- Drawable modsDrawable = score.Mods.Length > MAX_MODS_CONTRACTED ? modsCounter : modsContainer;
+ Drawable modsDrawable = score.Mods.Length > MAX_MODS ? modsCounter : modsContainer;
var drawables = new[] { flagBadgeAndDateContainer, modsDrawable }.Concat(statisticsLabels).ToArray();
for (int i = 0; i < drawables.Length; i++)
drawables[i].FadeIn(100 + i * 50);