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

354 lines
12 KiB
C#

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using TMPro;
public class FocusPage : MonoBehaviour
{
[Header("页面焦点元素")]
public List<Selectable> focusElements = new List<Selectable>();
//有些元素要提前录入
public List<Selectable> focusElementStart = new List<Selectable>();
private int currentFocusIndex = -1;
private Coroutine focusCoroutine;
private bool isSwitchingFocus = false;
private CustomTMPInputField currentCustomInputField;
private CustomTMPDropdown currentCustomTMPDropdown;
private bool isDropdownExpanded = false;
public int FocusCount => focusElements.Count;
public int CurrentFocusIndex => currentFocusIndex;
public Selectable CurrentFocus => GetCurrentFocusElement();
public bool HasPreviousFocus => currentFocusIndex > 0;
public bool HasNextFocus => currentFocusIndex < FocusCount - 1;
public bool isNeedStartShow = false;
void OnEnable()
{
FocusManager.Instance?.SetCurrentPage(this);
// 清除失效的引用
focusElements.RemoveAll(e => e == null);
ValidateFocusElements();
StartCoroutine(DelayedFindCustomComponents());
}
public void UpdateFouceByClick(Selectable selectable)
{
int index= focusElements.IndexOf(selectable);
SetFocusImmediate(index);
}
void OnDisable()
{
if (FocusManager.Instance?.GetCurrentPage() == this) ClearFocus();
}
private IEnumerator DelayedFindCustomComponents()
{
// 等一帧,保证子对象 Awake / OnEnable 执行完
yield return null;
FindCustomComponents();
}
private void FindCustomComponents()
{
focusElements.Clear();
if (focusElementStart.Count != 0)
{
foreach (var item in focusElementStart)
{
focusElements.Add(item);
}
}
// 获取所有 Selectable (包括隐藏)
Selectable[] allSelectables = GetComponentsInChildren<Selectable>(true);
foreach (var sel in allSelectables)
{
if (sel == null) continue;
// 必须是明确的自定义控件
bool isCustomInput = sel.GetComponent<CustomTMPInputField>() != null;
bool isCustomDropdown = sel.GetComponent<CustomTMPDropdown>() != null;
if (isCustomInput || isCustomDropdown)
focusElements.Add(sel);
}
if (FocusCount > 0&& isNeedStartShow) SetFocusImmediate(0);
}
void Update()
{
CheckDropdownState();
}
public bool focusDisabled = false;
private GameObject lastFocusedObj = null;
public void DisableFocus()
{
focusDisabled = true;
// 取消 EventSystem 选中
EventSystem.current.SetSelectedGameObject(null);
// 清除所有高亮
foreach (var sel in focusElements)
{
var ol = sel ? sel.GetComponent<Outline>() : null;
if (ol != null) ol.enabled = false;
}
}
public void EnableFocus()
{
focusDisabled = false;
if (lastFocusedObj != null)
{
EventSystem.current.SetSelectedGameObject(lastFocusedObj);
var ol = lastFocusedObj.GetComponent<Outline>();
if (ol != null) ol.enabled = true;
}
}
#region
public void FocusNext()
{
if (focusDisabled) return;
Debug.Log(CurrentFocusIndex+ focusDisabled.ToString());
if (isSwitchingFocus) return;
if (isDropdownExpanded) { DropdownSelectNext(); return; }
if (currentFocusIndex + 1 < focusElements.Count) SetFocusDelayed(currentFocusIndex + 1);
}
public void FocusPrevious()
{
if (focusDisabled) return;
Debug.Log(CurrentFocusIndex + focusDisabled.ToString());
if (isSwitchingFocus) return;
if (isDropdownExpanded) { DropdownSelectPrevious(); return; }
if (currentFocusIndex - 1 >= 0) SetFocusDelayed(currentFocusIndex - 1);
}
public void FocusFirst() { if (focusDisabled) return; if (!isSwitchingFocus && FocusCount > 0) SetFocusDelayed(0); }
public void FocusLast() { if (focusDisabled) return; if (!isSwitchingFocus && FocusCount > 0) SetFocusDelayed(FocusCount - 1); }
public bool TryMoveToNext()
{
if (isSwitchingFocus || currentFocusIndex + 1 >= FocusCount) return false;
SetFocusDelayed(currentFocusIndex + 1);
return true;
}
public bool TryMoveToPrevious()
{
if (isSwitchingFocus || currentFocusIndex - 1 < 0) return false;
SetFocusDelayed(currentFocusIndex - 1);
return true;
}
#endregion
#region 访
public CustomTMPInputField GetCurrentCustomInputField() => currentCustomInputField;
public void ActivateCurrentInputField() { currentCustomInputField?.Activate(); }
public void DeactivateCurrentInputField() { currentCustomInputField?.Deactivate(); }
public bool HasActiveInputField() => currentCustomInputField != null && currentCustomInputField.HasFocus();
#endregion
#region Dropdown
public void DropdownSelectNext() {
//if (isDropdownExpanded) currentCustomTMPDropdown?.SelectNext();
currentCustomTMPDropdown?.FocusedSelectNext();
}
public void DropdownSelectPrevious() {
//if (isDropdownExpanded) currentCustomTMPDropdown?.SelectPrevious();
currentCustomTMPDropdown?.FocusedSelectPrevious();
}
public void DropdownSubmit() { if (focusDisabled) return; currentCustomTMPDropdown?.SubmitSelection(); }
public void DropdownToggle() { if (currentCustomTMPDropdown != null) currentCustomTMPDropdown.ToggleDropdown(); StartCoroutine(UpdateDropdownNextFrame()); }
private IEnumerator UpdateDropdownNextFrame() { yield return null; CheckDropdownState(); FocusManager.Instance?.ForceUpdateButtonStates(); }
public bool IsDropdownExpanded() => isDropdownExpanded;
public bool IsOnDropdown() => currentCustomTMPDropdown != null;
public string GetCurrentDropdownValue() => currentCustomTMPDropdown != null && currentCustomTMPDropdown.options.Count > 0 ? currentCustomTMPDropdown.options[currentCustomTMPDropdown.value].text : "";
#endregion
#region
private void SetFocusImmediate(int index)
{
if (index < 0 || index >= focusElements.Count) return;
ClearCurrentFocus();
currentFocusIndex = index;
Selectable target = focusElements[index];
lastFocusedObj = focusElements[index].gameObject;
if (EventSystem.current != null)
{
EventSystem.current.SetSelectedGameObject(target.gameObject);
HandleSpecialFocus(target, false);
}
FocusManager.Instance?.ForceUpdateButtonStates();
}
private void SetFocusDelayed(int index, float delay = 0.05f)
{
if (isSwitchingFocus)
{
isSwitchingFocus = false;
if (focusCoroutine != null) StopCoroutine(focusCoroutine);
}
//if (focusCoroutine != null) StopCoroutine(focusCoroutine);
focusCoroutine = StartCoroutine(SetFocusCoroutine(index, delay));
}
private IEnumerator SetFocusCoroutine(int index, float delay)
{
if (isSwitchingFocus) yield break;
isSwitchingFocus = true;
yield return new WaitForSecondsRealtime(delay);
if (index >= 0 && index < focusElements.Count)
{
ClearCurrentFocus();
currentFocusIndex = index;
Selectable target = focusElements[index];
if (EventSystem.current != null && EventSystem.current.currentSelectedGameObject != target.gameObject)
EventSystem.current.SetSelectedGameObject(target.gameObject);
HandleSpecialFocus(target, true);
}
isSwitchingFocus = false;
FocusManager.Instance?.ForceUpdateButtonStates();
}
private void ClearCurrentFocus()
{
currentCustomInputField?.Deactivate();
CloseDropdown();
currentCustomInputField = null;
currentCustomTMPDropdown = null;
isDropdownExpanded = false;
// 关闭所有高亮
foreach (var elem in focusElements)
{
if (elem == null) continue; // 防止 MissingReference
var ol = elem.GetComponent<Outline>();
if (ol == null) continue; // Outline 已经被销毁或不存在
ol.enabled = false;
}
}
private void CloseDropdown()
{
if (currentCustomTMPDropdown != null)
{
currentCustomTMPDropdown.CloseDropdown();
isDropdownExpanded = false;
}
}
private void HandleSpecialFocus(Selectable element, bool autoExpand)
{
// 先关闭其他高亮
foreach (var elem in focusElements)
{
if (elem != element)
{
var ol = elem.GetComponent<Outline>();
if (ol != null) ol.enabled = false;
}
}
switch (element)
{
case CustomTMPInputField customInputField:
currentCustomInputField = customInputField;
currentCustomTMPDropdown = null;
isDropdownExpanded = false;
customInputField.Activate();
SetHighlight(customInputField, true);
if (!customInputField.GetComponent<Outline>().enabled)
SetHighlight(customInputField, true);
break;
case CustomTMPDropdown customTMPDropdown:
currentCustomTMPDropdown = customTMPDropdown;
currentCustomInputField = null;
isDropdownExpanded = false; // 不自动展开
SetHighlight(customTMPDropdown, true); // 高亮
if(!customTMPDropdown.GetComponent<Outline>().enabled)
SetHighlight(customTMPDropdown, true);
Debug.Log("打开物品高亮");
break;
default:
currentCustomInputField = null;
currentCustomTMPDropdown = null;
isDropdownExpanded = false;
break;
}
FocusManager.Instance?.ForceUpdateButtonStates();
}
private void SetHighlight(Selectable element, bool highlight)
{
Outline ol = element.GetComponent<Outline>();
if (ol != null) ol.enabled = highlight;
}
private void CheckDropdownState()
{
bool wasExpanded = isDropdownExpanded;
if (currentCustomTMPDropdown != null)
{
var dropdownField = typeof(TMP_Dropdown).GetField("m_Dropdown", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
if (dropdownField != null)
{
var dropdownGO = dropdownField.GetValue(currentCustomTMPDropdown) as GameObject;
isDropdownExpanded = dropdownGO != null && dropdownGO.activeInHierarchy;
}
else isDropdownExpanded = false;
}
else isDropdownExpanded = false;
if (wasExpanded != isDropdownExpanded)
FocusManager.Instance?.ForceUpdateButtonStates();
}
private Selectable GetCurrentFocusElement() => currentFocusIndex >= 0 && currentFocusIndex < focusElements.Count ? focusElements[currentFocusIndex] : null;
public void ClearFocus()
{
ClearCurrentFocus();
currentFocusIndex = -1;
if (EventSystem.current != null) EventSystem.current.SetSelectedGameObject(null);
FocusManager.Instance?.ForceUpdateButtonStates();
}
private void ValidateFocusElements() => focusElements.RemoveAll(e => e == null || !e.interactable);
#endregion
}