using UnityEngine; using UnityEngine.UI; using TMPro; using System.Collections; using System.Runtime.InteropServices; public class FocusManager : MonoBehaviour { // 单例实例 public static FocusManager Instance { get; private set; } [Header("导航按钮")] public Button nextFocusButton; public Button previousFocusButton; public Button firstFocusButton; public Button lastFocusButton; [Header("下拉菜单控制按钮")] public Button dropdownToggleButton; public Button dropdownNextButton; public Button dropdownPreviousButton; public Button dropdownSubmitButton; [Header("状态显示")] public TextMeshProUGUI dropdownStatusText; public TextMeshProUGUI pageStatusText; public TextMeshProUGUI inputStatusText; [Header("调试")] public TextMeshProUGUI debugText; private FocusPage currentPage; private bool isProcessingInput = false; // 防止重复输入 void Awake() { if (Instance == null) { Instance = this; DontDestroyOnLoad(gameObject); } else { Destroy(gameObject); return; } SetupButtonListeners(); Debug.Log("FocusManager 初始化完成"); } void Start() { ForceUpdateButtonStates(); UpdateStatusDisplay(); } void Update() { HandleKeyboardNavigation(); UpdateStatusDisplay(); UpdateDebugInfo(); } #region 公共方法 public void ForceUpdateButtonStates() { UpdateButtonStates(); } #endregion #region 页面管理 public void SetCurrentPage(FocusPage page) { if (page == null) return; if (currentPage != null) { currentPage.ClearFocus(); } currentPage = page; Debug.Log($"当前焦点页面更新为: {page.gameObject.name}"); ForceUpdateButtonStates(); UpdateStatusDisplay(); } public FocusPage GetCurrentPage() { return currentPage; } #endregion #region 焦点导航 public void FocusNext() { if (currentPage == null || isProcessingInput) return; StartCoroutine(ProcessInputCoroutine(() => currentPage.FocusNext())); } public void FocusPrevious() { if (currentPage == null || isProcessingInput) return; StartCoroutine(ProcessInputCoroutine(() => currentPage.FocusPrevious())); } public void FocusFirst() { if (currentPage == null || isProcessingInput) return; StartCoroutine(ProcessInputCoroutine(() => currentPage.FocusFirst())); } public void FocusLast() { if (currentPage == null || isProcessingInput) return; StartCoroutine(ProcessInputCoroutine(() => currentPage.FocusLast())); } public void SafeFocusNext() { if (currentPage == null || isProcessingInput) return; StartCoroutine(ProcessInputCoroutine(() => currentPage.TryMoveToNext())); } public void SafeFocusPrevious() { if (currentPage == null || isProcessingInput) return; StartCoroutine(ProcessInputCoroutine(() => currentPage.TryMoveToPrevious())); } private IEnumerator ProcessInputCoroutine(System.Action action) { if (isProcessingInput) yield break; isProcessingInput = true; action?.Invoke(); yield return new WaitForEndOfFrame(); isProcessingInput = false; ForceUpdateButtonStates(); } #endregion #region 自定义组件控制 public void ActivateCurrentInput() { currentPage?.ActivateCurrentInputField(); } public void DeactivateCurrentInput() { currentPage?.DeactivateCurrentInputField(); } public void InputText(string text) { var inputField = currentPage?.GetCurrentCustomInputField(); if (inputField != null) { TMPInputSimulator.InsertText(inputField, text); } } public void DeleteText() { var inputField = currentPage?.GetCurrentCustomInputField(); if (inputField != null) { TMPInputSimulator.Backspace(inputField); } } #endregion #region 下拉菜单控制 public void DropdownToggle() { if (isProcessingInput) return; StartCoroutine(ProcessInputCoroutine(() => currentPage?.DropdownToggle())); } public void DropdownSelectNext() { if (isProcessingInput) return; StartCoroutine(ProcessInputCoroutine(() => currentPage?.DropdownSelectNext())); } public void DropdownSelectPrevious() { if (isProcessingInput) return; StartCoroutine(ProcessInputCoroutine(() => currentPage?.DropdownSelectPrevious())); } public void DropdownSubmit() { if (isProcessingInput) return; StartCoroutine(ProcessInputCoroutine(() => currentPage?.DropdownSubmit())); } #endregion #region 按钮管理和状态显示 private void SetupButtonListeners() { if (nextFocusButton != null) nextFocusButton.onClick.AddListener(FocusNext); if (previousFocusButton != null) previousFocusButton.onClick.AddListener(FocusPrevious); if (firstFocusButton != null) firstFocusButton.onClick.AddListener(FocusFirst); if (lastFocusButton != null) lastFocusButton.onClick.AddListener(FocusLast); if (dropdownToggleButton != null) dropdownToggleButton.onClick.AddListener(DropdownToggle); if (dropdownNextButton != null) dropdownNextButton.onClick.AddListener(DropdownSelectNext); if (dropdownPreviousButton != null) dropdownPreviousButton.onClick.AddListener(DropdownSelectPrevious); if (dropdownSubmitButton != null) dropdownSubmitButton.onClick.AddListener(DropdownSubmit); } private void UpdateButtonStates() { if (currentPage == null) { SetAllButtonsInteractable(false); return; } bool isDropdownExpanded = currentPage.IsDropdownExpanded(); bool isOnDropdown = currentPage.IsOnDropdown(); bool hasInputFocus = currentPage.HasActiveInputField(); if (previousFocusButton != null) previousFocusButton.interactable = currentPage.HasPreviousFocus && !isDropdownExpanded && !hasInputFocus && !isProcessingInput; if (nextFocusButton != null) nextFocusButton.interactable = currentPage.HasNextFocus && !isDropdownExpanded && !hasInputFocus && !isProcessingInput; bool hasElements = currentPage.FocusCount > 0 && !isDropdownExpanded && !hasInputFocus && !isProcessingInput; if (firstFocusButton != null) firstFocusButton.interactable = hasElements; if (lastFocusButton != null) lastFocusButton.interactable = hasElements; bool dropdownButtonsEnabled = isDropdownExpanded && isOnDropdown && !hasInputFocus && !isProcessingInput; if (dropdownToggleButton != null) dropdownToggleButton.interactable = isOnDropdown && !hasInputFocus && !isProcessingInput; if (dropdownNextButton != null) dropdownNextButton.interactable = dropdownButtonsEnabled; if (dropdownPreviousButton != null) dropdownPreviousButton.interactable = dropdownButtonsEnabled; if (dropdownSubmitButton != null) dropdownSubmitButton.interactable = dropdownButtonsEnabled; } private void UpdateStatusDisplay() { if (currentPage == null) { if (dropdownStatusText != null) dropdownStatusText.text = "无活动页面"; if (pageStatusText != null) pageStatusText.text = ""; if (inputStatusText != null) inputStatusText.text = ""; return; } if (pageStatusText != null) pageStatusText.text = $"焦点: {currentPage.CurrentFocusIndex + 1}/{currentPage.FocusCount}"; if (inputStatusText != null) inputStatusText.text = currentPage.HasActiveInputField() ? "输入模式" : ""; if (dropdownStatusText != null) { if (currentPage.IsDropdownExpanded()) { dropdownStatusText.text = $"选择中: {currentPage.GetCurrentDropdownValue()}"; } else if (currentPage.IsOnDropdown()) { dropdownStatusText.text = "按[打开菜单]开始选择"; } else { dropdownStatusText.text = ""; } } } private void UpdateDebugInfo() { if (debugText != null && currentPage != null) { debugText.text = $"处理中: {isProcessingInput}\n" + $"输入焦点: {currentPage.HasActiveInputField()}\n" + $"下拉展开: {currentPage.IsDropdownExpanded()}"; } } private void SetAllButtonsInteractable(bool interactable) { if (previousFocusButton != null) previousFocusButton.interactable = interactable; if (nextFocusButton != null) nextFocusButton.interactable = interactable; if (firstFocusButton != null) firstFocusButton.interactable = interactable; if (lastFocusButton != null) lastFocusButton.interactable = interactable; if (dropdownToggleButton != null) dropdownToggleButton.interactable = interactable; if (dropdownNextButton != null) dropdownNextButton.interactable = interactable; if (dropdownPreviousButton != null) dropdownPreviousButton.interactable = interactable; if (dropdownSubmitButton != null) dropdownSubmitButton.interactable = interactable; } #endregion #region 键盘输入处理 private void HandleKeyboardNavigation() { if (currentPage == null || isProcessingInput) return; bool isDropdownExpanded = currentPage.IsDropdownExpanded(); bool hasInputFocus = currentPage.HasActiveInputField(); if (hasInputFocus) { if (Input.GetKeyDown(KeyCode.Escape)) { StartCoroutine(ProcessInputCoroutine(() => currentPage.DeactivateCurrentInputField())); } return; } if (isDropdownExpanded) { if (Input.GetKeyDown(KeyCode.UpArrow)) DropdownSelectPrevious(); if (Input.GetKeyDown(KeyCode.DownArrow)) DropdownSelectNext(); if (Input.GetKeyDown(KeyCode.Return) || Input.GetKeyDown(KeyCode.KeypadEnter)) DropdownSubmit(); if (Input.GetKeyDown(KeyCode.Space) || Input.GetKeyDown(KeyCode.Escape)) DropdownToggle(); } else { if (Input.GetKeyDown(KeyCode.Tab)) { if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift)) SafeFocusPrevious(); else SafeFocusNext(); } if (Input.GetKeyDown(KeyCode.Home)) FocusFirst(); if (Input.GetKeyDown(KeyCode.End)) FocusLast(); } } #endregion }