diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 75ad0511e6..6d3d7aa185 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -300,16 +300,18 @@ namespace osu.Game.Screens.Select root.AddChild(newSet); - // only reset scroll position if already near the scroll target. - // without this, during a large beatmap import it is impossible to navigate the carousel. - applyActiveCriteria(false, alwaysResetScrollPosition: false); - // check if we can/need to maintain our current selection. if (previouslySelectedID != null) select((CarouselItem)newSet.Beatmaps.FirstOrDefault(b => b.BeatmapInfo.ID == previouslySelectedID) ?? newSet); itemsCache.Invalidate(); - Schedule(() => BeatmapSetsChanged?.Invoke()); + Schedule(() => + { + if (!Scroll.UserScrolling) + ScrollToSelected(true); + + BeatmapSetsChanged?.Invoke(); + }); }); /// diff --git a/osu.Game/Screens/Select/Carousel/CarouselGroup.cs b/osu.Game/Screens/Select/Carousel/CarouselGroup.cs index b85e868b89..6ebe314072 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselGroup.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselGroup.cs @@ -4,6 +4,8 @@ using System.Collections.Generic; using System.Linq; +#nullable enable + namespace osu.Game.Screens.Select.Carousel { /// @@ -11,7 +13,7 @@ namespace osu.Game.Screens.Select.Carousel /// public class CarouselGroup : CarouselItem { - public override DrawableCarouselItem CreateDrawableRepresentation() => null; + public override DrawableCarouselItem? CreateDrawableRepresentation() => null; public IReadOnlyList Children => InternalChildren; @@ -23,6 +25,10 @@ namespace osu.Game.Screens.Select.Carousel /// private ulong currentChildID; + private Comparer? criteriaComparer; + + private FilterCriteria? lastCriteria; + public virtual void RemoveChild(CarouselItem i) { InternalChildren.Remove(i); @@ -36,10 +42,24 @@ namespace osu.Game.Screens.Select.Carousel { i.State.ValueChanged += state => ChildItemStateChanged(i, state.NewValue); i.ChildID = ++currentChildID; - InternalChildren.Add(i); + + if (lastCriteria != null) + { + i.Filter(lastCriteria); + + int index = InternalChildren.BinarySearch(i, criteriaComparer); + if (index < 0) index = ~index; // BinarySearch hacks multiple return values with 2's complement. + + InternalChildren.Insert(index, i); + } + else + { + // criteria may be null for initial population. the filtering will be applied post-add. + InternalChildren.Add(i); + } } - public CarouselGroup(List items = null) + public CarouselGroup(List? items = null) { if (items != null) InternalChildren = items; @@ -67,9 +87,12 @@ namespace osu.Game.Screens.Select.Carousel base.Filter(criteria); InternalChildren.ForEach(c => c.Filter(criteria)); + // IEnumerable.OrderBy() is used instead of List.Sort() to ensure sorting stability - var criteriaComparer = Comparer.Create((x, y) => x.CompareTo(criteria, y)); + criteriaComparer = Comparer.Create((x, y) => x.CompareTo(criteria, y)); InternalChildren = InternalChildren.OrderBy(c => c, criteriaComparer).ToList(); + + lastCriteria = criteria; } protected virtual void ChildItemStateChanged(CarouselItem item, CarouselItemState value)