DCS/ruiyiweiUX/Assets/Scripts/UI/TabNavigationSystem.cs

369 lines
9.7 KiB
C#
Raw Normal View History

2026-06-09 13:59:11 +08:00
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
/// <summary>
/// Tab键导航系统
/// 自动管理InputField之间的Tab键切换
/// </summary>
public class TabNavigationSystem : MonoBehaviour
{
[Header("导航设置")]
public List<Selectable> NavigationOrder = new List<Selectable>(); // 导航顺序
public bool AutoDetectInputFields = true; // 自动检测InputField
public bool WrapNavigation = true; // 是否循环导航
public bool EnableShiftReverse = true; // 是否支持Shift+Tab反向导航
[Header("调试")]
public bool ShowDebugInfo = false;
private int _currentIndex = -1;
private EventSystem _eventSystem;
void Start()
{
Initialize();
}
void Update()
{
HandleTabInput();
}
private void Initialize()
{
_eventSystem = EventSystem.current;
if (AutoDetectInputFields)
{
AutoDetectNavigationElements();
}
// 设置初始焦点
if (NavigationOrder.Count > 0)
{
SetFocus(0);
}
if (ShowDebugInfo)
{
Debug.Log($"TabNavigationSystem: 初始化完成,找到 {NavigationOrder.Count} 个可导航元素");
}
}
/// <summary>
/// 自动检测可导航元素
/// </summary>
private void AutoDetectNavigationElements()
{
NavigationOrder.Clear();
// 查找所有InputField
var inputFields = FindObjectsOfType<TMP_InputField>();
foreach (var inputField in inputFields)
{
if (inputField.interactable && inputField.gameObject.activeInHierarchy)
{
NavigationOrder.Add(inputField);
}
}
// 查找所有传统InputField
var legacyInputFields = FindObjectsOfType<InputField>();
foreach (var inputField in legacyInputFields)
{
if (inputField.interactable && inputField.gameObject.activeInHierarchy)
{
NavigationOrder.Add(inputField);
}
}
// 根据层级顺序排序
NavigationOrder.Sort((a, b) =>
{
// 先按Y坐标排序从上到下
var posA = a.transform.position;
var posB = b.transform.position;
if (Mathf.Abs(posA.y - posB.y) > 0.1f)
{
return posB.y.CompareTo(posA.y); // Y坐标大的在前上面的先
}
// Y坐标相近时按X坐标排序从左到右
return posA.x.CompareTo(posB.x);
});
}
/// <summary>
/// 处理Tab键输入
/// </summary>
private void HandleTabInput()
{
if (NavigationOrder.Count == 0) return;
// 检查Tab键
if (Input.GetKeyDown(KeyCode.Tab))
{
bool reverse = EnableShiftReverse && (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift));
if (reverse)
{
NavigatePrevious();
}
else
{
NavigateNext();
}
}
// 检查Enter键移动到下一个
if (Input.GetKeyDown(KeyCode.Return) || Input.GetKeyDown(KeyCode.KeypadEnter))
{
// 如果当前焦点是InputField则移动到下一个
var currentSelection = _eventSystem?.currentSelectedGameObject;
if (currentSelection != null)
{
var inputField = currentSelection.GetComponent<TMP_InputField>();
var legacyInputField = currentSelection.GetComponent<InputField>();
if (inputField != null || legacyInputField != null)
{
NavigateNext();
}
}
}
}
/// <summary>
/// 导航到下一个元素
/// </summary>
public void NavigateNext()
{
if (NavigationOrder.Count == 0) return;
_currentIndex++;
if (WrapNavigation)
{
_currentIndex %= NavigationOrder.Count;
}
else
{
_currentIndex = Mathf.Min(_currentIndex, NavigationOrder.Count - 1);
}
SetFocus(_currentIndex);
}
/// <summary>
/// 导航到上一个元素
/// </summary>
public void NavigatePrevious()
{
if (NavigationOrder.Count == 0) return;
_currentIndex--;
if (WrapNavigation)
{
if (_currentIndex < 0)
{
_currentIndex = NavigationOrder.Count - 1;
}
}
else
{
_currentIndex = Mathf.Max(_currentIndex, 0);
}
SetFocus(_currentIndex);
}
/// <summary>
/// 设置焦点到指定索引的元素
/// </summary>
public void SetFocus(int index)
{
if (index < 0 || index >= NavigationOrder.Count) return;
if (_eventSystem == null) return;
var target = NavigationOrder[index];
if (target == null || !target.interactable || !target.gameObject.activeInHierarchy)
{
// 跳过不可用的元素
if (WrapNavigation)
{
NavigateNext();
}
return;
}
_currentIndex = index;
// 设置选中
_eventSystem.SetSelectedGameObject(target.gameObject);
// 如果是InputField激活输入
var inputField = target.GetComponent<TMP_InputField>();
if (inputField != null)
{
inputField.Select();
inputField.ActivateInputField();
}
else
{
var legacyInputField = target.GetComponent<InputField>();
if (legacyInputField != null)
{
legacyInputField.Select();
legacyInputField.ActivateInputField();
}
}
if (ShowDebugInfo)
{
Debug.Log($"TabNavigationSystem: 焦点切换到 {target.name} (索引: {index})");
}
}
/// <summary>
/// 手动添加导航元素
/// </summary>
public void AddNavigationElement(Selectable element)
{
if (element != null && !NavigationOrder.Contains(element))
{
NavigationOrder.Add(element);
}
}
/// <summary>
/// 移除导航元素
/// </summary>
public void RemoveNavigationElement(Selectable element)
{
if (NavigationOrder.Contains(element))
{
int index = NavigationOrder.IndexOf(element);
NavigationOrder.Remove(element);
// 调整当前索引
if (_currentIndex >= index && _currentIndex > 0)
{
_currentIndex--;
}
}
}
/// <summary>
/// 刷新导航列表
/// </summary>
public void RefreshNavigation()
{
if (AutoDetectInputFields)
{
AutoDetectNavigationElements();
}
}
/// <summary>
/// 获取当前焦点元素
/// </summary>
public Selectable GetCurrentElement()
{
if (_currentIndex >= 0 && _currentIndex < NavigationOrder.Count)
{
return NavigationOrder[_currentIndex];
}
return null;
}
/// <summary>
/// 获取当前焦点索引
/// </summary>
public int GetCurrentIndex()
{
return _currentIndex;
}
/// <summary>
/// 跳转到指定元素
/// </summary>
public void FocusElement(Selectable element)
{
int index = NavigationOrder.IndexOf(element);
if (index >= 0)
{
SetFocus(index);
}
}
}
/// <summary>
/// Tab导航辅助组件
/// 可以附加到单独的InputField上提供更精细的控制
/// </summary>
public class TabNavigationHelper : MonoBehaviour
{
[Header("导航设置")]
public Selectable NextElement; // 下一个元素
public Selectable PreviousElement; // 上一个元素
public bool SkipThisElement = false; // 是否跳过此元素
private Selectable _thisSelectable;
void Start()
{
_thisSelectable = GetComponent<Selectable>();
}
void Update()
{
if (SkipThisElement) return;
// 检查是否是当前选中的元素
if (EventSystem.current?.currentSelectedGameObject == gameObject)
{
if (Input.GetKeyDown(KeyCode.Tab))
{
bool reverse = Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift);
if (reverse && PreviousElement != null)
{
FocusElement(PreviousElement);
}
else if (!reverse && NextElement != null)
{
FocusElement(NextElement);
}
}
}
}
private void FocusElement(Selectable element)
{
if (element != null && element.interactable && element.gameObject.activeInHierarchy)
{
EventSystem.current?.SetSelectedGameObject(element.gameObject);
var inputField = element.GetComponent<TMP_InputField>();
if (inputField != null)
{
inputField.Select();
inputField.ActivateInputField();
}
else
{
var legacyInputField = element.GetComponent<InputField>();
if (legacyInputField != null)
{
legacyInputField.Select();
legacyInputField.ActivateInputField();
}
}
}
}
}