1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-13 20:33:35 +08:00

Merge pull request #32610 from bdach/user-tags-on-beatmap-set-overlay

Show user tags on beatmap set overlay
This commit is contained in:
Dean Herbert
2025-03-28 17:10:57 +09:00
committed by GitHub
Unverified
8 changed files with 127 additions and 29 deletions
@@ -99,8 +99,35 @@ namespace osu.Game.Tests.Visual.Online
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
},
TopTags =
[
new APIBeatmapTag { TagId = 4, VoteCount = 1 },
new APIBeatmapTag { TagId = 2, VoteCount = 1 },
new APIBeatmapTag { TagId = 23, VoteCount = 5 },
],
},
},
RelatedTags =
[
new APITag
{
Id = 2,
Name = "song representation/simple",
Description = "Accessible and straightforward map design."
},
new APITag
{
Id = 4,
Name = "style/clean",
Description = "Visually uncluttered and organised patterns, often involving few overlaps and equal visual spacing between objects."
},
new APITag
{
Id = 23,
Name = "aim/aim control",
Description = "Patterns with velocity or direction changes which strongly go against a player's natural movement pattern."
}
]
});
});
@@ -128,6 +128,9 @@ namespace osu.Game.Online.API.Requests.Responses
[JsonProperty(@"converts")]
public APIBeatmap[]? Converts { get; set; }
[JsonProperty(@"related_tags")]
public APITag[]? RelatedTags { get; set; }
private BeatmapMetadata metadata => new BeatmapMetadata
{
Title = Title,
+43 -17
View File
@@ -2,6 +2,8 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
@@ -9,6 +11,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Overlays.BeatmapListing;
@@ -17,26 +20,22 @@ namespace osu.Game.Overlays.BeatmapSet
{
public partial class Info : Container
{
private const float metadata_width = 175;
private const float metadata_width = 185;
private const float spacing = 20;
private const float base_height = 220;
private const float base_height = 300;
private readonly Box successRateBackground;
private readonly Box background;
private readonly SuccessRate successRate;
private readonly MetadataSection<string[]?> userTags;
public readonly Bindable<APIBeatmapSet> BeatmapSet = new Bindable<APIBeatmapSet>();
public APIBeatmap? BeatmapInfo
{
get => successRate.Beatmap;
set => successRate.Beatmap = value;
}
public readonly Bindable<APIBeatmap> Beatmap = new Bindable<APIBeatmap>();
public Info()
{
SuccessRate successRate;
MetadataSectionNominators nominators;
MetadataSection source, tags;
MetadataSection source, mapperTags;
MetadataSectionGenre genre;
MetadataSectionLanguage language;
OsuSpriteText notRankedPlaceholder;
@@ -66,27 +65,30 @@ namespace osu.Game.Overlays.BeatmapSet
Child = new MetadataSectionDescription(),
},
},
new Container
new OsuScrollContainer
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
RelativeSizeAxes = Axes.Y,
Width = metadata_width,
Padding = new MarginPadding { Horizontal = 10 },
Padding = new MarginPadding { Left = 10 },
Margin = new MarginPadding { Right = BeatmapSetOverlay.RIGHT_WIDTH + spacing },
Masking = true,
ScrollbarOverlapsContent = false,
Child = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Full,
Padding = new MarginPadding { Right = 5 },
Children = new Drawable[]
{
nominators = new MetadataSectionNominators(),
source = new MetadataSectionSource(),
genre = new MetadataSectionGenre { Width = 0.5f },
language = new MetadataSectionLanguage { Width = 0.5f },
tags = new MetadataSectionTags(),
userTags = new MetadataSectionUserTags(),
mapperTags = new MetadataSectionMapperTags(),
},
},
},
@@ -121,18 +123,42 @@ namespace osu.Game.Overlays.BeatmapSet
},
};
BeatmapSet.ValueChanged += b =>
BeatmapSet.BindValueChanged(b =>
{
nominators.Metadata = (b.NewValue?.CurrentNominations ?? Array.Empty<BeatmapSetOnlineNomination>(), b.NewValue?.RelatedUsers ?? Array.Empty<APIUser>());
source.Metadata = b.NewValue?.Source ?? string.Empty;
tags.Metadata = b.NewValue?.Tags ?? string.Empty;
mapperTags.Metadata = b.NewValue?.Tags ?? string.Empty;
updateUserTags();
genre.Metadata = b.NewValue?.Genre ?? new BeatmapSetOnlineGenre { Id = (int)SearchGenre.Unspecified };
language.Metadata = b.NewValue?.Language ?? new BeatmapSetOnlineLanguage { Id = (int)SearchLanguage.Unspecified };
bool setHasLeaderboard = b.NewValue?.Status > 0;
successRate.Alpha = setHasLeaderboard ? 1 : 0;
notRankedPlaceholder.Alpha = setHasLeaderboard ? 0 : 1;
Height = setHasLeaderboard ? 270 : base_height;
};
});
Beatmap.BindValueChanged(b =>
{
successRate.Beatmap = b.NewValue;
updateUserTags();
});
}
private void updateUserTags()
{
if (Beatmap.Value?.TopTags == null || Beatmap.Value.TopTags.Length == 0 || BeatmapSet.Value?.RelatedTags == null)
{
userTags.Metadata = null;
return;
}
var tagsById = BeatmapSet.Value.RelatedTags.ToDictionary(t => t.Id);
userTags.Metadata = Beatmap.Value.TopTags
.Select(t => (topTag: t, relatedTag: tagsById.GetValueOrDefault(t.TagId)))
.Where(t => t.relatedTag != null)
// see https://github.com/ppy/osu-web/blob/bb3bd2e7c6f84f26066df5ea20a81c77ec9bb60a/resources/js/beatmapsets-show/controller.ts#L103-L106 for sort criteria
.OrderByDescending(t => t.topTag.VoteCount)
.ThenBy(t => t.relatedTag!.Name)
.Select(t => t.relatedTag!.Name)
.ToArray();
}
[BackgroundDependencyLoader]
@@ -7,10 +7,10 @@ using osu.Game.Online.Chat;
namespace osu.Game.Overlays.BeatmapSet
{
public partial class MetadataSectionTags : MetadataSection
public partial class MetadataSectionMapperTags : MetadataSection
{
public MetadataSectionTags(Action<string>? searchAction = null)
: base(MetadataType.Tags, searchAction)
public MetadataSectionMapperTags(Action<string>? searchAction = null)
: base(MetadataType.MapperTags, searchAction)
{
}
@@ -0,0 +1,39 @@
// 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 System;
using osu.Game.Graphics.Containers;
using osu.Game.Online.Chat;
namespace osu.Game.Overlays.BeatmapSet
{
public partial class MetadataSectionUserTags : MetadataSection<string[]?>
{
private readonly Action<string>? searchAction;
public MetadataSectionUserTags(Action<string>? searchAction = null)
: base(MetadataType.UserTags, null)
{
this.searchAction = searchAction;
}
protected override void AddMetadata(string[]? tags, LinkFlowContainer loaded)
{
if (tags == null)
return;
for (int i = 0; i <= tags.Length - 1; i++)
{
string tag = tags[i];
if (searchAction != null)
loaded.AddLink(tag, () => searchAction(tag));
else
loaded.AddLink(tag, LinkAction.SearchBeatmapSet, $@"tag=""""{tag}""""");
if (i != tags.Length - 1)
loaded.AddText(" ");
}
}
}
}
+6 -2
View File
@@ -1,6 +1,7 @@
// 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 System.ComponentModel;
using osu.Framework.Localisation;
using osu.Game.Resources.Localisation.Web;
@@ -8,8 +9,11 @@ namespace osu.Game.Overlays.BeatmapSet
{
public enum MetadataType
{
[LocalisableDescription(typeof(BeatmapsetsStrings), nameof(BeatmapsetsStrings.ShowInfoTags))]
Tags,
[Description("User Tags")] // TODO: use translated string after osu-resources update
UserTags,
[Description("Mapper Tags")] // TODO: use translated string after osu-resources update
MapperTags,
[LocalisableDescription(typeof(BeatmapsetsStrings), nameof(BeatmapsetsStrings.ShowInfoSource))]
Source,
+5 -6
View File
@@ -47,7 +47,10 @@ namespace osu.Game.Overlays
Spacing = new Vector2(0, 20),
Children = new Drawable[]
{
info = new Info(),
info = new Info
{
Beatmap = { BindTarget = Header.HeaderContent.Picker.Beatmap }
},
new ScoresContainer
{
Beatmap = { BindTarget = Header.HeaderContent.Picker.Beatmap }
@@ -60,11 +63,7 @@ namespace osu.Game.Overlays
info.BeatmapSet.BindTo(beatmapSet);
comments.BeatmapSet.BindTo(beatmapSet);
Header.HeaderContent.Picker.Beatmap.ValueChanged += b =>
{
info.BeatmapInfo = b.NewValue;
ScrollFlow.ScrollToStart();
};
Header.HeaderContent.Picker.Beatmap.ValueChanged += b => ScrollFlow.ScrollToStart();
}
[BackgroundDependencyLoader]
+1 -1
View File
@@ -133,7 +133,7 @@ namespace osu.Game.Screens.Select
{
description = new MetadataSectionDescription(query => songSelect?.Search(query)),
source = new MetadataSectionSource(query => songSelect?.Search(query)),
tags = new MetadataSectionTags(query => songSelect?.Search(query)),
tags = new MetadataSectionMapperTags(query => songSelect?.Search(query)),
},
},
},