diff --git a/osu.Game/Beatmaps/MetadataUtils.cs b/osu.Game/Beatmaps/MetadataUtils.cs new file mode 100644 index 0000000000..106de43493 --- /dev/null +++ b/osu.Game/Beatmaps/MetadataUtils.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. + +#nullable enable + +using System.Linq; +using System.Text; + +namespace osu.Game.Beatmaps +{ + /// + /// Groups utility methods used to handle beatmap metadata. + /// + public static class MetadataUtils + { + /// + /// Returns if the character can be used in and fields. + /// Characters not matched by this method can be placed in and . + /// + public static bool IsRomanised(char c) => c <= 0xFF; + + /// + /// Returns if the string can be used in and fields. + /// Strings not matched by this method can be placed in and . + /// + public static bool IsRomanised(string? str) => str == null || str.All(IsRomanised); + + /// + /// Returns a copy of with all characters that do not match removed. + /// + public static string StripNonRomanisedCharacters(string? str) + { + if (string.IsNullOrEmpty(str)) + return string.Empty; + + var stringBuilder = new StringBuilder(str.Length); + + foreach (var c in str) + { + if (IsRomanised(c)) + stringBuilder.Append(c); + } + + return stringBuilder.ToString().Trim(); + } + } +} diff --git a/osu.Game/Graphics/UserInterfaceV2/LabelledTextBox.cs b/osu.Game/Graphics/UserInterfaceV2/LabelledTextBox.cs index 266eb11319..cf2249685d 100644 --- a/osu.Game/Graphics/UserInterfaceV2/LabelledTextBox.cs +++ b/osu.Game/Graphics/UserInterfaceV2/LabelledTextBox.cs @@ -45,14 +45,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 Component.BorderColour = colours.Blue; } - protected virtual OsuTextBox CreateTextBox() => new OsuTextBox - { - CommitOnFocusLost = true, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.X, - CornerRadius = CORNER_RADIUS, - }; + protected virtual OsuTextBox CreateTextBox() => new OsuTextBox(); public override bool AcceptsFocus => true; @@ -64,6 +57,12 @@ namespace osu.Game.Graphics.UserInterfaceV2 protected override OsuTextBox CreateComponent() => CreateTextBox().With(t => { + t.CommitOnFocusLost = true; + t.Anchor = Anchor.Centre; + t.Origin = Anchor.Centre; + t.RelativeSizeAxes = Axes.X; + t.CornerRadius = CORNER_RADIUS; + t.OnCommit += (sender, newText) => OnCommit?.Invoke(sender, newText); }); } diff --git a/osu.Game/Screens/Edit/Setup/LabelledRomanisedTextBox.cs b/osu.Game/Screens/Edit/Setup/LabelledRomanisedTextBox.cs new file mode 100644 index 0000000000..ee9d86029e --- /dev/null +++ b/osu.Game/Screens/Edit/Setup/LabelledRomanisedTextBox.cs @@ -0,0 +1,20 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Beatmaps; +using osu.Game.Graphics.UserInterface; +using osu.Game.Graphics.UserInterfaceV2; + +namespace osu.Game.Screens.Edit.Setup +{ + internal class LabelledRomanisedTextBox : LabelledTextBox + { + protected override OsuTextBox CreateTextBox() => new RomanisedTextBox(); + + private class RomanisedTextBox : OsuTextBox + { + protected override bool CanAddCharacter(char character) + => MetadataUtils.IsRomanised(character); + } + } +} diff --git a/osu.Game/Screens/Edit/Setup/MetadataSection.cs b/osu.Game/Screens/Edit/Setup/MetadataSection.cs index 6d14f6a66f..c543242957 100644 --- a/osu.Game/Screens/Edit/Setup/MetadataSection.cs +++ b/osu.Game/Screens/Edit/Setup/MetadataSection.cs @@ -5,6 +5,7 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics.UserInterface; using osu.Framework.Localisation; +using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterfaceV2; namespace osu.Game.Screens.Edit.Setup @@ -12,7 +13,10 @@ namespace osu.Game.Screens.Edit.Setup internal class MetadataSection : SetupSection { private LabelledTextBox artistTextBox; + private LabelledTextBox romanisedArtistTextBox; + private LabelledTextBox titleTextBox; + private LabelledTextBox romanisedTitleTextBox; private LabelledTextBox creatorTextBox; private LabelledTextBox difficultyTextBox; @@ -24,28 +28,43 @@ namespace osu.Game.Screens.Edit.Setup [BackgroundDependencyLoader] private void load() { + var metadata = Beatmap.Metadata; + Children = new[] { - artistTextBox = createTextBox("Artist", Beatmap.Metadata.Artist), - titleTextBox = createTextBox("Title", Beatmap.Metadata.Title), + artistTextBox = createTextBox("Artist", + !string.IsNullOrEmpty(metadata.ArtistUnicode) ? metadata.ArtistUnicode : metadata.Artist), + romanisedArtistTextBox = createTextBox("Romanised Artist", + !string.IsNullOrEmpty(metadata.Artist) ? metadata.Artist : MetadataUtils.StripNonRomanisedCharacters(metadata.ArtistUnicode)), + Empty(), - creatorTextBox = createTextBox("Creator", Beatmap.Metadata.AuthorString), - difficultyTextBox = createTextBox("Difficulty Name", Beatmap.BeatmapInfo.Version), - sourceTextBox = createTextBox("Source", Beatmap.Metadata.Source), - tagsTextBox = createTextBox("Tags", Beatmap.Metadata.Tags) + + titleTextBox = createTextBox("Title", + !string.IsNullOrEmpty(metadata.TitleUnicode) ? metadata.TitleUnicode : metadata.Title), + romanisedTitleTextBox = createTextBox("Romanised Title", + !string.IsNullOrEmpty(metadata.Title) ? metadata.Title : MetadataUtils.StripNonRomanisedCharacters(metadata.ArtistUnicode)), + + Empty(), + + creatorTextBox = createTextBox("Creator", metadata.AuthorString), + difficultyTextBox = createTextBox("Difficulty Name", Beatmap.BeatmapInfo.Version), + sourceTextBox = createTextBox("Source", metadata.Source), + tagsTextBox = createTextBox("Tags", metadata.Tags) }; foreach (var item in Children.OfType()) item.OnCommit += onCommit; } - private LabelledTextBox createTextBox(string label, string initialValue) => new LabelledTextBox - { - Label = label, - FixedLabelWidth = LABEL_WIDTH, - Current = { Value = initialValue }, - TabbableContentContainer = this - }; + private TTextBox createTextBox(string label, string initialValue) + where TTextBox : LabelledTextBox, new() + => new TTextBox + { + Label = label, + FixedLabelWidth = LABEL_WIDTH, + Current = { Value = initialValue }, + TabbableContentContainer = this + }; protected override void LoadComplete() { @@ -53,16 +72,43 @@ namespace osu.Game.Screens.Edit.Setup if (string.IsNullOrEmpty(artistTextBox.Current.Value)) GetContainingInputManager().ChangeFocus(artistTextBox); + + artistTextBox.Current.BindValueChanged(artist => transferIfRomanised(artist.NewValue, romanisedArtistTextBox)); + titleTextBox.Current.BindValueChanged(title => transferIfRomanised(title.NewValue, romanisedTitleTextBox)); + updateReadOnlyState(); + } + + private void transferIfRomanised(string value, LabelledTextBox target) + { + if (MetadataUtils.IsRomanised(value)) + target.Current.Value = value; + + updateReadOnlyState(); + updateMetadata(); + } + + private void updateReadOnlyState() + { + romanisedArtistTextBox.ReadOnly = MetadataUtils.IsRomanised(artistTextBox.Current.Value); + romanisedTitleTextBox.ReadOnly = MetadataUtils.IsRomanised(titleTextBox.Current.Value); } private void onCommit(TextBox sender, bool newText) { if (!newText) return; - // for now, update these on commit rather than making BeatmapMetadata bindables. + // for now, update on commit rather than making BeatmapMetadata bindables. // after switching database engines we can reconsider if switching to bindables is a good direction. - Beatmap.Metadata.Artist = artistTextBox.Current.Value; - Beatmap.Metadata.Title = titleTextBox.Current.Value; + updateMetadata(); + } + + private void updateMetadata() + { + Beatmap.Metadata.ArtistUnicode = artistTextBox.Current.Value; + Beatmap.Metadata.Artist = romanisedArtistTextBox.Current.Value; + + Beatmap.Metadata.TitleUnicode = titleTextBox.Current.Value; + Beatmap.Metadata.Title = romanisedTitleTextBox.Current.Value; Beatmap.Metadata.AuthorString = creatorTextBox.Current.Value; Beatmap.BeatmapInfo.Version = difficultyTextBox.Current.Value;