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(); 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(); if (focusAnchor != null) { return; } ownedSelectable = GetComponent(); if (ownedSelectable == null) { ownedSelectable = gameObject.AddComponent(); } focusAnchor = ownedSelectable; } private bool TryGetFocusableSelectable(out Selectable selectable) { EnsureFocusAnchor(); if (IsSelectableFocusable(focusAnchor)) { selectable = focusAnchor; return true; } Selectable[] selectables = GetComponentsInChildren(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(true); scope?.InvalidateSelectableCache(); #endif } } }