98 lines
3.2 KiB
C#
98 lines
3.2 KiB
C#
|
|
using UnityEngine;
|
||
|
|
using UnityEngine.EventSystems;
|
||
|
|
|
||
|
|
namespace AlicizaX.UI
|
||
|
|
{
|
||
|
|
public sealed class RecyclerNavigationController
|
||
|
|
{
|
||
|
|
private readonly RecyclerView recyclerView;
|
||
|
|
|
||
|
|
public RecyclerNavigationController(RecyclerView recyclerView)
|
||
|
|
{
|
||
|
|
this.recyclerView = recyclerView;
|
||
|
|
}
|
||
|
|
|
||
|
|
public bool TryMove(ViewHolder currentHolder, MoveDirection direction, RecyclerNavigationOptions options)
|
||
|
|
{
|
||
|
|
if (recyclerView == null ||
|
||
|
|
recyclerView.RecyclerViewAdapter == null ||
|
||
|
|
currentHolder == null)
|
||
|
|
{
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
int itemCount = recyclerView.RecyclerViewAdapter.GetItemCount();
|
||
|
|
int realCount = recyclerView.RecyclerViewAdapter.GetRealCount();
|
||
|
|
if (itemCount <= 0 || realCount <= 0)
|
||
|
|
{
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
int step = GetStep(direction);
|
||
|
|
if (step == 0)
|
||
|
|
{
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool isLoopSource = itemCount != realCount;
|
||
|
|
bool allowWrap = isLoopSource && options.Wrap;
|
||
|
|
int nextLayoutIndex = currentHolder.Index + step;
|
||
|
|
if (allowWrap)
|
||
|
|
{
|
||
|
|
nextLayoutIndex = WrapIndex(nextLayoutIndex, realCount);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
nextLayoutIndex = Mathf.Clamp(nextLayoutIndex, 0, itemCount - 1);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!allowWrap && nextLayoutIndex == currentHolder.Index)
|
||
|
|
{
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
ScrollAlignment alignment = ResolveAlignment(direction, options.Alignment);
|
||
|
|
return recyclerView.TryFocusIndex(nextLayoutIndex, options.SmoothScroll, alignment);
|
||
|
|
}
|
||
|
|
|
||
|
|
private int GetStep(MoveDirection direction)
|
||
|
|
{
|
||
|
|
int unit = Mathf.Max(1, recyclerView.LayoutManager != null ? recyclerView.LayoutManager.Unit : 1);
|
||
|
|
bool vertical = recyclerView.Direction == Direction.Vertical;
|
||
|
|
|
||
|
|
return direction switch
|
||
|
|
{
|
||
|
|
MoveDirection.Left => vertical ? -1 : -unit,
|
||
|
|
MoveDirection.Right => vertical ? 1 : unit,
|
||
|
|
MoveDirection.Up => vertical ? -unit : -1,
|
||
|
|
MoveDirection.Down => vertical ? unit : 1,
|
||
|
|
_ => 0
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
private static int WrapIndex(int index, int count)
|
||
|
|
{
|
||
|
|
int wrapped = index % count;
|
||
|
|
return wrapped < 0 ? wrapped + count : wrapped;
|
||
|
|
}
|
||
|
|
|
||
|
|
private ScrollAlignment ResolveAlignment(MoveDirection direction, ScrollAlignment fallback)
|
||
|
|
{
|
||
|
|
if (recyclerView == null || recyclerView.LayoutManager == null)
|
||
|
|
{
|
||
|
|
return fallback;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool vertical = recyclerView.Direction == Direction.Vertical;
|
||
|
|
return direction switch
|
||
|
|
{
|
||
|
|
MoveDirection.Down when vertical => ScrollAlignment.End,
|
||
|
|
MoveDirection.Up when vertical => ScrollAlignment.Start,
|
||
|
|
MoveDirection.Right when !vertical => ScrollAlignment.End,
|
||
|
|
MoveDirection.Left when !vertical => ScrollAlignment.Start,
|
||
|
|
_ => fallback
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|