From 288be46b17d3c87347e2e8ed1df8f7af3df379e7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 Jan 2025 19:34:56 +0900 Subject: [PATCH] Add basic selection support --- .../Screens/SelectV2/BeatmapCarouselV2.cs | 54 ++++++++++++++++++- osu.Game/Screens/SelectV2/Carousel.cs | 40 ++++++++++++++ osu.Game/Screens/SelectV2/CarouselItem.cs | 7 ++- osu.Game/Screens/SelectV2/ICarouselFilter.cs | 2 +- osu.Game/Screens/SelectV2/ICarouselPanel.cs | 4 +- 5 files changed, 100 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/SelectV2/BeatmapCarouselV2.cs b/osu.Game/Screens/SelectV2/BeatmapCarouselV2.cs index a54c2aceff..37c33446da 100644 --- a/osu.Game/Screens/SelectV2/BeatmapCarouselV2.cs +++ b/osu.Game/Screens/SelectV2/BeatmapCarouselV2.cs @@ -14,6 +14,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Pooling; using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Events; using osu.Game.Beatmaps; using osu.Game.Database; using osu.Game.Graphics.Sprites; @@ -23,6 +24,7 @@ using osuTK.Graphics; namespace osu.Game.Screens.SelectV2 { + [Cached] public partial class BeatmapCarouselV2 : Carousel { private IBindableList detachedBeatmaps = null!; @@ -102,7 +104,48 @@ namespace osu.Game.Screens.SelectV2 public partial class BeatmapCarouselPanel : PoolableDrawable, ICarouselPanel { - public CarouselItem? Item { get; set; } + [Resolved] + private BeatmapCarouselV2 carousel { get; set; } = null!; + + public CarouselItem? Item + { + get => item; + set + { + item = value; + + selected.UnbindBindings(); + + if (item != null) + selected.BindTo(item.Selected); + } + } + + private readonly BindableBool selected = new BindableBool(); + private CarouselItem? item; + + [BackgroundDependencyLoader] + private void load() + { + selected.BindValueChanged(value => + { + if (value.NewValue) + { + BorderThickness = 5; + BorderColour = Color4.Pink; + } + else + { + BorderThickness = 0; + } + }); + } + + protected override void FreeAfterUse() + { + base.FreeAfterUse(); + Item = null; + } protected override void PrepareForUse() { @@ -111,6 +154,7 @@ namespace osu.Game.Screens.SelectV2 Debug.Assert(Item != null); Size = new Vector2(500, Item.DrawHeight); + Masking = true; InternalChildren = new Drawable[] { @@ -128,6 +172,12 @@ namespace osu.Game.Screens.SelectV2 } }; } + + protected override bool OnClick(ClickEvent e) + { + carousel.CurrentSelection = Item!.Model; + return true; + } } public class BeatmapCarouselItem : CarouselItem @@ -165,7 +215,7 @@ namespace osu.Game.Screens.SelectV2 CarouselItem? lastItem = null; - var newItems = new List(); + var newItems = new List(items.Count()); foreach (var item in items) { diff --git a/osu.Game/Screens/SelectV2/Carousel.cs b/osu.Game/Screens/SelectV2/Carousel.cs index 2f3c47a0a3..45dadc3455 100644 --- a/osu.Game/Screens/SelectV2/Carousel.cs +++ b/osu.Game/Screens/SelectV2/Carousel.cs @@ -77,8 +77,28 @@ namespace osu.Game.Screens.SelectV2 /// All items which are to be considered for display in this carousel. /// Mutating this list will automatically queue a . /// + /// + /// Note that an may add new items which are displayed but not tracked in this list. + /// protected readonly BindableList Items = new BindableList(); + /// + /// The currently selected model. + /// + /// + /// Setting this will ensure is set to true only on the matching . + /// Of note, if no matching item exists all items will be deselected while waiting for potential new item which matches. + /// + public virtual object? CurrentSelection + { + get => currentSelection; + set + { + currentSelection = value; + updateSelection(); + } + } + private List? displayedCarouselItems; private readonly DoublePrecisionScroll scroll; @@ -169,6 +189,8 @@ namespace osu.Game.Screens.SelectV2 displayedCarouselItems = items.ToList(); displayedRange = null; + updateSelection(); + void log(string text) => Logger.Log($"Carousel[op {cts.GetHashCode().ToString().Substring(0, 5)}] {stopwatch.ElapsedMilliseconds} ms: {text}"); } @@ -186,6 +208,24 @@ namespace osu.Game.Screens.SelectV2 #endregion + #region Selection handling + + private object? currentSelection; + + private void updateSelection() + { + if (displayedCarouselItems == null) return; + + // TODO: this is ugly, we probably should stop exposing CarouselItem externally. + foreach (var item in Items) + item.Selected.Value = item.Model == currentSelection; + + foreach (var item in displayedCarouselItems) + item.Selected.Value = item.Model == currentSelection; + } + + #endregion + #region Display handling private DisplayRange? displayedRange; diff --git a/osu.Game/Screens/SelectV2/CarouselItem.cs b/osu.Game/Screens/SelectV2/CarouselItem.cs index 69abe86205..4636e8a32f 100644 --- a/osu.Game/Screens/SelectV2/CarouselItem.cs +++ b/osu.Game/Screens/SelectV2/CarouselItem.cs @@ -2,22 +2,25 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Bindables; namespace osu.Game.Screens.SelectV2 { /// - /// Represents a single display item for display in a . + /// Represents a single display item for display in a . /// This is used to house information related to the attached model that helps with display and tracking. /// public abstract class CarouselItem : IComparable { + public readonly BindableBool Selected = new BindableBool(); + /// /// The model this item is representing. /// public readonly object Model; /// - /// The current Y position in the carousel. This is managed by and should not be set manually. + /// The current Y position in the carousel. This is managed by and should not be set manually. /// public double CarouselYPosition { get; set; } diff --git a/osu.Game/Screens/SelectV2/ICarouselFilter.cs b/osu.Game/Screens/SelectV2/ICarouselFilter.cs index 82aca18b85..f510a7cd4b 100644 --- a/osu.Game/Screens/SelectV2/ICarouselFilter.cs +++ b/osu.Game/Screens/SelectV2/ICarouselFilter.cs @@ -8,7 +8,7 @@ using System.Threading.Tasks; namespace osu.Game.Screens.SelectV2 { /// - /// An interface representing a filter operation which can be run on a . + /// An interface representing a filter operation which can be run on a . /// public interface ICarouselFilter { diff --git a/osu.Game/Screens/SelectV2/ICarouselPanel.cs b/osu.Game/Screens/SelectV2/ICarouselPanel.cs index 2f03bd8e26..97c585492c 100644 --- a/osu.Game/Screens/SelectV2/ICarouselPanel.cs +++ b/osu.Game/Screens/SelectV2/ICarouselPanel.cs @@ -6,7 +6,7 @@ using osu.Framework.Graphics; namespace osu.Game.Screens.SelectV2 { /// - /// An interface to be attached to any s which are used for display inside a . + /// An interface to be attached to any s which are used for display inside a . /// public interface ICarouselPanel { @@ -16,7 +16,7 @@ namespace osu.Game.Screens.SelectV2 double YPosition => Item!.CarouselYPosition; /// - /// The carousel item this drawable is representing. This is managed by and should not be set manually. + /// The carousel item this drawable is representing. This is managed by and should not be set manually. /// CarouselItem? Item { get; set; } }