Allow for the Value of BeatmapInfoDrawable to be formatted

This commit is contained in:
C0D3 M4513R 2022-11-03 18:54:55 +01:00
parent 502bfa950e
commit a435e365ea
No known key found for this signature in database
GPG Key ID: 3FF32B5F41A39834

View File

@ -2,12 +2,14 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System; using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using JetBrains.Annotations; using JetBrains.Annotations;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Localisation; using osu.Framework.Localisation;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Configuration; using osu.Game.Configuration;
@ -20,12 +22,13 @@ using osu.Game.Resources.Localisation.Web;
namespace osu.Game.Skinning.Components namespace osu.Game.Skinning.Components
{ {
[UsedImplicitly] [UsedImplicitly]
public class BeatmapInfoDrawable : Container, ISkinnableDrawable, IHasTooltip public class BeatmapInfoDrawable : Container, ISkinnableDrawable
{ {
private const BeatmapInfo default_beatmap_info = BeatmapInfo.StarRating;
public bool UsesFixedAnchor { get; set; } public bool UsesFixedAnchor { get; set; }
[SettingSource("Tracked Beatmap Info", "Which part of the BeatmapInformation should be tracked")] [SettingSource("Tracked Beatmap Info/Label", "Which part of the BeatmapInformation should be displayed. Gets overridden by complex changes to ValueFormat")]
public Bindable<BeatmapInfo> Type { get; } = new Bindable<BeatmapInfo>(BeatmapInfo.StarRating); public Bindable<BeatmapInfo> Type { get; } = new Bindable<BeatmapInfo>(default_beatmap_info);
[SettingSource("Show Label", "Should a Label be shown, as to which status is currently Displayed?")] [SettingSource("Show Label", "Should a Label be shown, as to which status is currently Displayed?")]
public BindableBool ShowLabel { get; } = new BindableBool(true); public BindableBool ShowLabel { get; } = new BindableBool(true);
@ -45,26 +48,37 @@ namespace osu.Game.Skinning.Components
[SettingSource("Show Label Suffix", "Should the Label Suffix be included?")] [SettingSource("Show Label Suffix", "Should the Label Suffix be included?")]
public BindableBool ShowLabelSuffix { get; } = new BindableBool(true); public BindableBool ShowLabelSuffix { get; } = new BindableBool(true);
[SettingSource("Value Prefix", "Add something to be shown before the Value")] [SettingSource("Value Formatting", "Bypass the restriction of 1 Info per element. Format is '{'+Type+'}' to substitue values. e.g. '{Song}' ")]
public Bindable<string> ValuePrefix { get; set; } = new Bindable<string>(""); public Bindable<string> ValueFormat { get; set; } = new Bindable<string>("{" + default_beatmap_info + "}");
[SettingSource("Show Value Prefix", "Should the Value Prefix be included?")]
public BindableBool ShowValuePrefix { get; } = new BindableBool();
[SettingSource("Value Suffix", "Add something to be shown after the Value")]
public Bindable<string> ValueSuffix { get; set; } = new Bindable<string>("");
[SettingSource("Show Value Suffix", "Should the Value Suffix be included?")]
public BindableBool ShowValueSuffix { get; } = new BindableBool();
[Resolved] [Resolved]
private IBindable<WorkingBeatmap> beatmap { get; set; } = null!; private IBindable<WorkingBeatmap> beatmap { get; set; } = null!;
private readonly Dictionary<BeatmapInfo, LocalisableString> valueDictionary = new Dictionary<BeatmapInfo, LocalisableString>();
private static readonly ImmutableDictionary<BeatmapInfo, LocalisableString> label_dictionary;
private readonly OsuSpriteText text; private readonly OsuSpriteText text;
public LocalisableString TooltipText { get; set; } static BeatmapInfoDrawable()
private LocalisableString value; {
private LocalisableString labelText; label_dictionary = new Dictionary<BeatmapInfo, LocalisableString>
{
[BeatmapInfo.CircleSize] = BeatmapsetsStrings.ShowStatsCs,
[BeatmapInfo.Accuracy] = BeatmapsetsStrings.ShowStatsAccuracy,
[BeatmapInfo.HPDrain] = BeatmapsetsStrings.ShowStatsDrain,
[BeatmapInfo.ApproachRate] = BeatmapsetsStrings.ShowStatsAr,
[BeatmapInfo.StarRating] = BeatmapsetsStrings.ShowStatsStars,
[BeatmapInfo.Song] = EditorSetupStrings.Title,
[BeatmapInfo.Artist] = EditorSetupStrings.Artist,
[BeatmapInfo.Difficulty] = EditorSetupStrings.DifficultyHeader,
//todo: is there a good alternative, to NotificationsOptionsMapping?
[BeatmapInfo.Mapper] = AccountsStrings.NotificationsOptionsMapping,
[BeatmapInfo.Length] = ArtistStrings.TracklistLength,
[BeatmapInfo.Status] = BeatmapDiscussionsStrings.IndexFormBeatmapsetStatusDefault,
[BeatmapInfo.BPM] = BeatmapsetsStrings.ShowStatsBpm,
[BeatmapInfo.Custom] = BeatmapInfo.Custom.ToString()
}.ToImmutableDictionary();
}
public BeatmapInfoDrawable() public BeatmapInfoDrawable()
{ {
@ -78,23 +92,78 @@ namespace osu.Game.Skinning.Components
Font = OsuFont.Default.With(size: 40) Font = OsuFont.Default.With(size: 40)
} }
}; };
foreach (var type in Enum.GetValues(typeof(BeatmapInfo)).Cast<BeatmapInfo>())
{
valueDictionary[type] = type.ToString();
}
}
/// <summary>
/// This will return the if the format-String contains of a singular replacement of type info, or not.
/// If there is only one one replacement of type info, it will also return the prefix/suffix (or null if no prefix/suffix exists).
/// </summary>
/// <param name="format">The format-String to work on</param>
/// <param name="info">The replacement Type to look for</param>
/// <returns>(true, prefix, suffix), if there is only one replacement of type info. Else (false, null, null)</returns>
private static (bool, string?, string?) isOnlyPrefixedOrSuffixed(string format, BeatmapInfo info)
{
string[] s = format.Split("{" + info + "}");
foreach (string si in s)
{
foreach (var type in Enum.GetValues(typeof(BeatmapInfo)).Cast<BeatmapInfo>())
{
if (si.Contains("{" + type + "}")) return (false, null, null);
}
}
//Debug.WriteLine($"format:'{format}', type:{info} is only prefixed/suffixed");
return (true,
s.Length >= 1 ? s[0] : null, //prefix
s.Length >= 2 ? s[1] : null //suffix
);
} }
protected override void LoadComplete() protected override void LoadComplete()
{ {
base.LoadComplete(); base.LoadComplete();
Type.BindValueChanged(_ => updateBeatmapContent()); Type.BindValueChanged(v =>
beatmap.BindValueChanged(_ => updateBeatmapContent(), true); {
string newDefault = "{" + v.NewValue + "}";
bool custom = v.NewValue == BeatmapInfo.Custom;
//If the ValueFormat is Default and the user did not change anything we should be able to just swap the strings.
//If it was Default before, it should be default after the Type is changed.
if (ValueFormat.IsDefault && !custom)
ValueFormat.Value = newDefault;
else
{
//In this if statement we decide if the ValueFormat has been trivially changed (so only been prefixed or suffixed)
(bool preOrSuffixed, string? prefix, string? suffix) = isOnlyPrefixedOrSuffixed(ValueFormat.Value, v.OldValue);
if (preOrSuffixed)
//If it has, we can keep the prefix and suffix and just change the thing that would be substituted.
ValueFormat.Value = (prefix ?? "") + newDefault + (suffix ?? "");
//else we just keep the ValueFormat. I determine here, that the user probably knows what they are doing, and how the ValueFormat works.
}
//Only if we could preserve the ValueFormat (so nothing was changed except a static prefix/suffix) I want to set the new Default.
ValueFormat.Default = newDefault;
updateLabel();
});
ValueFormat.BindValueChanged(f => updateLabel(), true);
beatmap.BindValueChanged(b =>
{
UpdateBeatmapContent(b.NewValue);
updateLabel();
}, true);
ShowLabel.BindValueChanged(_ => updateLabel()); ShowLabel.BindValueChanged(_ => updateLabel());
ValueBeforeLabel.BindValueChanged(_ => updateLabel()); ValueBeforeLabel.BindValueChanged(_ => updateLabel());
LabelPrefix.BindValueChanged(_ => updateLabel()); LabelPrefix.BindValueChanged(_ => updateLabel());
ShowLabelPrefix.BindValueChanged(_ => updateLabel()); ShowLabelPrefix.BindValueChanged(_ => updateLabel());
LabelSuffix.BindValueChanged(_ => updateLabel()); LabelSuffix.BindValueChanged(_ => updateLabel());
ShowLabelSuffix.BindValueChanged(_ => updateLabel()); ShowLabelSuffix.BindValueChanged(_ => updateLabel());
ValuePrefix.BindValueChanged(_ => updateLabel());
ShowValuePrefix.BindValueChanged(_ => updateLabel());
ValueSuffix.BindValueChanged(_ => updateLabel());
ShowValueSuffix.BindValueChanged(_ => updateLabel());
} }
private LocalisableString getLabelText() private LocalisableString getLabelText()
@ -103,16 +172,20 @@ namespace osu.Game.Skinning.Components
return LocalisableString.Format("{0}{1}{2}", return LocalisableString.Format("{0}{1}{2}",
ShowLabelPrefix.Value ? LabelPrefix.Value : "", ShowLabelPrefix.Value ? LabelPrefix.Value : "",
labelText, label_dictionary[Type.Value],
ShowLabelSuffix.Value ? LabelSuffix.Value : ""); ShowLabelSuffix.Value ? LabelSuffix.Value : "");
} }
private LocalisableString getValueText() private LocalisableString getValueText()
{ {
return LocalisableString.Format("{0}{1}{2}", string value = ValueFormat.Value;
ShowValuePrefix.Value ? ValuePrefix.Value : "",
value, foreach (var type in Enum.GetValues(typeof(BeatmapInfo)).Cast<BeatmapInfo>())
ShowValueSuffix.Value ? ValueSuffix.Value : ""); {
value = value.Replace("{" + type + "}", valueDictionary[type].ToString());
}
return value;
} }
private void updateLabel() private void updateLabel()
@ -126,124 +199,74 @@ namespace osu.Game.Skinning.Components
Height = text.Height; Height = text.Height;
} }
private void updateBeatmapContent() public void UpdateBeatmapContent(WorkingBeatmap workingBeatmap)
{ {
switch (Type.Value) //update cs
double cs = workingBeatmap.BeatmapInfo.Difficulty.CircleSize;
valueDictionary[BeatmapInfo.CircleSize] = cs.ToString("F2");
//update HP
double hp = workingBeatmap.BeatmapInfo.Difficulty.DrainRate;
valueDictionary[BeatmapInfo.HPDrain] = hp.ToString("F2");
//update od
double od = workingBeatmap.BeatmapInfo.Difficulty.OverallDifficulty;
valueDictionary[BeatmapInfo.Accuracy] = od.ToString("F2");
//update ar
double ar = workingBeatmap.BeatmapInfo.Difficulty.ApproachRate;
valueDictionary[BeatmapInfo.ApproachRate] = ar.ToString("F2");
//update sr
double sr = workingBeatmap.BeatmapInfo.StarRating;
valueDictionary[BeatmapInfo.StarRating] = sr.ToString("F2");
//update song title
valueDictionary[BeatmapInfo.Song] = workingBeatmap.BeatmapInfo.Metadata.Title;
//update artist
valueDictionary[BeatmapInfo.Artist] = workingBeatmap.BeatmapInfo.Metadata.Artist;
//update difficulty name
valueDictionary[BeatmapInfo.Difficulty] = workingBeatmap.BeatmapInfo.DifficultyName;
//update mapper
valueDictionary[BeatmapInfo.Mapper] = workingBeatmap.BeatmapInfo.Metadata.Author.Username;
//update Length
valueDictionary[BeatmapInfo.Length] = TimeSpan.FromMilliseconds(workingBeatmap.BeatmapInfo.Length).ToFormattedDuration();
//update Status
valueDictionary[BeatmapInfo.Status] = GetBetmapStatus(workingBeatmap.BeatmapInfo.Status);
//update BPM
valueDictionary[BeatmapInfo.BPM] = workingBeatmap.BeatmapInfo.BPM.ToString("F2");
valueDictionary[BeatmapInfo.Custom] = BeatmapInfo.Custom.ToString();
}
public static LocalisableString GetBetmapStatus(BeatmapOnlineStatus status)
{ {
case BeatmapInfo.CircleSize:
double cs = beatmap.Value.BeatmapInfo.Difficulty.CircleSize;
labelText = TooltipText = BeatmapsetsStrings.ShowStatsCs;
value = cs.ToString("F2");
break;
case BeatmapInfo.HPDrain:
double hp = beatmap.Value.BeatmapInfo.Difficulty.DrainRate;
labelText = TooltipText = BeatmapsetsStrings.ShowStatsDrain;
value = hp.ToString("F2");
break;
case BeatmapInfo.Accuracy:
double od = beatmap.Value.BeatmapInfo.Difficulty.OverallDifficulty;
labelText = TooltipText = BeatmapsetsStrings.ShowStatsAccuracy;
value = od.ToString("F2");
break;
case BeatmapInfo.ApproachRate:
double ar = beatmap.Value.BeatmapInfo.Difficulty.ApproachRate;
labelText = TooltipText = BeatmapsetsStrings.ShowStatsAr;
value = ar.ToString("F2");
break;
case BeatmapInfo.StarRating:
double sr = beatmap.Value.BeatmapInfo.StarRating;
labelText = TooltipText = BeatmapsetsStrings.ShowStatsStars;
value = sr.ToString("F2");
break;
case BeatmapInfo.Song:
string title = beatmap.Value.BeatmapInfo.Metadata.Title;
labelText = TooltipText = EditorSetupStrings.Title;
value = title;
break;
case BeatmapInfo.Artist:
string artist = beatmap.Value.BeatmapInfo.Metadata.Artist;
labelText = EditorSetupStrings.Artist;
TooltipText = BeatmapsetsStrings.ShowDetailsByArtist(artist);
value = artist;
break;
case BeatmapInfo.Difficulty:
string diff = beatmap.Value.BeatmapInfo.DifficultyName;
labelText = TooltipText = EditorSetupStrings.DifficultyHeader;
text.Current.Value = diff;
break;
case BeatmapInfo.Mapper:
string mapper = beatmap.Value.BeatmapInfo.Metadata.Author.Username;
//todo: is there a good alternative, to NotificationsOptionsMapping?
labelText = AccountsStrings.NotificationsOptionsMapping;
TooltipText = BeatmapsetsStrings.ShowDetailsMappedBy(mapper);
value = mapper;
break;
case BeatmapInfo.Length:
labelText = TooltipText = ArtistStrings.TracklistLength;
value = TimeSpan.FromMilliseconds(beatmap.Value.BeatmapInfo.Length).ToFormattedDuration();
break;
case BeatmapInfo.Status:
BeatmapOnlineStatus status = beatmap.Value.BeatmapInfo.Status;
TooltipText = labelText = BeatmapDiscussionsStrings.IndexFormBeatmapsetStatusDefault;
switch (status) switch (status)
{ {
case BeatmapOnlineStatus.Approved: case BeatmapOnlineStatus.Approved:
value = BeatmapsetsStrings.ShowStatusApproved; return BeatmapsetsStrings.ShowStatusApproved;
break;
case BeatmapOnlineStatus.Graveyard: case BeatmapOnlineStatus.Graveyard:
value = BeatmapsetsStrings.ShowStatusGraveyard; return BeatmapsetsStrings.ShowStatusGraveyard;
break;
case BeatmapOnlineStatus.Loved: case BeatmapOnlineStatus.Loved:
value = BeatmapsetsStrings.ShowStatusLoved; return BeatmapsetsStrings.ShowStatusLoved;
break;
case BeatmapOnlineStatus.None: case BeatmapOnlineStatus.None:
value = "None"; return "None";
break;
case BeatmapOnlineStatus.Pending: case BeatmapOnlineStatus.Pending:
value = BeatmapsetsStrings.ShowStatusPending; return BeatmapsetsStrings.ShowStatusPending;
break;
case BeatmapOnlineStatus.Qualified: case BeatmapOnlineStatus.Qualified:
value = BeatmapsetsStrings.ShowStatusQualified; return BeatmapsetsStrings.ShowStatusQualified;
break;
case BeatmapOnlineStatus.Ranked: case BeatmapOnlineStatus.Ranked:
value = BeatmapsetsStrings.ShowStatusRanked; return BeatmapsetsStrings.ShowStatusRanked;
break;
case BeatmapOnlineStatus.LocallyModified: case BeatmapOnlineStatus.LocallyModified:
value = SongSelectStrings.LocallyModified; return SongSelectStrings.LocallyModified;
break;
case BeatmapOnlineStatus.WIP: case BeatmapOnlineStatus.WIP:
value = BeatmapsetsStrings.ShowStatusWip; return BeatmapsetsStrings.ShowStatusWip;
break;
default:
return @"null";
} }
break;
case BeatmapInfo.BPM:
labelText = TooltipText = BeatmapsetsStrings.ShowStatsBpm;
value = beatmap.Value.BeatmapInfo.BPM.ToString("F2");
break;
}
updateLabel();
} }
} }
@ -261,5 +284,6 @@ namespace osu.Game.Skinning.Components
Length, Length,
Status, Status,
BPM, BPM,
Custom,
} }
} }