Files
Ez2Lazer/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs
Bartłomiej Dach e64e0f0ca9 Fix beatmap set overlay not showing user tags at all
Regressed in 60d9c358b8.

In general an `APIBeatmap`'s `BeatmapSet` need not be present. In the
usage site of `osu.Game.Overlays.BeatmapSet.Info`, `Beatmap` and
`BeatmapSet` were actually two separate bindables. Moving the logic to a
helper, and therefore implicitly moving `BeatmapSet` from tracking said
separate bindable to instead refer to `Beatmap.BeatmapSet` broke this.
2026-03-03 12:44:29 +01:00

394 lines
17 KiB
C#

// 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.
#nullable disable
using System;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Localisation;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Beatmaps.Drawables.Cards;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Chat;
using osu.Game.Overlays.BeatmapSet.Buttons;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Overlays.BeatmapSet
{
public partial class BeatmapSetHeaderContent : CompositeDrawable
{
public readonly Bindable<APIBeatmapSet> BeatmapSet = new Bindable<APIBeatmapSet>();
private const float transition_duration = 200;
private const float buttons_height = 45;
private const float buttons_spacing = 5;
public bool DownloadButtonsVisible => downloadButtonsContainer.Any();
public readonly Details Details;
public readonly BeatmapPicker Picker;
private readonly UpdateableOnlineBeatmapSetCover cover;
private readonly Box coverGradient;
private readonly LinkFlowContainer title, artist;
private readonly AuthorInfo author;
private readonly VideoIconPill videoIconPill;
private readonly StoryboardIconPill storyboardIconPill;
private ExternalLinkButton externalLink;
private readonly FillFlowContainer downloadButtonsContainer;
private readonly BeatmapAvailability beatmapAvailability;
private readonly BeatmapSetOnlineStatusPill onlineStatusPill;
private readonly FavouriteButton favouriteButton;
private readonly FillFlowContainer fadeContent;
private readonly LoadingSpinner loading;
private BeatmapDownloadTracker downloadTracker;
[Resolved]
private IAPIProvider api { get; set; }
[Resolved]
private BeatmapRulesetSelector rulesetSelector { get; set; }
public BeatmapSetHeaderContent()
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
InternalChild = new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
cover = new UpdateableOnlineBeatmapSetCover
{
RelativeSizeAxes = Axes.Both,
Masking = true,
},
coverGradient = new Box
{
RelativeSizeAxes = Axes.Both
},
},
},
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding
{
Vertical = BeatmapSetOverlay.Y_PADDING,
Left = WaveOverlayContainer.HORIZONTAL_PADDING,
Right = WaveOverlayContainer.HORIZONTAL_PADDING + BeatmapSetOverlay.RIGHT_WIDTH + 10,
},
Children = new Drawable[]
{
fadeContent = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Child = Picker = new BeatmapPicker(),
},
title = new MetadataFlowContainer(s =>
{
s.Font = OsuFont.GetFont(size: 30, weight: FontWeight.SemiBold, italics: true);
})
{
Margin = new MarginPadding { Top = 15 },
},
artist = new MetadataFlowContainer(s =>
{
s.Font = OsuFont.GetFont(size: 20, weight: FontWeight.Medium, italics: true);
})
{
Margin = new MarginPadding { Bottom = 20 },
},
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Child = author = new AuthorInfo(),
},
beatmapAvailability = new BeatmapAvailability(),
new Container
{
RelativeSizeAxes = Axes.X,
Height = buttons_height,
Margin = new MarginPadding { Top = 10 },
Children = new Drawable[]
{
favouriteButton = new FavouriteButton
{
BeatmapSet = { BindTarget = BeatmapSet }
},
downloadButtonsContainer = new FillFlowContainer
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = buttons_height + buttons_spacing },
Spacing = new Vector2(buttons_spacing),
},
}
},
},
},
}
},
loading = new LoadingSpinner
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Scale = new Vector2(1.5f),
},
new FillFlowContainer
{
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
AutoSizeAxes = Axes.Both,
Margin = new MarginPadding { Top = BeatmapSetOverlay.Y_PADDING, Right = WaveOverlayContainer.HORIZONTAL_PADDING },
Direction = FillDirection.Vertical,
Spacing = new Vector2(10),
Children = new Drawable[]
{
new FillFlowContainer
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10),
Children = new Drawable[]
{
onlineStatusPill = new BeatmapSetOnlineStatusPill
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
TextSize = 14,
TextPadding = new MarginPadding { Horizontal = 35, Vertical = 10 }
},
storyboardIconPill = new StoryboardIconPill
{
AutoSizeAxes = Axes.X,
RelativeSizeAxes = Axes.Y,
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
IconSize = new Vector2(34),
IconPadding = new MarginPadding(10),
},
videoIconPill = new VideoIconPill
{
AutoSizeAxes = Axes.X,
RelativeSizeAxes = Axes.Y,
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
IconSize = new Vector2(34),
IconPadding = new MarginPadding(10),
},
}
},
Details = new Details(),
},
},
}
};
Picker.Beatmap.ValueChanged += b =>
{
Details.BeatmapInfo = b.NewValue;
updateExternalLink();
onlineStatusPill.Status = b.NewValue?.Status ?? BeatmapOnlineStatus.None;
};
}
private void updateExternalLink()
{
if (externalLink != null)
externalLink.Link = Picker.Beatmap.Value?.GetOnlineURL(api) ?? BeatmapSet.Value?.GetOnlineURL(api);
}
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider)
{
coverGradient.Colour = ColourInfo.GradientVertical(colourProvider.Background6.Opacity(0.3f), colourProvider.Background6.Opacity(0.8f));
BeatmapSet.BindValueChanged(setInfo =>
{
var newBeatmapSet = setInfo.NewValue;
Picker.BeatmapSet = rulesetSelector.BeatmapSet = author.BeatmapSet = beatmapAvailability.BeatmapSet = Details.BeatmapSet = newBeatmapSet;
cover.OnlineInfo = newBeatmapSet;
downloadTracker?.RemoveAndDisposeImmediately();
if (newBeatmapSet == null)
{
onlineStatusPill.FadeTo(0.5f, 500, Easing.OutQuint);
videoIconPill.Hide();
storyboardIconPill.Hide();
fadeContent.Hide();
loading.Show();
downloadButtonsContainer.FadeOut(transition_duration);
favouriteButton.FadeOut(transition_duration);
}
else
{
foreach (var beatmap in newBeatmapSet.Beatmaps)
beatmap.BeatmapSet = newBeatmapSet;
downloadTracker = new BeatmapDownloadTracker(newBeatmapSet);
downloadTracker.State.BindValueChanged(_ => updateDownloadButtons());
AddInternal(downloadTracker);
fadeContent.FadeIn(500, Easing.OutQuint);
loading.Hide();
if (newBeatmapSet.HasVideo)
videoIconPill.Show();
else
videoIconPill.Hide();
if (newBeatmapSet.HasStoryboard)
storyboardIconPill.Show();
else
storyboardIconPill.Hide();
var titleText = new RomanisableString(newBeatmapSet.TitleUnicode, newBeatmapSet.Title);
var artistText = new RomanisableString(newBeatmapSet.ArtistUnicode, newBeatmapSet.Artist);
title.Clear();
artist.Clear();
title.AddLink(titleText, LinkAction.SearchBeatmapSet, LocalisableString.Interpolate($@"title=""""{titleText}"""""));
title.AddArbitraryDrawable(Empty().With(d => d.Width = 5));
title.AddArbitraryDrawable(externalLink = new ExternalLinkButton());
if (newBeatmapSet.HasExplicitContent)
{
title.AddArbitraryDrawable(Empty().With(d => d.Width = 10));
title.AddArbitraryDrawable(new ExplicitContentBeatmapBadge());
}
if (newBeatmapSet.FeaturedInSpotlight)
{
title.AddArbitraryDrawable(Empty().With(d => d.Width = 10));
title.AddArbitraryDrawable(new SpotlightBeatmapBadge());
}
artist.AddLink(artistText, LinkAction.SearchBeatmapSet, LocalisableString.Interpolate($@"artist=""""{artistText}"""""));
if (newBeatmapSet.TrackId != null)
{
artist.AddArbitraryDrawable(Empty().With(d => d.Width = 10));
artist.AddArbitraryDrawable(new FeaturedArtistBeatmapBadge());
}
updateExternalLink();
onlineStatusPill.FadeIn(500, Easing.OutQuint);
downloadButtonsContainer.FadeIn(transition_duration);
favouriteButton.FadeIn(transition_duration);
updateDownloadButtons();
}
}, true);
}
private void updateDownloadButtons()
{
if (BeatmapSet.Value == null) return;
if (BeatmapSet.Value.Availability.DownloadDisabled && downloadTracker.State.Value != DownloadState.LocallyAvailable)
{
downloadButtonsContainer.Clear();
return;
}
switch (downloadTracker.State.Value)
{
case DownloadState.LocallyAvailable:
// temporary for UX until new design is implemented.
downloadButtonsContainer.Child = new BeatmapDownloadButton(BeatmapSet.Value)
{
Width = 50,
RelativeSizeAxes = Axes.Y,
SelectedBeatmap = { BindTarget = Picker.Beatmap }
};
break;
case DownloadState.Downloading:
case DownloadState.Importing:
// temporary to avoid showing two buttons for maps with novideo. will be fixed in new beatmap overlay design.
downloadButtonsContainer.Child = new HeaderDownloadButton(BeatmapSet.Value);
break;
default:
downloadButtonsContainer.Child = new HeaderDownloadButton(BeatmapSet.Value);
if (BeatmapSet.Value.HasVideo)
downloadButtonsContainer.Add(new HeaderDownloadButton(BeatmapSet.Value, true));
break;
}
}
public partial class MetadataFlowContainer : LinkFlowContainer
{
public MetadataFlowContainer(Action<SpriteText> defaultCreationParameters = null)
: base(defaultCreationParameters)
{
TextAnchor = Anchor.CentreLeft;
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
}
protected override DrawableLinkCompiler CreateLinkCompiler(ITextPart textPart) => new MetadataLinkCompiler(textPart);
public partial class MetadataLinkCompiler : DrawableLinkCompiler
{
public MetadataLinkCompiler(ITextPart part)
: base(part)
{
}
[BackgroundDependencyLoader]
private void load()
{
IdleColour = Color4.White;
}
}
}
}
}