diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 94ba0caefb..f5b524e57c 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -16,6 +16,7 @@ using osu.Framework.Bindables; using osu.Framework.Caching; using osu.Framework.Threading; using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Graphics.Pooling; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Game.Beatmaps; @@ -136,6 +137,8 @@ namespace osu.Game.Screens.Select private IBindable> itemHidden; private IBindable> itemRestored; + private readonly DrawablePool setPool = new DrawablePool(100); + public BeatmapCarousel() { root = new CarouselRoot(this); @@ -146,9 +149,13 @@ namespace osu.Game.Screens.Select { Masking = false, RelativeSizeAxes = Axes.Both, - Child = scrollableContent = new Container + Children = new Drawable[] { - RelativeSizeAxes = Axes.X, + setPool, + scrollableContent = new Container + { + RelativeSizeAxes = Axes.X, + } } } }; @@ -580,11 +587,13 @@ namespace osu.Game.Screens.Select // Add those items within the previously found index range that should be displayed. for (int i = firstIndex; i < lastIndex; ++i) { - var panel = scrollableContent.FirstOrDefault(c => c.Item == visibleItems[i]); + var item = visibleItems[i]; + + var panel = scrollableContent.FirstOrDefault(c => c.Item == item); if (panel == null) { - panel = visibleItems[i].CreateDrawableRepresentation(); + panel = setPool.Get(p => p.Item = item); scrollableContent.Add(panel); } diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs index e34710f71d..15f622f3c4 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs @@ -43,8 +43,6 @@ namespace osu.Game.Screens.Select.Carousel .ForEach(AddChild); } - public override DrawableCarouselItem CreateDrawableRepresentation() => new DrawableCarouselBeatmapSet(this); - protected override CarouselItem GetNextToSelect() { if (LastSelected == null) diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs index 7d69251265..4135960e06 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs @@ -60,10 +60,12 @@ namespace osu.Game.Screens.Select.Carousel private CancellationTokenSource starDifficultyCancellationSource; public DrawableCarouselBeatmap(CarouselBeatmap panel) - : base(panel) { beatmap = panel.Beatmap; Height = HEIGHT; + + // todo: temporary + Item = panel; } [BackgroundDependencyLoader(true)] @@ -79,7 +81,7 @@ namespace osu.Game.Screens.Select.Carousel if (manager != null) hideRequested = manager.Hide; - Children = new Drawable[] + Content.Children = new Drawable[] { background = new Box { diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index aaa925c6f8..0bfec4f509 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -45,26 +45,40 @@ namespace osu.Game.Screens.Select.Carousel public override IEnumerable ChildItems => beatmapContainer?.Children ?? base.ChildItems; - private readonly BeatmapSetInfo beatmapSet; + private BeatmapSetInfo beatmapSet => (Item as CarouselBeatmapSet)?.BeatmapSet; private Container beatmapContainer; private Bindable beatmapSetState; - public DrawableCarouselBeatmapSet(CarouselBeatmapSet set) - : base(set) - { - beatmapSet = set.BeatmapSet; - } + [Resolved] + private BeatmapManager manager { get; set; } [BackgroundDependencyLoader(true)] - private void load(BeatmapManager manager, BeatmapSetOverlay beatmapOverlay) + private void load(BeatmapSetOverlay beatmapOverlay) { restoreHiddenRequested = s => s.Beatmaps.ForEach(manager.Restore); if (beatmapOverlay != null) viewDetails = beatmapOverlay.FetchAndShowBeatmapSet; - Children = new Drawable[] + // TODO: temporary. we probably want to *not* inherit DrawableCarouselItem for this class, but only the above header portion. + AddRangeInternal(new Drawable[] + { + beatmapContainer = new Container + { + X = 50, + Y = MAX_HEIGHT, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + }, + }); + } + + protected override void UpdateItem() + { + base.UpdateItem(); + + Content.Children = new Drawable[] { new DelayedLoadUnloadWrapper(() => { @@ -125,17 +139,7 @@ namespace osu.Game.Screens.Select.Carousel } }; - // TODO: temporary. we probably want to *not* inherit DrawableCarouselItem for this class, but only the above header portion. - AddRangeInternal(new Drawable[] - { - beatmapContainer = new Container - { - X = 50, - Y = MAX_HEIGHT, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - }, - }); + beatmapContainer.Clear(); beatmapSetState = Item.State.GetBoundCopy(); beatmapSetState.BindValueChanged(setSelected, true); @@ -153,11 +157,16 @@ namespace osu.Game.Screens.Select.Carousel float yPos = 0; - foreach (var item in ((CarouselBeatmapSet)Item).Beatmaps.Select(b => b.CreateDrawableRepresentation()).OfType()) + var carouselBeatmapSet = (CarouselBeatmapSet)Item; + + foreach (var item in carouselBeatmapSet.Children) { - item.Y = yPos; - beatmapContainer.Add(item); - yPos += item.Item.TotalHeight; + var beatmapPanel = item.CreateDrawableRepresentation(); + + beatmapPanel.Y = yPos; + yPos += item.TotalHeight; + + beatmapContainer.Add((DrawableCarouselBeatmap)beatmapPanel); } break; diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselItem.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselItem.cs index c0405b373d..4c4932c22a 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselItem.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselItem.cs @@ -2,14 +2,17 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; +using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; +using osu.Framework.Graphics.Pooling; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Framework.Utils; @@ -19,15 +22,32 @@ using osuTK.Graphics; namespace osu.Game.Screens.Select.Carousel { - public abstract class DrawableCarouselItem : Container + public abstract class DrawableCarouselItem : PoolableDrawable { public const float MAX_HEIGHT = 80; - public override bool RemoveWhenNotAlive => false; + public override bool IsPresent => base.IsPresent || Item?.Visible == true; - public override bool IsPresent => base.IsPresent || Item.Visible; + public CarouselItem Item + { + get => item; + set + { + if (item == value) + return; - public readonly CarouselItem Item; + if (item != null) + { + Item.Filtered.ValueChanged -= onStateChange; + Item.State.ValueChanged -= onStateChange; + } + + item = value; + + if (IsLoaded) + UpdateItem(); + } + } public virtual IEnumerable ChildItems => Enumerable.Empty(); @@ -37,12 +57,10 @@ namespace osu.Game.Screens.Select.Carousel private readonly Box hoverLayer; - protected override Container Content => nestedContainer; + protected Container Content => nestedContainer; - protected DrawableCarouselItem(CarouselItem item) + protected DrawableCarouselItem() { - Item = item; - Height = MAX_HEIGHT; RelativeSizeAxes = Axes.X; Alpha = 0; @@ -70,6 +88,7 @@ namespace osu.Game.Screens.Select.Carousel } private SampleChannel sampleHover; + private CarouselItem item; [BackgroundDependencyLoader] private void load(AudioManager audio, OsuColour colours) @@ -98,14 +117,27 @@ namespace osu.Game.Screens.Select.Carousel { base.LoadComplete(); - ApplyState(); - Item.Filtered.ValueChanged += _ => Schedule(ApplyState); - Item.State.ValueChanged += _ => Schedule(ApplyState); + UpdateItem(); } + protected virtual void UpdateItem() + { + if (item == null) + return; + + ApplyState(); + + Item.Filtered.ValueChanged += onStateChange; + Item.State.ValueChanged += onStateChange; + } + + private void onStateChange(ValueChangedEvent obj) => Schedule(ApplyState); + + private void onStateChange(ValueChangedEvent _) => Schedule(ApplyState); + protected virtual void ApplyState() { - if (!IsLoaded) return; + Debug.Assert(Item != null); switch (Item.State.Value) { @@ -126,6 +158,8 @@ namespace osu.Game.Screens.Select.Carousel protected virtual void Selected() { + Debug.Assert(Item != null); + Item.State.Value = CarouselItemState.Selected; BorderContainer.BorderThickness = 2.5f;