diff --git a/osu.Game/Overlays/BeatmapSet/PreviewButton.cs b/osu.Game/Overlays/BeatmapSet/PreviewButton.cs index bdb06106f0..f77a1f4a0a 100644 --- a/osu.Game/Overlays/BeatmapSet/PreviewButton.cs +++ b/osu.Game/Overlays/BeatmapSet/PreviewButton.cs @@ -2,7 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Allocation; -using osu.Framework.Audio; using osu.Framework.Audio.Track; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -12,9 +11,10 @@ using osu.Framework.Input; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; -using osu.Game.Graphics.UserInterface; using OpenTK; using OpenTK.Graphics; +using osu.Game.Overlays.Direct; +using osu.Framework.Configuration; namespace osu.Game.Overlays.BeatmapSet { @@ -22,73 +22,16 @@ namespace osu.Game.Overlays.BeatmapSet { private const float transition_duration = 500; - private readonly Container audioWrapper; private readonly Box bg, progress; - private readonly SpriteIcon icon; - private readonly LoadingAnimation loadingAnimation; + private readonly PlayButton playButton; - private Track preview; + private Track preview => playButton.Preview; + private Bindable playing => playButton.Playing; - private bool loading - { - set - { - if (value) - { - loadingAnimation.Show(); - icon.FadeOut(transition_duration * 5, Easing.OutQuint); - } - else - { - loadingAnimation.Hide(); - icon.FadeIn(transition_duration, Easing.OutQuint); - } - } - } - - private BeatmapSetInfo beatmapSet; public BeatmapSetInfo BeatmapSet { - get { return beatmapSet; } - set - { - if (value == beatmapSet) return; - beatmapSet = value; - - Playing = false; - preview = null; - } - } - - private bool playing; - public bool Playing - { - get { return playing; } - set - { - if (value == playing) return; - playing = value; - - if (preview == null) - { - loading = true; - audioWrapper.Child = new AsyncLoadWrapper(new AudioLoadWrapper(BeatmapSet) - { - OnLoadComplete = d => - { - loading = false; - - preview = (d as AudioLoadWrapper)?.Preview; - Playing = Playing; - updatePlayingState(); - }, - }); - - return; - } - - updatePlayingState(); - } + get { return playButton.BeatmapSet; } + set { playButton.BeatmapSet = value; } } public PreviewButton() @@ -97,7 +40,6 @@ namespace osu.Game.Overlays.BeatmapSet Children = new Drawable[] { - audioWrapper = new Container(), bg = new Box { RelativeSizeAxes = Axes.Both, @@ -116,22 +58,16 @@ namespace osu.Game.Overlays.BeatmapSet Alpha = 0f, }, }, - icon = new SpriteIcon + playButton = new PlayButton { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Icon = FontAwesome.fa_play, Size = new Vector2(18), - Shadow = false, - }, - loadingAnimation = new LoadingAnimation - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, }, }; - Action = () => Playing = !Playing; + Action = () => playing.Value = !playing.Value; + playing.ValueChanged += newValue => progress.FadeTo(newValue ? 1 : 0, 100); } [BackgroundDependencyLoader] @@ -144,20 +80,15 @@ namespace osu.Game.Overlays.BeatmapSet { base.Update(); - if (Playing && preview != null) + if (playing.Value && preview != null) { progress.Width = (float)(preview.CurrentTime / preview.Length); - if (preview.HasCompleted) - { - Playing = false; - preview = null; - } } } protected override void Dispose(bool isDisposing) { - Playing = false; + playing.Value = false; base.Dispose(isDisposing); } @@ -172,47 +103,5 @@ namespace osu.Game.Overlays.BeatmapSet bg.FadeColour(Color4.Black.Opacity(0.25f), 100); base.OnHoverLost(state); } - - private void updatePlayingState() - { - if (preview == null) return; - - if (Playing) - { - icon.Icon = FontAwesome.fa_stop; - progress.FadeIn(100); - - preview.Seek(0); - preview.Start(); - } - else - { - icon.Icon = FontAwesome.fa_play; - progress.FadeOut(100); - preview.Stop(); - } - } - - private class AudioLoadWrapper : Drawable - { - private readonly string preview; - - public Track Preview; - - public AudioLoadWrapper(BeatmapSetInfo set) - { - preview = set.OnlineInfo.Preview; - } - - [BackgroundDependencyLoader] - private void load(AudioManager audio) - { - if (!string.IsNullOrEmpty(preview)) - { - Preview = audio.Track.Get(preview); - Preview.Volume.Value = 0.5; - } - } - } } } diff --git a/osu.Game/Overlays/Direct/DirectGridPanel.cs b/osu.Game/Overlays/Direct/DirectGridPanel.cs index 1675a2f663..7464ee7fb8 100644 --- a/osu.Game/Overlays/Direct/DirectGridPanel.cs +++ b/osu.Game/Overlays/Direct/DirectGridPanel.cs @@ -21,6 +21,11 @@ namespace osu.Game.Overlays.Direct private const float vertical_padding = 5; private FillFlowContainer bottomPanel; + private PlayButton playButton; + private Box progressBar; + + protected override PlayButton PlayButton => playButton; + protected override Box PreviewBar => progressBar; public DirectGridPanel(BeatmapSetInfo beatmap) : base(beatmap) { @@ -87,6 +92,15 @@ namespace osu.Game.Overlays.Direct { RelativeSizeAxes = Axes.Both, }, + progressBar = new Box + { + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.X, + BypassAutoSizeAxes = Axes.Both, + Size = new Vector2(0, 3), + Alpha = 0, + Colour = colours.Yellow, + }, new FillFlowContainer { RelativeSizeAxes = Axes.X, @@ -178,6 +192,12 @@ namespace osu.Game.Overlays.Direct new Statistic(FontAwesome.fa_heart, SetInfo.OnlineInfo?.FavouriteCount ?? 0), }, }, + playButton = new PlayButton(SetInfo) + { + Margin = new MarginPadding { Top = 5, Left = 10 }, + Size = new Vector2(30), + Alpha = 0, + }, }); } } diff --git a/osu.Game/Overlays/Direct/DirectListPanel.cs b/osu.Game/Overlays/Direct/DirectListPanel.cs index 6702b7394c..5889a1bc12 100644 --- a/osu.Game/Overlays/Direct/DirectListPanel.cs +++ b/osu.Game/Overlays/Direct/DirectListPanel.cs @@ -28,8 +28,14 @@ namespace osu.Game.Overlays.Direct Height = height; } + private PlayButton playButton; + private Box progressBar; + + protected override PlayButton PlayButton => playButton; + protected override Box PreviewBar => progressBar; + [BackgroundDependencyLoader] - private void load(LocalisationEngine localisation) + private void load(LocalisationEngine localisation, OsuColour colours) { Content.CornerRadius = 5; @@ -48,29 +54,50 @@ namespace osu.Game.Overlays.Direct { new FillFlowContainer { + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, + Direction = FillDirection.Horizontal, + LayoutEasing = Easing.OutQuint, + LayoutDuration = 120, + Spacing = new Vector2(10, 0), Children = new Drawable[] { - new OsuSpriteText + playButton = new PlayButton(SetInfo) { - Current = localisation.GetUnicodePreference(SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title), - TextSize = 18, - Font = @"Exo2.0-BoldItalic", - }, - new OsuSpriteText - { - Current = localisation.GetUnicodePreference(SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist), - Font = @"Exo2.0-BoldItalic", + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + Size = new Vector2(height / 2), + FillMode = FillMode.Fit, + Alpha = 0, }, new FillFlowContainer { - AutoSizeAxes = Axes.X, - Height = 20, - Margin = new MarginPadding { Top = vertical_padding, Bottom = vertical_padding }, - Children = GetDifficultyIcons(), + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + new OsuSpriteText + { + Current = localisation.GetUnicodePreference(SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title), + TextSize = 18, + Font = @"Exo2.0-BoldItalic", + }, + new OsuSpriteText + { + Current = localisation.GetUnicodePreference(SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist), + Font = @"Exo2.0-BoldItalic", + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.X, + Height = 20, + Margin = new MarginPadding { Top = vertical_padding, Bottom = vertical_padding }, + Children = GetDifficultyIcons(), + }, + }, }, - }, + } }, new FillFlowContainer { @@ -126,6 +153,16 @@ namespace osu.Game.Overlays.Direct }, }, }, + progressBar = new Box + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.X, + BypassAutoSizeAxes = Axes.Y, + Size = new Vector2(0, 3), + Alpha = 0, + Colour = colours.Yellow, + }, }); } } diff --git a/osu.Game/Overlays/Direct/DirectPanel.cs b/osu.Game/Overlays/Direct/DirectPanel.cs index 4f7f1bb39e..ef89c0022b 100644 --- a/osu.Game/Overlays/Direct/DirectPanel.cs +++ b/osu.Game/Overlays/Direct/DirectPanel.cs @@ -20,6 +20,8 @@ using osu.Game.Online.API; using osu.Framework.Logging; using osu.Game.Overlays.Notifications; using osu.Game.Online.API.Requests; +using osu.Framework.Configuration; +using osu.Framework.Audio.Track; namespace osu.Game.Overlays.Direct { @@ -39,6 +41,11 @@ namespace osu.Game.Overlays.Direct private NotificationOverlay notifications; private BeatmapSetOverlay beatmapSetOverlay; + public Track Preview => PlayButton.Preview; + public Bindable PreviewPlaying => PlayButton.Playing; + protected abstract PlayButton PlayButton { get; } + protected abstract Box PreviewBar { get; } + protected override Container Content => content; protected DirectPanel(BeatmapSetInfo setInfo) @@ -104,10 +111,21 @@ namespace osu.Game.Overlays.Direct attachDownload(downloadRequest); } + protected override void Update() + { + base.Update(); + + if (PreviewPlaying && Preview != null) + { + PreviewBar.Width = (float)(Preview.CurrentTime / Preview.Length); + } + } + protected override bool OnHover(InputState state) { content.TweenEdgeEffectTo(edgeEffectHovered, hover_transition_time, Easing.OutQuint); content.MoveToY(-4, hover_transition_time, Easing.OutQuint); + PlayButton.FadeIn(120, Easing.InOutQuint); return base.OnHover(state); } @@ -116,6 +134,8 @@ namespace osu.Game.Overlays.Direct { content.TweenEdgeEffectTo(edgeEffectNormal, hover_transition_time, Easing.OutQuint); content.MoveToY(0, hover_transition_time, Easing.OutQuint); + if (!PreviewPlaying) + PlayButton.FadeOut(120, Easing.InOutQuint); base.OnHoverLost(state); } @@ -123,6 +143,7 @@ namespace osu.Game.Overlays.Direct protected override bool OnClick(InputState state) { ShowInformation(); + PreviewPlaying.Value = false; return true; } @@ -183,6 +204,9 @@ namespace osu.Game.Overlays.Direct { base.LoadComplete(); this.FadeInFromZero(200, Easing.Out); + + PreviewPlaying.ValueChanged += newValue => PlayButton.FadeTo(newValue || IsHovered ? 1 : 0, 120, Easing.InOutQuint); + PreviewPlaying.ValueChanged += newValue => PreviewBar.FadeTo(newValue ? 1 : 0, 120, Easing.InOutQuint); } protected List GetDifficultyIcons() diff --git a/osu.Game/Overlays/Direct/PlayButton.cs b/osu.Game/Overlays/Direct/PlayButton.cs new file mode 100644 index 0000000000..32435a4873 --- /dev/null +++ b/osu.Game/Overlays/Direct/PlayButton.cs @@ -0,0 +1,181 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Track; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Input; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Overlays.Direct +{ + public class PlayButton : Container + { + public readonly Bindable Playing = new Bindable(); + public Track Preview { get; private set; } + + private BeatmapSetInfo beatmapSet; + public BeatmapSetInfo BeatmapSet + { + get { return beatmapSet; } + set + { + if (value == beatmapSet) return; + beatmapSet = value; + + Playing.Value = false; + trackLoader = null; + Preview = null; + } + } + + private Color4 hoverColour; + private readonly SpriteIcon icon; + private readonly LoadingAnimation loadingAnimation; + + private const float transition_duration = 500; + + private bool loading + { + set + { + if (value) + { + loadingAnimation.Show(); + icon.FadeOut(transition_duration * 5, Easing.OutQuint); + } + else + { + loadingAnimation.Hide(); + icon.FadeIn(transition_duration, Easing.OutQuint); + } + } + } + + public PlayButton(BeatmapSetInfo setInfo = null) + { + BeatmapSet = setInfo; + AddRange(new Drawable[] + { + icon = new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + FillMode = FillMode.Fit, + RelativeSizeAxes = Axes.Both, + Icon = FontAwesome.fa_play, + }, + loadingAnimation = new LoadingAnimation(), + }); + + Playing.ValueChanged += playing => + { + icon.Icon = playing ? FontAwesome.fa_pause : FontAwesome.fa_play; + icon.FadeColour(playing || IsHovered ? hoverColour : Color4.White, 120, Easing.InOutQuint); + updatePreviewTrack(playing); + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colour) + { + hoverColour = colour.Yellow; + } + + protected override bool OnClick(InputState state) + { + Playing.Value = !Playing.Value; + return true; + } + + protected override bool OnHover(InputState state) + { + icon.FadeColour(hoverColour, 120, Easing.InOutQuint); + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + if (!Playing.Value) + icon.FadeColour(Color4.White, 120, Easing.InOutQuint); + base.OnHoverLost(state); + } + + protected override void Update() + { + base.Update(); + + if (Preview?.HasCompleted ?? false) + { + Playing.Value = false; + Preview = null; + } + } + + private void updatePreviewTrack(bool playing) + { + if (playing) + { + if (Preview == null) + { + beginAudioLoad(); + return; + } + + Preview.Seek(0); + Preview.Start(); + } + else + { + Preview?.Stop(); + } + } + + private TrackLoader trackLoader; + + private void beginAudioLoad() + { + if (trackLoader != null) return; + + Add(new AsyncLoadWrapper(trackLoader = new TrackLoader($"https://b.ppy.sh/preview/{BeatmapSet.OnlineBeatmapSetID}.mp3") + { + OnLoadComplete = d => + { + // we may have been replaced by another loader + if (trackLoader != d) return; + + Preview = (d as TrackLoader)?.Preview; + Playing.TriggerChange(); + }, + })); + } + + private class TrackLoader : Drawable + { + private readonly string preview; + + public Track Preview; + + public TrackLoader(string preview) + { + this.preview = preview; + } + + [BackgroundDependencyLoader] + private void load(AudioManager audio) + { + if (!string.IsNullOrEmpty(preview)) + { + Preview = audio.Track.Get(preview); + Preview.Volume.Value = 0.5; + } + } + } + } +} diff --git a/osu.Game/Overlays/DirectOverlay.cs b/osu.Game/Overlays/DirectOverlay.cs index 5b5003b30f..71dc558b26 100644 --- a/osu.Game/Overlays/DirectOverlay.cs +++ b/osu.Game/Overlays/DirectOverlay.cs @@ -32,6 +32,7 @@ namespace osu.Game.Overlays private readonly FillFlowContainer resultCountsContainer; private readonly OsuSpriteText resultCountsText; private FillFlowContainer panels; + private DirectPanel playing; protected override Color4 BackgroundColour => OsuColour.FromHex(@"485e74"); protected override Color4 TrianglesColourLight => OsuColour.FromHex(@"465b71"); @@ -201,6 +202,12 @@ namespace osu.Game.Overlays panels.FadeOut(200); panels.Expire(); panels = null; + + if (playing != null) + { + playing.PreviewPlaying.Value = false; + playing = null; + } } if (BeatmapSets == null) return; @@ -227,6 +234,17 @@ namespace osu.Game.Overlays { if (panels != null) ScrollFlow.Remove(panels); ScrollFlow.Add(panels = newPanels); + + foreach (DirectPanel panel in p.Children) + panel.PreviewPlaying.ValueChanged += newValue => + { + if (newValue) + { + if (playing != null && playing != panel) + playing.PreviewPlaying.Value = false; + playing = panel; + } + }; }); } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index b0d07efe73..d782e79b06 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -393,6 +393,7 @@ +