1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-29 04:49:58 +08:00

Switch online play screens to new header (#37074)

Specifically the one used on the daily challenge screen. Was bugging me
that playlists/multi have that old yellow header design used, so I've
changed it. Will probably come in handy once the footer/leaderboard
changes are in.

Also localized the headers while at it.

Multiplayer:


![osu_2026-03-23_20-03-22](https://github.com/user-attachments/assets/1f262aea-8bf9-412d-9c4a-89addd65bf38)

Playlists:


![osu_2026-03-23_20-34-18](https://github.com/user-attachments/assets/3f7d349e-7c03-4f56-8e57-1c65ae746235)

The only thing I'm wondering about is whether the detail thing
(participant count/playlist length) should be using the highlight colour
here. The old design had them be yellow, but I feel like the pink on
this one stands out too much.

---------

Co-authored-by: Dean Herbert <pe@ppy.sh>
This commit is contained in:
Krzysztof Gutkowski
2026-03-25 08:36:18 +01:00
committed by GitHub
Unverified
parent 98debc8d44
commit 86ef145f56
9 changed files with 83 additions and 111 deletions
@@ -140,13 +140,14 @@ namespace osu.Game.Tests.Visual.Playlists
room = new Room
{
RoomID = 1,
MaxAttempts = 10,
Playlist =
[
// osu! beatmap
new PlaylistItem(importedSet.Beatmaps[0])
{
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
Freestyle = true
Freestyle = true,
},
// osu! beatmap converted played in taiko
new PlaylistItem(importedSet.Beatmaps[1])
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
@@ -14,8 +15,20 @@ namespace osu.Game.Graphics.UserInterface
{
public partial class SectionHeader : CompositeDrawable
{
/// <summary>
/// Extra text to be shown in brackets next to the header.
/// Unlike the header itself, this can be updated during runtime.
/// </summary>
public readonly Bindable<string> DetailsText = new Bindable<string>();
private readonly LocalisableString text;
private OsuTextFlowContainer textFlow = null!;
private ITextPart? detailsPart;
[Resolved]
private OverlayColourProvider colourProvider { get; set; } = null!;
public SectionHeader(LocalisableString text)
{
this.text = text;
@@ -27,7 +40,7 @@ namespace osu.Game.Graphics.UserInterface
}
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider)
private void load()
{
InternalChild = new FillFlowContainer
{
@@ -37,7 +50,7 @@ namespace osu.Game.Graphics.UserInterface
Spacing = new Vector2(2),
Children = new Drawable[]
{
new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 16, weight: FontWeight.SemiBold))
textFlow = new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(size: 16, weight: FontWeight.SemiBold))
{
Text = text,
RelativeSizeAxes = Axes.X,
@@ -51,5 +64,21 @@ namespace osu.Game.Graphics.UserInterface
}
};
}
protected override void LoadComplete()
{
base.LoadComplete();
DetailsText.BindValueChanged(updateDetails, true);
}
private void updateDetails(ValueChangedEvent<string> details)
{
if (detailsPart != null)
textFlow.RemovePart(detailsPart);
if (!string.IsNullOrEmpty(details.NewValue))
detailsPart = textFlow.AddText($" ({details.NewValue})", t => t.Colour = colourProvider.Highlight1);
}
}
}
@@ -49,6 +49,31 @@ namespace osu.Game.Localisation
/// </summary>
public static LocalisableString PlaylistTrayDescription => new TranslatableString(getKey(@"playlist_tray_description"), @"Manage items on previous screen");
/// <summary>
/// "Beatmap queue"
/// </summary>
public static LocalisableString MultiplayerBeatmapQueue => new TranslatableString(getKey(@"multiplayer_beatmap_queue"), @"Beatmap queue");
/// <summary>
/// "Progress"
/// </summary>
public static LocalisableString PlaylistProgress => new TranslatableString(getKey(@"playlist_progress"), @"Progress");
/// <summary>
/// "Leaderboard"
/// </summary>
public static LocalisableString PlaylistLeaderboard => new TranslatableString(getKey(@"playlist_leaderboard"), @"Leaderboard");
/// <summary>
/// "Difficulty"
/// </summary>
public static LocalisableString Difficulty => new TranslatableString(getKey(@"difficulty"), @"Difficulty");
/// <summary>
/// "Chat"
/// </summary>
public static LocalisableString Chat => new TranslatableString(getKey(@"chat"), @"Chat");
private static string getKey(string key) => $@"{prefix}:{key}";
}
}
@@ -1,88 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Localisation;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osuTK;
namespace osu.Game.Screens.OnlinePlay.Components
{
/// <summary>
/// A header used in the multiplayer interface which shows text / details beneath a line.
/// </summary>
public partial class OverlinedHeader : CompositeDrawable
{
private bool showLine = true;
public bool ShowLine
{
get => showLine;
set
{
showLine = value;
line.Alpha = value ? 1 : 0;
}
}
public Bindable<string> Details = new Bindable<string>();
private readonly Circle line;
private readonly OsuSpriteText details;
public OverlinedHeader(LocalisableString title)
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Margin = new MarginPadding { Bottom = 5 };
InternalChild = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
line = new Circle
{
RelativeSizeAxes = Axes.X,
Height = 2,
},
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10, 0),
Children = new Drawable[]
{
new OsuSpriteText
{
Text = title,
Font = OsuFont.GetFont(size: 14, weight: FontWeight.SemiBold)
},
details = new OsuSpriteText
{
Font = OsuFont.GetFont(size: 14, weight: FontWeight.SemiBold)
},
}
},
}
};
Details.BindValueChanged(val => details.Text = val.NewValue);
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
line.Colour = colours.Yellow;
details.Colour = colours.Yellow;
}
}
}
@@ -3,19 +3,20 @@
using System.ComponentModel;
using osu.Framework.Allocation;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.Rooms;
using osu.Game.Rulesets;
namespace osu.Game.Screens.OnlinePlay.Components
{
public partial class OverlinedPlaylistHeader : OverlinedHeader
public partial class PlaylistHeader : SectionHeader
{
private readonly Room room;
[Resolved]
private RulesetStore rulesets { get; set; } = null!;
public OverlinedPlaylistHeader(Room room)
public PlaylistHeader(Room room)
: base("Playlist")
{
this.room = room;
@@ -36,7 +37,7 @@ namespace osu.Game.Screens.OnlinePlay.Components
}
private void updateDuration()
=> Details.Value = room.Playlist.GetTotalDuration(rulesets);
=> DetailsText.Value = $"{room.Playlist.GetTotalDuration(rulesets)}";
protected override void Dispose(bool isDisposing)
{
@@ -252,7 +252,7 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
{
new Drawable[]
{
new SectionHeader("Chat")
new SectionHeader(OnlinePlayStrings.Chat)
},
[new MatchChatDisplay(room) { RelativeSizeAxes = Axes.Both }]
},
@@ -19,6 +19,8 @@ using osu.Framework.Screens;
using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Cursor;
using osu.Game.Graphics.UserInterface;
using osu.Game.Localisation;
using osu.Game.Online;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests.Responses;
@@ -225,7 +227,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
new GridContainer
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding(content_padding),
Padding = new MarginPadding(content_padding) { Top = 10 },
ColumnDimensions = new[]
{
new Dimension(),
@@ -277,7 +279,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
{
new Drawable[]
{
new OverlinedHeader("Beatmap queue")
new SectionHeader(OnlinePlayStrings.MultiplayerBeatmapQueue)
},
new Drawable[]
{
@@ -309,7 +311,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
Alpha = 0,
Children = new Drawable[]
{
new OverlinedHeader("Extra mods"),
new SectionHeader("Extra mods"),
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
@@ -347,7 +349,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
Alpha = 0,
Children = new Drawable[]
{
new OverlinedHeader("Difficulty"),
new SectionHeader(OnlinePlayStrings.Difficulty),
userStyleDisplayContainer = new Container<DrawableRoomPlaylistItem>
{
RelativeSizeAxes = Axes.X,
@@ -370,7 +372,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
{
new Drawable[]
{
new OverlinedHeader("Chat")
new SectionHeader(OnlinePlayStrings.Chat)
},
new Drawable[]
{
@@ -2,13 +2,13 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.Multiplayer;
using osu.Game.Resources.Localisation.Web;
using osu.Game.Screens.OnlinePlay.Components;
namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants
{
public partial class ParticipantsListHeader : OverlinedHeader
public partial class ParticipantsListHeader : SectionHeader
{
[Resolved]
private MultiplayerClient client { get; set; } = null!;
@@ -26,7 +26,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants
if (room == null)
return;
Details.Value = room.Users.Count.ToString();
DetailsText.Value = $"{room.Users.Count}";
}
}
}
@@ -20,7 +20,9 @@ using osu.Framework.Screens;
using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Cursor;
using osu.Game.Graphics.UserInterface;
using osu.Game.Input;
using osu.Game.Localisation;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Online.Rooms;
@@ -218,7 +220,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
new GridContainer
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding(content_padding),
Padding = new MarginPadding(content_padding) { Top = 10 },
ColumnDimensions = new[]
{
new Dimension(),
@@ -244,7 +246,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
{
new Drawable[]
{
new OverlinedPlaylistHeader(room),
new PlaylistHeader(room),
},
new Drawable[]
{
@@ -291,7 +293,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
Alpha = 0,
Children = new Drawable[]
{
new OverlinedHeader("Extra mods"),
new SectionHeader("Extra mods"),
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
@@ -330,7 +332,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
Alpha = 0,
Children = new Drawable[]
{
new OverlinedHeader("Difficulty"),
new SectionHeader(OnlinePlayStrings.Difficulty),
userStyleDisplayContainer = new Container<DrawableRoomPlaylistItem>
{
RelativeSizeAxes = Axes.X,
@@ -350,14 +352,14 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
new OverlinedHeader("Progress"),
new RoomLocalUserInfo(room),
new SectionHeader(OnlinePlayStrings.PlaylistProgress),
new RoomLocalUserInfo(room) { Margin = new MarginPadding { Horizontal = 5 } },
}
}
},
new Drawable[]
{
new OverlinedHeader("Leaderboard")
new SectionHeader(OnlinePlayStrings.PlaylistLeaderboard)
},
new Drawable[]
{
@@ -380,7 +382,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
{
new Drawable[]
{
new OverlinedHeader("Chat")
new SectionHeader(OnlinePlayStrings.Chat)
},
new Drawable[]
{