com.alicizax.unity.ui.exten.../Runtime/UXComponent/Selectable/UXSelectable.cs

268 lines
9.2 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
namespace AlicizaX.UI.Extension
{
public class UXSelectable :
UIBehaviour,
IMoveHandler,
IPointerDownHandler, IPointerUpHandler,
IPointerEnterHandler, IPointerExitHandler,
ISelectHandler, IDeselectHandler
{
protected static UXSelectable[] s_Selectables = new UXSelectable[10];
protected static int s_SelectableCount = 0;
protected int m_CurrentIndex = -1;
protected bool m_EnableCalled = false;
protected bool isPointerInside { get; set; }
protected bool isPointerDown { get; set; }
protected bool hasSelection { get; set; }
protected bool m_GroupsAllowInteraction = true;
private readonly List<CanvasGroup> m_CanvasGroupCache = new List<CanvasGroup>();
[SerializeField] private UXNavigation m_Navigation = UXNavigation.defaultNavigation;
public UXNavigation navigation
{
get { return m_Navigation; }
set { if (SetPropertyUtility.SetStruct(ref m_Navigation, value)) OnSetProperty(); }
}
public static UXSelectable[] allSelectablesArray
{
get
{
UXSelectable[] temp = new UXSelectable[s_SelectableCount];
Array.Copy(s_Selectables, temp, s_SelectableCount);
return temp;
}
}
protected virtual void OnSetProperty()
{
// override if needed
}
protected override void OnEnable()
{
if (m_EnableCalled)
return;
base.OnEnable();
if (s_SelectableCount == s_Selectables.Length)
{
UXSelectable[] temp = new UXSelectable[s_Selectables.Length * 2];
Array.Copy(s_Selectables, temp, s_Selectables.Length);
s_Selectables = temp;
}
if (EventSystem.current && EventSystem.current.currentSelectedGameObject == gameObject)
{
hasSelection = true;
}
m_CurrentIndex = s_SelectableCount;
s_Selectables[m_CurrentIndex] = this;
s_SelectableCount++;
isPointerDown = false;
m_GroupsAllowInteraction = ParentGroupAllowsInteraction();
m_EnableCalled = true;
}
protected override void OnDisable()
{
if (!m_EnableCalled)
return;
s_SelectableCount--;
s_Selectables[s_SelectableCount].m_CurrentIndex = m_CurrentIndex;
s_Selectables[m_CurrentIndex] = s_Selectables[s_SelectableCount];
s_Selectables[s_SelectableCount] = null;
base.OnDisable();
m_EnableCalled = false;
}
protected void OnCanvasGroupChanged()
{
var parentGroupAllows = ParentGroupAllowsInteraction();
if (parentGroupAllows != m_GroupsAllowInteraction)
{
m_GroupsAllowInteraction = parentGroupAllows;
OnSetProperty();
}
}
protected bool ParentGroupAllowsInteraction()
{
Transform t = transform;
while (t != null)
{
t.GetComponents(m_CanvasGroupCache);
for (var i = 0; i < m_CanvasGroupCache.Count; i++)
{
if (m_CanvasGroupCache[i].enabled && !m_CanvasGroupCache[i].interactable)
return false;
if (m_CanvasGroupCache[i].ignoreParentGroups)
return true;
}
t = t.parent;
}
return true;
}
public virtual bool IsInteractable()
{
return m_GroupsAllowInteraction;
}
public UXSelectable FindSelectable(Vector3 dir)
{
dir = dir.normalized;
Vector3 localDir = Quaternion.Inverse(transform.rotation) * dir;
Vector3 pos = transform.TransformPoint(GetPointOnRectEdge(transform as RectTransform, localDir));
float maxScore = Mathf.NegativeInfinity;
float maxFurthestScore = Mathf.NegativeInfinity;
float score = 0;
bool wantsWrapAround = navigation.wrapAround && (m_Navigation.mode == UXNavigation.Mode.Vertical || m_Navigation.mode == UXNavigation.Mode.Horizontal);
UXSelectable bestPick = null;
UXSelectable bestFurthestPick = null;
for (int i = 0; i < s_SelectableCount; ++i)
{
UXSelectable sel = s_Selectables[i];
if (sel == this)
continue;
if (!sel.IsInteractable() || sel.navigation.mode == UXNavigation.Mode.None)
continue;
#if UNITY_EDITOR
if (Camera.current != null && !UnityEditor.SceneManagement.StageUtility.IsGameObjectRenderedByCamera(sel.gameObject, Camera.current))
continue;
#endif
var selRect = sel.transform as RectTransform;
Vector3 selCenter = selRect != null ? (Vector3)selRect.rect.center : Vector3.zero;
Vector3 myVector = sel.transform.TransformPoint(selCenter) - pos;
float dot = Vector3.Dot(dir, myVector);
if (wantsWrapAround && dot < 0)
{
score = -dot * myVector.sqrMagnitude;
if (score > maxFurthestScore)
{
maxFurthestScore = score;
bestFurthestPick = sel;
}
continue;
}
if (dot <= 0)
continue;
score = dot / myVector.sqrMagnitude;
if (score > maxScore)
{
maxScore = score;
bestPick = sel;
}
}
if (wantsWrapAround && null == bestPick) return bestFurthestPick;
return bestPick;
}
private static Vector3 GetPointOnRectEdge(RectTransform rect, Vector2 dir)
{
if (rect == null) return Vector3.zero;
if (dir != Vector2.zero) dir /= Mathf.Max(Mathf.Abs(dir.x), Mathf.Abs(dir.y));
dir = rect.rect.center + Vector2.Scale(rect.rect.size, dir * 0.5f);
return dir;
}
void Navigate(AxisEventData eventData, UXSelectable sel)
{
if (sel != null && sel.IsActive())
eventData.selectedObject = sel.gameObject;
}
public virtual UXSelectable FindSelectableOnLeft()
{
if (m_Navigation.mode == UXNavigation.Mode.Explicit)
return m_Navigation.selectOnLeft;
if ((m_Navigation.mode & UXNavigation.Mode.Horizontal) != 0)
return FindSelectable(transform.rotation * Vector3.left);
return null;
}
public virtual UXSelectable FindSelectableOnRight()
{
if (m_Navigation.mode == UXNavigation.Mode.Explicit)
return m_Navigation.selectOnRight;
if ((m_Navigation.mode & UXNavigation.Mode.Horizontal) != 0)
return FindSelectable(transform.rotation * Vector3.right);
return null;
}
public virtual UXSelectable FindSelectableOnUp()
{
if (m_Navigation.mode == UXNavigation.Mode.Explicit)
return m_Navigation.selectOnUp;
if ((m_Navigation.mode & UXNavigation.Mode.Vertical) != 0)
return FindSelectable(transform.rotation * Vector3.up);
return null;
}
public virtual UXSelectable FindSelectableOnDown()
{
if (m_Navigation.mode == UXNavigation.Mode.Explicit)
return m_Navigation.selectOnDown;
if ((m_Navigation.mode & UXNavigation.Mode.Vertical) != 0)
return FindSelectable(transform.rotation * Vector3.down);
return null;
}
public virtual void OnMove(AxisEventData eventData)
{
switch (eventData.moveDir)
{
case MoveDirection.Right:
Navigate(eventData, FindSelectableOnRight());
break;
case MoveDirection.Up:
Navigate(eventData, FindSelectableOnUp());
break;
case MoveDirection.Left:
Navigate(eventData, FindSelectableOnLeft());
break;
case MoveDirection.Down:
Navigate(eventData, FindSelectableOnDown());
break;
}
}
// default handlers to be overridden
public virtual void OnPointerDown(PointerEventData eventData) { }
public virtual void OnPointerUp(PointerEventData eventData) { }
public virtual void OnPointerEnter(PointerEventData eventData) { }
public virtual void OnPointerExit(PointerEventData eventData) { }
public virtual void OnSelect(BaseEventData eventData) { hasSelection = true; }
public virtual void OnDeselect(BaseEventData eventData) { hasSelection = false; }
}
}