using System;
using System.Reflection;
using System.Text;
using SLSUtilities.General;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
namespace Cielonos.Settings.UI
{
///
/// 设置条目 UI 的抽象基类。
///
/// 持有对设置实例和字段的反射引用,子类负责具体的 UI 交互逻辑。
/// 通过 绑定到指定设置实例的字段后,
/// 子类在 中配置 UI 组件,
/// 在 中同步字段值到 UI 显示。
///
///
/// 支持鼠标悬停时通过 /
/// 通知父级显示/隐藏右侧说明面板。
///
///
public abstract class SettingsEntryBase : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler
{
[SerializeField] protected TMP_Text labelText;
protected object settingsInstance;
protected FieldInfo fieldInfo;
protected Action onValueChanged;
/// 条目的显示标题。
public string DisplayLabel { get; protected set; }
/// 条目的说明文本(可为空,为空时悬停不触发说明面板)。
public string Description { get; protected set; }
/// 鼠标悬停进入时触发,参数为 (标题, 说明文本)。由 SettingsUIPage 设置。
public Action OnHoverEnter { get; set; }
/// 鼠标悬停离开时触发。由 SettingsUIPage 设置。
public Action OnHoverExit { get; set; }
///
/// 初始化设置条目,绑定到指定设置实例的字段。
///
/// 设置类实例(如 GameplaySettings)。
/// 要绑定的字段反射信息。
/// 字段值变更时的回调。
public virtual void Initialize(object instance, FieldInfo field, Action onChanged)
{
settingsInstance = instance;
fieldInfo = field;
onValueChanged = onChanged;
// 优先使用 SettingsDisplayAttribute 提供的本地化键
var displayAttr = field.GetCustomAttribute();
if (displayAttr != null)
{
string localized = displayAttr.LabelKey.Localize(displayAttr.TableName);
DisplayLabel = !string.IsNullOrEmpty(localized) ? localized : displayAttr.LabelKey;
if (!string.IsNullOrEmpty(displayAttr.DescriptionKey))
{
string localizedDesc = displayAttr.DescriptionKey.Localize(displayAttr.TableName);
Description = !string.IsNullOrEmpty(localizedDesc)
? localizedDesc
: displayAttr.DescriptionKey;
}
}
else
{
DisplayLabel = FormatFieldName(field.Name);
}
SetLabel(DisplayLabel);
SetupUI();
RefreshValue();
}
///
/// 子类实现:配置 UI 组件(如 Slider 的范围、Dropdown 的选项)。
/// 在 中于 之前调用。
///
protected abstract void SetupUI();
///
/// 子类实现:从字段读取当前值并更新 UI 显示。
///
public abstract void RefreshValue();
///
/// 将值写回设置字段,并通知外部值已变更。
///
protected void SetFieldValue(object value)
{
fieldInfo.SetValue(settingsInstance, value);
onValueChanged?.Invoke();
}
///
/// 从设置字段读取当前值。
///
protected object GetFieldValue()
{
return fieldInfo.GetValue(settingsInstance);
}
protected void SetLabel(string text)
{
if (labelText != null)
labelText.text = text;
}
// ──────────────────── 悬停事件 ────────────────────
public void OnPointerEnter(PointerEventData eventData)
{
OnHoverEnter?.Invoke(DisplayLabel, Description);
}
public void OnPointerExit(PointerEventData eventData)
{
OnHoverExit?.Invoke();
}
// ──────────────────── 工具方法 ────────────────────
///
/// 将 camelCase 字段名转换为可读格式。
///
/// cameraSensitivityX → "Camera Sensitivity X"
/// showFPS → "Show FPS"
/// invertYAxis → "Invert Y Axis"
///
///
protected static string FormatFieldName(string fieldName)
{
if (string.IsNullOrEmpty(fieldName)) return fieldName;
var sb = new StringBuilder(fieldName.Length + 4);
sb.Append(char.ToUpper(fieldName[0]));
for (int i = 1; i < fieldName.Length; i++)
{
char c = fieldName[i];
if (char.IsUpper(c))
{
bool prevIsLower = char.IsLower(fieldName[i - 1]);
bool nextIsLower = i + 1 < fieldName.Length && char.IsLower(fieldName[i + 1]);
if (prevIsLower || nextIsLower)
sb.Append(' ');
}
sb.Append(c);
}
return sb.ToString();
}
}
}