HKMBFZ/Assets/Scripts/Szz_Scripts/UI/CustomUI/FocusManager.cs

347 lines
11 KiB
C#

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
}