com.alicizax.unity.ui.exten.../Runtime/RecyclerView/ViewHolder/ViewHolder.Interaction.cs

300 lines
8.4 KiB
C#
Raw Normal View History

using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using Cysharp.Text;
namespace AlicizaX.UI
{
public abstract partial class ViewHolder :
IPointerClickHandler,
IPointerEnterHandler,
IPointerExitHandler,
2026-04-13 15:45:34 +08:00
#if UX_NAVIGATION
ISelectHandler,
IDeselectHandler,
IMoveHandler,
2026-04-13 15:45:34 +08:00
#endif
IBeginDragHandler,
IDragHandler,
2026-04-13 15:45:34 +08:00
IEndDragHandler
#if UX_NAVIGATION
,
ISubmitHandler,
ICancelHandler
2026-04-13 15:45:34 +08:00
#endif
{
[SerializeField]
private ItemInteractionFlags itemInteractionFlags = ItemInteractionFlags.None;
private IItemInteractionHost interactionHost;
private ItemInteractionFlags activeInteractionFlags;
private RecyclerItemSelectable ownedSelectable;
private Scroller parentScroller;
private bool missingSelectableLogged;
public ItemInteractionFlags ItemInteractionFlags
{
get => itemInteractionFlags;
set => itemInteractionFlags = value;
}
internal void BindInteractionHost(IItemInteractionHost host)
{
interactionHost = host;
activeInteractionFlags = host?.InteractionFlags ?? itemInteractionFlags;
parentScroller = RecyclerView != null ? RecyclerView.Scroller : null;
EnsureFocusAnchor();
if (ownedSelectable != null)
{
bool requiresSelection = RequiresSelection(activeInteractionFlags);
ownedSelectable.interactable = requiresSelection;
ownedSelectable.enabled = requiresSelection;
}
InvalidateNavigationScope();
}
internal void ClearInteractionHost()
{
interactionHost = null;
activeInteractionFlags = ItemInteractionFlags.None;
parentScroller = null;
if (ownedSelectable != null)
{
ownedSelectable.interactable = false;
ownedSelectable.enabled = false;
}
InvalidateNavigationScope();
}
public void OnPointerClick(PointerEventData eventData)
{
if ((activeInteractionFlags & ItemInteractionFlags.PointerClick) != 0)
{
interactionHost?.HandlePointerClick(eventData);
}
}
public void OnPointerEnter(PointerEventData eventData)
{
if ((activeInteractionFlags & ItemInteractionFlags.PointerEnter) != 0)
{
interactionHost?.HandlePointerEnter(eventData);
}
}
public void OnPointerExit(PointerEventData eventData)
{
if ((activeInteractionFlags & ItemInteractionFlags.PointerExit) != 0)
{
interactionHost?.HandlePointerExit(eventData);
}
}
public void OnSelect(BaseEventData eventData)
{
2026-04-13 15:45:34 +08:00
#if UX_NAVIGATION
#if INPUTSYSTEM_SUPPORT
UXNavigationRuntime.NotifySelection(gameObject);
#endif
if ((activeInteractionFlags & ItemInteractionFlags.Select) != 0)
{
interactionHost?.HandleSelect(eventData);
}
2026-04-13 15:45:34 +08:00
#endif
}
public void OnDeselect(BaseEventData eventData)
{
2026-04-13 15:45:34 +08:00
#if UX_NAVIGATION
if ((activeInteractionFlags & ItemInteractionFlags.Deselect) != 0)
{
interactionHost?.HandleDeselect(eventData);
}
2026-04-13 15:45:34 +08:00
#endif
}
public void OnMove(AxisEventData eventData)
{
2026-04-13 15:45:34 +08:00
#if UX_NAVIGATION
if ((activeInteractionFlags & ItemInteractionFlags.Move) != 0)
{
interactionHost?.HandleMove(eventData);
}
2026-04-13 15:45:34 +08:00
#endif
}
public void OnBeginDrag(PointerEventData eventData)
{
if ((activeInteractionFlags & ItemInteractionFlags.BeginDrag) != 0)
{
interactionHost?.HandleBeginDrag(eventData);
return;
}
parentScroller?.OnBeginDrag(eventData);
}
public void OnDrag(PointerEventData eventData)
{
if ((activeInteractionFlags & ItemInteractionFlags.Drag) != 0)
{
interactionHost?.HandleDrag(eventData);
return;
}
parentScroller?.OnDrag(eventData);
}
public void OnEndDrag(PointerEventData eventData)
{
if ((activeInteractionFlags & ItemInteractionFlags.EndDrag) != 0)
{
interactionHost?.HandleEndDrag(eventData);
return;
}
parentScroller?.OnEndDrag(eventData);
}
public void OnSubmit(BaseEventData eventData)
{
2026-04-13 15:45:34 +08:00
#if UX_NAVIGATION
if ((activeInteractionFlags & ItemInteractionFlags.Submit) != 0)
{
interactionHost?.HandleSubmit(eventData);
}
2026-04-13 15:45:34 +08:00
#endif
}
public void OnCancel(BaseEventData eventData)
{
2026-04-13 15:45:34 +08:00
#if UX_NAVIGATION
if ((activeInteractionFlags & ItemInteractionFlags.Cancel) != 0)
{
interactionHost?.HandleCancel(eventData);
}
2026-04-13 15:45:34 +08:00
#endif
}
private void EnsureFocusAnchor()
{
if (focusAnchor != null)
{
return;
}
focusAnchor = GetComponent<Selectable>();
2026-04-13 15:45:34 +08:00
#if !UX_NAVIGATION
if (focusAnchor is RecyclerItemSelectable)
{
focusAnchor = null;
}
#endif
if (focusAnchor != null)
{
return;
}
2026-04-13 15:45:34 +08:00
#if UX_NAVIGATION
ownedSelectable = GetComponent<RecyclerItemSelectable>();
#if UNITY_EDITOR || DEVELOPMENT_BUILD
if (ownedSelectable == null && RequiresSelection(activeInteractionFlags) && !missingSelectableLogged)
{
missingSelectableLogged = true;
UnityEngine.Debug.LogError(ZString.Format("RecyclerItemSelectable is missing on '{0}'. Add it in prefab/editor setup.", GetHierarchyPath(transform)));
}
#endif
focusAnchor = ownedSelectable;
2026-04-13 15:45:34 +08:00
#endif
}
private bool TryGetInteractionFocusTarget(out GameObject target)
2026-04-03 15:21:13 +08:00
{
target = null;
2026-04-03 15:21:13 +08:00
EnsureFocusAnchor();
if (IsSelectableFocusable(focusAnchor))
{
target = focusAnchor.gameObject;
return target != null;
2026-04-03 15:21:13 +08:00
}
if (selectableCache.Count == 0)
2026-04-03 15:21:13 +08:00
{
RefreshInteractionCache();
}
for (int i = 0; i < selectableCache.Count; i++)
{
Selectable candidate = selectableCache[i];
2026-04-03 15:21:13 +08:00
if (candidate == focusAnchor)
{
continue;
}
2026-04-13 15:45:34 +08:00
#if !UX_NAVIGATION
if (candidate is RecyclerItemSelectable)
{
continue;
}
#endif
2026-04-03 15:21:13 +08:00
if (IsSelectableFocusable(candidate))
{
target = candidate.gameObject;
return target != null;
2026-04-03 15:21:13 +08:00
}
}
return false;
}
private static bool RequiresSelection(ItemInteractionFlags interactionFlags)
{
2026-04-13 15:45:34 +08:00
#if !UX_NAVIGATION
return false;
#else
const ItemInteractionFlags selectionFlags =
ItemInteractionFlags.Select |
ItemInteractionFlags.Deselect |
ItemInteractionFlags.Move |
ItemInteractionFlags.Submit |
ItemInteractionFlags.Cancel;
return (interactionFlags & selectionFlags) != 0;
2026-04-13 15:45:34 +08:00
#endif
}
#if UNITY_EDITOR || DEVELOPMENT_BUILD
private static string GetHierarchyPath(Transform target)
{
if (target == null)
{
return "<null>";
}
string path = target.name;
Transform parent = target.parent;
while (parent != null)
{
path = ZString.Format("{0}/{1}", parent.name, path);
parent = parent.parent;
}
return path;
}
#endif
[System.Diagnostics.Conditional("INPUTSYSTEM_SUPPORT")]
private void InvalidateNavigationScope()
{
#if INPUTSYSTEM_SUPPORT && UX_NAVIGATION
var scope = GetComponentInParent<UnityEngine.UI.UXNavigationScope>(true);
scope?.InvalidateSelectableCache();
#endif
}
}
}