mirror of
https://github.com/ppy/osu.git
synced 2024-12-15 05:02:55 +08:00
Merge pull request #7924 from EVAST9919/beatmap-listing-expanded
Implement sorting by genre and language in BeatmapListingOverlay
This commit is contained in:
commit
c3b36d8f20
@ -5,6 +5,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Overlays;
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Overlays.BeatmapListing;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Online
|
||||
{
|
||||
@ -13,6 +14,7 @@ namespace osu.Game.Tests.Visual.Online
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(BeatmapListingOverlay),
|
||||
typeof(BeatmapListingFilterControl)
|
||||
};
|
||||
|
||||
protected override bool UseOnlineAPI => true;
|
||||
|
@ -15,25 +15,27 @@ using osuTK;
|
||||
|
||||
namespace osu.Game.Tests.Visual.UserInterface
|
||||
{
|
||||
public class TestSceneBeatmapListingSearchSection : OsuTestScene
|
||||
public class TestSceneBeatmapListingSearchControl : OsuTestScene
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(BeatmapListingSearchSection),
|
||||
typeof(BeatmapListingSearchControl),
|
||||
};
|
||||
|
||||
[Cached]
|
||||
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
|
||||
|
||||
private readonly BeatmapListingSearchSection section;
|
||||
private readonly BeatmapListingSearchControl control;
|
||||
|
||||
public TestSceneBeatmapListingSearchSection()
|
||||
public TestSceneBeatmapListingSearchControl()
|
||||
{
|
||||
OsuSpriteText query;
|
||||
OsuSpriteText ruleset;
|
||||
OsuSpriteText category;
|
||||
OsuSpriteText genre;
|
||||
OsuSpriteText language;
|
||||
|
||||
Add(section = new BeatmapListingSearchSection
|
||||
Add(control = new BeatmapListingSearchControl
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
@ -49,20 +51,24 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
query = new OsuSpriteText(),
|
||||
ruleset = new OsuSpriteText(),
|
||||
category = new OsuSpriteText(),
|
||||
genre = new OsuSpriteText(),
|
||||
language = new OsuSpriteText(),
|
||||
}
|
||||
});
|
||||
|
||||
section.Query.BindValueChanged(q => query.Text = $"Query: {q.NewValue}", true);
|
||||
section.Ruleset.BindValueChanged(r => ruleset.Text = $"Ruleset: {r.NewValue}", true);
|
||||
section.Category.BindValueChanged(c => category.Text = $"Category: {c.NewValue}", true);
|
||||
control.Query.BindValueChanged(q => query.Text = $"Query: {q.NewValue}", true);
|
||||
control.Ruleset.BindValueChanged(r => ruleset.Text = $"Ruleset: {r.NewValue}", true);
|
||||
control.Category.BindValueChanged(c => category.Text = $"Category: {c.NewValue}", true);
|
||||
control.Genre.BindValueChanged(g => genre.Text = $"Genre: {g.NewValue}", true);
|
||||
control.Language.BindValueChanged(l => language.Text = $"Language: {l.NewValue}", true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCovers()
|
||||
{
|
||||
AddStep("Set beatmap", () => section.BeatmapSet = beatmap_set);
|
||||
AddStep("Set beatmap (no cover)", () => section.BeatmapSet = no_cover_beatmap_set);
|
||||
AddStep("Set null beatmap", () => section.BeatmapSet = null);
|
||||
AddStep("Set beatmap", () => control.BeatmapSet = beatmap_set);
|
||||
AddStep("Set beatmap (no cover)", () => control.BeatmapSet = no_cover_beatmap_set);
|
||||
AddStep("Set null beatmap", () => control.BeatmapSet = null);
|
||||
}
|
||||
|
||||
private static readonly BeatmapSetInfo beatmap_set = new BeatmapSetInfo
|
@ -13,18 +13,17 @@ using osuTK;
|
||||
|
||||
namespace osu.Game.Tests.Visual.UserInterface
|
||||
{
|
||||
public class TestSceneBeatmapListingSort : OsuTestScene
|
||||
public class TestSceneBeatmapListingSortTabControl : OsuTestScene
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(BeatmapListingSortTabControl),
|
||||
typeof(OverlaySortTabControl<>),
|
||||
};
|
||||
|
||||
[Cached]
|
||||
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
|
||||
|
||||
public TestSceneBeatmapListingSort()
|
||||
public TestSceneBeatmapListingSortTabControl()
|
||||
{
|
||||
BeatmapListingSortTabControl control;
|
||||
OsuSpriteText current;
|
@ -8,7 +8,6 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.BeatmapListing;
|
||||
using osuTK;
|
||||
@ -20,8 +19,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(BeatmapSearchFilterRow<>),
|
||||
typeof(BeatmapSearchRulesetFilterRow),
|
||||
typeof(BeatmapSearchSmallFilterRow<>),
|
||||
typeof(BeatmapSearchRulesetFilterRow)
|
||||
};
|
||||
|
||||
[Cached]
|
||||
@ -42,8 +40,8 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new BeatmapSearchRulesetFilterRow(),
|
||||
new BeatmapSearchFilterRow<BeatmapSearchCategory>("Categories"),
|
||||
new BeatmapSearchSmallFilterRow<BeatmapSearchCategory>("Header Name")
|
||||
new BeatmapSearchFilterRow<SearchCategory>("Categories"),
|
||||
new BeatmapSearchFilterRow<SearchCategory>("Header Name")
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.ComponentModel;
|
||||
using osu.Framework.IO.Network;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.BeatmapListing;
|
||||
using osu.Game.Overlays.Direct;
|
||||
using osu.Game.Rulesets;
|
||||
|
||||
@ -11,20 +11,31 @@ namespace osu.Game.Online.API.Requests
|
||||
{
|
||||
public class SearchBeatmapSetsRequest : APIRequest<SearchBeatmapSetsResponse>
|
||||
{
|
||||
public SearchCategory SearchCategory { get; set; }
|
||||
|
||||
public DirectSortCriteria SortCriteria { get; set; }
|
||||
|
||||
public SortDirection SortDirection { get; set; }
|
||||
|
||||
public SearchGenre Genre { get; set; }
|
||||
|
||||
public SearchLanguage Language { get; set; }
|
||||
|
||||
private readonly string query;
|
||||
private readonly RulesetInfo ruleset;
|
||||
private readonly BeatmapSearchCategory searchCategory;
|
||||
private readonly DirectSortCriteria sortCriteria;
|
||||
private readonly SortDirection direction;
|
||||
private string directionString => direction == SortDirection.Descending ? @"desc" : @"asc";
|
||||
|
||||
public SearchBeatmapSetsRequest(string query, RulesetInfo ruleset, BeatmapSearchCategory searchCategory = BeatmapSearchCategory.Any, DirectSortCriteria sortCriteria = DirectSortCriteria.Ranked, SortDirection direction = SortDirection.Descending)
|
||||
private string directionString => SortDirection == SortDirection.Descending ? @"desc" : @"asc";
|
||||
|
||||
public SearchBeatmapSetsRequest(string query, RulesetInfo ruleset)
|
||||
{
|
||||
this.query = string.IsNullOrEmpty(query) ? string.Empty : System.Uri.EscapeDataString(query);
|
||||
this.ruleset = ruleset;
|
||||
this.searchCategory = searchCategory;
|
||||
this.sortCriteria = sortCriteria;
|
||||
this.direction = direction;
|
||||
|
||||
SearchCategory = SearchCategory.Any;
|
||||
SortCriteria = DirectSortCriteria.Ranked;
|
||||
SortDirection = SortDirection.Descending;
|
||||
Genre = SearchGenre.Any;
|
||||
Language = SearchLanguage.Any;
|
||||
}
|
||||
|
||||
protected override WebRequest CreateWebRequest()
|
||||
@ -35,31 +46,19 @@ namespace osu.Game.Online.API.Requests
|
||||
if (ruleset.ID.HasValue)
|
||||
req.AddParameter("m", ruleset.ID.Value.ToString());
|
||||
|
||||
req.AddParameter("s", searchCategory.ToString().ToLowerInvariant());
|
||||
req.AddParameter("sort", $"{sortCriteria.ToString().ToLowerInvariant()}_{directionString}");
|
||||
req.AddParameter("s", SearchCategory.ToString().ToLowerInvariant());
|
||||
|
||||
if (Genre != SearchGenre.Any)
|
||||
req.AddParameter("g", ((int)Genre).ToString());
|
||||
|
||||
if (Language != SearchLanguage.Any)
|
||||
req.AddParameter("l", ((int)Language).ToString());
|
||||
|
||||
req.AddParameter("sort", $"{SortCriteria.ToString().ToLowerInvariant()}_{directionString}");
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
protected override string Target => @"beatmapsets/search";
|
||||
}
|
||||
|
||||
public enum BeatmapSearchCategory
|
||||
{
|
||||
Any,
|
||||
|
||||
[Description("Has Leaderboard")]
|
||||
Leaderboard,
|
||||
Ranked,
|
||||
Qualified,
|
||||
Loved,
|
||||
Favourites,
|
||||
|
||||
[Description("Pending & WIP")]
|
||||
Pending,
|
||||
Graveyard,
|
||||
|
||||
[Description("My Maps")]
|
||||
Mine,
|
||||
}
|
||||
}
|
||||
|
163
osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs
Normal file
163
osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs
Normal file
@ -0,0 +1,163 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Effects;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Overlays.Direct;
|
||||
using osu.Game.Rulesets;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.BeatmapListing
|
||||
{
|
||||
public class BeatmapListingFilterControl : CompositeDrawable
|
||||
{
|
||||
public Action<List<BeatmapSetInfo>> SearchFinished;
|
||||
public Action SearchStarted;
|
||||
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private RulesetStore rulesets { get; set; }
|
||||
|
||||
private readonly BeatmapListingSearchControl searchControl;
|
||||
private readonly BeatmapListingSortTabControl sortControl;
|
||||
private readonly Box sortControlBackground;
|
||||
|
||||
private SearchBeatmapSetsRequest getSetsRequest;
|
||||
|
||||
public BeatmapListingFilterControl()
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
InternalChild = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(0, 10),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Masking = true,
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Colour = Color4.Black.Opacity(0.25f),
|
||||
Type = EdgeEffectType.Shadow,
|
||||
Radius = 3,
|
||||
Offset = new Vector2(0f, 1f),
|
||||
},
|
||||
Child = searchControl = new BeatmapListingSearchControl(),
|
||||
},
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 40,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
sortControlBackground = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
},
|
||||
sortControl = new BeatmapListingSortTabControl
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Margin = new MarginPadding { Left = 20 }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OverlayColourProvider colourProvider)
|
||||
{
|
||||
sortControlBackground.Colour = colourProvider.Background5;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
var sortCriteria = sortControl.Current;
|
||||
var sortDirection = sortControl.SortDirection;
|
||||
|
||||
searchControl.Query.BindValueChanged(query =>
|
||||
{
|
||||
sortCriteria.Value = string.IsNullOrEmpty(query.NewValue) ? DirectSortCriteria.Ranked : DirectSortCriteria.Relevance;
|
||||
sortDirection.Value = SortDirection.Descending;
|
||||
queueUpdateSearch(true);
|
||||
});
|
||||
|
||||
searchControl.Ruleset.BindValueChanged(_ => queueUpdateSearch());
|
||||
searchControl.Category.BindValueChanged(_ => queueUpdateSearch());
|
||||
searchControl.Genre.BindValueChanged(_ => queueUpdateSearch());
|
||||
searchControl.Language.BindValueChanged(_ => queueUpdateSearch());
|
||||
|
||||
sortCriteria.BindValueChanged(_ => queueUpdateSearch());
|
||||
sortDirection.BindValueChanged(_ => queueUpdateSearch());
|
||||
}
|
||||
|
||||
private ScheduledDelegate queryChangedDebounce;
|
||||
|
||||
private void queueUpdateSearch(bool queryTextChanged = false)
|
||||
{
|
||||
SearchStarted?.Invoke();
|
||||
|
||||
getSetsRequest?.Cancel();
|
||||
|
||||
queryChangedDebounce?.Cancel();
|
||||
queryChangedDebounce = Scheduler.AddDelayed(updateSearch, queryTextChanged ? 500 : 100);
|
||||
}
|
||||
|
||||
private void updateSearch()
|
||||
{
|
||||
getSetsRequest = new SearchBeatmapSetsRequest(searchControl.Query.Value, searchControl.Ruleset.Value)
|
||||
{
|
||||
SearchCategory = searchControl.Category.Value,
|
||||
SortCriteria = sortControl.Current.Value,
|
||||
SortDirection = sortControl.SortDirection.Value,
|
||||
Genre = searchControl.Genre.Value,
|
||||
Language = searchControl.Language.Value
|
||||
};
|
||||
|
||||
getSetsRequest.Success += response => Schedule(() => onSearchFinished(response));
|
||||
|
||||
api.Queue(getSetsRequest);
|
||||
}
|
||||
|
||||
private void onSearchFinished(SearchBeatmapSetsResponse response)
|
||||
{
|
||||
var beatmaps = response.BeatmapSets.Select(r => r.ToBeatmapSet(rulesets)).ToList();
|
||||
|
||||
searchControl.BeatmapSet = response.Total == 0 ? null : beatmaps.First();
|
||||
|
||||
SearchFinished?.Invoke(beatmaps);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
getSetsRequest?.Cancel();
|
||||
queryChangedDebounce?.Cancel();
|
||||
|
||||
base.Dispose(isDisposing);
|
||||
}
|
||||
}
|
||||
}
|
@ -5,8 +5,6 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Rulesets;
|
||||
using osuTK;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Beatmaps.Drawables;
|
||||
@ -14,16 +12,21 @@ using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osuTK.Graphics;
|
||||
using osu.Game.Rulesets;
|
||||
|
||||
namespace osu.Game.Overlays.BeatmapListing
|
||||
{
|
||||
public class BeatmapListingSearchSection : CompositeDrawable
|
||||
public class BeatmapListingSearchControl : CompositeDrawable
|
||||
{
|
||||
public Bindable<string> Query => textBox.Current;
|
||||
|
||||
public Bindable<RulesetInfo> Ruleset => modeFilter.Current;
|
||||
|
||||
public Bindable<BeatmapSearchCategory> Category => categoryFilter.Current;
|
||||
public Bindable<SearchCategory> Category => categoryFilter.Current;
|
||||
|
||||
public Bindable<SearchGenre> Genre => genreFilter.Current;
|
||||
|
||||
public Bindable<SearchLanguage> Language => languageFilter.Current;
|
||||
|
||||
public BeatmapSetInfo BeatmapSet
|
||||
{
|
||||
@ -42,12 +45,14 @@ namespace osu.Game.Overlays.BeatmapListing
|
||||
|
||||
private readonly BeatmapSearchTextBox textBox;
|
||||
private readonly BeatmapSearchRulesetFilterRow modeFilter;
|
||||
private readonly BeatmapSearchFilterRow<BeatmapSearchCategory> categoryFilter;
|
||||
private readonly BeatmapSearchFilterRow<SearchCategory> categoryFilter;
|
||||
private readonly BeatmapSearchFilterRow<SearchGenre> genreFilter;
|
||||
private readonly BeatmapSearchFilterRow<SearchLanguage> languageFilter;
|
||||
|
||||
private readonly Box background;
|
||||
private readonly UpdateableBeatmapSetCover beatmapCover;
|
||||
|
||||
public BeatmapListingSearchSection()
|
||||
public BeatmapListingSearchControl()
|
||||
{
|
||||
AutoSizeAxes = Axes.Y;
|
||||
RelativeSizeAxes = Axes.X;
|
||||
@ -97,7 +102,9 @@ namespace osu.Game.Overlays.BeatmapListing
|
||||
Children = new Drawable[]
|
||||
{
|
||||
modeFilter = new BeatmapSearchRulesetFilterRow(),
|
||||
categoryFilter = new BeatmapSearchFilterRow<BeatmapSearchCategory>(@"Categories"),
|
||||
categoryFilter = new BeatmapSearchFilterRow<SearchCategory>(@"Categories"),
|
||||
genreFilter = new BeatmapSearchFilterRow<SearchGenre>(@"Genre"),
|
||||
languageFilter = new BeatmapSearchFilterRow<SearchLanguage>(@"Language"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -105,7 +112,7 @@ namespace osu.Game.Overlays.BeatmapListing
|
||||
}
|
||||
});
|
||||
|
||||
Category.Value = BeatmapSearchCategory.Leaderboard;
|
||||
categoryFilter.Current.Value = SearchCategory.Leaderboard;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
@ -15,6 +15,8 @@ using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
using Humanizer;
|
||||
using osu.Game.Utils;
|
||||
|
||||
namespace osu.Game.Overlays.BeatmapListing
|
||||
{
|
||||
@ -53,8 +55,8 @@ namespace osu.Game.Overlays.BeatmapListing
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
Font = OsuFont.GetFont(size: 10),
|
||||
Text = headerName.ToUpper()
|
||||
Font = OsuFont.GetFont(size: 13),
|
||||
Text = headerName.Titleize()
|
||||
},
|
||||
CreateFilter().With(f =>
|
||||
{
|
||||
@ -81,7 +83,7 @@ namespace osu.Game.Overlays.BeatmapListing
|
||||
|
||||
if (typeof(T).IsEnum)
|
||||
{
|
||||
foreach (var val in (T[])Enum.GetValues(typeof(T)))
|
||||
foreach (var val in OrderAttributeUtils.GetValuesInOrder<T>())
|
||||
AddItem(val);
|
||||
}
|
||||
}
|
||||
|
@ -1,32 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
|
||||
namespace osu.Game.Overlays.BeatmapListing
|
||||
{
|
||||
public class BeatmapSearchSmallFilterRow<T> : BeatmapSearchFilterRow<T>
|
||||
{
|
||||
public BeatmapSearchSmallFilterRow(string headerName)
|
||||
: base(headerName)
|
||||
{
|
||||
}
|
||||
|
||||
protected override BeatmapSearchFilter CreateFilter() => new SmallBeatmapSearchFilter();
|
||||
|
||||
private class SmallBeatmapSearchFilter : BeatmapSearchFilter
|
||||
{
|
||||
protected override TabItem<T> CreateTabItem(T value) => new SmallTabItem(value);
|
||||
|
||||
private class SmallTabItem : FilterTabItem
|
||||
{
|
||||
public SmallTabItem(T value)
|
||||
: base(value)
|
||||
{
|
||||
}
|
||||
|
||||
protected override float TextSize => 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
26
osu.Game/Overlays/BeatmapListing/SearchCategory.cs
Normal file
26
osu.Game/Overlays/BeatmapListing/SearchCategory.cs
Normal file
@ -0,0 +1,26 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace osu.Game.Overlays.BeatmapListing
|
||||
{
|
||||
public enum SearchCategory
|
||||
{
|
||||
Any,
|
||||
|
||||
[Description("Has Leaderboard")]
|
||||
Leaderboard,
|
||||
Ranked,
|
||||
Qualified,
|
||||
Loved,
|
||||
Favourites,
|
||||
|
||||
[Description("Pending & WIP")]
|
||||
Pending,
|
||||
Graveyard,
|
||||
|
||||
[Description("My Maps")]
|
||||
Mine,
|
||||
}
|
||||
}
|
25
osu.Game/Overlays/BeatmapListing/SearchGenre.cs
Normal file
25
osu.Game/Overlays/BeatmapListing/SearchGenre.cs
Normal file
@ -0,0 +1,25 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace osu.Game.Overlays.BeatmapListing
|
||||
{
|
||||
public enum SearchGenre
|
||||
{
|
||||
Any = 0,
|
||||
Unspecified = 1,
|
||||
|
||||
[Description("Video Game")]
|
||||
VideoGame = 2,
|
||||
Anime = 3,
|
||||
Rock = 4,
|
||||
Pop = 5,
|
||||
Other = 6,
|
||||
Novelty = 7,
|
||||
|
||||
[Description("Hip Hop")]
|
||||
HipHop = 9,
|
||||
Electronic = 10
|
||||
}
|
||||
}
|
47
osu.Game/Overlays/BeatmapListing/SearchLanguage.cs
Normal file
47
osu.Game/Overlays/BeatmapListing/SearchLanguage.cs
Normal file
@ -0,0 +1,47 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Game.Utils;
|
||||
|
||||
namespace osu.Game.Overlays.BeatmapListing
|
||||
{
|
||||
[HasOrderedElements]
|
||||
public enum SearchLanguage
|
||||
{
|
||||
[Order(0)]
|
||||
Any,
|
||||
|
||||
[Order(11)]
|
||||
Other,
|
||||
|
||||
[Order(1)]
|
||||
English,
|
||||
|
||||
[Order(6)]
|
||||
Japanese,
|
||||
|
||||
[Order(2)]
|
||||
Chinese,
|
||||
|
||||
[Order(10)]
|
||||
Instrumental,
|
||||
|
||||
[Order(7)]
|
||||
Korean,
|
||||
|
||||
[Order(3)]
|
||||
French,
|
||||
|
||||
[Order(4)]
|
||||
German,
|
||||
|
||||
[Order(9)]
|
||||
Swedish,
|
||||
|
||||
[Order(8)]
|
||||
Spanish,
|
||||
|
||||
[Order(5)]
|
||||
Italian
|
||||
}
|
||||
}
|
@ -1,27 +1,23 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Effects;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Overlays.BeatmapListing;
|
||||
using osu.Game.Overlays.Direct;
|
||||
using osu.Game.Rulesets;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays
|
||||
{
|
||||
@ -30,14 +26,9 @@ namespace osu.Game.Overlays
|
||||
[Resolved]
|
||||
private PreviewTrackManager previewTrackManager { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private RulesetStore rulesets { get; set; }
|
||||
|
||||
private SearchBeatmapSetsRequest getSetsRequest;
|
||||
|
||||
private Drawable currentContent;
|
||||
private BeatmapListingSearchSection searchSection;
|
||||
private BeatmapListingSortTabControl sortControl;
|
||||
private LoadingLayer loadingLayer;
|
||||
private Container panelTarget;
|
||||
|
||||
public BeatmapListingOverlay()
|
||||
: base(OverlayColourScheme.Blue)
|
||||
@ -63,27 +54,13 @@ namespace osu.Game.Overlays
|
||||
AutoSizeAxes = Axes.Y,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(0, 10),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new FillFlowContainer
|
||||
new BeatmapListingHeader(),
|
||||
new BeatmapListingFilterControl
|
||||
{
|
||||
AutoSizeAxes = Axes.Y,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Direction = FillDirection.Vertical,
|
||||
Masking = true,
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Colour = Color4.Black.Opacity(0.25f),
|
||||
Type = EdgeEffectType.Shadow,
|
||||
Radius = 3,
|
||||
Offset = new Vector2(0f, 1f),
|
||||
},
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new BeatmapListingHeader(),
|
||||
searchSection = new BeatmapListingSearchSection(),
|
||||
}
|
||||
SearchStarted = onSearchStarted,
|
||||
SearchFinished = onSearchFinished,
|
||||
},
|
||||
new Container
|
||||
{
|
||||
@ -96,128 +73,41 @@ namespace osu.Game.Overlays
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = ColourProvider.Background4,
|
||||
},
|
||||
new FillFlowContainer
|
||||
panelTarget = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 40,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = ColourProvider.Background5
|
||||
},
|
||||
sortControl = new BeatmapListingSortTabControl
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Margin = new MarginPadding { Left = 20 }
|
||||
}
|
||||
}
|
||||
},
|
||||
new Container
|
||||
{
|
||||
AutoSizeAxes = Axes.Y,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Padding = new MarginPadding { Horizontal = 20 },
|
||||
Children = new Drawable[]
|
||||
{
|
||||
panelTarget = new Container
|
||||
{
|
||||
AutoSizeAxes = Axes.Y,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
},
|
||||
loadingLayer = new LoadingLayer(panelTarget),
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Padding = new MarginPadding { Horizontal = 20 }
|
||||
},
|
||||
loadingLayer = new LoadingLayer(panelTarget)
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
private CancellationTokenSource cancellationToken;
|
||||
|
||||
private void onSearchStarted()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
var sortCriteria = sortControl.Current;
|
||||
var sortDirection = sortControl.SortDirection;
|
||||
|
||||
searchSection.Query.BindValueChanged(query =>
|
||||
{
|
||||
sortCriteria.Value = string.IsNullOrEmpty(query.NewValue) ? DirectSortCriteria.Ranked : DirectSortCriteria.Relevance;
|
||||
sortDirection.Value = SortDirection.Descending;
|
||||
|
||||
queueUpdateSearch(true);
|
||||
});
|
||||
|
||||
searchSection.Ruleset.BindValueChanged(_ => queueUpdateSearch());
|
||||
searchSection.Category.BindValueChanged(_ => queueUpdateSearch());
|
||||
sortCriteria.BindValueChanged(_ => queueUpdateSearch());
|
||||
sortDirection.BindValueChanged(_ => queueUpdateSearch());
|
||||
}
|
||||
|
||||
private ScheduledDelegate queryChangedDebounce;
|
||||
|
||||
private LoadingLayer loadingLayer;
|
||||
private Container panelTarget;
|
||||
|
||||
private void queueUpdateSearch(bool queryTextChanged = false)
|
||||
{
|
||||
getSetsRequest?.Cancel();
|
||||
|
||||
queryChangedDebounce?.Cancel();
|
||||
queryChangedDebounce = Scheduler.AddDelayed(updateSearch, queryTextChanged ? 500 : 100);
|
||||
}
|
||||
|
||||
private void updateSearch()
|
||||
{
|
||||
if (!IsLoaded)
|
||||
return;
|
||||
|
||||
if (State.Value == Visibility.Hidden)
|
||||
return;
|
||||
|
||||
if (API == null)
|
||||
return;
|
||||
cancellationToken?.Cancel();
|
||||
|
||||
previewTrackManager.StopAnyPlaying(this);
|
||||
|
||||
loadingLayer.Show();
|
||||
|
||||
getSetsRequest = new SearchBeatmapSetsRequest(
|
||||
searchSection.Query.Value,
|
||||
searchSection.Ruleset.Value,
|
||||
searchSection.Category.Value,
|
||||
sortControl.Current.Value,
|
||||
sortControl.SortDirection.Value);
|
||||
|
||||
getSetsRequest.Success += response => Schedule(() => recreatePanels(response));
|
||||
|
||||
API.Queue(getSetsRequest);
|
||||
if (panelTarget.Any())
|
||||
loadingLayer.Show();
|
||||
}
|
||||
|
||||
private void recreatePanels(SearchBeatmapSetsResponse response)
|
||||
private void onSearchFinished(List<BeatmapSetInfo> beatmaps)
|
||||
{
|
||||
if (response.Total == 0)
|
||||
if (!beatmaps.Any())
|
||||
{
|
||||
searchSection.BeatmapSet = null;
|
||||
LoadComponentAsync(new NotFoundDrawable(), addContentToPlaceholder);
|
||||
LoadComponentAsync(new NotFoundDrawable(), addContentToPlaceholder, (cancellationToken = new CancellationTokenSource()).Token);
|
||||
return;
|
||||
}
|
||||
|
||||
var beatmaps = response.BeatmapSets.Select(r => r.ToBeatmapSet(rulesets)).ToList();
|
||||
|
||||
var newPanels = new FillFlowContainer<DirectPanel>
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
@ -232,18 +122,14 @@ namespace osu.Game.Overlays
|
||||
})
|
||||
};
|
||||
|
||||
LoadComponentAsync(newPanels, loaded =>
|
||||
{
|
||||
addContentToPlaceholder(loaded);
|
||||
searchSection.BeatmapSet = beatmaps.First();
|
||||
});
|
||||
LoadComponentAsync(newPanels, addContentToPlaceholder, (cancellationToken = new CancellationTokenSource()).Token);
|
||||
}
|
||||
|
||||
private void addContentToPlaceholder(Drawable content)
|
||||
{
|
||||
loadingLayer.Hide();
|
||||
|
||||
Drawable lastContent = currentContent;
|
||||
var lastContent = currentContent;
|
||||
|
||||
if (lastContent != null)
|
||||
{
|
||||
@ -262,9 +148,7 @@ namespace osu.Game.Overlays
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
getSetsRequest?.Cancel();
|
||||
queryChangedDebounce?.Cancel();
|
||||
|
||||
cancellationToken?.Cancel();
|
||||
base.Dispose(isDisposing);
|
||||
}
|
||||
|
||||
|
@ -6,20 +6,20 @@ using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Overlays.BeatmapListing;
|
||||
using osu.Game.Overlays.SearchableList;
|
||||
using osu.Game.Rulesets;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.Direct
|
||||
{
|
||||
public class FilterControl : SearchableListFilterControl<DirectSortCriteria, BeatmapSearchCategory>
|
||||
public class FilterControl : SearchableListFilterControl<DirectSortCriteria, SearchCategory>
|
||||
{
|
||||
private DirectRulesetSelector rulesetSelector;
|
||||
|
||||
protected override Color4 BackgroundColour => Color4Extensions.FromHex(@"384552");
|
||||
protected override DirectSortCriteria DefaultTab => DirectSortCriteria.Ranked;
|
||||
protected override BeatmapSearchCategory DefaultCategory => BeatmapSearchCategory.Leaderboard;
|
||||
protected override SearchCategory DefaultCategory => SearchCategory.Leaderboard;
|
||||
|
||||
protected override Drawable CreateSupplementaryControls() => rulesetSelector = new DirectRulesetSelector();
|
||||
|
||||
|
@ -16,6 +16,7 @@ using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Overlays.BeatmapListing;
|
||||
using osu.Game.Overlays.Direct;
|
||||
using osu.Game.Overlays.SearchableList;
|
||||
using osu.Game.Rulesets;
|
||||
@ -24,7 +25,7 @@ using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays
|
||||
{
|
||||
public class DirectOverlay : SearchableListOverlay<DirectTab, DirectSortCriteria, BeatmapSearchCategory>
|
||||
public class DirectOverlay : SearchableListOverlay<DirectTab, DirectSortCriteria, SearchCategory>
|
||||
{
|
||||
private const float panel_padding = 10f;
|
||||
|
||||
@ -40,7 +41,7 @@ namespace osu.Game.Overlays
|
||||
protected override Color4 TrianglesColourDark => Color4Extensions.FromHex(@"3f5265");
|
||||
|
||||
protected override SearchableListHeader<DirectTab> CreateHeader() => new Header();
|
||||
protected override SearchableListFilterControl<DirectSortCriteria, BeatmapSearchCategory> CreateFilterControl() => new FilterControl();
|
||||
protected override SearchableListFilterControl<DirectSortCriteria, SearchCategory> CreateFilterControl() => new FilterControl();
|
||||
|
||||
private IEnumerable<BeatmapSetInfo> beatmapSets;
|
||||
|
||||
@ -255,11 +256,11 @@ namespace osu.Game.Overlays
|
||||
|
||||
previewTrackManager.StopAnyPlaying(this);
|
||||
|
||||
getSetsRequest = new SearchBeatmapSetsRequest(
|
||||
currentQuery.Value,
|
||||
((FilterControl)Filter).Ruleset.Value,
|
||||
Filter.DisplayStyleControl.Dropdown.Current.Value,
|
||||
Filter.Tabs.Current.Value); //todo: sort direction (?)
|
||||
getSetsRequest = new SearchBeatmapSetsRequest(currentQuery.Value, ((FilterControl)Filter).Ruleset.Value)
|
||||
{
|
||||
SearchCategory = Filter.DisplayStyleControl.Dropdown.Current.Value,
|
||||
SortCriteria = Filter.Tabs.Current.Value
|
||||
};
|
||||
|
||||
getSetsRequest.Success += response =>
|
||||
{
|
||||
|
52
osu.Game/Utils/OrderAttribute.cs
Normal file
52
osu.Game/Utils/OrderAttribute.cs
Normal file
@ -0,0 +1,52 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace osu.Game.Utils
|
||||
{
|
||||
public static class OrderAttributeUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Get values of an enum in order. Supports custom ordering via <see cref="OrderAttribute"/>.
|
||||
/// </summary>
|
||||
public static IEnumerable<T> GetValuesInOrder<T>()
|
||||
{
|
||||
var type = typeof(T);
|
||||
|
||||
if (!type.IsEnum)
|
||||
throw new InvalidOperationException("T must be an enum");
|
||||
|
||||
IEnumerable<T> items = (T[])Enum.GetValues(type);
|
||||
|
||||
if (Attribute.GetCustomAttribute(type, typeof(HasOrderedElementsAttribute)) == null)
|
||||
return items;
|
||||
|
||||
return items.OrderBy(i =>
|
||||
{
|
||||
if (type.GetField(i.ToString()).GetCustomAttributes(typeof(OrderAttribute), false).FirstOrDefault() is OrderAttribute attr)
|
||||
return attr.Order;
|
||||
|
||||
throw new ArgumentException($"Not all values of {nameof(T)} have {nameof(OrderAttribute)} specified.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field)]
|
||||
public class OrderAttribute : Attribute
|
||||
{
|
||||
public readonly int Order;
|
||||
|
||||
public OrderAttribute(int order)
|
||||
{
|
||||
Order = order;
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Enum)]
|
||||
public class HasOrderedElementsAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user