1
0
mirror of https://github.com/ppy/osu.git synced 2025-02-23 03:43:00 +08:00

Merge pull request #31880 from peppy/team-logo-support

Add basic display support for team logos
This commit is contained in:
Dean Herbert 2025-02-14 22:59:57 +09:00 committed by GitHub
commit 4c851a327e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 297 additions and 39 deletions

View File

@ -73,6 +73,8 @@ namespace osu.Game.Tests.Skins
"Archives/modified-default-20241207.osk",
// Covers skinnable spectator list
"Archives/modified-argon-20250116.osk",
// Covers player team flag
"Archives/modified-argon-20250214.osk",
};
/// <summary>

View File

@ -0,0 +1,23 @@
// 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 Newtonsoft.Json;
namespace osu.Game.Online.API.Requests.Responses
{
[JsonObject(MemberSerialization.OptIn)]
public class APITeam
{
[JsonProperty(@"id")]
public int Id { get; set; } = 1;
[JsonProperty(@"name")]
public string Name { get; set; } = string.Empty;
[JsonProperty(@"short_name")]
public string ShortName { get; set; } = string.Empty;
[JsonProperty(@"flag_url")]
public string FlagUrl = string.Empty;
}
}

View File

@ -55,6 +55,10 @@ namespace osu.Game.Online.API.Requests.Responses
set => countryCodeString = value.ToString();
}
[JsonProperty(@"team")]
[CanBeNull]
public APITeam Team { get; set; }
[JsonProperty(@"profile_colour")]
public string Colour;

View File

@ -180,6 +180,7 @@ namespace osu.Game.Online.Leaderboards
Height = 28,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10f, 0f),
Margin = new MarginPadding { Bottom = -2 },
Children = new Drawable[]
{
flagBadgeAndDateContainer = new FillFlowContainer
@ -189,7 +190,7 @@ namespace osu.Game.Online.Leaderboards
RelativeSizeAxes = Axes.Y,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(5f, 0f),
Width = 87f,
Width = 114f,
Masking = true,
Children = new Drawable[]
{
@ -199,6 +200,12 @@ namespace osu.Game.Online.Leaderboards
Origin = Anchor.CentreLeft,
Size = new Vector2(28, 20),
},
new UpdateableTeamFlag(user.Team)
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Size = new Vector2(40, 20),
},
new DateLabel(Score.Date)
{
Anchor = Anchor.CentreLeft,
@ -206,15 +213,6 @@ namespace osu.Game.Online.Leaderboards
},
},
},
new FillFlowContainer
{
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Margin = new MarginPadding { Left = edge_margin },
Children = statisticsLabels
},
},
},
},
@ -234,6 +232,7 @@ namespace osu.Game.Online.Leaderboards
GlowColour = Color4Extensions.FromHex(@"83ccfa"),
Current = scoreManager.GetBindableTotalScoreString(Score),
Font = OsuFont.Numeric.With(size: 23),
Margin = new MarginPadding { Top = 1 },
},
RankContainer = new Container
{
@ -250,13 +249,32 @@ namespace osu.Game.Online.Leaderboards
},
},
},
modsContainer = new FillFlowContainer<ModIcon>
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
ChildrenEnumerable = Score.Mods.AsOrdered().Select(mod => new ModIcon(mod) { Scale = new Vector2(0.375f) })
Children = new Drawable[]
{
new FillFlowContainer
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Margin = new MarginPadding { Left = edge_margin },
Children = statisticsLabels
},
modsContainer = new FillFlowContainer<ModIcon>
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
ChildrenEnumerable = Score.Mods.AsOrdered().Select(mod => new ModIcon(mod) { Scale = new Vector2(0.34f) })
},
}
},
},
},
@ -324,7 +342,7 @@ namespace osu.Game.Online.Leaderboards
private partial class ScoreComponentLabel : Container, IHasTooltip
{
private const float icon_size = 20;
private const float icon_size = 16;
private readonly FillFlowContainer content;
public override bool Contains(Vector2 screenSpacePos) => content.Contains(screenSpacePos);
@ -340,7 +358,7 @@ namespace osu.Game.Online.Leaderboards
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Padding = new MarginPadding { Right = 10 },
Padding = new MarginPadding { Right = 5 },
Children = new Drawable[]
{
new Container
@ -375,7 +393,8 @@ namespace osu.Game.Online.Leaderboards
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Text = statistic.Value,
Font = OsuFont.GetFont(size: 17, weight: FontWeight.Bold, fixedWidth: true)
Spacing = new Vector2(-1, 0),
Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold, fixedWidth: true)
},
},
};
@ -406,7 +425,7 @@ namespace osu.Game.Online.Leaderboards
public DateLabel(DateTimeOffset date)
: base(date)
{
Font = OsuFont.GetFont(size: 17, weight: FontWeight.Bold, italics: true);
Font = OsuFont.GetFont(size: 13, weight: FontWeight.Bold, italics: true);
}
protected override string Format() => Date.ToShortRelativeTime(TimeSpan.FromSeconds(30));

View File

@ -160,7 +160,20 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
{
Size = new Vector2(19, 14),
},
username,
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(4),
Children = new Drawable[]
{
new UpdateableTeamFlag(score.User.Team)
{
Size = new Vector2(28, 14),
},
username,
}
},
#pragma warning disable 618
new StatisticText(score.MaxCombo, score.BeatmapInfo!.MaxCombo, @"0\x"),
#pragma warning restore 618

View File

@ -27,7 +27,9 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
private readonly UpdateableAvatar avatar;
private readonly LinkFlowContainer usernameText;
private readonly DrawableDate achievedOn;
private readonly UpdateableFlag flag;
private readonly UpdateableTeamFlag teamFlag;
public TopScoreUserSection()
{
@ -112,12 +114,30 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
},
}
},
flag = new UpdateableFlag
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Size = new Vector2(19, 14),
Margin = new MarginPadding { Top = 3 }, // makes spacing look more even
Direction = FillDirection.Horizontal,
Spacing = new Vector2(4),
Children = new Drawable[]
{
flag = new UpdateableFlag
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Size = new Vector2(19, 14),
Margin = new MarginPadding { Top = 3 }, // makes spacing look more even
},
teamFlag = new UpdateableTeamFlag
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Size = new Vector2(28, 14),
Margin = new MarginPadding { Top = 3 }, // makes spacing look more even
},
}
},
}
}
@ -139,6 +159,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
{
avatar.User = value.User;
flag.CountryCode = value.User.CountryCode;
teamFlag.Team = value.User.Team;
achievedOn.Date = value.Date;
usernameText.Clear();

View File

@ -80,7 +80,7 @@ namespace osu.Game.Overlays
protected override CountryCode GetCountryCode(APIUser item) => item.CountryCode;
protected override Drawable CreateFlagContent(APIUser item)
protected override Drawable[] CreateFlagContent(APIUser item)
{
var username = new LinkFlowContainer(t => t.Font = OsuFont.GetFont(size: TEXT_SIZE, italics: true))
{
@ -89,7 +89,7 @@ namespace osu.Game.Overlays
TextAnchor = Anchor.CentreLeft
};
username.AddUserLink(item);
return username;
return [username];
}
}
}

View File

@ -42,6 +42,7 @@ namespace osu.Game.Overlays.Profile.Header
private ExternalLinkButton openUserExternally = null!;
private OsuSpriteText titleText = null!;
private UpdateableFlag userFlag = null!;
private UpdateableTeamFlag teamFlag = null!;
private OsuHoverContainer userCountryContainer = null!;
private OsuSpriteText userCountryText = null!;
private GroupBadgeFlow groupBadgeFlow = null!;
@ -166,6 +167,10 @@ namespace osu.Game.Overlays.Profile.Header
{
Size = new Vector2(28, 20),
},
teamFlag = new UpdateableTeamFlag
{
Size = new Vector2(40, 20),
},
userCountryContainer = new OsuHoverContainer
{
AutoSizeAxes = Axes.Both,
@ -215,6 +220,7 @@ namespace osu.Game.Overlays.Profile.Header
usernameText.Text = user?.Username ?? string.Empty;
openUserExternally.Link = $@"{api.Endpoints.WebsiteUrl}/users/{user?.Id ?? 0}";
userFlag.CountryCode = user?.CountryCode ?? default;
teamFlag.Team = user?.Team;
userCountryText.Text = (user?.CountryCode ?? default).GetDescription();
userCountryContainer.Action = () => rankingsOverlay?.ShowCountry(user?.CountryCode ?? default);
supporterTag.SupportLevel = user?.SupportLevel ?? 0;

View File

@ -34,7 +34,7 @@ namespace osu.Game.Overlays.Rankings.Tables
protected override CountryCode GetCountryCode(CountryStatistics item) => item.Code;
protected override Drawable CreateFlagContent(CountryStatistics item) => new CountryName(item.Code);
protected override Drawable[] CreateFlagContent(CountryStatistics item) => [new CountryName(item.Code)];
protected override Drawable[] CreateAdditionalContent(CountryStatistics item) => new Drawable[]
{

View File

@ -80,7 +80,7 @@ namespace osu.Game.Overlays.Rankings.Tables
protected abstract CountryCode GetCountryCode(TModel item);
protected abstract Drawable CreateFlagContent(TModel item);
protected abstract Drawable[] CreateFlagContent(TModel item);
private OsuSpriteText createIndexDrawable(int index) => new RowText
{
@ -92,16 +92,13 @@ namespace osu.Game.Overlays.Rankings.Tables
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10, 0),
Spacing = new Vector2(5, 0),
Margin = new MarginPadding { Bottom = row_spacing },
Children = new[]
{
new UpdateableFlag(GetCountryCode(item))
{
Size = new Vector2(28, 20),
},
CreateFlagContent(item)
}
Children =
[
new UpdateableFlag(GetCountryCode(item)) { Size = new Vector2(28, 20) },
..CreateFlagContent(item)
]
};
protected class RankingsTableColumn : TableColumn

View File

@ -14,6 +14,8 @@ using osu.Game.Users;
using osu.Game.Scoring;
using osu.Framework.Localisation;
using osu.Game.Resources.Localisation.Web;
using osu.Game.Users.Drawables;
using osuTK;
namespace osu.Game.Overlays.Rankings.Tables
{
@ -61,7 +63,7 @@ namespace osu.Game.Overlays.Rankings.Tables
protected sealed override CountryCode GetCountryCode(UserStatistics item) => item.User.CountryCode;
protected sealed override Drawable CreateFlagContent(UserStatistics item)
protected sealed override Drawable[] CreateFlagContent(UserStatistics item)
{
var username = new LinkFlowContainer(t => t.Font = OsuFont.GetFont(size: TEXT_SIZE, italics: true))
{
@ -70,7 +72,7 @@ namespace osu.Game.Overlays.Rankings.Tables
TextAnchor = Anchor.CentreLeft
};
username.AddUserLink(item.User);
return username;
return [new UpdateableTeamFlag(item.User.Team) { Size = new Vector2(40, 20) }, username];
}
protected sealed override Drawable[] CreateAdditionalContent(UserStatistics item) => new[]

View File

@ -140,6 +140,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants
Size = new Vector2(28, 20),
CountryCode = user?.CountryCode ?? default
},
new UpdateableTeamFlag(user?.Team)
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Size = new Vector2(40, 20),
},
new OsuSpriteText
{
Anchor = Anchor.CentreLeft,

View File

@ -0,0 +1,56 @@
// 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.Game.Online.API;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Skinning;
using osu.Game.Users.Drawables;
using osuTK;
namespace osu.Game.Screens.Play.HUD
{
public partial class PlayerTeamFlag : CompositeDrawable, ISerialisableDrawable
{
protected override bool ReceivePositionalInputAtSubTree(Vector2 screenSpacePos) => false;
private readonly UpdateableTeamFlag flag;
private const float default_size = 40f;
[Resolved]
private GameplayState? gameplayState { get; set; }
[Resolved]
private IAPIProvider api { get; set; } = null!;
private IBindable<APIUser>? apiUser;
public PlayerTeamFlag()
{
Size = new Vector2(default_size, default_size / 2f);
InternalChild = flag = new UpdateableTeamFlag
{
RelativeSizeAxes = Axes.Both,
};
}
[BackgroundDependencyLoader]
private void load()
{
if (gameplayState != null)
flag.Team = gameplayState.Score.ScoreInfo.User.Team;
else
{
apiUser = api.LocalUser.GetBoundCopy();
apiUser.BindValueChanged(u => flag.Team = u.NewValue.Team, true);
}
}
public bool UsesFixedAnchor { get; set; }
}
}

View File

@ -339,6 +339,12 @@ namespace osu.Game.Screens.SelectV2.Leaderboards
Origin = Anchor.CentreLeft,
Size = new Vector2(24, 16),
},
new UpdateableTeamFlag(user.Team)
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Size = new Vector2(40, 20),
},
new DateLabel(score.Date)
{
Anchor = Anchor.CentreLeft,

View File

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Newtonsoft.Json;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Online.API;
@ -221,7 +222,15 @@ namespace osu.Game.Tests.Visual.OnlinePlay
: new APIUser
{
Id = id,
Username = $"User {id}"
Username = $"User {id}",
Team = RNG.NextBool()
? new APITeam
{
Name = "Collective Wangs",
ShortName = "WANG",
FlagUrl = "https://assets.ppy.sh/teams/logo/1/wanglogo.jpg",
}
: null,
})
.Where(u => u != null).ToList(),
});

View File

@ -0,0 +1,87 @@
// 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.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Localisation;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API.Requests.Responses;
namespace osu.Game.Users.Drawables
{
/// <summary>
/// A team logo which can update to a new team when needed.
/// </summary>
public partial class UpdateableTeamFlag : ModelBackedDrawable<APITeam?>
{
public APITeam? Team
{
get => Model;
set
{
Model = value;
Invalidate(Invalidation.Presence);
}
}
protected override double LoadDelay => 200;
public UpdateableTeamFlag(APITeam? team = null)
{
Team = team;
Masking = true;
}
protected override Drawable? CreateDrawable(APITeam? team)
{
if (team == null)
return Empty();
return new TeamFlag(team) { RelativeSizeAxes = Axes.Both };
}
// Generally we just want team flags to disappear if the user doesn't have one.
// This also handles fill flow cases and avoids spacing being added for non-displaying flags.
public override bool IsPresent => base.IsPresent && Team != null;
protected override void Update()
{
base.Update();
CornerRadius = DrawHeight / 8;
}
[LongRunningLoad]
public partial class TeamFlag : CompositeDrawable, IHasTooltip
{
private readonly APITeam team;
public LocalisableString TooltipText { get; }
public TeamFlag(APITeam team)
{
this.team = team;
TooltipText = team.Name;
}
[BackgroundDependencyLoader]
private void load(LargeTextureStore textures)
{
InternalChildren = new Drawable[]
{
new HoverClickSounds(),
new Sprite
{
RelativeSizeAxes = Axes.Both,
Texture = textures.Get(team.FlagUrl)
}
};
}
}
}
}

View File

@ -82,9 +82,10 @@ namespace osu.Game.Users
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(6),
Children = new Drawable[]
Children = new[]
{
CreateFlag(),
CreateTeamLogo(),
// supporter icon is being added later
}
}

View File

@ -130,6 +130,11 @@ namespace osu.Game.Users
Action = Action,
};
protected Drawable CreateTeamLogo() => new UpdateableTeamFlag(User.Team)
{
Size = new Vector2(52, 26),
};
public MenuItem[] ContextMenuItems
{
get

View File

@ -147,9 +147,10 @@ namespace osu.Game.Users
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(6),
Children = new Drawable[]
Children = new[]
{
CreateFlag(),
CreateTeamLogo(),
// supporter icon is being added later
}
}