diff --git a/osu.Android.props b/osu.Android.props index dec994bcb2..4887e3a95f 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCard.cs b/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCard.cs new file mode 100644 index 0000000000..d1b21547dc --- /dev/null +++ b/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCard.cs @@ -0,0 +1,181 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Drawables.Cards; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Overlays; +using osu.Game.Users; +using osuTK; + +namespace osu.Game.Tests.Visual.Beatmaps +{ + public class TestSceneBeatmapCard : OsuTestScene + { + private APIBeatmapSet[] testCases; + + #region Test case generation + + [BackgroundDependencyLoader] + private void load() + { + var normal = CreateAPIBeatmapSet(Ruleset.Value); + normal.HasVideo = true; + normal.HasStoryboard = true; + + var undownloadable = getUndownloadableBeatmapSet(); + + var someDifficulties = getManyDifficultiesBeatmapSet(11); + someDifficulties.Title = someDifficulties.TitleUnicode = "some difficulties"; + someDifficulties.Status = BeatmapSetOnlineStatus.Qualified; + + var manyDifficulties = getManyDifficultiesBeatmapSet(100); + manyDifficulties.Status = BeatmapSetOnlineStatus.Pending; + + var explicitMap = CreateAPIBeatmapSet(Ruleset.Value); + explicitMap.HasExplicitContent = true; + + var featuredMap = CreateAPIBeatmapSet(Ruleset.Value); + featuredMap.TrackId = 1; + + var explicitFeaturedMap = CreateAPIBeatmapSet(Ruleset.Value); + explicitFeaturedMap.HasExplicitContent = true; + explicitFeaturedMap.TrackId = 2; + + var longName = CreateAPIBeatmapSet(Ruleset.Value); + longName.Title = longName.TitleUnicode = "this track has an incredibly and implausibly long title"; + longName.Artist = longName.ArtistUnicode = "and this artist! who would have thunk it. it's really such a long name."; + longName.HasExplicitContent = true; + longName.TrackId = 444; + + testCases = new[] + { + normal, + undownloadable, + someDifficulties, + manyDifficulties, + explicitMap, + featuredMap, + explicitFeaturedMap, + longName + }; + } + + private APIBeatmapSet getUndownloadableBeatmapSet() => new APIBeatmapSet + { + OnlineID = 123, + Title = "undownloadable beatmap", + Artist = "test", + Source = "more tests", + Author = new User + { + Username = "BanchoBot", + Id = 3, + }, + Availability = new BeatmapSetOnlineAvailability + { + DownloadDisabled = true, + }, + Preview = @"https://b.ppy.sh/preview/12345.mp3", + PlayCount = 123, + FavouriteCount = 456, + BPM = 111, + HasVideo = true, + HasStoryboard = true, + Covers = new BeatmapSetOnlineCovers(), + Beatmaps = new List + { + new APIBeatmap + { + RulesetID = Ruleset.Value.OnlineID, + DifficultyName = "Test", + StarRating = 6.42, + } + } + }; + + private static APIBeatmapSet getManyDifficultiesBeatmapSet(int count) + { + var beatmaps = new List(); + + for (int i = 0; i < count; i++) + { + beatmaps.Add(new APIBeatmap + { + RulesetID = i % 4, + StarRating = 2 + i % 4 * 2, + }); + } + + return new APIBeatmapSet + { + OnlineID = 1, + Title = "many difficulties beatmap", + Artist = "test", + Author = new User + { + Username = "BanchoBot", + Id = 3, + }, + HasVideo = true, + HasStoryboard = true, + Covers = new BeatmapSetOnlineCovers(), + Beatmaps = beatmaps, + }; + } + + #endregion + + private Drawable createContent(OverlayColourScheme colourScheme, Func creationFunc) + { + var colourProvider = new OverlayColourProvider(colourScheme); + + return new DependencyProvidingContainer + { + RelativeSizeAxes = Axes.Both, + CachedDependencies = new (Type, object)[] + { + (typeof(OverlayColourProvider), colourProvider) + }, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colourProvider.Background5 + }, + new BasicScrollContainer + { + RelativeSizeAxes = Axes.Both, + Child = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Full, + Padding = new MarginPadding(10), + Spacing = new Vector2(10), + ChildrenEnumerable = testCases.Select(creationFunc) + } + } + } + }; + } + + private void createTestCase(Func creationFunc) + { + foreach (var scheme in Enum.GetValues(typeof(OverlayColourScheme)).Cast()) + AddStep($"set {scheme} scheme", () => Child = createContent(scheme, creationFunc)); + } + + [Test] + public void TestNormal() => createTestCase(beatmapSetInfo => new BeatmapCard(beatmapSetInfo)); + } +} diff --git a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs index 9a0cdb387d..035f438b89 100644 --- a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs +++ b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs @@ -131,7 +131,7 @@ namespace osu.Game.Beatmaps var localRulesetInfo = rulesetInfo as RulesetInfo; // Difficulty can only be computed if the beatmap and ruleset are locally available. - if (localBeatmapInfo == null || localRulesetInfo == null) + if (localBeatmapInfo == null || localBeatmapInfo.ID == 0 || localRulesetInfo == null) { // If not, fall back to the existing star difficulty (e.g. from an online source). return Task.FromResult(new StarDifficulty(beatmapInfo.StarRating, (beatmapInfo as IBeatmapOnlineInfo)?.MaxCombo ?? 0)); diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 0509a9db47..0caee8f9cd 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -249,6 +249,23 @@ namespace osu.Game.Beatmaps public IBindable>> DownloadFailed => beatmapModelDownloader.DownloadFailed; + // Temporary method until this class supports IBeatmapSetInfo or otherwise. + public bool Download(IBeatmapSetInfo model, bool minimiseDownloadSize = false) + { + return beatmapModelDownloader.Download(new BeatmapSetInfo + { + OnlineBeatmapSetID = model.OnlineID, + Metadata = new BeatmapMetadata + { + Title = model.Metadata?.Title, + Artist = model.Metadata?.Artist, + TitleUnicode = model.Metadata?.TitleUnicode, + ArtistUnicode = model.Metadata?.ArtistUnicode, + Author = new User { Username = model.Metadata?.Author }, + } + }, minimiseDownloadSize); + } + public bool Download(BeatmapSetInfo model, bool minimiseDownloadSize = false) { return beatmapModelDownloader.Download(model, minimiseDownloadSize); diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs new file mode 100644 index 0000000000..8136ebbd70 --- /dev/null +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs @@ -0,0 +1,280 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Input.Events; +using osu.Framework.Localisation; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Overlays; +using osu.Game.Overlays.BeatmapSet; +using osuTK; +using osu.Game.Overlays.BeatmapListing.Panels; +using osu.Game.Resources.Localisation.Web; +using osuTK.Graphics; + +namespace osu.Game.Beatmaps.Drawables.Cards +{ + public class BeatmapCard : OsuClickableContainer + { + public const float TRANSITION_DURATION = 400; + + private const float width = 408; + private const float height = 100; + private const float corner_radius = 10; + + private readonly APIBeatmapSet beatmapSet; + + private UpdateableOnlineBeatmapSetCover leftCover; + private FillFlowContainer iconArea; + + private Container mainContent; + private BeatmapCardContentBackground mainContentBackground; + + private GridContainer titleContainer; + private GridContainer artistContainer; + + [Resolved] + private OverlayColourProvider colourProvider { get; set; } + + public BeatmapCard(APIBeatmapSet beatmapSet) + : base(HoverSampleSet.Submit) + { + this.beatmapSet = beatmapSet; + } + + [BackgroundDependencyLoader] + private void load() + { + Width = width; + Height = height; + CornerRadius = corner_radius; + Masking = true; + + InternalChildren = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colourProvider.Background3 + }, + new Container + { + Name = @"Left (icon) area", + Size = new Vector2(height), + Children = new Drawable[] + { + leftCover = new UpdateableOnlineBeatmapSetCover(BeatmapSetCoverType.List) + { + RelativeSizeAxes = Axes.Both, + OnlineInfo = beatmapSet + }, + iconArea = new FillFlowContainer + { + Margin = new MarginPadding(5), + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(1) + } + } + }, + mainContent = new Container + { + Name = @"Main content", + X = height - corner_radius, + Height = height, + CornerRadius = corner_radius, + Masking = true, + Children = new Drawable[] + { + mainContentBackground = new BeatmapCardContentBackground(beatmapSet) + { + RelativeSizeAxes = Axes.Both, + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding + { + Horizontal = 10, + Vertical = 4 + }, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + titleContainer = new GridContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + ColumnDimensions = new[] + { + new Dimension(), + new Dimension(GridSizeMode.AutoSize) + }, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize) + }, + Content = new[] + { + new[] + { + new OsuSpriteText + { + Text = new RomanisableString(beatmapSet.TitleUnicode, beatmapSet.Title), + Font = OsuFont.Default.With(size: 22.5f, weight: FontWeight.SemiBold), + RelativeSizeAxes = Axes.X, + Truncate = true + }, + Empty() + } + } + }, + artistContainer = new GridContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + ColumnDimensions = new[] + { + new Dimension(), + new Dimension(GridSizeMode.AutoSize) + }, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize) + }, + Content = new[] + { + new[] + { + new OsuSpriteText + { + Text = createArtistText(), + Font = OsuFont.Default.With(size: 17.5f, weight: FontWeight.SemiBold), + RelativeSizeAxes = Axes.X, + Truncate = true + }, + Empty() + }, + } + }, + new LinkFlowContainer(s => + { + s.Shadow = false; + s.Font = OsuFont.GetFont(size: 14, weight: FontWeight.SemiBold); + }).With(d => + { + d.AutoSizeAxes = Axes.Both; + d.Margin = new MarginPadding { Top = 2 }; + d.AddText("mapped by ", t => t.Colour = colourProvider.Content2); + d.AddUserLink(beatmapSet.Author); + }), + } + }, + new FillFlowContainer + { + Name = @"Bottom content", + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Horizontal, + Padding = new MarginPadding + { + Horizontal = 10, + Vertical = 4 + }, + Spacing = new Vector2(4, 0), + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Children = new Drawable[] + { + new BeatmapSetOnlineStatusPill + { + AutoSizeAxes = Axes.Both, + Status = beatmapSet.Status, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft + }, + new DifficultySpectrumDisplay(beatmapSet) + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + DotSize = new Vector2(6, 12) + } + } + } + } + } + }; + + if (beatmapSet.HasVideo) + iconArea.Add(new IconPill(FontAwesome.Solid.Film)); + + if (beatmapSet.HasStoryboard) + iconArea.Add(new IconPill(FontAwesome.Solid.Image)); + + if (beatmapSet.HasExplicitContent) + { + titleContainer.Content[0][1] = new ExplicitContentBeatmapPill + { + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + Margin = new MarginPadding { Left = 5 } + }; + } + + if (beatmapSet.TrackId != null) + { + artistContainer.Content[0][1] = new FeaturedArtistBeatmapPill + { + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + Margin = new MarginPadding { Left = 5 } + }; + } + } + + protected override void LoadComplete() + { + base.LoadComplete(); + updateState(); + FinishTransforms(true); + } + + protected override bool OnHover(HoverEvent e) + { + updateState(); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + updateState(); + base.OnHoverLost(e); + } + + private LocalisableString createArtistText() + { + var romanisableArtist = new RomanisableString(beatmapSet.ArtistUnicode, beatmapSet.Artist); + return BeatmapsetsStrings.ShowDetailsByArtist(romanisableArtist); + } + + private void updateState() + { + float targetWidth = width - height; + if (IsHovered) + targetWidth -= 20; + + mainContent.ResizeWidthTo(targetWidth, TRANSITION_DURATION, Easing.OutQuint); + mainContentBackground.Dimmed.Value = IsHovered; + + leftCover.FadeColour(IsHovered ? OsuColour.Gray(0.2f) : Color4.White, TRANSITION_DURATION, Easing.OutQuint); + } + } +} diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardContentBackground.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardContentBackground.cs new file mode 100644 index 0000000000..392f5d1bfa --- /dev/null +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardContentBackground.cs @@ -0,0 +1,71 @@ +// 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 osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Overlays; + +namespace osu.Game.Beatmaps.Drawables.Cards +{ + public class BeatmapCardContentBackground : CompositeDrawable + { + public BindableBool Dimmed { get; } = new BindableBool(); + + private readonly Box background; + private readonly DelayedLoadUnloadWrapper cover; + + [Resolved] + private OverlayColourProvider colourProvider { get; set; } = null!; + + public BeatmapCardContentBackground(IBeatmapSetOnlineInfo onlineInfo) + { + InternalChildren = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both, + }, + cover = new DelayedLoadUnloadWrapper(() => createCover(onlineInfo), 500, 500) + { + RelativeSizeAxes = Axes.Both, + Colour = Colour4.Transparent + } + }; + } + + private static Drawable createCover(IBeatmapSetOnlineInfo onlineInfo) => new OnlineBeatmapSetCover(onlineInfo) + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + FillMode = FillMode.Fill + }; + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + background.Colour = colourProvider.Background2; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + Dimmed.BindValueChanged(_ => updateState(), true); + FinishTransforms(true); + } + + private void updateState() => Schedule(() => + { + background.FadeColour(Dimmed.Value ? colourProvider.Background4 : colourProvider.Background2, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); + + var gradient = ColourInfo.GradientHorizontal(Colour4.White.Opacity(0), Colour4.White.Opacity(0.2f)); + cover.FadeColour(gradient, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); + }); + } +} diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs index 64412675bb..6e573cc2a0 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs @@ -60,8 +60,9 @@ namespace osu.Game.Beatmaps.Drawables /// The ruleset to show the difficulty with. /// The mods to show the difficulty with. /// Whether to display a tooltip when hovered. - public DifficultyIcon([NotNull] IBeatmapInfo beatmapInfo, [CanBeNull] IRulesetInfo ruleset, [CanBeNull] IReadOnlyList mods, bool shouldShowTooltip = true) - : this(beatmapInfo, shouldShowTooltip) + /// Whether to perform difficulty lookup (including calculation if necessary). + public DifficultyIcon([NotNull] IBeatmapInfo beatmapInfo, [CanBeNull] IRulesetInfo ruleset, [CanBeNull] IReadOnlyList mods, bool shouldShowTooltip = true, bool performBackgroundDifficultyLookup = true) + : this(beatmapInfo, shouldShowTooltip, performBackgroundDifficultyLookup) { this.ruleset = ruleset ?? beatmapInfo.Ruleset; this.mods = mods ?? Array.Empty(); diff --git a/osu.Game/Graphics/Containers/LinkFlowContainer.cs b/osu.Game/Graphics/Containers/LinkFlowContainer.cs index 0b43c16ebe..7d1210d0e3 100644 --- a/osu.Game/Graphics/Containers/LinkFlowContainer.cs +++ b/osu.Game/Graphics/Containers/LinkFlowContainer.cs @@ -7,12 +7,10 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics.Sprites; using System.Collections.Generic; -using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Localisation; using osu.Framework.Platform; -using osu.Game.Graphics.Sprites; using osu.Game.Users; namespace osu.Game.Graphics.Containers @@ -58,23 +56,14 @@ namespace osu.Game.Graphics.Containers AddText(text.Substring(previousLinkEnd)); } - public void AddLink(string text, string url, Action creationParameters = null) => + public void AddLink(LocalisableString text, string url, Action creationParameters = null) => createLink(CreateChunkFor(text, true, CreateSpriteText, creationParameters), new LinkDetails(LinkAction.External, url), url); - public void AddLink(string text, Action action, string tooltipText = null, Action creationParameters = null) + public void AddLink(LocalisableString text, Action action, string tooltipText = null, Action creationParameters = null) => createLink(CreateChunkFor(text, true, CreateSpriteText, creationParameters), new LinkDetails(LinkAction.Custom, string.Empty), tooltipText, action); - public void AddLink(string text, LinkAction action, string argument, string tooltipText = null, Action creationParameters = null) - => createLink(CreateChunkFor(text, true, CreateSpriteText, creationParameters), new LinkDetails(action, argument), tooltipText); - public void AddLink(LocalisableString text, LinkAction action, string argument, string tooltipText = null, Action creationParameters = null) - { - var spriteText = new OsuSpriteText { Text = text }; - - AddText(spriteText, creationParameters); - RemoveInternal(spriteText); // TODO: temporary, will go away when TextParts support localisation properly. - createLink(new TextPartManual(spriteText.Yield()), new LinkDetails(action, argument), tooltipText); - } + => createLink(CreateChunkFor(text, true, CreateSpriteText, creationParameters), new LinkDetails(action, argument), tooltipText); public void AddLink(IEnumerable text, LinkAction action, string linkArgument, string tooltipText = null) { diff --git a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs index e8f80dec57..da511d8212 100644 --- a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs +++ b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; +using osu.Framework.Localisation; using osu.Game.Graphics.Containers; namespace osu.Game.Graphics.UserInterface @@ -19,7 +20,7 @@ namespace osu.Game.Graphics.UserInterface /// protected virtual bool PlaySoundsOnUserChange => true; - public string LabelText + public LocalisableString LabelText { set { diff --git a/osu.Game/Graphics/UserInterfaceV2/LabelledDrawable.cs b/osu.Game/Graphics/UserInterfaceV2/LabelledDrawable.cs index d5f76733cf..95884f1515 100644 --- a/osu.Game/Graphics/UserInterfaceV2/LabelledDrawable.cs +++ b/osu.Game/Graphics/UserInterfaceV2/LabelledDrawable.cs @@ -8,6 +8,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Localisation; using osu.Game.Graphics.Containers; using osu.Game.Overlays; using osuTK; @@ -156,18 +157,18 @@ namespace osu.Game.Graphics.UserInterfaceV2 descriptionText.Colour = osuColour.Yellow; } - public string Label + public LocalisableString Label { set => labelText.Text = value; } - public string Description + public LocalisableString Description { set { descriptionText.Text = value; - if (!string.IsNullOrEmpty(value)) + if (value == default) descriptionText.Show(); else descriptionText.Hide(); diff --git a/osu.Game/Online/API/APIRequest.cs b/osu.Game/Online/API/APIRequest.cs index d60c9cfe65..69d72226ba 100644 --- a/osu.Game/Online/API/APIRequest.cs +++ b/osu.Game/Online/API/APIRequest.cs @@ -107,7 +107,8 @@ namespace osu.Game.Online.API WebRequest = CreateWebRequest(); WebRequest.Failed += Fail; WebRequest.AllowRetryOnTimeout = false; - WebRequest.AddHeader("Authorization", $"Bearer {API.AccessToken}"); + if (!string.IsNullOrEmpty(API.AccessToken)) + WebRequest.AddHeader("Authorization", $"Bearer {API.AccessToken}"); if (isFailing) return; diff --git a/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs b/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs index 2e41723f34..5395fe0429 100644 --- a/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs +++ b/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs @@ -37,6 +37,7 @@ namespace osu.Game.Online.API.Requests.Responses public DateTimeOffset Date { get; set; } [JsonProperty(@"beatmap")] + [CanBeNull] public APIBeatmap Beatmap { get; set; } [JsonProperty("accuracy")] @@ -46,6 +47,7 @@ namespace osu.Game.Online.API.Requests.Responses public double? PP { get; set; } [JsonProperty(@"beatmapset")] + [CanBeNull] public APIBeatmapSet BeatmapSet { set @@ -96,7 +98,7 @@ namespace osu.Game.Online.API.Requests.Responses { TotalScore = TotalScore, MaxCombo = MaxCombo, - BeatmapInfo = Beatmap.ToBeatmapInfo(rulesets), + BeatmapInfo = Beatmap?.ToBeatmapInfo(rulesets), User = User, Accuracy = Accuracy, OnlineScoreID = OnlineID, diff --git a/osu.Game/Online/Placeholders/ClickablePlaceholder.cs b/osu.Game/Online/Placeholders/ClickablePlaceholder.cs index 936ad79c64..054a4a3c39 100644 --- a/osu.Game/Online/Placeholders/ClickablePlaceholder.cs +++ b/osu.Game/Online/Placeholders/ClickablePlaceholder.cs @@ -3,6 +3,7 @@ using System; using osu.Framework.Graphics.Sprites; +using osu.Framework.Localisation; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; @@ -12,7 +13,7 @@ namespace osu.Game.Online.Placeholders { public Action Action; - public ClickablePlaceholder(string actionMessage, IconUsage icon) + public ClickablePlaceholder(LocalisableString actionMessage, IconUsage icon) { OsuTextFlowContainer textFlow; diff --git a/osu.Game/Online/Placeholders/MessagePlaceholder.cs b/osu.Game/Online/Placeholders/MessagePlaceholder.cs index 7342765ca4..1676ba6cf2 100644 --- a/osu.Game/Online/Placeholders/MessagePlaceholder.cs +++ b/osu.Game/Online/Placeholders/MessagePlaceholder.cs @@ -3,14 +3,15 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; +using osu.Framework.Localisation; namespace osu.Game.Online.Placeholders { public class MessagePlaceholder : Placeholder { - private readonly string message; + private readonly LocalisableString message; - public MessagePlaceholder(string message) + public MessagePlaceholder(LocalisableString message) { AddIcon(FontAwesome.Solid.ExclamationCircle, cp => { diff --git a/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs b/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs index 6cd735af23..a642e283f9 100644 --- a/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs +++ b/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs @@ -53,7 +53,10 @@ namespace osu.Game.Online.Rooms downloadTracker?.RemoveAndDisposeImmediately(); downloadTracker = new BeatmapDownloadTracker(item.NewValue.Beatmap.Value.BeatmapSet); - downloadTracker.State.BindValueChanged(_ => updateAvailability()); + + AddInternal(downloadTracker); + + downloadTracker.State.BindValueChanged(_ => updateAvailability(), true); downloadTracker.Progress.BindValueChanged(_ => { if (downloadTracker.State.Value != DownloadState.Downloading) @@ -63,9 +66,7 @@ namespace osu.Game.Online.Rooms // we don't want to flood the network with this, so rate limit how often we send progress updates. if (progressUpdate?.Completed != false) progressUpdate = Scheduler.AddDelayed(updateAvailability, progressUpdate == null ? 0 : 500); - }); - - AddInternal(downloadTracker); + }, true); }, true); } diff --git a/osu.Game/Online/Rooms/SubmitRoomScoreRequest.cs b/osu.Game/Online/Rooms/SubmitRoomScoreRequest.cs index 9e432fa99e..d5da6c401c 100644 --- a/osu.Game/Online/Rooms/SubmitRoomScoreRequest.cs +++ b/osu.Game/Online/Rooms/SubmitRoomScoreRequest.cs @@ -5,6 +5,7 @@ using System.Net.Http; using Newtonsoft.Json; using osu.Framework.IO.Network; using osu.Game.Online.API; +using osu.Game.Online.Solo; using osu.Game.Scoring; namespace osu.Game.Online.Rooms @@ -14,14 +15,14 @@ namespace osu.Game.Online.Rooms private readonly long scoreId; private readonly long roomId; private readonly long playlistItemId; - private readonly ScoreInfo scoreInfo; + private readonly SubmittableScore score; public SubmitRoomScoreRequest(long scoreId, long roomId, long playlistItemId, ScoreInfo scoreInfo) { this.scoreId = scoreId; this.roomId = roomId; this.playlistItemId = playlistItemId; - this.scoreInfo = scoreInfo; + score = new SubmittableScore(scoreInfo); } protected override WebRequest CreateWebRequest() @@ -31,7 +32,7 @@ namespace osu.Game.Online.Rooms req.ContentType = "application/json"; req.Method = HttpMethod.Put; - req.AddRaw(JsonConvert.SerializeObject(scoreInfo, new JsonSerializerSettings + req.AddRaw(JsonConvert.SerializeObject(score, new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore })); diff --git a/osu.Game/Online/ScoreDownloadTracker.cs b/osu.Game/Online/ScoreDownloadTracker.cs index 675dbf608c..e679071ac1 100644 --- a/osu.Game/Online/ScoreDownloadTracker.cs +++ b/osu.Game/Online/ScoreDownloadTracker.cs @@ -35,7 +35,11 @@ namespace osu.Game.Online return; // Used to interact with manager classes that don't support interface types. Will eventually be replaced. - var scoreInfo = new ScoreInfo { OnlineScoreID = TrackedItem.OnlineScoreID }; + var scoreInfo = new ScoreInfo + { + ID = TrackedItem.ID, + OnlineScoreID = TrackedItem.OnlineScoreID + }; if (Manager.IsAvailableLocally(scoreInfo)) UpdateState(DownloadState.LocallyAvailable); diff --git a/osu.Game/Overlays/AccountCreation/ScreenEntry.cs b/osu.Game/Overlays/AccountCreation/ScreenEntry.cs index 8ee3b1cb2e..2ba8fc3ae2 100644 --- a/osu.Game/Overlays/AccountCreation/ScreenEntry.cs +++ b/osu.Game/Overlays/AccountCreation/ScreenEntry.cs @@ -5,7 +5,6 @@ using System; using System.Linq; using System.Threading.Tasks; using osu.Framework.Allocation; -using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Utils; @@ -135,7 +134,16 @@ namespace osu.Game.Overlays.AccountCreation characterCheckText = passwordDescription.AddText("8 characters long"); passwordDescription.AddText(". Choose something long but also something you will remember, like a line from your favourite song."); - passwordTextBox.Current.ValueChanged += password => { characterCheckText.Drawables.ForEach(s => s.Colour = password.NewValue.Length == 0 ? Color4.White : Interpolation.ValueAt(password.NewValue.Length, Color4.OrangeRed, Color4.YellowGreen, 0, 8, Easing.In)); }; + passwordTextBox.Current.BindValueChanged(_ => updateCharacterCheckTextColour(), true); + characterCheckText.DrawablePartsRecreated += _ => updateCharacterCheckTextColour(); + } + + private void updateCharacterCheckTextColour() + { + string password = passwordTextBox.Text; + + foreach (var d in characterCheckText.Drawables) + d.Colour = password.Length == 0 ? Color4.White : Interpolation.ValueAt(password.Length, Color4.OrangeRed, Color4.YellowGreen, 0, 8, Easing.In); } public override void OnEntering(IScreen last) diff --git a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs index d7c2837f4d..5ed49cf384 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs @@ -86,7 +86,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels break; default: - beatmaps.Download(new BeatmapSetInfo { OnlineBeatmapSetID = beatmapSet.OnlineID }, noVideoSetting.Value); + beatmaps.Download(beatmapSet, noVideoSetting.Value); break; } }; diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs index 6862864c55..bd7723d3c0 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs @@ -108,7 +108,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons return; } - beatmaps.Download(new BeatmapSetInfo { OnlineBeatmapSetID = beatmapSet.OnlineID }, noVideo); + beatmaps.Download(beatmapSet, noVideo); }; localUser.BindTo(api.LocalUser); diff --git a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs index 5cc598ae70..e84eee15be 100644 --- a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs +++ b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs @@ -101,7 +101,7 @@ namespace osu.Game.Overlays.Changelog t.Colour = colour.PinkLighter; }) { - Text = ChangelogStrings.SupportText2.ToString(), + Text = ChangelogStrings.SupportText2, Margin = new MarginPadding { Top = 10 }, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, diff --git a/osu.Game/Overlays/Dialog/PopupDialog.cs b/osu.Game/Overlays/Dialog/PopupDialog.cs index 78ef2ec795..0f953f92bb 100644 --- a/osu.Game/Overlays/Dialog/PopupDialog.cs +++ b/osu.Game/Overlays/Dialog/PopupDialog.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; +using osu.Framework.Localisation; using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Containers; using osuTK; @@ -42,9 +43,9 @@ namespace osu.Game.Overlays.Dialog set => icon.Icon = value; } - private string headerText; + private LocalisableString headerText; - public string HeaderText + public LocalisableString HeaderText { get => headerText; set @@ -57,9 +58,9 @@ namespace osu.Game.Overlays.Dialog } } - private string bodyText; + private LocalisableString bodyText; - public string BodyText + public LocalisableString BodyText { get => bodyText; set diff --git a/osu.Game/Overlays/Music/PlaylistItem.cs b/osu.Game/Overlays/Music/PlaylistItem.cs index eea2a9dc7e..04c12b8cd7 100644 --- a/osu.Game/Overlays/Music/PlaylistItem.cs +++ b/osu.Game/Overlays/Music/PlaylistItem.cs @@ -25,11 +25,8 @@ namespace osu.Game.Overlays.Music private TextFlowContainer text; private ITextPart titlePart; - private ILocalisedBindableString title; - private ILocalisedBindableString artist; - - private Color4 selectedColour; - private Color4 artistColour; + [Resolved] + private OsuColour colours { get; set; } public PlaylistItem(BeatmapSetInfo item) : base(item) @@ -40,22 +37,15 @@ namespace osu.Game.Overlays.Music } [BackgroundDependencyLoader] - private void load(OsuColour colours, LocalisationManager localisation) + private void load() { - selectedColour = colours.Yellow; - artistColour = colours.Gray9; HandleColour = colours.Gray5; - - title = localisation.GetLocalisedString(new RomanisableString(Model.Metadata.TitleUnicode, Model.Metadata.Title)); - artist = localisation.GetLocalisedString(new RomanisableString(Model.Metadata.ArtistUnicode, Model.Metadata.Artist)); } protected override void LoadComplete() { base.LoadComplete(); - artist.BindValueChanged(_ => recreateText(), true); - SelectedSet.BindValueChanged(set => { if (set.OldValue?.Equals(Model) != true && set.NewValue?.Equals(Model) != true) @@ -68,7 +58,7 @@ namespace osu.Game.Overlays.Music private void updateSelectionState(bool instant) { foreach (Drawable s in titlePart.Drawables) - s.FadeColour(SelectedSet.Value?.Equals(Model) == true ? selectedColour : Color4.White, instant ? 0 : FADE_DURATION); + s.FadeColour(SelectedSet.Value?.Equals(Model) == true ? colours.Yellow : Color4.White, instant ? 0 : FADE_DURATION); } protected override Drawable CreateContent() => text = new OsuTextFlowContainer @@ -77,18 +67,23 @@ namespace osu.Game.Overlays.Music AutoSizeAxes = Axes.Y, }; - private void recreateText() + protected override void LoadAsyncComplete() { - text.Clear(); + base.LoadAsyncComplete(); - // space after the title to put a space between the title and artist - titlePart = text.AddText(title.Value + @" ", sprite => sprite.Font = OsuFont.GetFont(weight: FontWeight.Regular)); + var title = new RomanisableString(Model.Metadata.TitleUnicode, Model.Metadata.Title); + var artist = new RomanisableString(Model.Metadata.ArtistUnicode, Model.Metadata.Artist); + + titlePart = text.AddText(title, sprite => sprite.Font = OsuFont.GetFont(weight: FontWeight.Regular)); updateSelectionState(true); + titlePart.DrawablePartsRecreated += _ => updateSelectionState(true); - text.AddText(artist.Value, sprite => + text.AddText(@" "); // to separate the title from the artist. + + text.AddText(artist, sprite => { sprite.Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold); - sprite.Colour = artistColour; + sprite.Colour = colours.Gray9; sprite.Padding = new MarginPadding { Top = 1 }; }); } diff --git a/osu.Game/Overlays/Notifications/ProgressNotification.cs b/osu.Game/Overlays/Notifications/ProgressNotification.cs index b27e15dd2c..c44b88ad29 100644 --- a/osu.Game/Overlays/Notifications/ProgressNotification.cs +++ b/osu.Game/Overlays/Notifications/ProgressNotification.cs @@ -10,6 +10,7 @@ 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.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; @@ -22,7 +23,7 @@ namespace osu.Game.Overlays.Notifications { private const float loading_spinner_size = 22; - public string Text + public LocalisableString Text { set => Schedule(() => textDrawable.Text = value); } diff --git a/osu.Game/Overlays/Notifications/SimpleNotification.cs b/osu.Game/Overlays/Notifications/SimpleNotification.cs index 3a3136b1ea..17ec12a4ca 100644 --- a/osu.Game/Overlays/Notifications/SimpleNotification.cs +++ b/osu.Game/Overlays/Notifications/SimpleNotification.cs @@ -7,6 +7,7 @@ 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.Graphics; using osu.Game.Graphics.Containers; using osuTK; @@ -15,9 +16,9 @@ namespace osu.Game.Overlays.Notifications { public class SimpleNotification : Notification { - private string text = string.Empty; + private LocalisableString text; - public string Text + public LocalisableString Text { get => text; set diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs index ca5534dbc2..fb464e1b41 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs @@ -91,7 +91,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks { new OsuSpriteText { - Text = $"{Score.Beatmap.DifficultyName}", + Text = $"{Score.Beatmap?.DifficultyName}", Font = OsuFont.GetFont(size: 12, weight: FontWeight.Regular), Colour = colours.Yellow }, diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/DirectorySelectScreen.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/DirectorySelectScreen.cs index e509cac2f1..0814e3c824 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/DirectorySelectScreen.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/DirectorySelectScreen.cs @@ -83,7 +83,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance cp.Font = OsuFont.Default.With(size: 24); }) { - Text = HeaderText.ToString(), + Text = HeaderText, TextAnchor = Anchor.TopCentre, Margin = new MarginPadding(10), RelativeSizeAxes = Axes.X, diff --git a/osu.Game/Overlays/Settings/SettingsCheckbox.cs b/osu.Game/Overlays/Settings/SettingsCheckbox.cs index 8b7ac80a5b..8a8fed4d30 100644 --- a/osu.Game/Overlays/Settings/SettingsCheckbox.cs +++ b/osu.Game/Overlays/Settings/SettingsCheckbox.cs @@ -16,8 +16,7 @@ namespace osu.Game.Overlays.Settings public override LocalisableString LabelText { get => labelText; - // checkbox doesn't properly support localisation yet. - set => ((OsuCheckbox)Control).LabelText = (labelText = value).ToString(); + set => ((OsuCheckbox)Control).LabelText = labelText = value; } } } diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index b593dea576..e709be1343 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -68,7 +68,7 @@ namespace osu.Game.Overlays.Settings { set { - bool hasValue = !string.IsNullOrWhiteSpace(value.ToString()); + bool hasValue = value != default; if (warningText == null) { @@ -80,7 +80,7 @@ namespace osu.Game.Overlays.Settings } warningText.Alpha = hasValue ? 1 : 0; - warningText.Text = value.ToString(); // TODO: Remove ToString() call after TextFlowContainer supports localisation (see https://github.com/ppy/osu-framework/issues/4636). + warningText.Text = value ?? default; } } diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index eab81186d5..5b4284dc2f 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -58,6 +58,11 @@ namespace osu.Game.Rulesets.Difficulty return CreateDifficultyAttributes(Beatmap, playableMods, skills, clockRate); } + /// + /// Calculates the difficulty of the beatmap and returns a set of representing the difficulty at every relevant time value in the beatmap. + /// + /// The mods that should be applied to the beatmap. + /// The set of . public List CalculateTimed(params Mod[] mods) { preProcess(mods); @@ -77,7 +82,7 @@ namespace osu.Game.Rulesets.Difficulty foreach (var skill in skills) skill.ProcessInternal(hitObject); - attribs.Add(new TimedDifficultyAttributes(hitObject.EndTime, CreateDifficultyAttributes(progressiveBeatmap, playableMods, skills, clockRate))); + attribs.Add(new TimedDifficultyAttributes(hitObject.EndTime * clockRate, CreateDifficultyAttributes(progressiveBeatmap, playableMods, skills, clockRate))); } return attribs; diff --git a/osu.Game/Rulesets/Difficulty/TimedDifficultyAttributes.cs b/osu.Game/Rulesets/Difficulty/TimedDifficultyAttributes.cs index 973b2dacb2..2509971389 100644 --- a/osu.Game/Rulesets/Difficulty/TimedDifficultyAttributes.cs +++ b/osu.Game/Rulesets/Difficulty/TimedDifficultyAttributes.cs @@ -11,9 +11,21 @@ namespace osu.Game.Rulesets.Difficulty /// public class TimedDifficultyAttributes : IComparable { + /// + /// The non-clock-adjusted time value at which the attributes take effect. + /// public readonly double Time; + + /// + /// The attributes. + /// public readonly DifficultyAttributes Attributes; + /// + /// Creates new . + /// + /// The non-clock-adjusted time value at which the attributes take effect. + /// The attributes. public TimedDifficultyAttributes(double time, DifficultyAttributes attributes) { Time = time; diff --git a/osu.Game/Screens/Edit/Timing/SliderWithTextBoxInput.cs b/osu.Game/Screens/Edit/Timing/SliderWithTextBoxInput.cs index 10a5771520..6c004a7c8b 100644 --- a/osu.Game/Screens/Edit/Timing/SliderWithTextBoxInput.cs +++ b/osu.Game/Screens/Edit/Timing/SliderWithTextBoxInput.cs @@ -6,6 +6,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; +using osu.Framework.Localisation; using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Overlays.Settings; @@ -16,7 +17,7 @@ namespace osu.Game.Screens.Edit.Timing { private readonly SettingsSlider slider; - public SliderWithTextBoxInput(string labelText) + public SliderWithTextBoxInput(LocalisableString labelText) { LabelledTextBox textbox; diff --git a/osu.Game/Screens/OnlinePlay/Components/BeatmapTitle.cs b/osu.Game/Screens/OnlinePlay/Components/BeatmapTitle.cs index 2901758332..e948c1adae 100644 --- a/osu.Game/Screens/OnlinePlay/Components/BeatmapTitle.cs +++ b/osu.Game/Screens/OnlinePlay/Components/BeatmapTitle.cs @@ -7,7 +7,6 @@ using osu.Framework.Graphics; using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; using osu.Game.Online.Chat; namespace osu.Game.Screens.OnlinePlay.Components @@ -69,24 +68,14 @@ namespace osu.Game.Screens.OnlinePlay.Components } else { - textFlow.AddLink(new[] - { - new OsuSpriteText - { - Text = new RomanisableString(beatmap.Value.Metadata.ArtistUnicode, beatmap.Value.Metadata.Artist), - Font = OsuFont.GetFont(size: TextSize), - }, - new OsuSpriteText - { - Text = " - ", - Font = OsuFont.GetFont(size: TextSize), - }, - new OsuSpriteText - { - Text = new RomanisableString(beatmap.Value.Metadata.TitleUnicode, beatmap.Value.Metadata.Title), - Font = OsuFont.GetFont(size: TextSize), - } - }, LinkAction.OpenBeatmap, beatmap.Value.OnlineID.ToString(), "Open beatmap"); + var metadataInfo = beatmap.Value.Metadata; + + string artistUnicode = string.IsNullOrEmpty(metadataInfo.ArtistUnicode) ? metadataInfo.Artist : metadataInfo.ArtistUnicode; + string titleUnicode = string.IsNullOrEmpty(metadataInfo.TitleUnicode) ? metadataInfo.Title : metadataInfo.TitleUnicode; + + var title = new RomanisableString($"{artistUnicode} - {titleUnicode}".Trim(), $"{metadataInfo.Artist} - {metadataInfo.Title}".Trim()); + + textFlow.AddLink(title, LinkAction.OpenBeatmap, beatmap.Value.OnlineID.ToString(), "Open beatmap"); } } } diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs index 69ab7225ac..675b5e4c04 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs @@ -105,13 +105,12 @@ namespace osu.Game.Screens.OnlinePlay private void refresh() { - difficultyIconContainer.Child = new DifficultyIcon(beatmap.Value, ruleset.Value, requiredMods) { Size = new Vector2(32) }; + difficultyIconContainer.Child = new DifficultyIcon(beatmap.Value, ruleset.Value, requiredMods, performBackgroundDifficultyLookup: false) { Size = new Vector2(32) }; beatmapText.Clear(); beatmapText.AddLink(Item.Beatmap.Value.GetDisplayTitleRomanisable(), LinkAction.OpenBeatmap, Item.Beatmap.Value.OnlineBeatmapID.ToString(), null, text => { text.Truncate = true; - text.RelativeSizeAxes = Axes.X; }); authorText.Clear(); diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistCountPill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistCountPill.cs index 2fe3c7b668..ef2c2df4a6 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistCountPill.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistCountPill.cs @@ -4,6 +4,7 @@ using System.Collections.Specialized; using Humanizer; using osu.Framework.Allocation; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Game.Graphics; using osu.Game.Graphics.Containers; @@ -46,7 +47,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components private void updateCount(object sender, NotifyCollectionChangedEventArgs e) { count.Clear(); - count.AddText(Playlist.Count.ToString(), s => s.Font = s.Font.With(weight: FontWeight.Bold)); + count.AddText(Playlist.Count.ToLocalisableString(), s => s.Font = s.Font.With(weight: FontWeight.Bold)); count.AddText(" "); count.AddText("Beatmap".ToQuantity(Playlist.Count, ShowQuantityAs.None)); } diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 2de72beaad..89eed14e6d 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -188,8 +188,8 @@ namespace osu.Game.Screens.Select RelativeSizeAxes = Axes.Both; - titleBinding = localisation.GetLocalisedString(new RomanisableString(metadata.TitleUnicode, metadata.Title)); - artistBinding = localisation.GetLocalisedString(new RomanisableString(metadata.ArtistUnicode, metadata.Artist)); + titleBinding = localisation.GetLocalisedBindableString(new RomanisableString(metadata.TitleUnicode, metadata.Title)); + artistBinding = localisation.GetLocalisedBindableString(new RomanisableString(metadata.ArtistUnicode, metadata.Artist)); const float top_height = 0.7f; diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index c1c3336b5c..972d64a997 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 0baf067a63..f6e4f61fde 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - +