From 4011da033bba3c51cf591857f2fb14ad8c8e10d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 23 Oct 2021 17:59:52 +0200 Subject: [PATCH 1/6] Split off thumbnail to separate component --- .../Beatmaps/Drawables/Cards/BeatmapCard.cs | 26 ++++----- .../Drawables/Cards/BeatmapCardThumbnail.cs | 54 +++++++++++++++++++ 2 files changed, 63 insertions(+), 17 deletions(-) create mode 100644 osu.Game/Beatmaps/Drawables/Cards/BeatmapCardThumbnail.cs diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs index e3af253db9..d52e8f6c20 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs @@ -23,7 +23,6 @@ using osuTK; using osu.Game.Overlays.BeatmapListing.Panels; using osu.Game.Resources.Localisation.Web; -using osuTK.Graphics; using DownloadButton = osu.Game.Beatmaps.Drawables.Cards.Buttons.DownloadButton; namespace osu.Game.Beatmaps.Drawables.Cards @@ -42,7 +41,7 @@ public class BeatmapCard : OsuClickableContainer private readonly BeatmapDownloadTracker downloadTracker; - private UpdateableOnlineBeatmapSetCover leftCover; + private BeatmapCardThumbnail thumbnail; private FillFlowContainer leftIconArea; private Container rightAreaBackground; @@ -98,24 +97,16 @@ private void load() Colour = Colour4.White }, }, - new Container + thumbnail = new BeatmapCardThumbnail(beatmapSet) { Name = @"Left (icon) area", Size = new Vector2(height), - Children = new Drawable[] + Child = leftIconArea = new FillFlowContainer { - leftCover = new UpdateableOnlineBeatmapSetCover(BeatmapSetCoverType.List) - { - RelativeSizeAxes = Axes.Both, - OnlineInfo = beatmapSet - }, - leftIconArea = new FillFlowContainer - { - Margin = new MarginPadding(5), - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(1) - } + Margin = new MarginPadding(5), + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(1) } }, new Container @@ -395,10 +386,11 @@ private void updateState() if (IsHovered) targetWidth = targetWidth - icon_area_width + corner_radius; + thumbnail.Dimmed.Value = IsHovered; + mainContent.ResizeWidthTo(targetWidth, TRANSITION_DURATION, Easing.OutQuint); mainContentBackground.Dimmed.Value = IsHovered; - leftCover.FadeColour(IsHovered ? OsuColour.Gray(0.2f) : Color4.White, TRANSITION_DURATION, Easing.OutQuint); statisticsContainer.FadeTo(IsHovered ? 1 : 0, TRANSITION_DURATION, Easing.OutQuint); rightAreaBackground.FadeColour(downloadTracker.State.Value == DownloadState.LocallyAvailable ? colours.Lime0 : colourProvider.Background3, TRANSITION_DURATION, Easing.OutQuint); diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardThumbnail.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardThumbnail.cs new file mode 100644 index 0000000000..b9304e744a --- /dev/null +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardThumbnail.cs @@ -0,0 +1,54 @@ +// 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.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; +using osu.Game.Online.API.Requests.Responses; +using osuTK.Graphics; + +namespace osu.Game.Beatmaps.Drawables.Cards +{ + public class BeatmapCardThumbnail : Container + { + public BindableBool Dimmed { get; } = new BindableBool(); + + private readonly APIBeatmapSet beatmapSetInfo; + + private readonly UpdateableOnlineBeatmapSetCover cover; + private readonly Container content; + + protected override Container Content => content; + + public BeatmapCardThumbnail(APIBeatmapSet beatmapSetInfo) + { + this.beatmapSetInfo = beatmapSetInfo; + + InternalChildren = new Drawable[] + { + cover = new UpdateableOnlineBeatmapSetCover(BeatmapSetCoverType.List) + { + RelativeSizeAxes = Axes.Both, + OnlineInfo = beatmapSetInfo + }, + content = new Container + { + RelativeSizeAxes = Axes.Both + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + Dimmed.BindValueChanged(_ => updateState(), true); + FinishTransforms(true); + } + + private void updateState() + { + cover.FadeColour(Dimmed.Value ? OsuColour.Gray(0.2f) : Color4.White, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); + } + } +} From b44db9f5e5036fd59cb4e62f2b2a40df56aaff14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 23 Oct 2021 18:05:30 +0200 Subject: [PATCH 2/6] Add test scene for thumbnail component --- .../Beatmaps/TestSceneBeatmapCardThumbnail.cs | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCardThumbnail.cs diff --git a/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCardThumbnail.cs b/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCardThumbnail.cs new file mode 100644 index 0000000000..b7c87219ca --- /dev/null +++ b/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCardThumbnail.cs @@ -0,0 +1,32 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Beatmaps.Drawables.Cards; +using osu.Game.Overlays; +using osuTK; + +namespace osu.Game.Tests.Visual.Beatmaps +{ + public class TestSceneBeatmapCardThumbnail : OsuTestScene + { + [Cached] + private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue); + + [Test] + public void TestThumbnailPreview() + { + BeatmapCardThumbnail thumbnail = null; + + AddStep("create thumbnail", () => Child = thumbnail = new BeatmapCardThumbnail(CreateAPIBeatmapSet(Ruleset.Value)) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(200) + }); + AddToggleStep("toggle dim", dimmed => thumbnail.Dimmed.Value = dimmed); + } + } +} From 5d13686cdf689bd42b7dc9e992cb9bb19fcf8dfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 23 Oct 2021 18:26:28 +0200 Subject: [PATCH 3/6] Add play button for card preview --- .../Drawables/Cards/BeatmapCardThumbnail.cs | 3 ++ .../Drawables/Cards/Buttons/PlayButton.cs | 34 +++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 osu.Game/Beatmaps/Drawables/Cards/Buttons/PlayButton.cs diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardThumbnail.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardThumbnail.cs index b9304e744a..75ed5bdcfe 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardThumbnail.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardThumbnail.cs @@ -17,6 +17,7 @@ public class BeatmapCardThumbnail : Container private readonly APIBeatmapSet beatmapSetInfo; private readonly UpdateableOnlineBeatmapSetCover cover; + private readonly PlayButton playButton; private readonly Container content; protected override Container Content => content; @@ -32,6 +33,7 @@ public BeatmapCardThumbnail(APIBeatmapSet beatmapSetInfo) RelativeSizeAxes = Axes.Both, OnlineInfo = beatmapSetInfo }, + playButton = new PlayButton(), content = new Container { RelativeSizeAxes = Axes.Both @@ -48,6 +50,7 @@ protected override void LoadComplete() private void updateState() { + playButton.FadeTo(Dimmed.Value ? 1 : 0, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); cover.FadeColour(Dimmed.Value ? OsuColour.Gray(0.2f) : Color4.White, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); } } diff --git a/osu.Game/Beatmaps/Drawables/Cards/Buttons/PlayButton.cs b/osu.Game/Beatmaps/Drawables/Cards/Buttons/PlayButton.cs new file mode 100644 index 0000000000..c1cbac1c59 --- /dev/null +++ b/osu.Game/Beatmaps/Drawables/Cards/Buttons/PlayButton.cs @@ -0,0 +1,34 @@ +// 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.Sprites; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osuTK; + +namespace osu.Game.Beatmaps.Drawables.Cards.Buttons +{ + public class PlayButton : OsuHoverContainer + { + public PlayButton() + { + Anchor = Origin = Anchor.Centre; + + Child = new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Icon = FontAwesome.Solid.Play, + Size = new Vector2(14) + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + HoverColour = colours.Yellow; + } + } +} From 9164f006aa17ac62a17fda40e4260237fc353cbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 23 Oct 2021 18:41:31 +0200 Subject: [PATCH 4/6] Implement basic behaviour of play button --- .../Beatmaps/TestSceneBeatmapCardThumbnail.cs | 42 ++++++++++++++++++- .../Drawables/Cards/BeatmapCardThumbnail.cs | 15 +++++-- .../Drawables/Cards/Buttons/PlayButton.cs | 38 ++++++++++++++--- 3 files changed, 83 insertions(+), 12 deletions(-) diff --git a/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCardThumbnail.cs b/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCardThumbnail.cs index b7c87219ca..73424241cb 100644 --- a/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCardThumbnail.cs +++ b/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCardThumbnail.cs @@ -1,17 +1,24 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Testing; using osu.Game.Beatmaps.Drawables.Cards; +using osu.Game.Beatmaps.Drawables.Cards.Buttons; using osu.Game.Overlays; using osuTK; +using osuTK.Input; namespace osu.Game.Tests.Visual.Beatmaps { - public class TestSceneBeatmapCardThumbnail : OsuTestScene + public class TestSceneBeatmapCardThumbnail : OsuManualInputManagerTestScene { + private PlayButton playButton => this.ChildrenOfType().Single(); + [Cached] private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue); @@ -26,7 +33,38 @@ public void TestThumbnailPreview() Origin = Anchor.Centre, Size = new Vector2(200) }); - AddToggleStep("toggle dim", dimmed => thumbnail.Dimmed.Value = dimmed); + AddStep("enable dim", () => thumbnail.Dimmed.Value = true); + AddUntilStep("button visible", () => playButton.IsPresent); + + AddStep("click button", () => + { + InputManager.MoveMouseTo(playButton); + InputManager.Click(MouseButton.Left); + }); + iconIs(FontAwesome.Solid.Stop); + + AddStep("click again", () => + { + InputManager.MoveMouseTo(playButton); + InputManager.Click(MouseButton.Left); + }); + iconIs(FontAwesome.Solid.Play); + + AddStep("click again", () => + { + InputManager.MoveMouseTo(playButton); + InputManager.Click(MouseButton.Left); + }); + iconIs(FontAwesome.Solid.Stop); + + AddStep("disable dim", () => thumbnail.Dimmed.Value = false); + AddWaitStep("wait some", 3); + AddAssert("button still visible", () => playButton.IsPresent); + + AddStep("end track playback", () => playButton.Playing.Value = false); + AddUntilStep("button hidden", () => !playButton.IsPresent); } + + private void iconIs(IconUsage usage) => AddAssert("icon is correct", () => playButton.ChildrenOfType().Single().Icon.Equals(usage)); } } diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardThumbnail.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardThumbnail.cs index 75ed5bdcfe..ce1f856b84 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardThumbnail.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardThumbnail.cs @@ -4,6 +4,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps.Drawables.Cards.Buttons; using osu.Game.Graphics; using osu.Game.Online.API.Requests.Responses; using osuTK.Graphics; @@ -33,7 +34,10 @@ public BeatmapCardThumbnail(APIBeatmapSet beatmapSetInfo) RelativeSizeAxes = Axes.Both, OnlineInfo = beatmapSetInfo }, - playButton = new PlayButton(), + playButton = new PlayButton(beatmapSetInfo) + { + RelativeSizeAxes = Axes.Both + }, content = new Container { RelativeSizeAxes = Axes.Both @@ -44,14 +48,17 @@ public BeatmapCardThumbnail(APIBeatmapSet beatmapSetInfo) protected override void LoadComplete() { base.LoadComplete(); - Dimmed.BindValueChanged(_ => updateState(), true); + Dimmed.BindValueChanged(_ => updateState()); + playButton.Playing.BindValueChanged(_ => updateState(), true); FinishTransforms(true); } private void updateState() { - playButton.FadeTo(Dimmed.Value ? 1 : 0, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); - cover.FadeColour(Dimmed.Value ? OsuColour.Gray(0.2f) : Color4.White, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); + bool shouldDim = Dimmed.Value || playButton.Playing.Value; + + playButton.FadeTo(shouldDim ? 1 : 0, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); + cover.FadeColour(shouldDim ? OsuColour.Gray(0.2f) : Color4.White, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); } } } diff --git a/osu.Game/Beatmaps/Drawables/Cards/Buttons/PlayButton.cs b/osu.Game/Beatmaps/Drawables/Cards/Buttons/PlayButton.cs index c1cbac1c59..c48999ed1a 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/Buttons/PlayButton.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/Buttons/PlayButton.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; @@ -12,17 +13,30 @@ namespace osu.Game.Beatmaps.Drawables.Cards.Buttons { public class PlayButton : OsuHoverContainer { - public PlayButton() + public BindableBool Playing { get; } = new BindableBool(); + + private readonly BeatmapSetInfo beatmapSetInfo; + + private readonly SpriteIcon icon; + + public PlayButton(BeatmapSetInfo beatmapSetInfo) { + this.beatmapSetInfo = beatmapSetInfo; + Anchor = Origin = Anchor.Centre; - Child = new SpriteIcon + Children = new Drawable[] { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Icon = FontAwesome.Solid.Play, - Size = new Vector2(14) + icon = new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Icon = FontAwesome.Solid.Play, + Size = new Vector2(14) + }, }; + + Action = () => Playing.Toggle(); } [BackgroundDependencyLoader] @@ -30,5 +44,17 @@ private void load(OsuColour colours) { HoverColour = colours.Yellow; } + + protected override void LoadComplete() + { + base.LoadComplete(); + + Playing.BindValueChanged(_ => updateState(), true); + } + + private void updateState() + { + icon.Icon = Playing.Value ? FontAwesome.Solid.Stop : FontAwesome.Solid.Play; + } } } From 1a1603f0dba556e30a598a1deabe468a3455d810 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 23 Oct 2021 19:27:07 +0200 Subject: [PATCH 5/6] Implement preview track playback --- .../Beatmaps/TestSceneBeatmapCardThumbnail.cs | 7 +- .../Beatmaps/Drawables/Cards/BeatmapCard.cs | 5 +- .../Drawables/Cards/BeatmapCardThumbnail.cs | 49 ++++++++-- .../Drawables/Cards/Buttons/PlayButton.cs | 92 ++++++++++++++++++- 4 files changed, 135 insertions(+), 18 deletions(-) diff --git a/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCardThumbnail.cs b/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCardThumbnail.cs index 73424241cb..59df0dc7ca 100644 --- a/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCardThumbnail.cs +++ b/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCardThumbnail.cs @@ -41,6 +41,7 @@ public void TestThumbnailPreview() InputManager.MoveMouseTo(playButton); InputManager.Click(MouseButton.Left); }); + AddUntilStep("wait for start", () => playButton.Playing.Value && playButton.Enabled.Value); iconIs(FontAwesome.Solid.Stop); AddStep("click again", () => @@ -48,6 +49,7 @@ public void TestThumbnailPreview() InputManager.MoveMouseTo(playButton); InputManager.Click(MouseButton.Left); }); + AddUntilStep("wait for stop", () => !playButton.Playing.Value && playButton.Enabled.Value); iconIs(FontAwesome.Solid.Play); AddStep("click again", () => @@ -55,16 +57,17 @@ public void TestThumbnailPreview() InputManager.MoveMouseTo(playButton); InputManager.Click(MouseButton.Left); }); + AddUntilStep("wait for start", () => playButton.Playing.Value && playButton.Enabled.Value); iconIs(FontAwesome.Solid.Stop); AddStep("disable dim", () => thumbnail.Dimmed.Value = false); AddWaitStep("wait some", 3); AddAssert("button still visible", () => playButton.IsPresent); - AddStep("end track playback", () => playButton.Playing.Value = false); + AddUntilStep("wait for track to end", () => !playButton.Playing.Value); AddUntilStep("button hidden", () => !playButton.IsPresent); } - private void iconIs(IconUsage usage) => AddAssert("icon is correct", () => playButton.ChildrenOfType().Single().Icon.Equals(usage)); + private void iconIs(IconUsage usage) => AddUntilStep("icon is correct", () => playButton.ChildrenOfType().Any(icon => icon.Icon.Equals(usage))); } } diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs index d52e8f6c20..4ffad0f065 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs @@ -101,6 +101,7 @@ private void load() { Name = @"Left (icon) area", Size = new Vector2(height), + Padding = new MarginPadding { Right = corner_radius }, Child = leftIconArea = new FillFlowContainer { Margin = new MarginPadding(5), @@ -310,10 +311,10 @@ private void load() }; if (beatmapSet.HasVideo) - leftIconArea.Add(new IconPill(FontAwesome.Solid.Film)); + leftIconArea.Add(new IconPill(FontAwesome.Solid.Film) { IconSize = new Vector2(20) }); if (beatmapSet.HasStoryboard) - leftIconArea.Add(new IconPill(FontAwesome.Solid.Image)); + leftIconArea.Add(new IconPill(FontAwesome.Solid.Image) { IconSize = new Vector2(20) }); if (beatmapSet.HasExplicitContent) { diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardThumbnail.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardThumbnail.cs index ce1f856b84..f11a5916e1 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardThumbnail.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardThumbnail.cs @@ -1,12 +1,16 @@ // 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.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps.Drawables.Cards.Buttons; using osu.Game.Graphics; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Overlays; +using osu.Game.Screens.Ranking.Expanded.Accuracy; +using osuTK; using osuTK.Graphics; namespace osu.Game.Beatmaps.Drawables.Cards @@ -15,18 +19,22 @@ public class BeatmapCardThumbnail : Container { public BindableBool Dimmed { get; } = new BindableBool(); - private readonly APIBeatmapSet beatmapSetInfo; + public new MarginPadding Padding + { + get => foreground.Padding; + set => foreground.Padding = value; + } private readonly UpdateableOnlineBeatmapSetCover cover; + private readonly Container foreground; private readonly PlayButton playButton; + private readonly SmoothCircularProgress progress; private readonly Container content; protected override Container Content => content; public BeatmapCardThumbnail(APIBeatmapSet beatmapSetInfo) { - this.beatmapSetInfo = beatmapSetInfo; - InternalChildren = new Drawable[] { cover = new UpdateableOnlineBeatmapSetCover(BeatmapSetCoverType.List) @@ -34,22 +42,45 @@ public BeatmapCardThumbnail(APIBeatmapSet beatmapSetInfo) RelativeSizeAxes = Axes.Both, OnlineInfo = beatmapSetInfo }, - playButton = new PlayButton(beatmapSetInfo) + foreground = new Container { - RelativeSizeAxes = Axes.Both - }, - content = new Container - { - RelativeSizeAxes = Axes.Both + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + playButton = new PlayButton(beatmapSetInfo) + { + RelativeSizeAxes = Axes.Both + }, + progress = new SmoothCircularProgress + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(50), + InnerRadius = 0.2f + }, + content = new Container + { + RelativeSizeAxes = Axes.Both + } + } } }; } + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + progress.Colour = colourProvider.Highlight1; + } + protected override void LoadComplete() { base.LoadComplete(); Dimmed.BindValueChanged(_ => updateState()); + playButton.Playing.BindValueChanged(_ => updateState(), true); + ((IBindable)progress.Current).BindTo(playButton.Progress); + FinishTransforms(true); } diff --git a/osu.Game/Beatmaps/Drawables/Cards/Buttons/PlayButton.cs b/osu.Game/Beatmaps/Drawables/Cards/Buttons/PlayButton.cs index c48999ed1a..4574d37da0 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/Buttons/PlayButton.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/Buttons/PlayButton.cs @@ -1,25 +1,43 @@ // 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.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; +using osu.Game.Audio; using osu.Game.Graphics; using osu.Game.Graphics.Containers; +using osu.Game.Graphics.UserInterface; using osuTK; namespace osu.Game.Beatmaps.Drawables.Cards.Buttons { public class PlayButton : OsuHoverContainer { + public IBindable Progress => progress; + private readonly BindableDouble progress = new BindableDouble(); + public BindableBool Playing { get; } = new BindableBool(); - private readonly BeatmapSetInfo beatmapSetInfo; + private readonly IBeatmapSetInfo beatmapSetInfo; + + protected override IEnumerable EffectTargets => icon.Yield(); private readonly SpriteIcon icon; + private readonly LoadingSpinner loadingSpinner; - public PlayButton(BeatmapSetInfo beatmapSetInfo) + [Resolved] + private PreviewTrackManager previewTrackManager { get; set; } = null!; + + private PreviewTrack? previewTrack; + + public PlayButton(IBeatmapSetInfo beatmapSetInfo) { this.beatmapSetInfo = beatmapSetInfo; @@ -34,6 +52,10 @@ public PlayButton(BeatmapSetInfo beatmapSetInfo) Icon = FontAwesome.Solid.Play, Size = new Vector2(14) }, + loadingSpinner = new LoadingSpinner + { + Size = new Vector2(14) + } }; Action = () => Playing.Toggle(); @@ -49,12 +71,72 @@ protected override void LoadComplete() { base.LoadComplete(); - Playing.BindValueChanged(_ => updateState(), true); + Playing.BindValueChanged(updateState, true); } - private void updateState() + protected override void Update() { - icon.Icon = Playing.Value ? FontAwesome.Solid.Stop : FontAwesome.Solid.Play; + base.Update(); + + if (Playing.Value && previewTrack != null && previewTrack.TrackLoaded) + progress.Value = previewTrack.CurrentTime / previewTrack.Length; + else + progress.Value = 0; + } + + private void updateState(ValueChangedEvent playing) + { + icon.Icon = playing.NewValue ? FontAwesome.Solid.Stop : FontAwesome.Solid.Play; + + if (!playing.NewValue) + { + stopPreview(); + return; + } + + if (previewTrack == null) + { + toggleLoading(true); + LoadComponentAsync(previewTrack = previewTrackManager.Get(beatmapSetInfo), onPreviewLoaded); + } + else + tryStartPreview(); + } + + private void stopPreview() + { + toggleLoading(false); + Playing.Value = false; + previewTrack?.Stop(); + } + + private void onPreviewLoaded(PreviewTrack loadedPreview) + { + // another async load might have completed before this one. + // if so, do not make any changes. + if (loadedPreview != previewTrack) + return; + + AddInternal(loadedPreview); + toggleLoading(false); + + loadedPreview.Stopped += () => Schedule(() => Playing.Value = false); + + if (Playing.Value) + tryStartPreview(); + } + + private void tryStartPreview() + { + if (previewTrack?.Start() == false) + Playing.Value = false; + } + + private void toggleLoading(bool loading) + { + Enabled.Value = !loading; + icon.FadeTo(loading ? 0 : 1, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); + loadingSpinner.State.Value = loading ? Visibility.Visible : Visibility.Hidden; } } } From 1907b42f8252900ff0d3d83fa34f0eb1d5d9f6e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 24 Nov 2021 20:34:33 +0100 Subject: [PATCH 6/6] Use constant online IDs in tests to avoid preview not working randomly --- .../Visual/Beatmaps/TestSceneBeatmapCard.cs | 13 ++++++++++--- .../Beatmaps/TestSceneBeatmapCardThumbnail.cs | 14 ++++++++++---- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCard.cs b/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCard.cs index addec15881..a45ba30ccd 100644 --- a/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCard.cs +++ b/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCard.cs @@ -23,6 +23,11 @@ namespace osu.Game.Tests.Visual.Beatmaps { public class TestSceneBeatmapCard : OsuTestScene { + /// + /// All cards on this scene use a common online ID to ensure that map download, preview tracks, etc. can be tested manually with online sources. + /// + private const int online_id = 163112; + private DummyAPIAccess dummyAPI => (DummyAPIAccess)API; private APIBeatmapSet[] testCases; @@ -38,7 +43,6 @@ private void load() var normal = CreateAPIBeatmapSet(Ruleset.Value); normal.HasVideo = true; normal.HasStoryboard = true; - normal.OnlineID = 241526; var withStatistics = CreateAPIBeatmapSet(Ruleset.Value); withStatistics.Title = withStatistics.TitleUnicode = "play favourite stats"; @@ -106,6 +110,9 @@ private void load() explicitFeaturedMap, longName }; + + foreach (var testCase in testCases) + testCase.OnlineID = online_id; } private APIBeatmapSet getUndownloadableBeatmapSet() => new APIBeatmapSet @@ -191,9 +198,9 @@ public void SetUpSteps() private void ensureSoleilyRemoved() { AddUntilStep("ensure manager loaded", () => beatmaps != null); - AddStep("remove soleily", () => + AddStep("remove map", () => { - var beatmap = beatmaps.QueryBeatmapSet(b => b.OnlineID == 241526); + var beatmap = beatmaps.QueryBeatmapSet(b => b.OnlineID == online_id); if (beatmap != null) beatmaps.Delete(beatmap); }); diff --git a/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCardThumbnail.cs b/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCardThumbnail.cs index 59df0dc7ca..1b4542d946 100644 --- a/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCardThumbnail.cs +++ b/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCardThumbnail.cs @@ -27,11 +27,17 @@ public void TestThumbnailPreview() { BeatmapCardThumbnail thumbnail = null; - AddStep("create thumbnail", () => Child = thumbnail = new BeatmapCardThumbnail(CreateAPIBeatmapSet(Ruleset.Value)) + AddStep("create thumbnail", () => { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(200) + var beatmapSet = CreateAPIBeatmapSet(Ruleset.Value); + beatmapSet.OnlineID = 241526; // ID hardcoded to ensure that the preview track exists online. + + Child = thumbnail = new BeatmapCardThumbnail(beatmapSet) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(200) + }; }); AddStep("enable dim", () => thumbnail.Dimmed.Value = true); AddUntilStep("button visible", () => playButton.IsPresent);