1
0
mirror of https://github.com/ppy/osu.git synced 2025-03-24 19:17:20 +08:00

Add difficulty, artist and title sort examples

Also:

- Adds hinting at grouping and header status of items
- Passes through criteria and prepare for grouping tests.
- Makes `Filters` list `protected` because naming clash with `Filter()`
  on `BeatmapCarousel`.
This commit is contained in:
Dean Herbert 2025-01-14 19:37:28 +09:00
parent d97a3270a5
commit 7e8a80a0e5
No known key found for this signature in database
6 changed files with 106 additions and 9 deletions

View File

@ -17,10 +17,13 @@ using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Graphics.Containers;
using osu.Game.Screens.Select;
using osu.Game.Screens.Select.Filter;
using osu.Game.Screens.SelectV2;
using osu.Game.Tests.Beatmaps;
using osu.Game.Tests.Resources;
using osuTK.Graphics;
using BeatmapCarousel = osu.Game.Screens.SelectV2.BeatmapCarousel;
namespace osu.Game.Tests.Visual.SongSelect
{
@ -123,6 +126,11 @@ namespace osu.Game.Tests.Visual.SongSelect
},
};
});
AddStep("sort by title", () =>
{
carousel.Filter(new FilterCriteria { Sort = SortMode.Title });
});
}
[Test]
@ -139,6 +147,26 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("remove all beatmaps", () => beatmapSets.Clear());
}
[Test]
public void TestSorting()
{
AddStep("add 10 beatmaps", () =>
{
for (int i = 0; i < 10; i++)
beatmapSets.Add(TestResources.CreateTestBeatmapSetInfo(RNG.Next(1, 4)));
});
AddStep("sort by difficulty", () =>
{
carousel.Filter(new FilterCriteria { Sort = SortMode.Difficulty });
});
AddStep("sort by artist", () =>
{
carousel.Filter(new FilterCriteria { Sort = SortMode.Artist });
});
}
[Test]
public void TestScrollPositionVelocityMaintained()
{

View File

@ -31,8 +31,8 @@ namespace osu.Game.Screens.SelectV2
Filters = new ICarouselFilter[]
{
new BeatmapCarouselFilterSorting(),
new BeatmapCarouselFilterGrouping(),
new BeatmapCarouselFilterSorting(() => Criteria),
new BeatmapCarouselFilterGrouping(() => Criteria),
};
AddInternal(carouselPanelPool);

View File

@ -1,19 +1,36 @@
// 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 System.Threading;
using System.Threading.Tasks;
using osu.Game.Beatmaps;
using osu.Game.Screens.Select;
namespace osu.Game.Screens.SelectV2
{
public class BeatmapCarouselFilterGrouping : ICarouselFilter
{
private readonly Func<FilterCriteria> getCriteria;
public BeatmapCarouselFilterGrouping(Func<FilterCriteria> getCriteria)
{
this.getCriteria = getCriteria;
}
public async Task<IEnumerable<CarouselItem>> Run(IEnumerable<CarouselItem> items, CancellationToken cancellationToken) => await Task.Run(() =>
{
// TODO: perform grouping based on FilterCriteria
var criteria = getCriteria();
if (criteria.SplitOutDifficulties)
{
foreach (var item in items)
((BeatmapCarouselItem)item).HasGroupHeader = false;
return items;
}
CarouselItem? lastItem = null;
@ -23,15 +40,18 @@ namespace osu.Game.Screens.SelectV2
{
cancellationToken.ThrowIfCancellationRequested();
if (item.Model is BeatmapInfo b1)
if (item.Model is BeatmapInfo b)
{
// Add set header
if (lastItem == null || (lastItem.Model is BeatmapInfo b2 && b2.BeatmapSet!.OnlineID != b1.BeatmapSet!.OnlineID))
newItems.Add(new BeatmapCarouselItem(b1.BeatmapSet!));
if (lastItem == null || (lastItem.Model is BeatmapInfo b2 && b2.BeatmapSet!.OnlineID != b.BeatmapSet!.OnlineID))
newItems.Add(new BeatmapCarouselItem(b.BeatmapSet!) { IsGroupHeader = true });
}
newItems.Add(item);
lastItem = item;
var beatmapCarouselItem = (BeatmapCarouselItem)item;
beatmapCarouselItem.HasGroupHeader = true;
}
return newItems;

View File

@ -1,22 +1,59 @@
// 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 System.Threading;
using System.Threading.Tasks;
using osu.Game.Beatmaps;
using osu.Game.Screens.Select;
using osu.Game.Screens.Select.Filter;
using osu.Game.Utils;
namespace osu.Game.Screens.SelectV2
{
public class BeatmapCarouselFilterSorting : ICarouselFilter
{
private readonly Func<FilterCriteria> getCriteria;
public BeatmapCarouselFilterSorting(Func<FilterCriteria> getCriteria)
{
this.getCriteria = getCriteria;
}
public async Task<IEnumerable<CarouselItem>> Run(IEnumerable<CarouselItem> items, CancellationToken cancellationToken) => await Task.Run(() =>
{
var criteria = getCriteria();
return items.OrderDescending(Comparer<CarouselItem>.Create((a, b) =>
{
int comparison = 0;
if (a.Model is BeatmapInfo ab && b.Model is BeatmapInfo bb)
return ab.OnlineID.CompareTo(bb.OnlineID);
{
switch (criteria.Sort)
{
case SortMode.Artist:
comparison = OrdinalSortByCaseStringComparer.DEFAULT.Compare(ab.BeatmapSet!.Metadata.Artist, bb.BeatmapSet!.Metadata.Artist);
if (comparison == 0)
goto case SortMode.Title;
break;
case SortMode.Difficulty:
comparison = ab.StarRating.CompareTo(bb.StarRating);
break;
case SortMode.Title:
comparison = OrdinalSortByCaseStringComparer.DEFAULT.Compare(ab.BeatmapSet!.Metadata.Title, bb.BeatmapSet!.Metadata.Title);
break;
default:
throw new ArgumentOutOfRangeException();
}
}
if (comparison != 0) return comparison;
if (a is BeatmapCarouselItem aItem && b is BeatmapCarouselItem bItem)
return aItem.ID.CompareTo(bItem.ID);

View File

@ -11,7 +11,19 @@ namespace osu.Game.Screens.SelectV2
{
public readonly Guid ID;
public override float DrawHeight => Model is BeatmapInfo ? 40 : 80;
/// <summary>
/// Whether this item has a header providing extra information for it.
/// When displaying items which don't have header, we should make sure enough information is included inline.
/// </summary>
public bool HasGroupHeader { get; set; }
/// <summary>
/// Whether this item is a group header.
/// Group headers are generally larger in display. Setting this will account for the size difference.
/// </summary>
public bool IsGroupHeader { get; set; }
public override float DrawHeight => IsGroupHeader ? 80 : 40;
public BeatmapCarouselItem(object model)
: base(model)

View File

@ -29,7 +29,7 @@ namespace osu.Game.Screens.SelectV2
/// <summary>
/// A collection of filters which should be run each time a <see cref="QueueFilter"/> is executed.
/// </summary>
public IEnumerable<ICarouselFilter> Filters { get; init; } = Enumerable.Empty<ICarouselFilter>();
protected IEnumerable<ICarouselFilter> Filters { get; init; } = Enumerable.Empty<ICarouselFilter>();
/// <summary>
/// Height of the area above the carousel that should be treated as visible due to transparency of elements in front of it.