2019-01-24 16:43:03 +08:00
|
|
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
|
|
|
// See the LICENCE file in the repository root for full licence text.
|
2018-04-13 17:19:50 +08:00
|
|
|
|
2017-12-16 16:32:21 +08:00
|
|
|
using System;
|
2020-10-12 14:36:26 +08:00
|
|
|
using System.Collections.Generic;
|
2017-12-12 16:48:38 +08:00
|
|
|
using System.Linq;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
2017-12-12 16:48:38 +08:00
|
|
|
namespace osu.Game.Screens.Select.Carousel
|
|
|
|
{
|
|
|
|
/// <summary>
|
2022-07-26 14:39:30 +08:00
|
|
|
/// A group which ensures at least one item is selected (if the group itself is selected).
|
2017-12-12 16:48:38 +08:00
|
|
|
/// </summary>
|
|
|
|
public class CarouselGroupEagerSelect : CarouselGroup
|
|
|
|
{
|
2018-04-02 14:16:10 +08:00
|
|
|
public CarouselGroupEagerSelect()
|
2017-12-12 16:48:38 +08:00
|
|
|
{
|
2019-02-22 16:51:39 +08:00
|
|
|
State.ValueChanged += state =>
|
2017-12-12 16:48:38 +08:00
|
|
|
{
|
2019-02-22 16:51:39 +08:00
|
|
|
if (state.NewValue == CarouselItemState.Selected)
|
2017-12-16 15:14:37 +08:00
|
|
|
attemptSelection();
|
2017-12-12 16:48:38 +08:00
|
|
|
};
|
|
|
|
}
|
2018-04-13 17:19:50 +08:00
|
|
|
|
2018-04-02 14:16:10 +08:00
|
|
|
/// <summary>
|
|
|
|
/// The last selected item.
|
|
|
|
/// </summary>
|
2023-01-09 02:02:48 +08:00
|
|
|
protected CarouselItem? LastSelected { get; private set; }
|
2018-04-13 17:19:50 +08:00
|
|
|
|
2017-12-16 16:32:21 +08:00
|
|
|
/// <summary>
|
|
|
|
/// We need to keep track of the index for cases where the selection is removed but we want to select a new item based on its old location.
|
|
|
|
/// </summary>
|
2017-12-16 15:14:37 +08:00
|
|
|
private int lastSelectedIndex;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
2017-12-17 03:30:56 +08:00
|
|
|
/// <summary>
|
|
|
|
/// To avoid overhead during filter operations, we don't attempt any selections until after all
|
2022-07-26 14:39:30 +08:00
|
|
|
/// items have been filtered. This bool will be true during the base <see cref="Filter(FilterCriteria)"/>
|
2017-12-17 03:30:56 +08:00
|
|
|
/// operation.
|
|
|
|
/// </summary>
|
2023-12-20 18:09:07 +08:00
|
|
|
protected bool DisableSelection;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
2017-12-16 15:14:37 +08:00
|
|
|
public override void Filter(FilterCriteria criteria)
|
|
|
|
{
|
2023-12-20 18:09:07 +08:00
|
|
|
DisableSelection = true;
|
2017-12-16 15:14:37 +08:00
|
|
|
base.Filter(criteria);
|
2023-12-20 18:09:07 +08:00
|
|
|
DisableSelection = false;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
2017-12-16 15:14:37 +08:00
|
|
|
attemptSelection();
|
|
|
|
}
|
2018-04-13 17:19:50 +08:00
|
|
|
|
2022-07-21 15:06:06 +08:00
|
|
|
public override void RemoveItem(CarouselItem i)
|
2017-12-16 16:32:21 +08:00
|
|
|
{
|
2022-07-21 15:06:06 +08:00
|
|
|
base.RemoveItem(i);
|
2018-04-13 17:19:50 +08:00
|
|
|
|
2018-04-02 14:16:10 +08:00
|
|
|
if (i != LastSelected)
|
2017-12-16 16:32:21 +08:00
|
|
|
updateSelectedIndex();
|
|
|
|
}
|
2018-04-13 17:19:50 +08:00
|
|
|
|
2022-07-21 15:16:39 +08:00
|
|
|
private bool addingItems;
|
2022-01-20 20:57:16 +08:00
|
|
|
|
2022-07-21 15:16:39 +08:00
|
|
|
public void AddItems(IEnumerable<CarouselItem> items)
|
2020-10-12 14:36:26 +08:00
|
|
|
{
|
2022-07-21 15:16:39 +08:00
|
|
|
addingItems = true;
|
2022-01-20 20:57:16 +08:00
|
|
|
|
2020-10-12 14:36:26 +08:00
|
|
|
foreach (var i in items)
|
2022-07-21 15:06:06 +08:00
|
|
|
AddItem(i);
|
2022-01-20 20:57:16 +08:00
|
|
|
|
2022-07-21 15:16:39 +08:00
|
|
|
addingItems = false;
|
2020-10-12 14:36:26 +08:00
|
|
|
|
|
|
|
attemptSelection();
|
|
|
|
}
|
|
|
|
|
2022-07-21 15:06:06 +08:00
|
|
|
public override void AddItem(CarouselItem i)
|
2017-12-16 16:32:21 +08:00
|
|
|
{
|
2022-07-21 15:06:06 +08:00
|
|
|
base.AddItem(i);
|
2022-07-21 15:16:39 +08:00
|
|
|
if (!addingItems)
|
2022-01-20 20:57:16 +08:00
|
|
|
attemptSelection();
|
2017-12-16 16:32:21 +08:00
|
|
|
}
|
2018-04-13 17:19:50 +08:00
|
|
|
|
2017-12-16 15:14:37 +08:00
|
|
|
protected override void ChildItemStateChanged(CarouselItem item, CarouselItemState value)
|
2017-12-15 23:33:09 +08:00
|
|
|
{
|
2017-12-16 15:14:37 +08:00
|
|
|
base.ChildItemStateChanged(item, value);
|
2018-04-13 17:19:50 +08:00
|
|
|
|
2017-12-16 15:14:37 +08:00
|
|
|
switch (value)
|
2017-12-15 23:33:09 +08:00
|
|
|
{
|
2017-12-16 15:14:37 +08:00
|
|
|
case CarouselItemState.Selected:
|
2017-12-16 16:32:21 +08:00
|
|
|
updateSelected(item);
|
2017-12-16 15:14:37 +08:00
|
|
|
break;
|
2019-04-01 11:44:46 +08:00
|
|
|
|
2017-12-16 15:14:37 +08:00
|
|
|
case CarouselItemState.NotSelected:
|
2017-12-17 04:53:13 +08:00
|
|
|
case CarouselItemState.Collapsed:
|
2017-12-16 15:14:37 +08:00
|
|
|
attemptSelection();
|
|
|
|
break;
|
2017-12-15 23:33:09 +08:00
|
|
|
}
|
|
|
|
}
|
2018-04-13 17:19:50 +08:00
|
|
|
|
2017-12-16 15:14:37 +08:00
|
|
|
private void attemptSelection()
|
|
|
|
{
|
2023-12-20 18:09:07 +08:00
|
|
|
if (DisableSelection) return;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
2017-12-16 15:14:37 +08:00
|
|
|
// we only perform eager selection if we are a currently selected group.
|
2019-02-21 17:56:34 +08:00
|
|
|
if (State.Value != CarouselItemState.Selected) return;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
2022-07-26 14:39:30 +08:00
|
|
|
// we only perform eager selection if none of our items are in a selected state already.
|
2022-07-21 15:06:06 +08:00
|
|
|
if (Items.Any(i => i.State.Value == CarouselItemState.Selected)) return;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
2018-04-02 14:16:10 +08:00
|
|
|
PerformSelection();
|
|
|
|
}
|
2018-04-13 17:19:50 +08:00
|
|
|
|
2021-08-26 23:31:19 +08:00
|
|
|
/// <summary>
|
|
|
|
/// Finds the item this group would select next if it attempted selection
|
|
|
|
/// </summary>
|
|
|
|
/// <returns>An unfiltered item nearest to the last selected one or null if all items are filtered</returns>
|
2023-12-19 18:58:49 +08:00
|
|
|
public virtual CarouselItem? GetNextToSelect()
|
2020-03-26 06:19:54 +08:00
|
|
|
{
|
2022-10-25 12:34:21 +08:00
|
|
|
if (Items.Count == 0)
|
|
|
|
return null;
|
|
|
|
|
2021-08-26 23:31:19 +08:00
|
|
|
int forwardsIndex = lastSelectedIndex;
|
2022-10-26 10:03:11 +08:00
|
|
|
int backwardsIndex = Math.Min(lastSelectedIndex, Items.Count - 1);
|
2021-08-26 02:42:15 +08:00
|
|
|
|
2022-10-25 17:57:15 +08:00
|
|
|
while (true)
|
2021-08-26 02:42:15 +08:00
|
|
|
{
|
2022-10-25 17:57:15 +08:00
|
|
|
bool hasBackwards = backwardsIndex >= 0 && backwardsIndex < Items.Count;
|
|
|
|
bool hasForwards = forwardsIndex < Items.Count;
|
|
|
|
|
|
|
|
if (!hasBackwards && !hasForwards)
|
|
|
|
return null;
|
|
|
|
|
2022-10-25 11:48:17 +08:00
|
|
|
if (hasForwards && !Items[forwardsIndex].Filtered.Value)
|
2022-08-26 18:32:49 +08:00
|
|
|
return Items[forwardsIndex];
|
2022-10-25 11:48:17 +08:00
|
|
|
|
|
|
|
if (hasBackwards && !Items[backwardsIndex].Filtered.Value)
|
2022-08-26 18:32:49 +08:00
|
|
|
return Items[backwardsIndex];
|
2021-08-26 23:31:19 +08:00
|
|
|
|
|
|
|
forwardsIndex++;
|
|
|
|
backwardsIndex--;
|
|
|
|
}
|
2020-03-26 06:19:54 +08:00
|
|
|
}
|
|
|
|
|
2018-04-02 14:16:10 +08:00
|
|
|
protected virtual void PerformSelection()
|
|
|
|
{
|
2023-01-09 02:02:48 +08:00
|
|
|
CarouselItem? nextToSelect = GetNextToSelect();
|
2018-04-13 17:19:50 +08:00
|
|
|
|
2017-12-16 15:14:37 +08:00
|
|
|
if (nextToSelect != null)
|
|
|
|
nextToSelect.State.Value = CarouselItemState.Selected;
|
2017-12-16 16:32:21 +08:00
|
|
|
else
|
|
|
|
updateSelected(null);
|
|
|
|
}
|
2018-04-13 17:19:50 +08:00
|
|
|
|
2023-01-09 02:02:48 +08:00
|
|
|
private void updateSelected(CarouselItem? newSelection)
|
2017-12-16 16:32:21 +08:00
|
|
|
{
|
2020-03-19 17:07:39 +08:00
|
|
|
if (newSelection != null)
|
|
|
|
LastSelected = newSelection;
|
2017-12-16 16:32:21 +08:00
|
|
|
updateSelectedIndex();
|
2017-12-16 15:14:37 +08:00
|
|
|
}
|
2018-04-13 17:19:50 +08:00
|
|
|
|
2022-07-21 15:06:06 +08:00
|
|
|
private void updateSelectedIndex() => lastSelectedIndex = LastSelected == null ? 0 : Math.Max(0, GetIndexOfItem(LastSelected));
|
2017-12-12 16:48:38 +08:00
|
|
|
}
|
|
|
|
}
|