Files
2026-05-13 20:29:56 +01:00

186 lines
5.5 KiB
C#

using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using System;
namespace BriarQueen.UI.Menus.Components
{
[RequireComponent(typeof(Button))]
public class UnderlineButton : MonoBehaviour,
IPointerEnterHandler,
IPointerExitHandler,
IPointerClickHandler,
ISelectHandler,
IDeselectHandler
{
[Header("References")]
[SerializeField] protected TextMeshProUGUI _label;
[SerializeField]
protected TextMeshProUGUI _contextLabel;
[SerializeField]
protected string _contextText;
protected Button _button;
protected UnderlineButtonGroup _group;
protected bool _isHovered;
public event Action<UnderlineButton> SelectionRequested;
public event Action<UnderlineButton> HoverEntered;
public event Action<UnderlineButton> HoverExited;
public bool IsSelected { get; private set; }
// True when this button is registered with a group
public bool IsGrouped => _group != null;
protected virtual void Awake()
{
_button = GetComponent<Button>();
SetUnderline(false);
}
protected virtual void OnDisable()
{
_isHovered = false;
SetUnderline(false);
}
// ── Group registration ────────────────────────────────────────
/// <summary>Called by UnderlineButtonGroup when this button is bound.</summary>
internal void RegisterGroup(UnderlineButtonGroup group)
{
_group = group;
}
/// <summary>Called by UnderlineButtonGroup when this button is unbound.</summary>
internal void UnregisterGroup()
{
_group = null;
}
// ── Pointer / Selection events ────────────────────────────────
public void OnPointerEnter(PointerEventData eventData)
{
Debug.Log($"[UnderlineButton] OnPointerEnter {gameObject.name}");
if (_button != null && !_button.interactable) return;
_isHovered = true;
if (IsGrouped)
HoverEntered?.Invoke(this);
else
RefreshStandaloneState();
UpdateContextText();
}
public void OnPointerExit(PointerEventData eventData)
{
_isHovered = false;
if (IsGrouped)
HoverExited?.Invoke(this);
else
RefreshStandaloneState();
UpdateContextText();
}
public void OnPointerClick(PointerEventData eventData)
{
Debug.Log($"[UnderlineButton] OnPointerClick {gameObject.name}");
if (_button != null && !_button.interactable) return;
SelectionRequested?.Invoke(this);
}
public void OnSelect(BaseEventData eventData)
{
if (_button != null && !_button.interactable) return;
_isHovered = true;
if (IsGrouped)
{
HoverEntered?.Invoke(this);
SelectionRequested?.Invoke(this);
}
else
{
IsSelected = true;
RefreshStandaloneState();
}
UpdateContextText();
}
public void OnDeselect(BaseEventData eventData)
{
_isHovered = false;
if (IsGrouped)
HoverExited?.Invoke(this);
else
{
IsSelected = false;
RefreshStandaloneState();
}
UpdateContextText();
}
// ── Public API ────────────────────────────────────────────────
/// <summary>Called by the group to update logical selected state.</summary>
public void SetSelectedState(bool selected)
{
IsSelected = selected;
}
/// <summary>Called by the group to drive the underline visual directly.</summary>
public void SetUnderline(bool underlined)
{
if (_label == null) return;
if (underlined)
_label.fontStyle |= FontStyles.Underline;
else
_label.fontStyle &= ~FontStyles.Underline;
}
public GameObject GetSelectableGameObject() => gameObject;
// ── Standalone logic ──────────────────────────────────────────
/// <summary>
/// Used when not in a group — the button manages its own
/// underline based on hover and selected state directly.
/// </summary>
private void RefreshStandaloneState()
{
if (_button != null && !_button.interactable)
{
SetUnderline(false);
return;
}
SetUnderline(IsSelected || _isHovered);
}
private void UpdateContextText()
{
if (_contextLabel == null)
return;
if(IsSelected || _isHovered)
_contextLabel.text = _contextText;
else
_contextLabel.text = string.Empty;
}
}
}