226 lines
9.4 KiB
C#
226 lines
9.4 KiB
C#
// ---------------- FocusManager ----------------
|
||
using UnityEngine;
|
||
using UnityEngine.UI;
|
||
using TMPro;
|
||
using System.Collections;
|
||
|
||
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;
|
||
|
||
public FocusPage currentPage;
|
||
private bool isProcessingInput = false;
|
||
|
||
void Awake()
|
||
{
|
||
if (Instance == null) { Instance = this;}
|
||
//else { Destroy(gameObject); return; }
|
||
SetupButtonListeners();
|
||
}
|
||
|
||
void Update()
|
||
{
|
||
HandleKeyboardNavigation();
|
||
UpdateStatusDisplay();
|
||
UpdateDebugInfo();
|
||
}
|
||
|
||
public void ForceUpdateButtonStates() => UpdateButtonStates();
|
||
|
||
public void SetCurrentPage(FocusPage page)
|
||
{
|
||
if (page == null) return;
|
||
currentPage?.ClearFocus();
|
||
currentPage = page;
|
||
ForceUpdateButtonStates();
|
||
UpdateStatusDisplay();
|
||
}
|
||
|
||
public FocusPage GetCurrentPage() => currentPage;
|
||
|
||
#region 焦点导航
|
||
public void FocusNext() { if (currentPage == null || isProcessingInput || currentPage.focusDisabled) return; StartCoroutine(ProcessInputCoroutine(() => currentPage.FocusNext())); }
|
||
public void FocusPrevious() { if (currentPage == null || isProcessingInput || currentPage.focusDisabled) return; StartCoroutine(ProcessInputCoroutine(() => currentPage.FocusPrevious())); }
|
||
public void FocusFirst() { if (currentPage == null || isProcessingInput || currentPage.focusDisabled) return; StartCoroutine(ProcessInputCoroutine(() => currentPage.FocusFirst())); }
|
||
public void FocusLast() { if (currentPage == null || isProcessingInput|| currentPage.focusDisabled) return; StartCoroutine(ProcessInputCoroutine(() => currentPage.FocusLast())); }
|
||
|
||
public void DropdownSelectNext() { if (currentPage == null || isProcessingInput || currentPage.focusDisabled) return; StartCoroutine(ProcessInputCoroutine(() => currentPage.DropdownSelectNext())); }
|
||
public void DropdownSelectPrevious() { if (currentPage == null || isProcessingInput || currentPage.focusDisabled) return; StartCoroutine(ProcessInputCoroutine(() => currentPage.DropdownSelectPrevious())); }
|
||
public void DropdownSubmit() { if (currentPage == null || isProcessingInput || currentPage.focusDisabled) return; StartCoroutine(ProcessInputCoroutine(() => currentPage.DropdownSubmit())); }
|
||
public void DropdownToggle() { if (currentPage == null || isProcessingInput || currentPage.focusDisabled) return; StartCoroutine(ProcessInputCoroutine(() => currentPage.DropdownToggle())); }
|
||
|
||
private IEnumerator ProcessInputCoroutine(System.Action action)
|
||
{
|
||
if (isProcessingInput) yield break;
|
||
isProcessingInput = true;
|
||
|
||
action?.Invoke();
|
||
|
||
// 不要等到 EndOfFrame
|
||
yield return null;
|
||
|
||
isProcessingInput = false;
|
||
UpdateButtonStates();
|
||
}
|
||
#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;
|
||
|
||
// Submit可用条件
|
||
if (dropdownToggleButton != null) dropdownToggleButton.interactable = isOnDropdown && !hasInputFocus && !isProcessingInput;
|
||
if (dropdownNextButton != null) dropdownNextButton.interactable = isDropdownExpanded && !hasInputFocus && !isProcessingInput;
|
||
if (dropdownPreviousButton != null) dropdownPreviousButton.interactable = isDropdownExpanded && !hasInputFocus && !isProcessingInput;
|
||
if (dropdownSubmitButton != null) dropdownSubmitButton.interactable = (isDropdownExpanded || isOnDropdown) && !hasInputFocus && !isProcessingInput;
|
||
}
|
||
|
||
|
||
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 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()}";
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
#region 键盘导航
|
||
private void HandleKeyboardNavigation()
|
||
{
|
||
if (currentPage == null) return;
|
||
|
||
bool hasInputFocus = currentPage.HasActiveInputField();
|
||
|
||
if (hasInputFocus)
|
||
{
|
||
if (Input.GetKeyDown(KeyCode.Escape))
|
||
StartCoroutine(ProcessInputCoroutine(() => currentPage.DeactivateCurrentInputField()));
|
||
return;
|
||
}
|
||
|
||
// Dropdown键盘操作,直接调用
|
||
if (currentPage.IsOnDropdown())
|
||
{
|
||
if (Input.GetKeyDown(KeyCode.UpArrow)) currentPage.DropdownSelectPrevious();
|
||
if (Input.GetKeyDown(KeyCode.DownArrow)) currentPage.DropdownSelectNext();
|
||
|
||
//if (Input.GetKeyDown(KeyCode.Return) || Input.GetKeyDown(KeyCode.KeypadEnter))
|
||
// currentPage.DropdownSubmit(); // 内部判断是打开还是提交
|
||
|
||
//if (Input.GetKeyDown(KeyCode.Space) || Input.GetKeyDown(KeyCode.Escape))
|
||
// currentPage.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
|
||
}
|