// 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 System.Collections.Generic; using osu.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics.UserInterface; namespace osu.Game.Graphics.Containers { /// /// A FillFlowContainer that provides functionality to cycle selection between children /// The selection wraps around when overflowing past the first or last child. /// public class SelectionCycleFillFlowContainer : FillFlowContainer where T : Drawable, IStateful { private int? selectedIndex; private void setSelected(int? value) { if (selectedIndex == value) return; // Deselect the previously-selected button if (selectedIndex.HasValue) this[selectedIndex.Value].State = SelectionState.NotSelected; selectedIndex = value; // Select the newly-selected button if (selectedIndex.HasValue) this[selectedIndex.Value].State = SelectionState.Selected; } public void SelectNext() { if (!selectedIndex.HasValue || selectedIndex == Count - 1) setSelected(0); else setSelected(selectedIndex.Value + 1); } public void SelectPrevious() { if (!selectedIndex.HasValue || selectedIndex == 0) setSelected(Count - 1); else setSelected(selectedIndex.Value - 1); } public void Deselect() => setSelected(null); public void Select(T item) { var newIndex = IndexOf(item); if (newIndex < 0) setSelected(null); else setSelected(IndexOf(item)); } public T Selected => (selectedIndex >= 0 && selectedIndex < Count) ? this[selectedIndex.Value] : null; private readonly Dictionary> handlerMap = new Dictionary>(); public override void Add(T drawable) { // This event is used to update selection state when modified within the drawable itself. // It is added to a dictionary so that we can unsubscribe if the drawable is removed from this container handlerMap[drawable] = state => selectionChanged(drawable, state); drawable.StateChanged += handlerMap[drawable]; base.Add(drawable); } public override bool Remove(T drawable) { if (!base.Remove(drawable)) return false; if (handlerMap.TryGetValue(drawable, out var action)) { drawable.StateChanged -= action; handlerMap.Remove(drawable); } return true; } private void selectionChanged(T drawable, SelectionState state) { if (state == SelectionState.NotSelected) Deselect(); else Select(drawable); } } }