diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 9d98a0f165..850c640770 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -1,25 +1,38 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; +using System.Collections.Generic; +using OpenTK.Graphics; using osu.Framework.Screens; using osu.Game.Screens.Backgrounds; -using OpenTK.Graphics; +using osu.Game.Screens.Select; namespace osu.Game.Screens.Edit { internal class Editor : ScreenWhiteBox { + protected override IEnumerable PossibleChildren => new[] { typeof(EditSongSelect) }; + protected override BackgroundScreen CreateBackground() => new BackgroundScreenCustom(@"Backgrounds/bg4"); + protected override void OnResuming(Screen last) + { + Beatmap?.Track?.Stop(); + base.OnResuming(last); + } + protected override void OnEntering(Screen last) { base.OnEntering(last); Background.Schedule(() => Background.FadeColour(Color4.DarkGray, 500)); + Beatmap?.Track?.Stop(); } protected override bool OnExiting(Screen next) { Background.Schedule(() => Background.FadeColour(Color4.White, 500)); + Beatmap?.Track?.Start(); return base.OnExiting(next); } } diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 7053face9d..33bffef219 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -10,6 +10,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Screens.Backgrounds; using osu.Game.Screens.Charts; using osu.Game.Screens.Direct; +using osu.Game.Screens.Edit; using osu.Game.Screens.Multiplayer; using OpenTK; using osu.Game.Screens.Select; @@ -44,7 +45,7 @@ namespace osu.Game.Screens.Menu { OnChart = delegate { Push(new ChartListing()); }, OnDirect = delegate { Push(new OnlineListing()); }, - OnEdit = delegate { Push(new EditSongSelect()); }, + OnEdit = delegate { Push(new Editor()); }, OnSolo = delegate { Push(new PlaySongSelect()); }, OnMulti = delegate { Push(new Lobby()); }, OnTest = delegate { Push(new TestBrowser()); }, diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 5264bee78c..f82c35598a 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -3,20 +3,21 @@ using System; using System.Collections.Generic; -using osu.Framework.Allocation; +using System.Linq; using OpenTK; using OpenTK.Graphics; +using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Sprites; -using osu.Game.Beatmaps; -using osu.Game.Database; -using osu.Framework.Graphics.Colour; -using osu.Game.Beatmaps.Drawables; -using System.Linq; +using osu.Framework.Graphics.Transforms; using osu.Framework.MathUtils; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Drawables; +using osu.Game.Database; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Modes; @@ -25,7 +26,7 @@ using osu.Game.Modes.Objects.Types; namespace osu.Game.Screens.Select { - internal class BeatmapInfoWedge : Container + internal class BeatmapInfoWedge : OverlayContainer { private static readonly Vector2 wedged_container_shear = new Vector2(0.15f, 0); @@ -54,18 +55,32 @@ namespace osu.Game.Screens.Select this.game = game; } + protected override bool HideOnEscape => false; + + protected override void PopIn() + { + MoveToX(0, 800, EasingTypes.OutQuint); + RotateTo(0, 800, EasingTypes.OutQuint); + } + + protected override void PopOut() + { + MoveToX(-100, 800, EasingTypes.InQuint); + RotateTo(10, 800, EasingTypes.InQuint); + } + public void UpdateBeatmap(WorkingBeatmap beatmap) { if (beatmap?.BeatmapInfo == null) { - FadeOut(250); + State = Visibility.Hidden; beatmapInfoContainer?.FadeOut(250); beatmapInfoContainer?.Expire(); beatmapInfoContainer = null; return; } - FadeIn(250); + State = Visibility.Visible; var lastContainer = beatmapInfoContainer; float newDepth = lastContainer?.Depth + 1 ?? 0; diff --git a/osu.Game/Screens/Select/EditSongSelect.cs b/osu.Game/Screens/Select/EditSongSelect.cs index 4d07de2de8..1a9d37f069 100644 --- a/osu.Game/Screens/Select/EditSongSelect.cs +++ b/osu.Game/Screens/Select/EditSongSelect.cs @@ -1,19 +1,12 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; -using System.Collections.Generic; -using osu.Game.Screens.Backgrounds; -using osu.Game.Screens.Edit; - namespace osu.Game.Screens.Select { - internal class EditSongSelect : ScreenWhiteBox + public class EditSongSelect : SongSelect { - protected override IEnumerable PossibleChildren => new[] { - typeof(Editor) - }; + protected override bool ShowFooter => false; - protected override BackgroundScreen CreateBackground() => new BackgroundScreenCustom(@"Backgrounds/bg4"); + protected override void OnSelected() => Exit(); } } diff --git a/osu.Game/Screens/Select/Footer.cs b/osu.Game/Screens/Select/Footer.cs index 4af72cecd9..fae1cb5d4d 100644 --- a/osu.Game/Screens/Select/Footer.cs +++ b/osu.Game/Screens/Select/Footer.cs @@ -4,6 +4,7 @@ using System; using OpenTK; using OpenTK.Graphics; +using OpenTK.Input; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -34,15 +35,25 @@ namespace osu.Game.Screens.Select public OsuLogo StartButton; - public void AddButton(string text, Color4 colour, Action action) + /// Text on the button. + /// Colour of the button. + /// Hotkey of the button. + /// Action the button does. + /// + /// Higher depth to be put on the left, and lower to be put on the right. + /// Notice this is different to ! + /// + public void AddButton(string text, Color4 colour, Action action, Key? hotkey = null, float depth = 0) { var button = new FooterButton { Text = text, Height = play_song_select_button_height, Width = play_song_select_button_width, + Depth = depth, SelectedColour = colour, DeselectedColour = colour.Opacity(0.5f), + Hotkey = hotkey, }; button.Hovered = () => updateModeLight(button); @@ -89,7 +100,7 @@ namespace osu.Game.Screens.Select { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, - Action = () => OnBack?.Invoke(), + Action = () => OnBack?.Invoke() }, new FillFlowContainer { diff --git a/osu.Game/Screens/Select/FooterButton.cs b/osu.Game/Screens/Select/FooterButton.cs index daa09f03a3..2e245d205f 100644 --- a/osu.Game/Screens/Select/FooterButton.cs +++ b/osu.Game/Screens/Select/FooterButton.cs @@ -1,14 +1,15 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using OpenTK; using OpenTK.Graphics; +using OpenTK.Input; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; -using osu.Framework.Input; -using System; using osu.Framework.Graphics.Transforms; +using osu.Framework.Input; using osu.Game.Graphics.Sprites; namespace osu.Game.Screens.Select @@ -34,7 +35,7 @@ namespace osu.Game.Screens.Select set { deselectedColour = value; - if(light.Colour != SelectedColour) + if (light.Colour != SelectedColour) light.Colour = value; } } @@ -83,6 +84,7 @@ namespace osu.Game.Screens.Select public Action Hovered; public Action HoverLost; + public Key? Hotkey; protected override bool OnHover(InputState state) { @@ -119,5 +121,15 @@ namespace osu.Game.Screens.Select return base.OnClick(state); } + protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + { + if (!args.Repeat && args.Key == Hotkey) + { + OnClick(state); + return true; + } + + return base.OnKeyDown(state, args); + } } } diff --git a/osu.Game/Screens/Select/MatchSongSelect.cs b/osu.Game/Screens/Select/MatchSongSelect.cs index 5be41edd23..282cd06126 100644 --- a/osu.Game/Screens/Select/MatchSongSelect.cs +++ b/osu.Game/Screens/Select/MatchSongSelect.cs @@ -1,12 +1,10 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Game.Screens.Backgrounds; - namespace osu.Game.Screens.Select { - internal class MatchSongSelect : ScreenWhiteBox + public class MatchSongSelect : SongSelect { - protected override BackgroundScreen CreateBackground() => new BackgroundScreenCustom(@"Backgrounds/bg4"); + protected override void OnSelected() => Exit(); } } diff --git a/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs b/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs index 32901ad919..cfe8eea07f 100644 --- a/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs +++ b/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs @@ -3,6 +3,7 @@ using OpenTK; using OpenTK.Graphics; +using OpenTK.Input; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -48,6 +49,8 @@ namespace osu.Game.Screens.Select.Options set { secondLine.Text = value; } } + public Key? HotKey; + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) { flash.FadeTo(0.1f, 1000, EasingTypes.OutQuint); @@ -69,6 +72,17 @@ namespace osu.Game.Screens.Select.Options return base.OnClick(state); } + protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + { + if (!args.Repeat && args.Key == HotKey) + { + OnClick(state); + return true; + } + + return false; + } + public override bool Contains(Vector2 screenSpacePos) => box.Contains(screenSpacePos); public BeatmapOptionsButton() diff --git a/osu.Game/Screens/Select/Options/BeatmapOptionsClearLocalScoresButton.cs b/osu.Game/Screens/Select/Options/BeatmapOptionsClearLocalScoresButton.cs deleted file mode 100644 index 66a6dac358..0000000000 --- a/osu.Game/Screens/Select/Options/BeatmapOptionsClearLocalScoresButton.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Game.Graphics; - -namespace osu.Game.Screens.Select.Options -{ - public class BeatmapOptionsClearLocalScoresButton : BeatmapOptionsButton - { - [BackgroundDependencyLoader] - private void load(OsuColour colour) - { - ButtonColour = colour.Purple; - } - - public BeatmapOptionsClearLocalScoresButton() - { - Icon = FontAwesome.fa_eraser; - FirstLineText = @"Clear"; - SecondLineText = @"local scores"; - } - } -} diff --git a/osu.Game/Screens/Select/Options/BeatmapOptionsDeleteButton.cs b/osu.Game/Screens/Select/Options/BeatmapOptionsDeleteButton.cs deleted file mode 100644 index 563cf41a87..0000000000 --- a/osu.Game/Screens/Select/Options/BeatmapOptionsDeleteButton.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Game.Graphics; - -namespace osu.Game.Screens.Select.Options -{ - public class BeatmapOptionsDeleteButton : BeatmapOptionsButton - { - [BackgroundDependencyLoader] - private void load(OsuColour colour) - { - ButtonColour = colour.Pink; - } - - public BeatmapOptionsDeleteButton() - { - Icon = FontAwesome.fa_trash; - FirstLineText = @"Delete"; - SecondLineText = @"Beatmap"; - } - } -} diff --git a/osu.Game/Screens/Select/Options/BeatmapOptionsEditButton.cs b/osu.Game/Screens/Select/Options/BeatmapOptionsEditButton.cs deleted file mode 100644 index f88c0df168..0000000000 --- a/osu.Game/Screens/Select/Options/BeatmapOptionsEditButton.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Game.Graphics; - -namespace osu.Game.Screens.Select.Options -{ - public class BeatmapOptionsEditButton : BeatmapOptionsButton - { - [BackgroundDependencyLoader] - private void load(OsuColour colour) - { - ButtonColour = colour.Yellow; - } - - public BeatmapOptionsEditButton() - { - Icon = FontAwesome.fa_pencil; - FirstLineText = @"Edit"; - SecondLineText = @"Beatmap"; - } - } -} diff --git a/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs b/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs index 2c327b3658..690669ffc1 100644 --- a/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs +++ b/osu.Game/Screens/Select/Options/BeatmapOptionsOverlay.cs @@ -6,11 +6,13 @@ using System.Collections.Generic; using System.Linq; using OpenTK; using OpenTK.Graphics; +using OpenTK.Input; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Transforms; +using osu.Game.Graphics; namespace osu.Game.Screens.Select.Options { @@ -24,11 +26,6 @@ namespace osu.Game.Screens.Select.Options private Box holder; private FillFlowContainer buttonsContainer; - public Action OnRemoveFromUnplayed; - public Action OnClearLocalScores; - public Action OnEdit; - public Action OnDelete; - protected override void PopIn() { base.PopIn(); @@ -85,45 +82,38 @@ namespace osu.Game.Screens.Select.Options AutoSizeAxes = Axes.X, Origin = Anchor.BottomLeft, Anchor = Anchor.BottomLeft, - Children = new BeatmapOptionsButton[] - { - new BeatmapOptionsRemoveFromUnplayedButton - { - Action = () => - { - Hide(); - OnRemoveFromUnplayed?.Invoke(); - }, - }, - new BeatmapOptionsClearLocalScoresButton - { - Action = () => - { - Hide(); - OnClearLocalScores?.Invoke(); - }, - }, - new BeatmapOptionsEditButton - { - Action = () => - { - Hide(); - OnEdit?.Invoke(); - }, - }, - new BeatmapOptionsDeleteButton - { - Action = () => - { - Hide(); - OnDelete?.Invoke(); - }, - }, - }, }, }; } + /// Text in the first line. + /// Text in the second line. + /// Colour of the button. + /// Icon of the button. + /// Hotkey of the button. + /// Action the button does. + /// + /// Lower depth to be put on the left, and higher to be put on the right. + /// Notice this is different to ! + /// + public void AddButton(string firstLine, string secondLine, FontAwesome icon, Color4 colour, Action action, Key? hotkey = null, float depth = 0) + { + buttonsContainer.Add(new BeatmapOptionsButton + { + FirstLineText = firstLine, + SecondLineText = secondLine, + Icon = icon, + ButtonColour = colour, + Depth = depth, + Action = () => + { + Hide(); + action?.Invoke(); + }, + HotKey = hotkey + }); + } + private class ButtonFlow : FillFlowContainer { protected override IComparer DepthComparer => new ReverseCreationOrderDepthComparer(); diff --git a/osu.Game/Screens/Select/Options/BeatmapOptionsRemoveFromUnplayedButton.cs b/osu.Game/Screens/Select/Options/BeatmapOptionsRemoveFromUnplayedButton.cs deleted file mode 100644 index eeab78ef02..0000000000 --- a/osu.Game/Screens/Select/Options/BeatmapOptionsRemoveFromUnplayedButton.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Game.Graphics; - -namespace osu.Game.Screens.Select.Options -{ - public class BeatmapOptionsRemoveFromUnplayedButton : BeatmapOptionsButton - { - [BackgroundDependencyLoader] - private void load(OsuColour colour) - { - ButtonColour = colour.Purple; - } - - public BeatmapOptionsRemoveFromUnplayedButton() - { - Icon = FontAwesome.fa_times_circle_o; - FirstLineText = @"Remove"; - SecondLineText = @"from Unplayed"; - } - } -} diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index 09ba794122..53f3a3a596 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -1,473 +1,69 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; +using OpenTK.Input; using osu.Framework.Allocation; -using osu.Framework.Audio; -using osu.Framework.Audio.Track; -using osu.Framework.Configuration; -using osu.Framework.Screens; using osu.Framework.Graphics; using osu.Framework.Graphics.Primitives; +using osu.Framework.Screens; using osu.Game.Beatmaps; -using osu.Game.Database; -using osu.Game.Modes; -using osu.Game.Screens.Backgrounds; -using OpenTK; -using osu.Game.Screens.Play; -using osu.Framework.Audio.Sample; -using osu.Framework.Graphics.Transforms; -using osu.Game.Beatmaps.Drawables; -using osu.Game.Graphics.Containers; using osu.Game.Graphics; -using osu.Framework.Input; -using OpenTK.Input; -using System.Collections.Generic; -using osu.Framework.Threading; using osu.Game.Overlays.Mods; -using osu.Game.Overlays; -using osu.Game.Screens.Select.Options; +using osu.Game.Screens.Edit; +using osu.Game.Screens.Play; namespace osu.Game.Screens.Select { - public class PlaySongSelect : OsuScreen + public class PlaySongSelect : SongSelect { - private Bindable playMode = new Bindable(); - private BeatmapDatabase database; - protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap); - - private CarouselContainer carousel; - private TrackManager trackManager; - private DialogOverlay dialogOverlay; - - private static readonly Vector2 wedged_container_size = new Vector2(0.5f, 225); - private BeatmapInfoWedge beatmapInfoWedge; - + private OsuScreen player; private ModSelectOverlay modSelect; - private static readonly Vector2 background_blur = new Vector2(20); - private CancellationTokenSource initialAddSetsTask; - - private SampleChannel sampleChangeDifficulty; - private SampleChannel sampleChangeBeatmap; - - private List beatmapGroups; - - private BeatmapOptionsOverlay beatmapOptions; - private Footer footer; - - private OsuScreen player; - - private FilterControl filter; - public FilterControl Filter + public PlaySongSelect() { - get + Add(modSelect = new ModSelectOverlay { - return filter; - } - private set - { - if (filter != value) - { - filter = value; - filterChanged(); - } - } + RelativeSizeAxes = Axes.X, + Origin = Anchor.BottomCentre, + Anchor = Anchor.BottomCentre, + Margin = new MarginPadding { Bottom = 50 } + }); } - [BackgroundDependencyLoader(permitNulls: true)] - private void load(BeatmapDatabase beatmaps, AudioManager audio, DialogOverlay dialog, Framework.Game game, - OsuGame osu, OsuColour colours) + [BackgroundDependencyLoader] + private void load(OsuColour colours) { - const float carousel_width = 640; - const float filter_height = 100; + Footer.AddButton(@"mods", colours.Yellow, modSelect.ToggleVisibility, Key.F1, float.MaxValue); - beatmapGroups = new List(); - Children = new Drawable[] + BeatmapOptions.AddButton(@"Remove", @"from unplayed", FontAwesome.fa_times_circle_o, colours.Purple, null, Key.Number1); + BeatmapOptions.AddButton(@"Clear", @"local scores", FontAwesome.fa_eraser, colours.Purple, null, Key.Number2); + BeatmapOptions.AddButton(@"Edit", @"Beatmap", FontAwesome.fa_pencil, colours.Yellow, () => { - new ParallaxContainer - { - Padding = new MarginPadding { Top = filter_height }, - ParallaxAmount = 0.005f, - RelativeSizeAxes = Axes.Both, - Children = new[] - { - new WedgeBackground - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding - { - Right = carousel_width * 0.76f - }, - }, - } - }, - carousel = new CarouselContainer - { - RelativeSizeAxes = Axes.Y, - Size = new Vector2(carousel_width, 1), - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - }, - filter = new FilterControl - { - RelativeSizeAxes = Axes.X, - Height = filter_height, - FilterChanged = () => filterChanged(), - Exit = Exit, - }, - beatmapInfoWedge = new BeatmapInfoWedge - { - Alpha = 0, - Size = wedged_container_size, - RelativeSizeAxes = Axes.X, - Margin = new MarginPadding - { - Top = 20, - Right = 20, - }, - }, - beatmapOptions = new BeatmapOptionsOverlay - { - OnRemoveFromUnplayed = null, - OnClearLocalScores = null, - OnEdit = null, - OnDelete = promptDelete, - Margin = new MarginPadding - { - Bottom = 50, - }, - }, - modSelect = new ModSelectOverlay - { - RelativeSizeAxes = Axes.X, - Origin = Anchor.BottomCentre, - Anchor = Anchor.BottomCentre, - Margin = new MarginPadding - { - Bottom = 50, - }, - }, - footer = new Footer - { - OnBack = Exit, - OnStart = () => - { - if (player != null || Beatmap == null) - return; - - Beatmap.PreferredPlayMode = playMode.Value; - - (player = new PlayerLoader(new Player - { - Beatmap = Beatmap, //eagerly set this so it's present before push. - })).LoadAsync(Game, l => Push(player)); - } - }, - }; - - footer.AddButton(@"mods", colours.Yellow, modSelect.ToggleVisibility); - footer.AddButton(@"random", colours.Green, carousel.SelectRandom); - footer.AddButton(@"options", colours.Blue, beatmapOptions.ToggleVisibility); - - if (osu != null) - playMode.BindTo(osu.PlayMode); - playMode.ValueChanged += playMode_ValueChanged; - - if (database == null) - database = beatmaps; - - database.BeatmapSetAdded += onBeatmapSetAdded; - database.BeatmapSetRemoved += onBeatmapSetRemoved; - - trackManager = audio.Track; - dialogOverlay = dialog; - - sampleChangeDifficulty = audio.Sample.Get(@"SongSelect/select-difficulty"); - sampleChangeBeatmap = audio.Sample.Get(@"SongSelect/select-expand"); - - initialAddSetsTask = new CancellationTokenSource(); - - Task.Factory.StartNew(() => addBeatmapSets(game, initialAddSetsTask.Token), initialAddSetsTask.Token); + ValidForResume = false; + Push(new Editor()); + }, Key.Number3); } - private ScheduledDelegate filterTask; - - private void filterChanged(bool debounce = true, bool eagerSelection = true) + protected override void OnBeatmapChanged(WorkingBeatmap beatmap) { - filterTask?.Cancel(); - filterTask = Scheduler.AddDelayed(() => - { - filterTask = null; - var search = filter.Search; - BeatmapGroup newSelection = null; - carousel.Sort(filter.Sort); - foreach (var beatmapGroup in carousel) - { - var set = beatmapGroup.BeatmapSet; - - bool hasCurrentMode = set.Beatmaps.Any(bm => bm.Mode == playMode); - - bool match = hasCurrentMode; - - match &= string.IsNullOrEmpty(search) - || (set.Metadata.Artist ?? "").IndexOf(search, StringComparison.InvariantCultureIgnoreCase) != -1 - || (set.Metadata.ArtistUnicode ?? "").IndexOf(search, StringComparison.InvariantCultureIgnoreCase) != -1 - || (set.Metadata.Title ?? "").IndexOf(search, StringComparison.InvariantCultureIgnoreCase) != -1 - || (set.Metadata.TitleUnicode ?? "").IndexOf(search, StringComparison.InvariantCultureIgnoreCase) != -1; - - if (match) - { - if (newSelection == null || beatmapGroup.BeatmapSet.OnlineBeatmapSetID == Beatmap.BeatmapSetInfo.OnlineBeatmapSetID) - { - if (newSelection != null) - newSelection.State = BeatmapGroupState.Collapsed; - newSelection = beatmapGroup; - } - else - beatmapGroup.State = BeatmapGroupState.Collapsed; - } - else - { - beatmapGroup.State = BeatmapGroupState.Hidden; - } - } - - if (newSelection != null) - { - if (newSelection.BeatmapPanels.Any(b => b.Beatmap.ID == Beatmap.BeatmapInfo.ID)) - carousel.SelectBeatmap(Beatmap.BeatmapInfo, false); - else if (eagerSelection) - carousel.SelectBeatmap(newSelection.BeatmapSet.Beatmaps[0], false); - } - }, debounce ? 250 : 0); - } - - private void onBeatmapSetAdded(BeatmapSetInfo s) => Schedule(() => addBeatmapSet(s, Game, true)); - - private void onBeatmapSetRemoved(BeatmapSetInfo s) => Schedule(() => removeBeatmapSet(s)); - - protected override void OnEntering(Screen last) - { - base.OnEntering(last); - ensurePlayingSelected(); - - changeBackground(Beatmap); - - Content.FadeInFromZero(250); - - beatmapInfoWedge.MoveToX(-50); - beatmapInfoWedge.MoveToX(0, 800, EasingTypes.OutQuint); - - filter.Activate(); + beatmap?.Mods.BindTo(modSelect.SelectedMods); + base.OnBeatmapChanged(beatmap); } protected override void OnResuming(Screen last) { player = null; - - changeBackground(Beatmap); - ensurePlayingSelected(); base.OnResuming(last); - - Content.FadeIn(250); - - Content.ScaleTo(1, 250, EasingTypes.OutSine); - - filter.Activate(); } - protected override void OnSuspending(Screen next) + protected override void OnSelected() { - Content.ScaleTo(1.1f, 250, EasingTypes.InSine); + if (player != null) return; - Content.FadeOut(250); - - filter.Deactivate(); - base.OnSuspending(next); - } - - protected override bool OnExiting(Screen next) - { - beatmapInfoWedge.MoveToX(-100, 800, EasingTypes.InQuint); - beatmapInfoWedge.RotateTo(10, 800, EasingTypes.InQuint); - - Content.FadeOut(100); - - filter.Deactivate(); - return base.OnExiting(next); - } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - - database.BeatmapSetAdded -= onBeatmapSetAdded; - database.BeatmapSetRemoved -= onBeatmapSetRemoved; - - initialAddSetsTask.Cancel(); - } - - private void playMode_ValueChanged(object sender, EventArgs e) - { - filterChanged(false); - } - - private void changeBackground(WorkingBeatmap beatmap) - { - var backgroundModeBeatmap = Background as BackgroundScreenBeatmap; - if (backgroundModeBeatmap != null) + (player = new PlayerLoader(new Player { - backgroundModeBeatmap.Beatmap = beatmap; - backgroundModeBeatmap.BlurTo(background_blur, 1000); - backgroundModeBeatmap.FadeTo(1, 250); - } - - beatmapInfoWedge.UpdateBeatmap(beatmap); - } - - /// - /// The global Beatmap was changed. - /// - protected override void OnBeatmapChanged(WorkingBeatmap beatmap) - { - base.OnBeatmapChanged(beatmap); - - beatmap?.Mods.BindTo(modSelect.SelectedMods); - - //todo: change background in selectionChanged instead; support per-difficulty backgrounds. - changeBackground(beatmap); - carousel.SelectBeatmap(beatmap?.BeatmapInfo); - } - - /// - /// selection has been changed as the result of interaction with the carousel. - /// - private void selectionChanged(BeatmapGroup group, BeatmapInfo beatmap) - { - bool beatmapSetChange = false; - - if (!beatmap.Equals(Beatmap?.BeatmapInfo)) - { - if (beatmap.BeatmapSetInfoID == Beatmap?.BeatmapInfo.BeatmapSetInfoID) - sampleChangeDifficulty.Play(); - else - { - sampleChangeBeatmap.Play(); - beatmapSetChange = true; - } - Beatmap = database.GetWorkingBeatmap(beatmap, Beatmap); - } - ensurePlayingSelected(beatmapSetChange); - } - - private void ensurePlayingSelected(bool preview = false) - { - Track track = Beatmap?.Track; - - if (track != null) - { - trackManager.SetExclusive(track); - if (preview) - track.Seek(Beatmap.Beatmap.Metadata.PreviewTime); - track.Start(); - } - } - - private void addBeatmapSet(BeatmapSetInfo beatmapSet, Framework.Game game, bool select = false) - { - beatmapSet = database.GetWithChildren(beatmapSet.ID); - beatmapSet.Beatmaps.ForEach(b => - { - database.GetChildren(b); - if (b.Metadata == null) b.Metadata = beatmapSet.Metadata; - }); - - var group = new BeatmapGroup(beatmapSet, database) - { - SelectionChanged = selectionChanged, - StartRequested = b => footer.StartButton.TriggerClick() - }; - - //for the time being, let's completely load the difficulty panels in the background. - //this likely won't scale so well, but allows us to completely async the loading flow. - Task.WhenAll(group.BeatmapPanels.Select(panel => panel.LoadAsync(game))).ContinueWith(task => Schedule(delegate - { - beatmapGroups.Add(group); - - group.State = BeatmapGroupState.Collapsed; - carousel.AddGroup(group); - - filterChanged(false, false); - - if (Beatmap == null || select) - carousel.SelectBeatmap(beatmapSet.Beatmaps.First()); - else - carousel.SelectBeatmap(Beatmap.BeatmapInfo); - })); - } - - private void removeBeatmapSet(BeatmapSetInfo beatmapSet) - { - var group = beatmapGroups.Find(b => b.BeatmapSet.ID == beatmapSet.ID); - if (group == null) return; - - if (carousel.SelectedGroup == group) - carousel.SelectNext(); - - beatmapGroups.Remove(group); - carousel.RemoveGroup(group); - - if (beatmapGroups.Count == 0) - Beatmap = null; - } - - private void addBeatmapSets(Framework.Game game, CancellationToken token) - { - foreach (var beatmapSet in database.Query().Where(b => !b.DeletePending)) - { - if (token.IsCancellationRequested) return; - addBeatmapSet(beatmapSet, game); - } - } - - private void promptDelete() - { - if (Beatmap != null) - dialogOverlay?.Push(new BeatmapDeleteDialog(Beatmap)); - } - - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) - { - if (args.Repeat) return false; - - switch (args.Key) - { - case Key.F1: - modSelect.ToggleVisibility(); - return true; - case Key.F2: - carousel.SelectRandom(); - return true; - case Key.F3: - beatmapOptions.ToggleVisibility(); - return true; - case Key.Enter: - footer.StartButton.TriggerClick(); - return true; - case Key.Delete: - if (state.Keyboard.ShiftPressed) - { - promptDelete(); - return true; - } - break; - } - - return base.OnKeyDown(state, args); + Beatmap = Beatmap, //eagerly set this so it's present before push. + })).LoadAsync(Game, l => Push(player)); } } } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs new file mode 100644 index 0000000000..ca8b353c57 --- /dev/null +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -0,0 +1,454 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using OpenTK; +using OpenTK.Input; +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; +using osu.Framework.Audio.Track; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Primitives; +using osu.Framework.Graphics.Transforms; +using osu.Framework.Input; +using osu.Framework.Screens; +using osu.Framework.Threading; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Drawables; +using osu.Game.Database; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Modes; +using osu.Game.Overlays; +using osu.Game.Screens.Backgrounds; +using osu.Game.Screens.Select.Options; + +namespace osu.Game.Screens.Select +{ + public abstract class SongSelect : OsuScreen + { + private Bindable playMode = new Bindable(); + private BeatmapDatabase database; + protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap); + + private CarouselContainer carousel; + private TrackManager trackManager; + private DialogOverlay dialogOverlay; + + private static readonly Vector2 wedged_container_size = new Vector2(0.5f, 225); + private BeatmapInfoWedge beatmapInfoWedge; + + private static readonly Vector2 background_blur = new Vector2(20); + private CancellationTokenSource initialAddSetsTask; + + private SampleChannel sampleChangeDifficulty; + private SampleChannel sampleChangeBeatmap; + + private List beatmapGroups; + + protected virtual bool ShowFooter => true; + + /// + /// Can be null if == false + /// + protected readonly BeatmapOptionsOverlay BeatmapOptions; + + /// + /// Can be null if == false + /// + protected readonly Footer Footer; + + private FilterControl filter; + public FilterControl Filter + { + get + { + return filter; + } + private set + { + if (filter != value) + { + filter = value; + filterChanged(); + } + } + } + + protected SongSelect() + { + const float carousel_width = 640; + const float filter_height = 100; + + beatmapGroups = new List(); + Add(new ParallaxContainer + { + Padding = new MarginPadding { Top = filter_height }, + ParallaxAmount = 0.005f, + RelativeSizeAxes = Axes.Both, + Children = new[] + { + new WedgeBackground + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Right = carousel_width * 0.76f }, + } + } + }); + Add(carousel = new CarouselContainer + { + RelativeSizeAxes = Axes.Y, + Size = new Vector2(carousel_width, 1), + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + }); + Add(filter = new FilterControl + { + RelativeSizeAxes = Axes.X, + Height = filter_height, + FilterChanged = () => filterChanged(), + Exit = Exit, + }); + Add(beatmapInfoWedge = new BeatmapInfoWedge + { + Alpha = 0, + Size = wedged_container_size, + RelativeSizeAxes = Axes.X, + Margin = new MarginPadding + { + Top = 20, + Right = 20, + }, + X = -50, + }); + + if (ShowFooter) + { + Add(BeatmapOptions = new BeatmapOptionsOverlay + { + Margin = new MarginPadding + { + Bottom = 50, + }, + }); + Add(Footer = new Footer + { + OnBack = Exit, + OnStart = raiseSelect, + }); + } + } + + [BackgroundDependencyLoader(permitNulls: true)] + private void load(BeatmapDatabase beatmaps, AudioManager audio, DialogOverlay dialog, Framework.Game game, + OsuGame osu, OsuColour colours) + { + if (Footer != null) + { + Footer.AddButton(@"random", colours.Green, SelectRandom, Key.F2); + Footer.AddButton(@"options", colours.Blue, BeatmapOptions.ToggleVisibility, Key.F3); + + BeatmapOptions.AddButton(@"Delete", @"Beatmap", FontAwesome.fa_trash, colours.Pink, promptDelete, Key.Number4, float.MaxValue); + } + + if (osu != null) + playMode.BindTo(osu.PlayMode); + playMode.ValueChanged += playMode_ValueChanged; + + if (database == null) + database = beatmaps; + + database.BeatmapSetAdded += onBeatmapSetAdded; + database.BeatmapSetRemoved += onBeatmapSetRemoved; + + trackManager = audio.Track; + dialogOverlay = dialog; + + sampleChangeDifficulty = audio.Sample.Get(@"SongSelect/select-difficulty"); + sampleChangeBeatmap = audio.Sample.Get(@"SongSelect/select-expand"); + + initialAddSetsTask = new CancellationTokenSource(); + + Task.Factory.StartNew(() => addBeatmapSets(game, initialAddSetsTask.Token), initialAddSetsTask.Token); + } + + private void raiseSelect() + { + if (Beatmap == null) return; + + Beatmap.PreferredPlayMode = playMode.Value; + OnSelected(); + } + + public void SelectRandom() => carousel.SelectRandom(); + protected abstract void OnSelected(); + + private ScheduledDelegate filterTask; + + private void filterChanged(bool debounce = true, bool eagerSelection = true) + { + filterTask?.Cancel(); + filterTask = Scheduler.AddDelayed(() => + { + filterTask = null; + var search = filter.Search; + BeatmapGroup newSelection = null; + carousel.Sort(filter.Sort); + foreach (var beatmapGroup in carousel) + { + var set = beatmapGroup.BeatmapSet; + + bool hasCurrentMode = set.Beatmaps.Any(bm => bm.Mode == playMode); + + bool match = hasCurrentMode; + + match &= string.IsNullOrEmpty(search) + || (set.Metadata.Artist ?? "").IndexOf(search, StringComparison.InvariantCultureIgnoreCase) != -1 + || (set.Metadata.ArtistUnicode ?? "").IndexOf(search, StringComparison.InvariantCultureIgnoreCase) != -1 + || (set.Metadata.Title ?? "").IndexOf(search, StringComparison.InvariantCultureIgnoreCase) != -1 + || (set.Metadata.TitleUnicode ?? "").IndexOf(search, StringComparison.InvariantCultureIgnoreCase) != -1; + + if (match) + { + if (newSelection == null || beatmapGroup.BeatmapSet.OnlineBeatmapSetID == Beatmap.BeatmapSetInfo.OnlineBeatmapSetID) + { + if (newSelection != null) + newSelection.State = BeatmapGroupState.Collapsed; + newSelection = beatmapGroup; + } + else + beatmapGroup.State = BeatmapGroupState.Collapsed; + } + else + { + beatmapGroup.State = BeatmapGroupState.Hidden; + } + } + + if (newSelection != null) + { + if (newSelection.BeatmapPanels.Any(b => b.Beatmap.ID == Beatmap.BeatmapInfo.ID)) + carousel.SelectBeatmap(Beatmap.BeatmapInfo, false); + else if (eagerSelection) + carousel.SelectBeatmap(newSelection.BeatmapSet.Beatmaps[0], false); + } + }, debounce ? 250 : 0); + } + + private void onBeatmapSetAdded(BeatmapSetInfo s) => Schedule(() => addBeatmapSet(s, Game, true)); + + private void onBeatmapSetRemoved(BeatmapSetInfo s) => Schedule(() => removeBeatmapSet(s)); + + protected override void OnEntering(Screen last) + { + base.OnEntering(last); + ensurePlayingSelected(); + + changeBackground(Beatmap); + + Content.FadeInFromZero(250); + + beatmapInfoWedge.State = Visibility.Visible; + + filter.Activate(); + } + + protected override void OnResuming(Screen last) + { + changeBackground(Beatmap); + ensurePlayingSelected(); + base.OnResuming(last); + + Content.FadeIn(250); + + Content.ScaleTo(1, 250, EasingTypes.OutSine); + + filter.Activate(); + } + + protected override void OnSuspending(Screen next) + { + Content.ScaleTo(1.1f, 250, EasingTypes.InSine); + + Content.FadeOut(250); + + filter.Deactivate(); + base.OnSuspending(next); + } + + protected override bool OnExiting(Screen next) + { + beatmapInfoWedge.State = Visibility.Hidden; + + Content.FadeOut(100); + + filter.Deactivate(); + return base.OnExiting(next); + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + database.BeatmapSetAdded -= onBeatmapSetAdded; + database.BeatmapSetRemoved -= onBeatmapSetRemoved; + + initialAddSetsTask.Cancel(); + } + + private void playMode_ValueChanged(object sender, EventArgs e) + { + filterChanged(false); + } + + private void changeBackground(WorkingBeatmap beatmap) + { + var backgroundModeBeatmap = Background as BackgroundScreenBeatmap; + if (backgroundModeBeatmap != null) + { + backgroundModeBeatmap.Beatmap = beatmap; + backgroundModeBeatmap.BlurTo(background_blur, 1000); + backgroundModeBeatmap.FadeTo(1, 250); + } + + beatmapInfoWedge.UpdateBeatmap(beatmap); + } + + /// + /// The global Beatmap was changed. + /// + protected override void OnBeatmapChanged(WorkingBeatmap beatmap) + { + base.OnBeatmapChanged(beatmap); + + //todo: change background in selectionChanged instead; support per-difficulty backgrounds. + changeBackground(beatmap); + carousel.SelectBeatmap(beatmap?.BeatmapInfo); + } + + /// + /// selection has been changed as the result of interaction with the carousel. + /// + private void selectionChanged(BeatmapGroup group, BeatmapInfo beatmap) + { + bool beatmapSetChange = false; + + if (!beatmap.Equals(Beatmap?.BeatmapInfo)) + { + if (beatmap.BeatmapSetInfoID == Beatmap?.BeatmapInfo.BeatmapSetInfoID) + sampleChangeDifficulty.Play(); + else + { + sampleChangeBeatmap.Play(); + beatmapSetChange = true; + } + Beatmap = database.GetWorkingBeatmap(beatmap, Beatmap); + } + ensurePlayingSelected(beatmapSetChange); + } + + private void ensurePlayingSelected(bool preview = false) + { + Track track = Beatmap?.Track; + + if (track != null) + { + trackManager.SetExclusive(track); + if (preview) + track.Seek(Beatmap.Beatmap.Metadata.PreviewTime); + track.Start(); + } + } + + private void addBeatmapSet(BeatmapSetInfo beatmapSet, Framework.Game game, bool select = false) + { + beatmapSet = database.GetWithChildren(beatmapSet.ID); + beatmapSet.Beatmaps.ForEach(b => + { + database.GetChildren(b); + if (b.Metadata == null) b.Metadata = beatmapSet.Metadata; + }); + + var group = new BeatmapGroup(beatmapSet, database) + { + SelectionChanged = selectionChanged, + StartRequested = b => raiseSelect() + }; + + //for the time being, let's completely load the difficulty panels in the background. + //this likely won't scale so well, but allows us to completely async the loading flow. + Task.WhenAll(group.BeatmapPanels.Select(panel => panel.LoadAsync(game))).ContinueWith(task => Schedule(delegate + { + beatmapGroups.Add(group); + + group.State = BeatmapGroupState.Collapsed; + carousel.AddGroup(group); + + filterChanged(false, false); + + if (Beatmap == null || select) + carousel.SelectBeatmap(beatmapSet.Beatmaps.First()); + else + carousel.SelectBeatmap(Beatmap.BeatmapInfo); + })); + } + + private void removeBeatmapSet(BeatmapSetInfo beatmapSet) + { + var group = beatmapGroups.Find(b => b.BeatmapSet.ID == beatmapSet.ID); + if (group == null) return; + + if (carousel.SelectedGroup == group) + carousel.SelectNext(); + + beatmapGroups.Remove(group); + carousel.RemoveGroup(group); + + if (beatmapGroups.Count == 0) + Beatmap = null; + } + + private void addBeatmapSets(Framework.Game game, CancellationToken token) + { + foreach (var beatmapSet in database.Query().Where(b => !b.DeletePending)) + { + if (token.IsCancellationRequested) return; + addBeatmapSet(beatmapSet, game); + } + } + + private void promptDelete() + { + if (Beatmap != null) + dialogOverlay?.Push(new BeatmapDeleteDialog(Beatmap)); + } + + protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + { + if (args.Repeat) return false; + + switch (args.Key) + { + case Key.Enter: + raiseSelect(); + return true; + case Key.Delete: + if (state.Keyboard.ShiftPressed) + { + promptDelete(); + return true; + } + break; + } + + return base.OnKeyDown(state, args); + } + } +} diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 678e89f0a8..cfa3993639 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -204,6 +204,7 @@ + @@ -342,11 +343,7 @@ - - - -