diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs index edc1696456..f420ad976b 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs @@ -21,6 +21,8 @@ namespace osu.Game.Tests.Visual.Online protected override bool UseOnlineAPI => true; + private int nextBeatmapSetId = 1; + public TestSceneBeatmapSetOverlay() { Add(overlay = new TestBeatmapSetOverlay()); @@ -240,12 +242,23 @@ namespace osu.Game.Tests.Visual.Online { AddStep("show explicit map", () => { - var beatmapSet = CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet; + var beatmapSet = getBeatmapSet(); beatmapSet.OnlineInfo.HasExplicitContent = true; overlay.ShowBeatmapSet(beatmapSet); }); } + [Test] + public void TestFeaturedBeatmap() + { + AddStep("show featured map", () => + { + var beatmapSet = getBeatmapSet(); + beatmapSet.OnlineInfo.TrackId = 1; + overlay.ShowBeatmapSet(beatmapSet); + }); + } + [Test] public void TestHide() { @@ -308,6 +321,14 @@ namespace osu.Game.Tests.Visual.Online }; } + private BeatmapSetInfo getBeatmapSet() + { + var beatmapSet = CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet; + // Make sure the overlay is reloaded (see `BeatmapSetInfo.Equals`). + beatmapSet.OnlineBeatmapSetID = nextBeatmapSetId++; + return beatmapSet; + } + private void downloadAssert(bool shown) { AddAssert($"is download button {(shown ? "shown" : "hidden")}", () => overlay.Header.HeaderContent.DownloadButtonsVisible == shown); diff --git a/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs b/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs index fd5f306e07..722010ace2 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs @@ -99,16 +99,23 @@ namespace osu.Game.Tests.Visual.Online [BackgroundDependencyLoader] private void load(RulesetStore rulesets) { - var normal = CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet; + var normal = getBeatmapSet(); normal.OnlineInfo.HasVideo = true; normal.OnlineInfo.HasStoryboard = true; var undownloadable = getUndownloadableBeatmapSet(); var manyDifficulties = getManyDifficultiesBeatmapSet(rulesets); - var explicitMap = CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet; + var explicitMap = getBeatmapSet(); explicitMap.OnlineInfo.HasExplicitContent = true; + var featuredMap = getBeatmapSet(); + featuredMap.OnlineInfo.TrackId = 1; + + var explicitFeaturedMap = getBeatmapSet(); + explicitFeaturedMap.OnlineInfo.HasExplicitContent = true; + explicitFeaturedMap.OnlineInfo.TrackId = 2; + Child = new BasicScrollContainer { RelativeSizeAxes = Axes.Both, @@ -125,13 +132,19 @@ namespace osu.Game.Tests.Visual.Online new GridBeatmapPanel(undownloadable), new GridBeatmapPanel(manyDifficulties), new GridBeatmapPanel(explicitMap), + new GridBeatmapPanel(featuredMap), + new GridBeatmapPanel(explicitFeaturedMap), new ListBeatmapPanel(normal), new ListBeatmapPanel(undownloadable), new ListBeatmapPanel(manyDifficulties), - new ListBeatmapPanel(explicitMap) + new ListBeatmapPanel(explicitMap), + new ListBeatmapPanel(featuredMap), + new ListBeatmapPanel(explicitFeaturedMap) }, }, }; + + BeatmapSetInfo getBeatmapSet() => CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet; } } } diff --git a/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs b/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs index 48f1f0ce68..3658dbab83 100644 --- a/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs @@ -90,6 +90,12 @@ namespace osu.Game.Beatmaps /// The song language of this beatmap set. /// public BeatmapSetOnlineLanguage Language { get; set; } + + /// + /// The track ID of this beatmap set. + /// Non-null only if the track is linked to a featured artist track entry. + /// + public int? TrackId { get; set; } } public class BeatmapSetOnlineGenre diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs index 45d9c9405f..f653a654ca 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs @@ -63,6 +63,9 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty(@"ratings")] private int[] ratings { get; set; } + [JsonProperty(@"track_id")] + private int? trackId { get; set; } + [JsonProperty(@"user_id")] private int creatorId { @@ -106,7 +109,8 @@ namespace osu.Game.Online.API.Requests.Responses Availability = availability, HasFavourited = hasFavourited, Genre = genre, - Language = language + Language = language, + TrackId = trackId }, }; diff --git a/osu.Game/Overlays/BeatmapListing/Panels/GridBeatmapPanel.cs b/osu.Game/Overlays/BeatmapListing/Panels/GridBeatmapPanel.cs index 4d5c387c4a..c078127353 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/GridBeatmapPanel.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/GridBeatmapPanel.cs @@ -25,7 +25,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels private const float horizontal_padding = 10; private const float vertical_padding = 5; - private FillFlowContainer bottomPanel, statusContainer, titleContainer; + private FillFlowContainer bottomPanel, statusContainer, titleContainer, artistContainer; private PlayButton playButton; private Box progressBar; @@ -89,11 +89,19 @@ namespace osu.Game.Overlays.BeatmapListing.Panels }, } }, - new OsuSpriteText + artistContainer = new FillFlowContainer { - Text = new RomanisableString(SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist), - Font = OsuFont.GetFont(weight: FontWeight.Bold, italics: true) - }, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Children = new Drawable[] + { + new OsuSpriteText + { + Text = new RomanisableString(SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist), + Font = OsuFont.GetFont(weight: FontWeight.Bold, italics: true) + } + } + } }, }, new Container @@ -213,6 +221,16 @@ namespace osu.Game.Overlays.BeatmapListing.Panels }); } + if (SetInfo.OnlineInfo?.TrackId != null) + { + artistContainer.Add(new FeaturedArtistBeatmapPill + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Margin = new MarginPadding { Left = 10f, Top = 2f }, + }); + } + if (SetInfo.OnlineInfo?.HasVideo ?? false) { statusContainer.Add(new IconPill(FontAwesome.Solid.Film)); diff --git a/osu.Game/Overlays/BeatmapListing/Panels/ListBeatmapPanel.cs b/osu.Game/Overlays/BeatmapListing/Panels/ListBeatmapPanel.cs index 00ffd168c1..5011749c5f 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/ListBeatmapPanel.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/ListBeatmapPanel.cs @@ -27,7 +27,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels private const float vertical_padding = 5; private const float height = 70; - private FillFlowContainer statusContainer, titleContainer; + private FillFlowContainer statusContainer, titleContainer, artistContainer; protected BeatmapPanelDownloadButton DownloadButton; private PlayButton playButton; private Box progressBar; @@ -112,10 +112,18 @@ namespace osu.Game.Overlays.BeatmapListing.Panels }, } }, - new OsuSpriteText + artistContainer = new FillFlowContainer { - Text = new RomanisableString(SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist), - Font = OsuFont.GetFont(weight: FontWeight.Bold, italics: true) + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Children = new[] + { + new OsuSpriteText + { + Text = new RomanisableString(SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist), + Font = OsuFont.GetFont(weight: FontWeight.Bold, italics: true) + }, + }, }, } }, @@ -227,6 +235,16 @@ namespace osu.Game.Overlays.BeatmapListing.Panels }); } + if (SetInfo.OnlineInfo?.TrackId != null) + { + artistContainer.Add(new FeaturedArtistBeatmapPill + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Margin = new MarginPadding { Left = 10f, Top = 2f }, + }); + } + if (SetInfo.OnlineInfo?.HasVideo ?? false) { statusContainer.Add(new IconPill(FontAwesome.Solid.Film) { IconSize = new Vector2(20) }); diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs index a61640a02e..c3b6444a24 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs @@ -37,6 +37,7 @@ namespace osu.Game.Overlays.BeatmapSet private readonly OsuSpriteText title, artist; private readonly AuthorInfo author; private readonly ExplicitContentBeatmapPill explicitContentPill; + private readonly FeaturedArtistBeatmapPill featuredArtistPill; private readonly FillFlowContainer downloadButtonsContainer; private readonly BeatmapAvailability beatmapAvailability; private readonly BeatmapSetOnlineStatusPill onlineStatusPill; @@ -129,10 +130,25 @@ namespace osu.Game.Overlays.BeatmapSet } } }, - artist = new OsuSpriteText + new FillFlowContainer { - Font = OsuFont.GetFont(size: 20, weight: FontWeight.Medium, italics: true), - Margin = new MarginPadding { Bottom = 20 } + Direction = FillDirection.Horizontal, + AutoSizeAxes = Axes.Both, + Margin = new MarginPadding { Bottom = 20 }, + Children = new Drawable[] + { + artist = new OsuSpriteText + { + Font = OsuFont.GetFont(size: 20, weight: FontWeight.Medium, italics: true), + }, + featuredArtistPill = new FeaturedArtistBeatmapPill + { + Alpha = 0f, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Margin = new MarginPadding { Left = 10 } + } + } }, new Container { @@ -233,6 +249,7 @@ namespace osu.Game.Overlays.BeatmapSet artist.Text = new RomanisableString(setInfo.NewValue.Metadata.ArtistUnicode, setInfo.NewValue.Metadata.Artist); explicitContentPill.Alpha = setInfo.NewValue.OnlineInfo.HasExplicitContent ? 1 : 0; + featuredArtistPill.Alpha = setInfo.NewValue.OnlineInfo.TrackId != null ? 1 : 0; onlineStatusPill.FadeIn(500, Easing.OutQuint); onlineStatusPill.Status = setInfo.NewValue.OnlineInfo.Status; diff --git a/osu.Game/Overlays/BeatmapSet/FeaturedArtistBeatmapPill.cs b/osu.Game/Overlays/BeatmapSet/FeaturedArtistBeatmapPill.cs new file mode 100644 index 0000000000..fdee0799ff --- /dev/null +++ b/osu.Game/Overlays/BeatmapSet/FeaturedArtistBeatmapPill.cs @@ -0,0 +1,47 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Extensions.LocalisationExtensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Resources.Localisation.Web; + +namespace osu.Game.Overlays.BeatmapSet +{ + public class FeaturedArtistBeatmapPill : CompositeDrawable + { + public FeaturedArtistBeatmapPill() + { + AutoSizeAxes = Axes.Both; + } + + [BackgroundDependencyLoader(true)] + private void load(OsuColour colours, OverlayColourProvider colourProvider) + { + InternalChild = new CircularContainer + { + Masking = true, + AutoSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colourProvider?.Background5 ?? colours.Gray2, + }, + new OsuSpriteText + { + Margin = new MarginPadding { Horizontal = 10f, Vertical = 2f }, + Text = BeatmapsetsStrings.FeaturedArtistBadgeLabel.ToUpper(), + Font = OsuFont.GetFont(size: 10, weight: FontWeight.SemiBold), + Colour = OverlayColourProvider.Blue.Colour1, + } + } + }; + } + } +}