From f6aa448523b5e5fafe2c694d215671c08cd431df Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Oct 2020 13:21:21 +0900 Subject: [PATCH] Store y positions inside items rather than in a separate array --- osu.Game/Screens/Select/BeatmapCarousel.cs | 54 +++++++++++++------ .../Screens/Select/Carousel/CarouselItem.cs | 10 +++- 2 files changed, 46 insertions(+), 18 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 78012e8bfd..87e930fe8a 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -116,7 +116,6 @@ namespace osu.Game.Screens.Select }); } - private readonly List yPositions = new List(); private readonly List visibleItems = new List(); private readonly Cached itemsCache = new Cached(); @@ -570,23 +569,15 @@ namespace osu.Game.Screens.Select if (revalidateItems) updateYPositions(); - // Find index range of all items that should be on-screen - int firstIndex = yPositions.BinarySearch(visibleUpperBound); - if (firstIndex < 0) firstIndex = ~firstIndex; - int lastIndex = yPositions.BinarySearch(visibleBottomBound); - if (lastIndex < 0) lastIndex = ~lastIndex; - - // as we can't be 100% sure on the size of individual carousel drawables, - // always play it safe and extend bounds by one. - firstIndex = Math.Max(0, firstIndex - 1); - lastIndex = Math.Min(yPositions.Count, lastIndex + 1); + var (firstIndex, lastIndex) = getDisplayRange(); if (revalidateItems || firstIndex != displayedRange.first || lastIndex != displayedRange.last) { Logger.Log("revalidation requested"); // Remove all items that should no longer be on-screen - scrollableContent.RemoveAll(p => p.Y + p.Item.TotalHeight < visibleUpperBound || p.Y > visibleBottomBound); + // TODO: figure out a more resilient way of doing this removal. + // scrollableContent.RemoveAll(p => p.Y + p.Item.TotalHeight < visibleUpperBound || p.Y > visibleBottomBound); displayedRange = (firstIndex, lastIndex); @@ -601,7 +592,7 @@ namespace osu.Game.Screens.Select { Logger.Log($"getting panel for {item} from pool"); panel = setPool.Get(p => p.Item = item); - panel.Y = yPositions[i]; + panel.Y = item.CarouselYPosition; panel.Depth = i; panel.ClearTransforms(); @@ -611,9 +602,9 @@ namespace osu.Game.Screens.Select else { if (panel.IsPresent) - panel.MoveToY(yPositions[i], 800, Easing.OutQuint); + panel.MoveToY(item.CarouselYPosition, 800, Easing.OutQuint); else - panel.Y = yPositions[i]; + panel.Y = item.CarouselYPosition; scrollableContent.ChangeChildDepth(panel, i); } @@ -626,6 +617,22 @@ namespace osu.Game.Screens.Select updateItem(p); } + private (int firstIndex, int lastIndex) getDisplayRange() + { + // Find index range of all items that should be on-screen + // TODO: reduce allocs of CarouselBoundsItem. + int firstIndex = visibleItems.BinarySearch(new CarouselBoundsItem(visibleUpperBound)); + if (firstIndex < 0) firstIndex = ~firstIndex; + int lastIndex = visibleItems.BinarySearch(new CarouselBoundsItem(visibleBottomBound)); + if (lastIndex < 0) lastIndex = ~lastIndex; + + // as we can't be 100% sure on the size of individual carousel drawables, + // always play it safe and extend bounds by one. + firstIndex = Math.Max(0, firstIndex - 1); + lastIndex = Math.Min(visibleItems.Count, lastIndex + 1); + return (firstIndex, lastIndex); + } + protected override void UpdateAfterChildren() { base.UpdateAfterChildren(); @@ -698,7 +705,6 @@ namespace osu.Game.Screens.Select /// The Y position of the currently selected item. private void updateYPositions() { - yPositions.Clear(); visibleItems.Clear(); float currentY = visibleHalfHeight; @@ -715,7 +721,7 @@ namespace osu.Game.Screens.Select case CarouselBeatmapSet set: { visibleItems.Add(set); - yPositions.Add(currentY); + set.CarouselYPosition = currentY; if (item.State.Value == CarouselItemState.Selected) { @@ -815,6 +821,20 @@ namespace osu.Game.Screens.Select p.SetMultiplicativeAlpha(Math.Clamp(1.75f - 1.5f * dist, 0, 1)); } + /// + /// A carousel item strictly used for binary search purposes. + /// + private class CarouselBoundsItem : CarouselItem + { + public CarouselBoundsItem(in float pos) + { + CarouselYPosition = pos; + } + + public override DrawableCarouselItem CreateDrawableRepresentation() => + throw new NotImplementedException(); + } + private class CarouselRoot : CarouselGroupEagerSelect { private readonly BeatmapCarousel carousel; diff --git a/osu.Game/Screens/Select/Carousel/CarouselItem.cs b/osu.Game/Screens/Select/Carousel/CarouselItem.cs index 004d9779ef..4bd477412d 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselItem.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselItem.cs @@ -1,14 +1,20 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Framework.Bindables; namespace osu.Game.Screens.Select.Carousel { - public abstract class CarouselItem + public abstract class CarouselItem : IComparable { public virtual float TotalHeight => 0; + /// + /// An externally defined value used to determine this item's vertical display offset relative to the carousel. + /// + public float CarouselYPosition; + public readonly BindableBool Filtered = new BindableBool(); public readonly Bindable State = new Bindable(CarouselItemState.NotSelected); @@ -42,6 +48,8 @@ namespace osu.Game.Screens.Select.Carousel } public virtual int CompareTo(FilterCriteria criteria, CarouselItem other) => ChildID.CompareTo(other.ChildID); + + public int CompareTo(CarouselItem other) => CarouselYPosition.CompareTo(other.CarouselYPosition); } public enum CarouselItemState