com.alicizax.unity.ui.exten.../Runtime/RecyclerView/Interaction/ItemInteractionProxy.cs
2026-04-03 15:21:13 +08:00

254 lines
7.0 KiB
C#

using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
namespace AlicizaX.UI
{
[DisallowMultipleComponent]
public sealed class ItemInteractionProxy : MonoBehaviour,
IPointerClickHandler,
IPointerEnterHandler,
IPointerExitHandler,
ISelectHandler,
IDeselectHandler,
IMoveHandler,
IBeginDragHandler,
IDragHandler,
IEndDragHandler,
ISubmitHandler,
ICancelHandler
{
private IItemInteractionHost host;
private ItemInteractionFlags flags;
private Selectable focusAnchor;
private RecyclerItemSelectable ownedSelectable;
private Scroller parentScroller;
internal void Bind(IItemInteractionHost interactionHost)
{
host = interactionHost;
flags = interactionHost?.InteractionFlags ?? ItemInteractionFlags.None;
parentScroller = GetComponentInParent<Scroller>();
EnsureFocusAnchor();
if (ownedSelectable != null)
{
bool requiresSelection = RequiresSelection(flags);
ownedSelectable.interactable = requiresSelection;
ownedSelectable.enabled = requiresSelection;
}
InvalidateNavigationScope();
}
public void Clear()
{
host = null;
flags = ItemInteractionFlags.None;
parentScroller = null;
if (ownedSelectable != null)
{
ownedSelectable.interactable = false;
ownedSelectable.enabled = false;
}
InvalidateNavigationScope();
}
public Selectable GetSelectable()
{
EnsureFocusAnchor();
return focusAnchor;
}
internal bool TryGetFocusTarget(out GameObject target)
{
target = null;
if (!TryGetFocusableSelectable(out Selectable selectable))
{
return false;
}
target = selectable.gameObject;
return target != null;
}
public void OnPointerClick(PointerEventData eventData)
{
if ((flags & ItemInteractionFlags.PointerClick) != 0)
{
host?.HandlePointerClick(eventData);
}
}
public void OnPointerEnter(PointerEventData eventData)
{
if ((flags & ItemInteractionFlags.PointerEnter) != 0)
{
host?.HandlePointerEnter(eventData);
}
}
public void OnPointerExit(PointerEventData eventData)
{
if ((flags & ItemInteractionFlags.PointerExit) != 0)
{
host?.HandlePointerExit(eventData);
}
}
public void OnSelect(BaseEventData eventData)
{
if ((flags & ItemInteractionFlags.Select) != 0)
{
host?.HandleSelect(eventData);
}
}
public void OnDeselect(BaseEventData eventData)
{
if ((flags & ItemInteractionFlags.Deselect) != 0)
{
host?.HandleDeselect(eventData);
}
}
public void OnMove(AxisEventData eventData)
{
if ((flags & ItemInteractionFlags.Move) != 0)
{
host?.HandleMove(eventData);
}
}
public void OnBeginDrag(PointerEventData eventData)
{
if ((flags & ItemInteractionFlags.BeginDrag) != 0)
{
host?.HandleBeginDrag(eventData);
return;
}
parentScroller?.OnBeginDrag(eventData);
}
public void OnDrag(PointerEventData eventData)
{
if ((flags & ItemInteractionFlags.Drag) != 0)
{
host?.HandleDrag(eventData);
return;
}
parentScroller?.OnDrag(eventData);
}
public void OnEndDrag(PointerEventData eventData)
{
if ((flags & ItemInteractionFlags.EndDrag) != 0)
{
host?.HandleEndDrag(eventData);
return;
}
parentScroller?.OnEndDrag(eventData);
}
public void OnSubmit(BaseEventData eventData)
{
if ((flags & ItemInteractionFlags.Submit) != 0)
{
host?.HandleSubmit(eventData);
}
}
public void OnCancel(BaseEventData eventData)
{
if ((flags & ItemInteractionFlags.Cancel) != 0)
{
host?.HandleCancel(eventData);
}
}
private void EnsureFocusAnchor()
{
if (focusAnchor != null)
{
return;
}
focusAnchor = GetComponent<Selectable>();
if (focusAnchor != null)
{
return;
}
ownedSelectable = GetComponent<RecyclerItemSelectable>();
if (ownedSelectable == null)
{
ownedSelectable = gameObject.AddComponent<RecyclerItemSelectable>();
}
focusAnchor = ownedSelectable;
}
private bool TryGetFocusableSelectable(out Selectable selectable)
{
EnsureFocusAnchor();
if (IsSelectableFocusable(focusAnchor))
{
selectable = focusAnchor;
return true;
}
Selectable[] selectables = GetComponentsInChildren<Selectable>(true);
for (int i = 0; i < selectables.Length; i++)
{
Selectable candidate = selectables[i];
if (candidate == focusAnchor)
{
continue;
}
if (IsSelectableFocusable(candidate))
{
selectable = candidate;
return true;
}
}
selectable = null;
return false;
}
private static bool IsSelectableFocusable(Selectable selectable)
{
return selectable != null &&
selectable.IsActive() &&
selectable.IsInteractable();
}
private static bool RequiresSelection(ItemInteractionFlags interactionFlags)
{
const ItemInteractionFlags selectionFlags =
ItemInteractionFlags.Select |
ItemInteractionFlags.Deselect |
ItemInteractionFlags.Move |
ItemInteractionFlags.Submit |
ItemInteractionFlags.Cancel;
return (interactionFlags & selectionFlags) != 0;
}
[System.Diagnostics.Conditional("INPUTSYSTEM_SUPPORT")]
private void InvalidateNavigationScope()
{
#if INPUTSYSTEM_SUPPORT && UX_NAVIGATION
var scope = GetComponentInParent<UnityEngine.UI.UXNavigationScope>(true);
scope?.InvalidateSelectableCache();
#endif
}
}
}