diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.cs index d07b90025f..1670741cbd 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.cs @@ -6,6 +6,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Game.Beatmaps; using osu.Game.Overlays; using osu.Game.Rulesets.Osu; @@ -22,8 +23,6 @@ namespace osu.Game.Tests.Visual.UserInterface [BackgroundDependencyLoader] private void load() { - Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); - nowPlayingOverlay = new NowPlayingOverlay { Origin = Anchor.Centre, @@ -37,9 +36,38 @@ namespace osu.Game.Tests.Visual.UserInterface [Test] public void TestShowHideDisable() { + AddStep(@"set beatmap", () => Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo)); AddStep(@"show", () => nowPlayingOverlay.Show()); AddToggleStep(@"toggle beatmap lock", state => Beatmap.Disabled = state); AddStep(@"hide", () => nowPlayingOverlay.Hide()); } + + [Test] + public void TestLongMetadata() + { + AddStep(@"set metadata within tolerance", () => Beatmap.Value = CreateWorkingBeatmap(new Beatmap + { + Metadata = + { + Artist = "very very very very very very very very very very verry long artist", + ArtistUnicode = "very very very very very very very very very very verry long artist", + Title = "very very very very very verry long title", + TitleUnicode = "very very very very very verry long title", + } + })); + + AddStep(@"set metadata outside bounds", () => Beatmap.Value = CreateWorkingBeatmap(new Beatmap + { + Metadata = + { + Artist = "very very very very very very very very very very verrry long artist", + ArtistUnicode = "very very very very very very very very very very verrry long artist", + Title = "very very very very very verrry long title", + TitleUnicode = "very very very very very verrry long title", + } + })); + + AddStep(@"show", () => nowPlayingOverlay.Show()); + } } } diff --git a/osu.Game/Overlays/NowPlayingOverlay.cs b/osu.Game/Overlays/NowPlayingOverlay.cs index 7e4f4f4e9b..29052ace8e 100644 --- a/osu.Game/Overlays/NowPlayingOverlay.cs +++ b/osu.Game/Overlays/NowPlayingOverlay.cs @@ -33,6 +33,7 @@ namespace osu.Game.Overlays public LocalisableString Title => NowPlayingStrings.HeaderTitle; public LocalisableString Description => NowPlayingStrings.HeaderDescription; + private const float player_width = 400; private const float player_height = 130; private const float transition_length = 800; private const float progress_height = 10; @@ -47,7 +48,7 @@ namespace osu.Game.Overlays private IconButton nextButton = null!; private IconButton playlistButton = null!; - private SpriteText title = null!, artist = null!; + private ScrollingTextContainer title = null!, artist = null!; private PlaylistOverlay? playlist; @@ -70,7 +71,7 @@ namespace osu.Game.Overlays public NowPlayingOverlay() { - Width = 400; + Width = player_width; Margin = new MarginPadding(margin); } @@ -101,7 +102,7 @@ namespace osu.Game.Overlays Children = new[] { background = Empty(), - title = new OsuSpriteText + title = new ScrollingTextContainer { Origin = Anchor.BottomCentre, Anchor = Anchor.TopCentre, @@ -110,7 +111,7 @@ namespace osu.Game.Overlays Colour = Color4.White, Text = @"Nothing to play", }, - artist = new OsuSpriteText + artist = new ScrollingTextContainer { Origin = Anchor.TopCentre, Anchor = Anchor.TopCentre, @@ -319,15 +320,15 @@ namespace osu.Game.Overlays switch (direction) { case TrackChangeDirection.Next: - newBackground.Position = new Vector2(400, 0); + newBackground.Position = new Vector2(player_width, 0); newBackground.MoveToX(0, 500, Easing.OutCubic); - background.MoveToX(-400, 500, Easing.OutCubic); + background.MoveToX(-player_width, 500, Easing.OutCubic); break; case TrackChangeDirection.Prev: - newBackground.Position = new Vector2(-400, 0); + newBackground.Position = new Vector2(-player_width, 0); newBackground.MoveToX(0, 500, Easing.OutCubic); - background.MoveToX(400, 500, Easing.OutCubic); + background.MoveToX(player_width, 500, Easing.OutCubic); break; } @@ -469,5 +470,103 @@ namespace osu.Game.Overlays base.OnHoverLost(e); } } + + private partial class ScrollingTextContainer : CompositeDrawable + { + private const float initial_move_delay = 1000; + private const float pixels_per_second = 50; + + private OsuSpriteText mainSpriteText = null!; + private OsuSpriteText fillerSpriteText = null!; + + private LocalisableString text; + + public LocalisableString Text + { + get => text; + set + { + text = value; + + if (IsLoaded) + updateText(); + } + } + + private FontUsage font = OsuFont.Default; + + public FontUsage Font + { + get => font; + set + { + font = value; + + if (IsLoaded) + updateFontAndText(); + } + } + + public ScrollingTextContainer() + { + AutoSizeAxes = Axes.Both; + } + + [BackgroundDependencyLoader] + private void load() + { + InternalChild = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Children = new[] + { + mainSpriteText = new OsuSpriteText { Padding = new MarginPadding { Horizontal = margin } }, + fillerSpriteText = new OsuSpriteText { Padding = new MarginPadding { Horizontal = margin }, Alpha = 0 }, + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + updateFontAndText(); + } + + private void updateFontAndText() + { + mainSpriteText.Font = font; + fillerSpriteText.Font = font; + + updateText(); + } + + private void updateText() + { + mainSpriteText.Text = text; + fillerSpriteText.Alpha = 0; + + ClearTransforms(); + X = 0; + + float textOverflowWidth = mainSpriteText.Width - player_width; + + // apply half margin of tolerance on both sides before the text scrolls + if (textOverflowWidth > margin) + { + fillerSpriteText.Alpha = 1; + fillerSpriteText.Text = text; + + float initialX = (textOverflowWidth + mainSpriteText.Width) / 2; + float targetX = (textOverflowWidth - mainSpriteText.Width) / 2; + + this.MoveToX(initialX) + .Delay(initial_move_delay) + .MoveToX(targetX, mainSpriteText.Width * 1000 / pixels_per_second) + .Loop(); + } + } + } } }