1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-17 21:13:01 +08:00
Files
osu-lazer/osu.Game/Screens/SelectV2/BeatmapMetadataWedge.cs
T
2025-04-23 16:23:37 +09:00

358 lines
16 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.
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Containers;
using osu.Game.Online;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Chat;
using osuTK;
namespace osu.Game.Screens.SelectV2
{
public partial class BeatmapMetadataWedge : VisibilityContainer
{
private MetadataDisplay creator = null!;
private MetadataDisplay source = null!;
private MetadataDisplay genre = null!;
private MetadataDisplay language = null!;
private MetadataDisplay tag = null!;
private MetadataDisplay submitted = null!;
private MetadataDisplay ranked = null!;
private Drawable ratingsWedge = null!;
private SuccessRateDisplay successRateDisplay = null!;
private UserRatingDisplay userRatingDisplay = null!;
private RatingSpreadDisplay ratingSpreadDisplay = null!;
private Drawable failRetryWedge = null!;
private FailRetryDisplay failRetryDisplay = null!;
protected override bool StartHidden => true;
[Resolved]
private IBindable<WorkingBeatmap> beatmap { get; set; } = null!;
[Resolved]
private IAPIProvider api { get; set; } = null!;
private IBindable<APIState> apiState = null!;
[Resolved]
private ILinkHandler? linkHandler { get; set; }
[Resolved]
private SongSelect? songSelect { get; set; }
[BackgroundDependencyLoader]
private void load()
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Padding = new MarginPadding { Top = 4f };
Width = 0.9f;
InternalChild = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0f, 4f),
Shear = OsuGame.SHEAR,
Children = new[]
{
new ShearAligningWrapper(new Container
{
CornerRadius = 10,
Masking = true,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
new WedgeBackground(),
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Shear = -OsuGame.SHEAR,
Padding = new MarginPadding { Left = SongSelect.WEDGE_CONTENT_MARGIN, Right = 35, Vertical = 16 },
Children = new Drawable[]
{
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0f, 10f),
Children = new Drawable[]
{
new GridContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
RowDimensions = new[] { new Dimension(GridSizeMode.AutoSize) },
ColumnDimensions = new[]
{
new Dimension(),
new Dimension(),
new Dimension(),
},
Content = new[]
{
new[]
{
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0f, 10f),
Children = new[]
{
creator = new MetadataDisplay("Creator"),
genre = new MetadataDisplay("Genre"),
},
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0f, 10f),
Children = new[]
{
source = new MetadataDisplay("Source"),
language = new MetadataDisplay("Language"),
},
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0f, 10f),
Children = new[]
{
submitted = new MetadataDisplay("Submitted"),
ranked = new MetadataDisplay("Ranked"),
},
},
},
},
},
tag = new MetadataDisplay("Tags"),
},
},
},
},
},
}),
new ShearAligningWrapper(ratingsWedge = new Container
{
Alpha = 0f,
CornerRadius = 10,
Masking = true,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
new WedgeBackground(),
new GridContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Shear = -OsuGame.SHEAR,
RowDimensions = new[] { new Dimension(GridSizeMode.AutoSize) },
ColumnDimensions = new[]
{
new Dimension(),
new Dimension(GridSizeMode.Absolute, 10),
new Dimension(),
new Dimension(GridSizeMode.Absolute, 10),
new Dimension(),
},
Padding = new MarginPadding { Left = SongSelect.WEDGE_CONTENT_MARGIN, Right = 40f, Vertical = 16 },
Content = new[]
{
new[]
{
successRateDisplay = new SuccessRateDisplay(),
Empty(),
userRatingDisplay = new UserRatingDisplay(),
Empty(),
ratingSpreadDisplay = new RatingSpreadDisplay(),
},
},
},
}
}),
new ShearAligningWrapper(failRetryWedge = new Container
{
Alpha = 0f,
CornerRadius = 10,
Masking = true,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
new WedgeBackground(),
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Shear = -OsuGame.SHEAR,
Padding = new MarginPadding { Left = SongSelect.WEDGE_CONTENT_MARGIN, Right = 40f, Vertical = 16 },
Child = failRetryDisplay = new FailRetryDisplay(),
},
},
}),
}
};
}
protected override void LoadComplete()
{
base.LoadComplete();
beatmap.BindValueChanged(_ => updateDisplay());
apiState = api.State.GetBoundCopy();
apiState.BindValueChanged(_ => Scheduler.AddOnce(updateDisplay), true);
}
private const double transition_duration = 300;
protected override void PopIn()
{
this.FadeIn(transition_duration, Easing.OutQuint)
.MoveToX(0, transition_duration, Easing.OutQuint);
updateSubWedgeVisibility();
}
protected override void PopOut()
{
this.FadeOut(transition_duration, Easing.OutQuint)
.MoveToX(-100, transition_duration, Easing.OutQuint);
updateSubWedgeVisibility();
}
private void updateSubWedgeVisibility()
{
// We could consider hiding individual wedges based on zero data in the future.
// Needs some experimentation on what looks good.
if (State.Value == Visibility.Visible && currentOnlineBeatmapSet != null)
{
ratingsWedge.FadeIn(transition_duration, Easing.OutQuint)
.MoveToX(0, transition_duration, Easing.OutQuint);
failRetryWedge.Delay(100)
.FadeIn(transition_duration, Easing.OutQuint)
.MoveToX(0, transition_duration, Easing.OutQuint);
}
else
{
ratingsWedge.FadeOut(transition_duration, Easing.OutQuint)
.MoveToX(-50, transition_duration, Easing.OutQuint);
failRetryWedge.Delay(100)
.FadeOut(transition_duration, Easing.OutQuint)
.MoveToX(-50, transition_duration, Easing.OutQuint);
}
}
private void updateDisplay()
{
var metadata = beatmap.Value.Metadata;
var beatmapSetInfo = beatmap.Value.BeatmapSetInfo;
creator.Data = (metadata.Author.Username, () => linkHandler?.HandleLink(new LinkDetails(LinkAction.OpenUserProfile, metadata.Author)));
if (!string.IsNullOrEmpty(metadata.Source))
source.Data = (metadata.Source, () => songSelect?.Search(metadata.Source));
else
source.Data = ("-", null);
tag.Tags = (metadata.Tags.Split(' '), t => songSelect?.Search(t));
submitted.Date = beatmapSetInfo.DateSubmitted;
ranked.Date = beatmapSetInfo.DateRanked;
if (currentOnlineBeatmapSet == null || currentOnlineBeatmapSet.OnlineID != beatmapSetInfo.OnlineID)
refetchBeatmapSet();
updateOnlineDisplay();
}
private APIBeatmapSet? currentOnlineBeatmapSet;
private GetBeatmapSetRequest? currentRequest;
private void refetchBeatmapSet()
{
var beatmapSetInfo = beatmap.Value.BeatmapSetInfo;
currentRequest?.Cancel();
currentRequest = null;
currentOnlineBeatmapSet = null;
if (beatmapSetInfo.OnlineID >= 1)
{
// todo: consider introducing a BeatmapSetLookupCache for caching benefits.
currentRequest = new GetBeatmapSetRequest(beatmapSetInfo.OnlineID);
currentRequest.Failure += _ => updateOnlineDisplay();
currentRequest.Success += s =>
{
currentOnlineBeatmapSet = s;
updateOnlineDisplay();
};
api.Queue(currentRequest);
}
}
private void updateOnlineDisplay()
{
if (currentRequest?.CompletionState == APIRequestCompletionState.Waiting)
{
genre.Data = null;
language.Data = null;
return;
}
if (currentOnlineBeatmapSet == null)
{
genre.Data = ("-", null);
language.Data = ("-", null);
}
else
{
var beatmapInfo = beatmap.Value.BeatmapInfo;
var onlineBeatmapSet = currentOnlineBeatmapSet;
var onlineBeatmap = onlineBeatmapSet.Beatmaps.SingleOrDefault(b => b.OnlineID == beatmapInfo.OnlineID);
genre.Data = (onlineBeatmapSet.Genre.Name, () => songSelect?.Search(onlineBeatmapSet.Genre.Name));
language.Data = (onlineBeatmapSet.Language.Name, () => songSelect?.Search(onlineBeatmapSet.Language.Name));
if (onlineBeatmap != null)
{
userRatingDisplay.Data = onlineBeatmapSet.Ratings;
ratingSpreadDisplay.Data = onlineBeatmapSet.Ratings;
successRateDisplay.Data = (onlineBeatmap.PassCount, onlineBeatmap.PlayCount);
failRetryDisplay.Data = onlineBeatmap.FailTimes ?? new APIFailTimes();
}
}
updateSubWedgeVisibility();
}
}
}