From 7dcbefd50fafecdf27f8ab84019bd3515957cb98 Mon Sep 17 00:00:00 2001 From: Alex Amadori Date: Fri, 17 Feb 2017 17:41:53 +0100 Subject: [PATCH 01/44] Implemented basic sorting logic --- osu.Game/Screens/Select/CarouselContainer.cs | 38 ++++++++++++++++++++ osu.Game/Screens/Select/FilterControl.cs | 8 ++--- osu.Game/Screens/Select/PlaySongSelect.cs | 2 ++ 3 files changed, 44 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Select/CarouselContainer.cs b/osu.Game/Screens/Select/CarouselContainer.cs index 86cfc89763..260f2d0998 100644 --- a/osu.Game/Screens/Select/CarouselContainer.cs +++ b/osu.Game/Screens/Select/CarouselContainer.cs @@ -184,6 +184,44 @@ namespace osu.Game.Screens.Select ScrollTo(selectedY, animated); } + public void Sort(FilterControl.SortMode mode) { + switch (mode) { + case FilterControl.SortMode.Artist: + groups.Sort((x, y) => + { + return string.Compare(x.BeatmapSet.Metadata.Artist, y.BeatmapSet.Metadata.Artist); + }); + break; + case FilterControl.SortMode.Title: + groups.Sort((x, y) => + { + return string.Compare(x.BeatmapSet.Metadata.Title, y.BeatmapSet.Metadata.Title); + }); + break; + case FilterControl.SortMode.Author: + groups.Sort((x, y) => + { + return string.Compare(x.BeatmapSet.Metadata.Author, y.BeatmapSet.Metadata.Author); + }); + break; + case FilterControl.SortMode.Difficulty: + groups.Sort((x, y) => + { + if (x.BeatmapSet.Beatmaps.First().BaseDifficulty.OverallDifficulty > + y.BeatmapSet.Beatmaps.First().BaseDifficulty.OverallDifficulty) + return 1; + else if (Equals(x.BeatmapSet.Beatmaps.First().BaseDifficulty.OverallDifficulty, + y.BeatmapSet.Beatmaps.First().BaseDifficulty.OverallDifficulty)) + return 0; + else + return -1; + }); + break; + default: + throw new NotImplementedException(); + } + } + private static float offsetX(float dist, float halfHeight) { // The radius of the circle the carousel moves on. diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs index 975a3f9ca3..e4af7b8d02 100644 --- a/osu.Game/Screens/Select/FilterControl.cs +++ b/osu.Game/Screens/Select/FilterControl.cs @@ -250,9 +250,9 @@ namespace osu.Game.Screens.Select public enum SortMode { - Arist, + Artist, BPM, - Creator, + Author, DateAdded, Difficulty, Length, @@ -263,9 +263,9 @@ namespace osu.Game.Screens.Select public enum GroupMode { NoGrouping, - Arist, + Artist, BPM, - Creator, + Author, DateAdded, Difficulty, Length, diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index e206c5d5fa..edd3880a36 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -174,6 +174,7 @@ namespace osu.Game.Screens.Select filterTask = null; var search = filter.Search; BeatmapGroup newSelection = null; + carousel.Sort(filter.Sort); foreach (var beatmapGroup in carousel) { var set = beatmapGroup.BeatmapSet; @@ -373,6 +374,7 @@ namespace osu.Game.Screens.Select if (token.IsCancellationRequested) return; addBeatmapSet(beatmapSet, game); } + filterChanged(); } protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) From 6b3ecc28b87f58bd4f9ebdff9def1584c5d2719a Mon Sep 17 00:00:00 2001 From: Alex Amadori Date: Fri, 17 Feb 2017 23:32:14 +0100 Subject: [PATCH 02/44] Fixed BeatmapPanels disappearing --- osu.Game/Screens/Select/CarouselContainer.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/osu.Game/Screens/Select/CarouselContainer.cs b/osu.Game/Screens/Select/CarouselContainer.cs index 260f2d0998..9ffc5a183a 100644 --- a/osu.Game/Screens/Select/CarouselContainer.cs +++ b/osu.Game/Screens/Select/CarouselContainer.cs @@ -176,6 +176,7 @@ namespace osu.Game.Screens.Select if (SelectedGroup != null && SelectedGroup != group && SelectedGroup.State != BeatmapGroupState.Hidden) SelectedGroup.State = BeatmapGroupState.Collapsed; + group.State = BeatmapGroupState.Expanded; SelectedGroup = group; panel.State = PanelSelectedState.Selected; SelectedPanel = panel; @@ -220,6 +221,20 @@ namespace osu.Game.Screens.Select default: throw new NotImplementedException(); } + scrollableContent.Clear(false); + lifetime.Clear(); + foreach (BeatmapGroup group in groups) + { + group.Header.Depth = -scrollableContent.Children.Count(); + scrollableContent.Add(group.Header); + + foreach (BeatmapPanel panel in group.BeatmapPanels) + { + panel.Depth = -scrollableContent.Children.Count(); + scrollableContent.Add(panel); + } + } + SelectGroup(groups.FirstOrDefault(), groups.First().BeatmapPanels.FirstOrDefault()); } private static float offsetX(float dist, float halfHeight) From 1cd93f79b378898feb13ac0142d4be53ff8c5751 Mon Sep 17 00:00:00 2001 From: Alex Amadori Date: Sat, 18 Feb 2017 07:59:01 +0100 Subject: [PATCH 03/44] General sorting improvements --- osu.Game/Screens/Select/CarouselContainer.cs | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/osu.Game/Screens/Select/CarouselContainer.cs b/osu.Game/Screens/Select/CarouselContainer.cs index 9ffc5a183a..a121d4b3eb 100644 --- a/osu.Game/Screens/Select/CarouselContainer.cs +++ b/osu.Game/Screens/Select/CarouselContainer.cs @@ -188,26 +188,18 @@ namespace osu.Game.Screens.Select public void Sort(FilterControl.SortMode mode) { switch (mode) { case FilterControl.SortMode.Artist: - groups.Sort((x, y) => - { - return string.Compare(x.BeatmapSet.Metadata.Artist, y.BeatmapSet.Metadata.Artist); - }); + groups.Sort((x, y) => string.Compare(x.BeatmapSet.Metadata.Artist, y.BeatmapSet.Metadata.Artist)); break; case FilterControl.SortMode.Title: - groups.Sort((x, y) => - { - return string.Compare(x.BeatmapSet.Metadata.Title, y.BeatmapSet.Metadata.Title); - }); + groups.Sort((x, y) => string.Compare(x.BeatmapSet.Metadata.Title, y.BeatmapSet.Metadata.Title)); break; case FilterControl.SortMode.Author: - groups.Sort((x, y) => - { - return string.Compare(x.BeatmapSet.Metadata.Author, y.BeatmapSet.Metadata.Author); - }); + groups.Sort((x, y) => string.Compare(x.BeatmapSet.Metadata.Author, y.BeatmapSet.Metadata.Author)); break; case FilterControl.SortMode.Difficulty: groups.Sort((x, y) => { + // TODO: replace with star rating once implemented if (x.BeatmapSet.Beatmaps.First().BaseDifficulty.OverallDifficulty > y.BeatmapSet.Beatmaps.First().BaseDifficulty.OverallDifficulty) return 1; From 6bbbbd8f969612653666d197d74798e9b3cf3f2a Mon Sep 17 00:00:00 2001 From: Alex Amadori Date: Sat, 18 Feb 2017 15:11:40 +0100 Subject: [PATCH 04/44] Implemented sorting in TestCasePlaySong --- .../Tests/TestCasePlaySongSelect.cs | 25 ++++++++++++++++--- osu.Game/Screens/Select/CarouselContainer.cs | 7 +++--- osu.Game/Screens/Select/FilterControl.cs | 10 +++++++- osu.Game/Screens/Select/PlaySongSelect.cs | 6 ++++- 4 files changed, 39 insertions(+), 9 deletions(-) diff --git a/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs b/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs index 47fac4d339..009d7b8830 100644 --- a/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs +++ b/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs @@ -1,6 +1,7 @@ // 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.Desktop.VisualTests.Platform; using osu.Framework.GameModes.Testing; @@ -14,10 +15,17 @@ namespace osu.Desktop.VisualTests.Tests { private BeatmapDatabase db, oldDb; private TestStorage storage; + private Random rnd = new Random(); + private PlaySongSelect SongSelect = new PlaySongSelect(); public override string Name => @"Song Select"; public override string Description => @"with fake data"; + public Action OnArtist; + public Action OnTitle; + public Action OnAuthor; + public Action OnDifficulty; + public override void Reset() { base.Reset(); @@ -35,6 +43,16 @@ namespace osu.Desktop.VisualTests.Tests db.Import(sets); } + OnArtist = () => SongSelect.Filter.Sort = FilterControl.SortMode.Artist; + OnTitle = () => SongSelect.Filter.Sort = FilterControl.SortMode.Title; + OnAuthor = () => SongSelect.Filter.Sort = FilterControl.SortMode.Author; + OnDifficulty = () => SongSelect.Filter.Sort = FilterControl.SortMode.Difficulty; + + AddButton(@"Sort by Artist", OnArtist); + AddButton(@"Sort by Artist", OnTitle); + AddButton(@"Sort by Artist", OnAuthor); + AddButton(@"Sort by Artist", OnDifficulty); + Add(new PlaySongSelect()); } @@ -59,9 +77,10 @@ namespace osu.Desktop.VisualTests.Tests Metadata = new BeatmapMetadata { OnlineBeatmapSetID = 1234 + i, - Artist = "MONACA", - Title = "Black Song", - Author = "Some Guy", + // Create random metadata, then we can check if sorting works based on these + Artist = "MONACA " + rnd.Next(0, 9), + Title = "Black Song " + rnd.Next(0, 9), + Author = "Some Guy " + rnd.Next(0, 9), }, Beatmaps = new List(new[] { diff --git a/osu.Game/Screens/Select/CarouselContainer.cs b/osu.Game/Screens/Select/CarouselContainer.cs index a121d4b3eb..8a346543ba 100644 --- a/osu.Game/Screens/Select/CarouselContainer.cs +++ b/osu.Game/Screens/Select/CarouselContainer.cs @@ -199,13 +199,12 @@ namespace osu.Game.Screens.Select case FilterControl.SortMode.Difficulty: groups.Sort((x, y) => { - // TODO: replace with star rating once implemented + /*TODO: replace with star rating once implemented + * Assumes BeatmapSets not to be grouped - or to be by difficulty, + * otherwise this sorting makes little sense - or does it? */ if (x.BeatmapSet.Beatmaps.First().BaseDifficulty.OverallDifficulty > y.BeatmapSet.Beatmaps.First().BaseDifficulty.OverallDifficulty) return 1; - else if (Equals(x.BeatmapSet.Beatmaps.First().BaseDifficulty.OverallDifficulty, - y.BeatmapSet.Beatmaps.First().BaseDifficulty.OverallDifficulty)) - return 0; else return -1; }); diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs index e4af7b8d02..c660ce48f0 100644 --- a/osu.Game/Screens/Select/FilterControl.cs +++ b/osu.Game/Screens/Select/FilterControl.cs @@ -21,7 +21,15 @@ namespace osu.Game.Screens.Select public Action FilterChanged; public string Search => searchTextBox.Text; - public SortMode Sort { get; private set; } = SortMode.Title; + private SortMode sort = SortMode.Title; + public SortMode Sort { + get { return sort; } + set { + sort = value; + FilterChanged?.Invoke(); + } + } + public Action Exit; private SearchTextBox searchTextBox; diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index edd3880a36..82a93c70d2 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -54,8 +54,12 @@ namespace osu.Game.Screens.Select private Footer footer; + private FilterControl filter; + public FilterControl Filter { + get; private set; + } + Player player; - FilterControl filter; private void start() { From f48c83b787c040fe28c558f298ac5f058d4dd9d5 Mon Sep 17 00:00:00 2001 From: Alex Amadori Date: Sat, 18 Feb 2017 15:56:29 +0100 Subject: [PATCH 05/44] Fix silly sorting mistakes --- .../Tests/TestCasePlaySongSelect.cs | 18 +++++++++--------- osu.Game/Screens/Select/FilterControl.cs | 7 +++++-- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs b/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs index b41d48b99a..47fc86ca84 100644 --- a/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs +++ b/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs @@ -43,19 +43,19 @@ namespace osu.Desktop.VisualTests.Tests db.Import(sets); } - OnArtist = () => SongSelect.Filter.Sort = FilterControl.SortMode.Artist; - OnTitle = () => SongSelect.Filter.Sort = FilterControl.SortMode.Title; - OnAuthor = () => SongSelect.Filter.Sort = FilterControl.SortMode.Author; - OnDifficulty = () => SongSelect.Filter.Sort = FilterControl.SortMode.Difficulty; + OnArtist = () => { SongSelect.Filter.Sort = FilterControl.SortMode.Artist; }; + OnTitle = () => { SongSelect.Filter.Sort = FilterControl.SortMode.Title; }; + OnAuthor = () => { SongSelect.Filter.Sort = FilterControl.SortMode.Author; }; + OnDifficulty = () => { SongSelect.Filter.Sort = FilterControl.SortMode.Difficulty; }; AddButton(@"Sort by Artist", OnArtist); - AddButton(@"Sort by Artist", OnTitle); - AddButton(@"Sort by Artist", OnAuthor); - AddButton(@"Sort by Artist", OnDifficulty); + AddButton(@"Sort by Title", OnTitle); + AddButton(@"Sort by Author", OnAuthor); + AddButton(@"Sort by Difficulty", OnDifficulty); - Add(new PlaySongSelect()); + Add(SongSelect); } - + protected override void Dispose(bool isDisposing) { if (oldDb != null) diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs index c660ce48f0..b2c13a980e 100644 --- a/osu.Game/Screens/Select/FilterControl.cs +++ b/osu.Game/Screens/Select/FilterControl.cs @@ -25,8 +25,11 @@ namespace osu.Game.Screens.Select public SortMode Sort { get { return sort; } set { - sort = value; - FilterChanged?.Invoke(); + if (sort != value) + { + sort = value; + FilterChanged?.Invoke(); + } } } From 21cf96ec1009f2481e8bb7ec0a79a4d8a55fc0e2 Mon Sep 17 00:00:00 2001 From: Alex Amadori Date: Sat, 18 Feb 2017 16:23:13 +0100 Subject: [PATCH 06/44] More sorting rookie mistakes --- .../Tests/TestCasePlaySongSelect.cs | 6 +++--- osu.Game/Screens/Select/PlaySongSelect.cs | 10 +++++++++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs b/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs index 47fc86ca84..e0e5b2cc82 100644 --- a/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs +++ b/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs @@ -16,7 +16,7 @@ namespace osu.Desktop.VisualTests.Tests private BeatmapDatabase db, oldDb; private TestStorage storage; private Random rnd = new Random(); - private PlaySongSelect SongSelect = new PlaySongSelect(); + private PlaySongSelect SongSelect; public override string Name => @"Song Select"; public override string Description => @"with fake data"; @@ -43,6 +43,8 @@ namespace osu.Desktop.VisualTests.Tests db.Import(sets); } + + Add(SongSelect = new PlaySongSelect()); OnArtist = () => { SongSelect.Filter.Sort = FilterControl.SortMode.Artist; }; OnTitle = () => { SongSelect.Filter.Sort = FilterControl.SortMode.Title; }; OnAuthor = () => { SongSelect.Filter.Sort = FilterControl.SortMode.Author; }; @@ -52,8 +54,6 @@ namespace osu.Desktop.VisualTests.Tests AddButton(@"Sort by Title", OnTitle); AddButton(@"Sort by Author", OnAuthor); AddButton(@"Sort by Difficulty", OnDifficulty); - - Add(SongSelect); } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index 56d904c155..2291daa1c6 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -56,7 +56,15 @@ namespace osu.Game.Screens.Select private FilterControl filter; public FilterControl Filter { - get; private set; + get { + return filter; + } + private set { + if (filter != value) { + filter = value; + filterChanged(); + } + } } Player player; From 87ec0e36ea0a07654c9dd9627f411286bbd883fe Mon Sep 17 00:00:00 2001 From: Alex Amadori Date: Sat, 18 Feb 2017 16:45:46 +0100 Subject: [PATCH 07/44] Complying with rule "Instance fields (private)" --- .../Tests/TestCasePlaySongSelect.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs b/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs index e0e5b2cc82..52b78267d6 100644 --- a/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs +++ b/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs @@ -16,7 +16,7 @@ namespace osu.Desktop.VisualTests.Tests private BeatmapDatabase db, oldDb; private TestStorage storage; private Random rnd = new Random(); - private PlaySongSelect SongSelect; + private PlaySongSelect songSelect; public override string Name => @"Song Select"; public override string Description => @"with fake data"; @@ -44,11 +44,11 @@ namespace osu.Desktop.VisualTests.Tests db.Import(sets); } - Add(SongSelect = new PlaySongSelect()); - OnArtist = () => { SongSelect.Filter.Sort = FilterControl.SortMode.Artist; }; - OnTitle = () => { SongSelect.Filter.Sort = FilterControl.SortMode.Title; }; - OnAuthor = () => { SongSelect.Filter.Sort = FilterControl.SortMode.Author; }; - OnDifficulty = () => { SongSelect.Filter.Sort = FilterControl.SortMode.Difficulty; }; + Add(songSelect = new PlaySongSelect()); + OnArtist = () => { songSelect.Filter.Sort = FilterControl.SortMode.Artist; }; + OnTitle = () => { songSelect.Filter.Sort = FilterControl.SortMode.Title; }; + OnAuthor = () => { songSelect.Filter.Sort = FilterControl.SortMode.Author; }; + OnDifficulty = () => { songSelect.Filter.Sort = FilterControl.SortMode.Difficulty; }; AddButton(@"Sort by Artist", OnArtist); AddButton(@"Sort by Title", OnTitle); From a6fbfc8349bf57d31ccd3479f6648f0526110c67 Mon Sep 17 00:00:00 2001 From: Alex Amadori Date: Mon, 20 Feb 2017 17:49:02 +0100 Subject: [PATCH 08/44] Using random generator from osu-framework --- osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs b/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs index 52b78267d6..9fad8992d2 100644 --- a/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs +++ b/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using osu.Desktop.VisualTests.Platform; using osu.Framework.Screens.Testing; +using osu.Framework.MathUtils; using osu.Game.Database; using osu.Game.Modes; using osu.Game.Screens.Select; @@ -15,7 +16,6 @@ namespace osu.Desktop.VisualTests.Tests { private BeatmapDatabase db, oldDb; private TestStorage storage; - private Random rnd = new Random(); private PlaySongSelect songSelect; public override string Name => @"Song Select"; @@ -78,9 +78,9 @@ namespace osu.Desktop.VisualTests.Tests { OnlineBeatmapSetID = 1234 + i, // Create random metadata, then we can check if sorting works based on these - Artist = "MONACA " + rnd.Next(0, 9), - Title = "Black Song " + rnd.Next(0, 9), - Author = "Some Guy " + rnd.Next(0, 9), + Artist = "MONACA " + RNG.Next(0, 9), + Title = "Black Song " + RNG.Next(0, 9), + Author = "Some Guy " + RNG.Next(0, 9), }, Beatmaps = new List(new[] { From 3d19199218b72e70e8e5f4ba94c55031eaf08846 Mon Sep 17 00:00:00 2001 From: Alex Amadori Date: Tue, 21 Feb 2017 18:27:39 +0100 Subject: [PATCH 09/44] Fixed for star difficulty --- osu.Game/Screens/Select/CarouselContainer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Select/CarouselContainer.cs b/osu.Game/Screens/Select/CarouselContainer.cs index 8a346543ba..f4d2842638 100644 --- a/osu.Game/Screens/Select/CarouselContainer.cs +++ b/osu.Game/Screens/Select/CarouselContainer.cs @@ -202,8 +202,8 @@ namespace osu.Game.Screens.Select /*TODO: replace with star rating once implemented * Assumes BeatmapSets not to be grouped - or to be by difficulty, * otherwise this sorting makes little sense - or does it? */ - if (x.BeatmapSet.Beatmaps.First().BaseDifficulty.OverallDifficulty > - y.BeatmapSet.Beatmaps.First().BaseDifficulty.OverallDifficulty) + if (x.BeatmapSet.Beatmaps.First().StarDifficulty > + y.BeatmapSet.Beatmaps.First().StarDifficulty) return 1; else return -1; From fbb16295ae4445a941ec508ee70b59ccf627ddfe Mon Sep 17 00:00:00 2001 From: Alex Amadori Date: Tue, 21 Feb 2017 18:43:10 +0100 Subject: [PATCH 10/44] Improved delegate syntax --- .../Tests/TestCasePlaySongSelect.cs | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs b/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs index 9fad8992d2..6cc2d62402 100644 --- a/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs +++ b/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs @@ -21,11 +21,6 @@ namespace osu.Desktop.VisualTests.Tests public override string Name => @"Song Select"; public override string Description => @"with fake data"; - public Action OnArtist; - public Action OnTitle; - public Action OnAuthor; - public Action OnDifficulty; - public override void Reset() { base.Reset(); @@ -45,15 +40,11 @@ namespace osu.Desktop.VisualTests.Tests } Add(songSelect = new PlaySongSelect()); - OnArtist = () => { songSelect.Filter.Sort = FilterControl.SortMode.Artist; }; - OnTitle = () => { songSelect.Filter.Sort = FilterControl.SortMode.Title; }; - OnAuthor = () => { songSelect.Filter.Sort = FilterControl.SortMode.Author; }; - OnDifficulty = () => { songSelect.Filter.Sort = FilterControl.SortMode.Difficulty; }; - AddButton(@"Sort by Artist", OnArtist); - AddButton(@"Sort by Title", OnTitle); - AddButton(@"Sort by Author", OnAuthor); - AddButton(@"Sort by Difficulty", OnDifficulty); + AddButton(@"Sort by Artist", delegate { songSelect.Filter.Sort = FilterControl.SortMode.Artist; }); + AddButton(@"Sort by Title", delegate { songSelect.Filter.Sort = FilterControl.SortMode.Title; }); + AddButton(@"Sort by Author", delegate { songSelect.Filter.Sort = FilterControl.SortMode.Author; }); + AddButton(@"Sort by Difficulty", delegate { songSelect.Filter.Sort = FilterControl.SortMode.Difficulty; }); } protected override void Dispose(bool isDisposing) From c94da4fcf051deb7362b2db5adabf3ca28dc0951 Mon Sep 17 00:00:00 2001 From: DrabWeb Date: Fri, 24 Feb 2017 00:05:37 -0400 Subject: [PATCH 11/44] Added popup dialog --- .../Tests/TestCasePopupDialog.cs | 85 ++++ .../osu.Desktop.VisualTests.csproj | 1 + osu.Game/Overlays/Dialog/PopupDialog.cs | 383 ++++++++++++++++++ osu.Game/Overlays/Dialog/PopupDialogButton.cs | 170 ++++++++ osu.Game/osu.Game.csproj | 5 + 5 files changed, 644 insertions(+) create mode 100644 osu.Desktop.VisualTests/Tests/TestCasePopupDialog.cs create mode 100644 osu.Game/Overlays/Dialog/PopupDialog.cs create mode 100644 osu.Game/Overlays/Dialog/PopupDialogButton.cs diff --git a/osu.Desktop.VisualTests/Tests/TestCasePopupDialog.cs b/osu.Desktop.VisualTests/Tests/TestCasePopupDialog.cs new file mode 100644 index 0000000000..5cff6d452a --- /dev/null +++ b/osu.Desktop.VisualTests/Tests/TestCasePopupDialog.cs @@ -0,0 +1,85 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Screens.Testing; +using osu.Game.Graphics; +using osu.Game.Overlays.Dialog; + +namespace osu.Desktop.VisualTests.Tests +{ + public class TestCasePopupDialog : TestCase + { + public override string Name => @"Popup Dialog"; + + public override string Description => @"With various dialogs"; + + public override void Reset() + { + base.Reset(); + + var firstDialog = new PopupDialog + { + RelativeSizeAxes = Axes.Both, + Icon = FontAwesome.fa_trash_o, + ContextText = @"DELETE BEATMAP", + HeaderText = @"Confirm deletion of", + BodyText = @"Ayase Rie - Yuima-ru*World TVver.", + Buttons = new PopupDialogButton[] + { + new PopupDialogOKButton + { + Title = @"I never want to see this again.", + Action = () => System.Console.WriteLine(@"OK"), + }, + new PopupDialogCancelButton + { + Title = @"Firetruck, I still want quick ranks!", + Action = () => System.Console.WriteLine(@"Cancel"), + }, + }, + }; + var secondDialog = new PopupDialog + { + RelativeSizeAxes = Axes.Both, + Icon = FontAwesome.fa_gear, + ContextText = @"BEATMAP OPTIONS", + HeaderText = @"What do you want to do with", + BodyText = "Camellia as \"Bang Riot\" - Blastix Riotz", + Buttons = new PopupDialogButton[] + { + new PopupDialogOKButton + { + Title = @"Manage collections", + }, + new PopupDialogOKButton + { + Title = @"Delete...", + }, + new PopupDialogOKButton + { + Title = @"Remove from unplayed", + }, + new PopupDialogOKButton + { + Title = @"Clear local scores", + }, + new PopupDialogOKButton + { + Title = @"Edit", + }, + new PopupDialogCancelButton + { + Title = @"Cancel", + }, + }, + }; + + Add(firstDialog); + Add(secondDialog); + + AddButton("dialog #1", firstDialog.ToggleVisibility); + AddButton("dialog #2", secondDialog.ToggleVisibility); + } + } +} diff --git a/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj b/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj index cd899d4dd2..4e9bd92cbd 100644 --- a/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj +++ b/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj @@ -191,6 +191,7 @@ + diff --git a/osu.Game/Overlays/Dialog/PopupDialog.cs b/osu.Game/Overlays/Dialog/PopupDialog.cs new file mode 100644 index 0000000000..fb281ac030 --- /dev/null +++ b/osu.Game/Overlays/Dialog/PopupDialog.cs @@ -0,0 +1,383 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.Linq; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Transformations; +using osu.Framework.MathUtils; +using osu.Game.Graphics; +using osu.Game.Graphics.Backgrounds; +using osu.Game.Graphics.Sprites; + +namespace osu.Game.Overlays.Dialog +{ + public class PopupDialog : OverlayContainer + { + private const float header_body_offset = 4; + private readonly Vector2 ring_size = new Vector2(100f); + private readonly Vector2 ring_minified_size = new Vector2(20f); + private readonly Vector2 buttons_enter_spacing = new Vector2(0f, 100f); + + private const float enter_duration = 500; + private readonly EasingTypes enter_easing = EasingTypes.OutQuint; + + private const float exit_duration = 500; + private const float button_fade_duration = 200; + private readonly EasingTypes exit_easing = EasingTypes.InSine; + + private PopupDialogTriangles triangles; + private Container dialogContainer, iconRing, headerBodyContainer; + private TextAwesome iconText; + private OsuSpriteText contextLabel, headerLabel, bodyLabel; + private FlowContainer buttonsContainer; + + public FontAwesome Icon + { + get + { + return iconText.Icon; + } + set + { + iconText.Icon = value; + } + } + + public string ContextText + { + get + { + return contextLabel.Text; + } + set + { + contextLabel.Text = value.ToUpper(); + } + } + + public string HeaderText + { + get + { + return headerLabel.Text; + } + set + { + headerLabel.Text = value; + } + } + + public string BodyText + { + get + { + return bodyLabel.Text; + } + set + { + bodyLabel.Text = value; + } + } + + public PopupDialogButton[] Buttons + { + get + { + // buttonsContainer cannot be a FlowContainer because there is a crash on TransformSpacing if it is (probably a bug and will be fixed) + + var buttons = new List(); + foreach (Container c in buttonsContainer.Children) + { + var button = (PopupDialogButton)c; + if (button != null) buttons.Add(button); + } + return buttons.ToArray(); + } + set + { + buttonsContainer.Children = value; + + // Simple way to allow direct action setting on the button but we can still call our own logic here + foreach (PopupDialogButton b in value) + { + b.AlwaysPresent = true; + var action = b.Action; + b.Action = () => + { + fadeOutAllBut(b); + Hide(); + action?.Invoke(); + }; + } + } + } + + protected override void PopIn() + { + // Reset various animations, but only if the entire dialog animation completed + if (dialogContainer.Alpha == 0) + { + iconRing.ResizeTo(ring_minified_size); + buttonsContainer.TransformSpacingTo(buttons_enter_spacing); + headerBodyContainer.Alpha = 0; + } + + foreach (PopupDialogButton b in Buttons) + b.FadeIn(button_fade_duration, enter_easing); + + triangles.SlideIn(); + dialogContainer.FadeIn(enter_duration, enter_easing); + iconRing.ResizeTo(ring_size, enter_duration, enter_easing); + headerBodyContainer.FadeIn(enter_duration, enter_easing); + buttonsContainer.MoveToY(0, enter_duration, enter_easing); + buttonsContainer.TransformSpacingTo(new Vector2(0f), enter_duration, enter_easing); + } + + protected override void PopOut() + { + triangles.SlideOut(); + dialogContainer.FadeOut(exit_duration, exit_easing); + headerBodyContainer.FadeOut(exit_duration, exit_easing); + } + + private void fadeOutAllBut(PopupDialogButton button) + { + foreach (PopupDialogButton b in Buttons) + { + if (b != button) + { + b.FadeOut(button_fade_duration, exit_easing); + } + } + } + + public PopupDialog() + { + Children = new Drawable[] + { + triangles = new PopupDialogTriangles + { + RelativeSizeAxes = Axes.Both, + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + Width = 0.5f, + }, + dialogContainer = new Container + { + RelativeSizeAxes = Axes.Both, + Origin = Anchor.BottomCentre, + Anchor = Anchor.BottomCentre, + Width = 0.4f, + Alpha = 0, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black.Opacity(200), + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Height = 0.5f, + Children = new Drawable[] + { + new Container + { + Origin = Anchor.BottomCentre, + Anchor = Anchor.BottomCentre, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + new FlowContainer + { + Origin = Anchor.BottomCentre, + Anchor = Anchor.TopCentre, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FlowDirections.Vertical, + Spacing = new Vector2(0f, 15f), + Children = new Drawable[] + { + new Container + { + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + Size = ring_size, + Children = new Drawable[] + { + iconRing = new CircularContainer + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + BorderColour = Color4.White, + BorderThickness = 10f, + Size = ring_minified_size, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black.Opacity(0), + }, + iconText = new TextAwesome + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Icon = FontAwesome.fa_close, + TextSize = 50, + }, + }, + }, + } + }, + contextLabel = new OsuSpriteText + { + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + Text = @"CONTEXT", + Font = @"Exo2.0-Bold", + }, + }, + }, + headerBodyContainer = new Container + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + RelativeSizeAxes = Axes.X, + Height = 100, + Alpha = 0, + Children = new Drawable[] + { + headerLabel = new OsuSpriteText + { + Origin = Anchor.BottomCentre, + Anchor = Anchor.Centre, + Position = new Vector2(0f, -header_body_offset), + Text = @"Header", + Font = @"Exo2.0-Bold", + TextSize = 18, + Alpha = 0.75f, + BlendingMode = BlendingMode.Additive, + }, + bodyLabel = new OsuSpriteText + { + Origin = Anchor.TopCentre, + Anchor = Anchor.Centre, + Position = new Vector2(0f, header_body_offset), + Text = @"Body", + Font = @"Exo2.0-BoldItalic", + TextSize = 18, + }, + }, + }, + }, + }, + }, + }, + buttonsContainer = new FlowContainer + { + RelativeSizeAxes = Axes.Both, + Height = 0.5f, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Direction = FlowDirections.Vertical, + Spacing = buttons_enter_spacing, + Position = new Vector2(0f, buttons_enter_spacing.Y), + }, + }, + }, + }; + } + } + + class PopupDialogTriangles : Triangles + { + private Color4[] colours; + + protected override float SpawnRatio => spawnRatio; + private float spawnRatio = 0f; + + [BackgroundDependencyLoader] + private void load(OsuColour colour) + { + colours = new Color4[] + { + colour.BlueLight, + colour.Blue, + colour.PinkLight, + colour.Pink, + colour.YellowLight, + colour.Yellow, + colour.PurpleLight, + colour.Purple, + }; + } + + protected override Color4 GetTriangleShade() => colours[RNG.Next(0, colours.Length - 1)]; + + private const float triangle_moving_speed = 300; + private const float triangle_normal_speed = 20000; + private float triangleMoveSpeed; + + protected override void Update() + { + base.Update(); + foreach (Drawable d in Children) + { + d.Position -= new Vector2(0, (float)(d.Scale.X * (Time.Elapsed / triangleMoveSpeed))); + } + } + + public void SlideIn() + { + triangleMoveSpeed = triangle_moving_speed; + TransformFloatTo(spawnRatio, 1f, 600, EasingTypes.None, new TransformFloatSpawnRatio()); + TransformFloatTo(triangleMoveSpeed, triangle_normal_speed, 600, EasingTypes.InQuint, new TransformFloatSpeed()); + } + + public void SlideOut() + { + TransformFloatTo(spawnRatio, 0f, 500, EasingTypes.None, new TransformFloatSpawnRatio()); + TransformFloatTo(triangleMoveSpeed, triangle_moving_speed, 500, EasingTypes.OutExpo, new TransformFloatSpeed()); + } + + class TransformFloatSpeed : TransformFloat + { + public override void Apply(Drawable d) + { + base.Apply(d); + PopupDialogTriangles t = d as PopupDialogTriangles; + t.triangleMoveSpeed = CurrentValue; + } + } + + class TransformFloatSpawnRatio : TransformFloat + { + public override void Apply(Drawable d) + { + base.Apply(d); + PopupDialogTriangles t = d as PopupDialogTriangles; + t.spawnRatio = CurrentValue; + } + } + + protected override void LoadComplete() + { + // override so the triangles don't do the initial fill + } + + public PopupDialogTriangles() + { + TriangleScale = 2f; + } + } +} \ No newline at end of file diff --git a/osu.Game/Overlays/Dialog/PopupDialogButton.cs b/osu.Game/Overlays/Dialog/PopupDialogButton.cs new file mode 100644 index 0000000000..ec86fd0637 --- /dev/null +++ b/osu.Game/Overlays/Dialog/PopupDialogButton.cs @@ -0,0 +1,170 @@ +// 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 osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; +using osu.Game.Graphics.Backgrounds; +using osu.Game.Graphics.Sprites; + +namespace osu.Game.Overlays.Dialog +{ + public class PopupDialogButton : ClickableContainer + { + private float height = 50; + private float foreground_shear = 0.2f; + + private Box background, foreground; + private Triangles triangles; + private OsuSpriteText label; + + public string Title + { + get + { + return label.Text; + } + set + { + label.Text = value; + } + } + + public Color4 ForegroundColour + { + get + { + return foreground.Colour; + } + set + { + foreground.Colour = value; + } + } + + public Color4 BackgroundColour + { + get + { + return background.Colour; + } + set + { + background.Colour = value; + } + } + + public Color4 TrianglesColourLight + { + get + { + return triangles.ColourLight; + } + set + { + triangles.ColourLight = value; + } + } + + public Color4 TrianglesColourDark + { + get + { + return triangles.ColourDark; + } + set + { + triangles.ColourDark = value; + } + } + + public PopupDialogButton() + { + RelativeSizeAxes = Axes.X; + Height = height; + + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Masking = true, + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + }, + triangles = new Triangles + { + RelativeSizeAxes = Axes.Both, + } + }, + }, + new Container + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Width = 0.8f, + Shear = new Vector2(foreground_shear, 0f), + Masking = true, + EdgeEffect = new EdgeEffect + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Black.Opacity(50), + Radius = 5, + }, + Children = new Drawable[] + { + foreground = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.White, + EdgeSmoothness = new Vector2(2, 0), + }, + label = new OsuSpriteText + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Shear = new Vector2(-foreground_shear, 0f), + Text = @"Button", + Font = @"Exo2.0-Bold", + TextSize = 18, + }, + }, + }, + }; + } + } + + public class PopupDialogOKButton : PopupDialogButton + { + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + BackgroundColour = colours.PinkDark; + ForegroundColour = colours.Pink; + TrianglesColourDark = colours.PinkDarker; + TrianglesColourLight = colours.Pink; + } + } + + public class PopupDialogCancelButton : PopupDialogButton + { + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + BackgroundColour = colours.BlueDark; + ForegroundColour = colours.Blue; + TrianglesColourDark = colours.BlueDarker; + TrianglesColourLight = colours.Blue; + } + } +} diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index a336f54227..56f5597b5b 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -277,6 +277,8 @@ + + @@ -297,6 +299,9 @@ + + +