116 lines
3.8 KiB
C#
116 lines
3.8 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 !UX_NAVIGATION
|
|
return false;
|
|
#else
|
|
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 originalIndex = currentHolder.Index;
|
|
int currentIndex = currentHolder.Index;
|
|
int nextLayoutIndex = currentIndex;
|
|
int maxAttempts = allowWrap ? realCount : itemCount;
|
|
ScrollAlignment alignment = ResolveAlignment(direction, options.Alignment);
|
|
for (int attempt = 0; attempt < maxAttempts; attempt++)
|
|
{
|
|
nextLayoutIndex += step;
|
|
if (allowWrap)
|
|
{
|
|
nextLayoutIndex = WrapIndex(nextLayoutIndex, realCount);
|
|
}
|
|
else
|
|
{
|
|
nextLayoutIndex = Mathf.Clamp(nextLayoutIndex, 0, itemCount - 1);
|
|
if (nextLayoutIndex == currentIndex)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (recyclerView.TryFocusIndex(nextLayoutIndex, options.SmoothScroll, alignment))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
currentIndex = nextLayoutIndex;
|
|
}
|
|
|
|
recyclerView.TryFocusIndex(originalIndex, false, alignment);
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
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
|
|
};
|
|
}
|
|
}
|
|
}
|