mirror of
https://github.com/ppy/osu.git
synced 2024-11-11 10:07:52 +08:00
Merge pull request #20317 from frenzibyte/improve-beatmap-listing-sorting
Improve sorting criteria in beatmap listing overlay
This commit is contained in:
commit
77646fd3ce
@ -3,9 +3,13 @@
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.BeatmapListing;
|
||||
@ -15,12 +19,13 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
{
|
||||
public class TestSceneBeatmapListingSortTabControl : OsuTestScene
|
||||
{
|
||||
private readonly BeatmapListingSortTabControl control;
|
||||
|
||||
[Cached]
|
||||
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
|
||||
|
||||
public TestSceneBeatmapListingSortTabControl()
|
||||
{
|
||||
BeatmapListingSortTabControl control;
|
||||
OsuSpriteText current;
|
||||
OsuSpriteText direction;
|
||||
|
||||
@ -45,5 +50,83 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
control.SortDirection.BindValueChanged(sortDirection => direction.Text = $"Sort direction: {sortDirection.NewValue}", true);
|
||||
control.Current.BindValueChanged(criteria => current.Text = $"Criteria: {criteria.NewValue}", true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestRankedSort()
|
||||
{
|
||||
criteriaShowsOnCategory(true, SortCriteria.Ranked, SearchCategory.Any);
|
||||
criteriaShowsOnCategory(true, SortCriteria.Ranked, SearchCategory.Leaderboard);
|
||||
criteriaShowsOnCategory(true, SortCriteria.Ranked, SearchCategory.Ranked);
|
||||
criteriaShowsOnCategory(true, SortCriteria.Ranked, SearchCategory.Qualified);
|
||||
criteriaShowsOnCategory(true, SortCriteria.Ranked, SearchCategory.Loved);
|
||||
criteriaShowsOnCategory(true, SortCriteria.Ranked, SearchCategory.Favourites);
|
||||
criteriaShowsOnCategory(false, SortCriteria.Ranked, SearchCategory.Pending);
|
||||
criteriaShowsOnCategory(false, SortCriteria.Ranked, SearchCategory.Wip);
|
||||
criteriaShowsOnCategory(false, SortCriteria.Ranked, SearchCategory.Graveyard);
|
||||
criteriaShowsOnCategory(true, SortCriteria.Ranked, SearchCategory.Mine);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUpdatedSort()
|
||||
{
|
||||
criteriaShowsOnCategory(true, SortCriteria.Updated, SearchCategory.Any);
|
||||
criteriaShowsOnCategory(false, SortCriteria.Updated, SearchCategory.Leaderboard);
|
||||
criteriaShowsOnCategory(false, SortCriteria.Updated, SearchCategory.Ranked);
|
||||
criteriaShowsOnCategory(false, SortCriteria.Updated, SearchCategory.Qualified);
|
||||
criteriaShowsOnCategory(false, SortCriteria.Updated, SearchCategory.Loved);
|
||||
criteriaShowsOnCategory(true, SortCriteria.Updated, SearchCategory.Favourites);
|
||||
criteriaShowsOnCategory(true, SortCriteria.Updated, SearchCategory.Pending);
|
||||
criteriaShowsOnCategory(true, SortCriteria.Updated, SearchCategory.Wip);
|
||||
criteriaShowsOnCategory(true, SortCriteria.Updated, SearchCategory.Graveyard);
|
||||
criteriaShowsOnCategory(true, SortCriteria.Updated, SearchCategory.Mine);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNominationsSort()
|
||||
{
|
||||
criteriaShowsOnCategory(false, SortCriteria.Nominations, SearchCategory.Any);
|
||||
criteriaShowsOnCategory(false, SortCriteria.Nominations, SearchCategory.Leaderboard);
|
||||
criteriaShowsOnCategory(false, SortCriteria.Nominations, SearchCategory.Ranked);
|
||||
criteriaShowsOnCategory(false, SortCriteria.Nominations, SearchCategory.Qualified);
|
||||
criteriaShowsOnCategory(false, SortCriteria.Nominations, SearchCategory.Loved);
|
||||
criteriaShowsOnCategory(false, SortCriteria.Nominations, SearchCategory.Favourites);
|
||||
criteriaShowsOnCategory(true, SortCriteria.Nominations, SearchCategory.Pending);
|
||||
criteriaShowsOnCategory(false, SortCriteria.Nominations, SearchCategory.Wip);
|
||||
criteriaShowsOnCategory(false, SortCriteria.Nominations, SearchCategory.Graveyard);
|
||||
criteriaShowsOnCategory(false, SortCriteria.Nominations, SearchCategory.Mine);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestResetNoQuery()
|
||||
{
|
||||
resetUsesCriteriaOnCategory(SortCriteria.Ranked, SearchCategory.Any);
|
||||
resetUsesCriteriaOnCategory(SortCriteria.Ranked, SearchCategory.Leaderboard);
|
||||
resetUsesCriteriaOnCategory(SortCriteria.Ranked, SearchCategory.Ranked);
|
||||
resetUsesCriteriaOnCategory(SortCriteria.Ranked, SearchCategory.Qualified);
|
||||
resetUsesCriteriaOnCategory(SortCriteria.Ranked, SearchCategory.Loved);
|
||||
resetUsesCriteriaOnCategory(SortCriteria.Ranked, SearchCategory.Favourites);
|
||||
resetUsesCriteriaOnCategory(SortCriteria.Updated, SearchCategory.Pending);
|
||||
resetUsesCriteriaOnCategory(SortCriteria.Updated, SearchCategory.Wip);
|
||||
resetUsesCriteriaOnCategory(SortCriteria.Updated, SearchCategory.Graveyard);
|
||||
resetUsesCriteriaOnCategory(SortCriteria.Updated, SearchCategory.Mine);
|
||||
}
|
||||
|
||||
private void criteriaShowsOnCategory(bool expected, SortCriteria criteria, SearchCategory category)
|
||||
{
|
||||
AddAssert($"{criteria.ToString().ToLowerInvariant()} {(expected ? "shown" : "not shown")} on {category.ToString().ToLowerInvariant()}", () =>
|
||||
{
|
||||
control.Reset(category, false);
|
||||
return control.ChildrenOfType<TabControl<SortCriteria>>().Single().Items.Contains(criteria) == expected;
|
||||
});
|
||||
}
|
||||
|
||||
private void resetUsesCriteriaOnCategory(SortCriteria criteria, SearchCategory category)
|
||||
{
|
||||
AddAssert($"reset uses {criteria.ToString().ToLowerInvariant()} on {category.ToString().ToLowerInvariant()}", () =>
|
||||
{
|
||||
control.Reset(category, false);
|
||||
return control.Current.Value == criteria;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -151,19 +151,20 @@ namespace osu.Game.Overlays.BeatmapListing
|
||||
|
||||
config.BindWith(OsuSetting.BeatmapListingCardSize, cardSize);
|
||||
|
||||
var sortCriteria = sortControl.Current;
|
||||
var sortDirection = sortControl.SortDirection;
|
||||
|
||||
searchControl.Query.BindValueChanged(query =>
|
||||
searchControl.Query.BindValueChanged(_ =>
|
||||
{
|
||||
sortCriteria.Value = string.IsNullOrEmpty(query.NewValue) ? SortCriteria.Ranked : SortCriteria.Relevance;
|
||||
sortDirection.Value = SortDirection.Descending;
|
||||
resetSortControl();
|
||||
queueUpdateSearch(true);
|
||||
});
|
||||
|
||||
searchControl.Category.BindValueChanged(_ =>
|
||||
{
|
||||
resetSortControl();
|
||||
queueUpdateSearch();
|
||||
});
|
||||
|
||||
searchControl.General.CollectionChanged += (_, _) => queueUpdateSearch();
|
||||
searchControl.Ruleset.BindValueChanged(_ => queueUpdateSearch());
|
||||
searchControl.Category.BindValueChanged(_ => queueUpdateSearch());
|
||||
searchControl.Genre.BindValueChanged(_ => queueUpdateSearch());
|
||||
searchControl.Language.BindValueChanged(_ => queueUpdateSearch());
|
||||
searchControl.Extra.CollectionChanged += (_, _) => queueUpdateSearch();
|
||||
@ -171,8 +172,8 @@ namespace osu.Game.Overlays.BeatmapListing
|
||||
searchControl.Played.BindValueChanged(_ => queueUpdateSearch());
|
||||
searchControl.ExplicitContent.BindValueChanged(_ => queueUpdateSearch());
|
||||
|
||||
sortCriteria.BindValueChanged(_ => queueUpdateSearch());
|
||||
sortDirection.BindValueChanged(_ => queueUpdateSearch());
|
||||
sortControl.Current.BindValueChanged(_ => queueUpdateSearch());
|
||||
sortControl.SortDirection.BindValueChanged(_ => queueUpdateSearch());
|
||||
|
||||
apiUser = api.LocalUser.GetBoundCopy();
|
||||
apiUser.BindValueChanged(_ => queueUpdateSearch());
|
||||
@ -199,6 +200,8 @@ namespace osu.Game.Overlays.BeatmapListing
|
||||
performRequest();
|
||||
}
|
||||
|
||||
private void resetSortControl() => sortControl.Reset(searchControl.Category.Value, !string.IsNullOrEmpty(searchControl.Query.Value));
|
||||
|
||||
private void queueUpdateSearch(bool queryTextChanged = false)
|
||||
{
|
||||
SearchStarted?.Invoke();
|
||||
|
@ -17,18 +17,65 @@ namespace osu.Game.Overlays.BeatmapListing
|
||||
{
|
||||
public readonly Bindable<SortDirection> SortDirection = new Bindable<SortDirection>(Overlays.SortDirection.Descending);
|
||||
|
||||
public BeatmapListingSortTabControl()
|
||||
private SearchCategory? lastCategory;
|
||||
private bool? lastHasQuery;
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
Current.Value = SortCriteria.Ranked;
|
||||
base.LoadComplete();
|
||||
Reset(SearchCategory.Leaderboard, false);
|
||||
}
|
||||
|
||||
public void Reset(SearchCategory category, bool hasQuery)
|
||||
{
|
||||
if (category != lastCategory || hasQuery != lastHasQuery)
|
||||
{
|
||||
TabControl.Clear();
|
||||
|
||||
TabControl.AddItem(SortCriteria.Title);
|
||||
TabControl.AddItem(SortCriteria.Artist);
|
||||
TabControl.AddItem(SortCriteria.Difficulty);
|
||||
|
||||
if (category == SearchCategory.Any || category > SearchCategory.Loved)
|
||||
TabControl.AddItem(SortCriteria.Updated);
|
||||
|
||||
if (category < SearchCategory.Pending || category == SearchCategory.Mine)
|
||||
TabControl.AddItem(SortCriteria.Ranked);
|
||||
|
||||
TabControl.AddItem(SortCriteria.Rating);
|
||||
TabControl.AddItem(SortCriteria.Plays);
|
||||
TabControl.AddItem(SortCriteria.Favourites);
|
||||
|
||||
if (hasQuery)
|
||||
TabControl.AddItem(SortCriteria.Relevance);
|
||||
|
||||
if (category == SearchCategory.Pending)
|
||||
TabControl.AddItem(SortCriteria.Nominations);
|
||||
}
|
||||
|
||||
var nonQueryCriteria = category >= SearchCategory.Pending ? SortCriteria.Updated : SortCriteria.Ranked;
|
||||
|
||||
Current.Value = hasQuery ? SortCriteria.Relevance : nonQueryCriteria;
|
||||
SortDirection.Value = Overlays.SortDirection.Descending;
|
||||
|
||||
// if the new criteria isn't different from the previous one,
|
||||
// then re-adding tab items will not mark the current tab as selected.
|
||||
// see: https://github.com/ppy/osu-framework/issues/5412
|
||||
TabControl.Current.TriggerChange();
|
||||
|
||||
lastCategory = category;
|
||||
lastHasQuery = hasQuery;
|
||||
}
|
||||
|
||||
protected override SortTabControl CreateControl() => new BeatmapSortTabControl
|
||||
{
|
||||
SortDirection = { BindTarget = SortDirection }
|
||||
SortDirection = { BindTarget = SortDirection },
|
||||
};
|
||||
|
||||
private class BeatmapSortTabControl : SortTabControl
|
||||
{
|
||||
protected override bool AddEnumEntriesAutomatically => false;
|
||||
|
||||
public readonly Bindable<SortDirection> SortDirection = new Bindable<SortDirection>();
|
||||
|
||||
protected override TabItem<SortCriteria> CreateTabItem(SortCriteria value) => new BeatmapSortTabItem(value)
|
||||
|
@ -19,6 +19,9 @@ namespace osu.Game.Overlays.BeatmapListing
|
||||
[LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.ListingSearchSortingDifficulty))]
|
||||
Difficulty,
|
||||
|
||||
[LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.ListingSearchSortingUpdated))]
|
||||
Updated,
|
||||
|
||||
[LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.ListingSearchSortingRanked))]
|
||||
Ranked,
|
||||
|
||||
@ -32,6 +35,9 @@ namespace osu.Game.Overlays.BeatmapListing
|
||||
Favourites,
|
||||
|
||||
[LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.ListingSearchSortingRelevance))]
|
||||
Relevance
|
||||
Relevance,
|
||||
|
||||
[LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.ListingSearchSortingNominations))]
|
||||
Nominations,
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,8 @@ namespace osu.Game.Overlays
|
||||
{
|
||||
public class OverlaySortTabControl<T> : CompositeDrawable, IHasCurrentValue<T>
|
||||
{
|
||||
public TabControl<T> TabControl { get; }
|
||||
|
||||
private readonly BindableWithCurrent<T> current = new BindableWithCurrent<T>();
|
||||
|
||||
public Bindable<T> Current
|
||||
@ -59,7 +61,7 @@ namespace osu.Game.Overlays
|
||||
Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold),
|
||||
Text = SortStrings.Default
|
||||
},
|
||||
CreateControl().With(c =>
|
||||
TabControl = CreateControl().With(c =>
|
||||
{
|
||||
c.Anchor = Anchor.CentreLeft;
|
||||
c.Origin = Anchor.CentreLeft;
|
||||
|
Loading…
Reference in New Issue
Block a user