175 lines
6.0 KiB
C#
175 lines
6.0 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.EventSystems;
|
|
|
|
namespace BriarQueen.UI.Menus.Components
|
|
{
|
|
public class UnderlineButtonGroup : MonoBehaviour
|
|
{
|
|
[Header("Buttons")]
|
|
[SerializeField] private UnderlineButton[] _initialButtons = Array.Empty<UnderlineButton>();
|
|
|
|
[SerializeField] private int _defaultSelectedIndex;
|
|
[SerializeField] private bool _selectDefaultOnEnable = true;
|
|
[SerializeField] private bool _syncEventSystemSelection = true;
|
|
|
|
// Runtime list — combines inspector-assigned and dynamically added buttons
|
|
private readonly List<UnderlineButton> _buttons = new();
|
|
|
|
public UnderlineButton Current { get; private set; }
|
|
public UnderlineButton CurrentHovered { get; private set; }
|
|
|
|
private void Reset()
|
|
{
|
|
_initialButtons = GetComponentsInChildren<UnderlineButton>(true);
|
|
}
|
|
|
|
private void OnEnable()
|
|
{
|
|
// Register inspector-assigned buttons first
|
|
foreach (var button in _initialButtons)
|
|
AddButton(button);
|
|
|
|
if (_selectDefaultOnEnable && _buttons.Count > 0)
|
|
SelectIndex(_defaultSelectedIndex, instant: true);
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
// Remove all — clears both initial and runtime-added buttons
|
|
for (var i = _buttons.Count - 1; i >= 0; i--)
|
|
RemoveButton(_buttons[i]);
|
|
|
|
CurrentHovered = null;
|
|
Current = null;
|
|
}
|
|
|
|
// ── Public API ────────────────────────────────────────────────
|
|
|
|
/// <summary>
|
|
/// Registers a button with the group at runtime.
|
|
/// Safe to call multiple times — ignores already-registered buttons.
|
|
/// </summary>
|
|
public void AddButton(UnderlineButton button)
|
|
{
|
|
if (button == null || _buttons.Contains(button)) return;
|
|
|
|
_buttons.Add(button);
|
|
button.RegisterGroup(this);
|
|
button.SelectionRequested += HandleSelectionRequested;
|
|
button.HoverEntered += HandleHoverEntered;
|
|
button.HoverExited += HandleHoverExited;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Unregisters a button from the group.
|
|
/// Safe to call on buttons not in the group.
|
|
/// </summary>
|
|
public void RemoveButton(UnderlineButton button)
|
|
{
|
|
if (button == null || !_buttons.Contains(button)) return;
|
|
|
|
// If this was the selected or hovered button, clear those references
|
|
if (Current == button) Current = null;
|
|
if (CurrentHovered == button) CurrentHovered = null;
|
|
|
|
button.UnregisterGroup();
|
|
button.SelectionRequested -= HandleSelectionRequested;
|
|
button.HoverEntered -= HandleHoverEntered;
|
|
button.HoverExited -= HandleHoverExited;
|
|
button.SetUnderline(false);
|
|
button.SetSelectedState(false);
|
|
|
|
_buttons.Remove(button);
|
|
|
|
RefreshAllButtonStates();
|
|
}
|
|
|
|
/// <summary>Removes all dynamically added buttons, leaving inspector-assigned ones.</summary>
|
|
public void RemoveAllDynamicButtons()
|
|
{
|
|
for (var i = _buttons.Count - 1; i >= 0; i--)
|
|
{
|
|
var button = _buttons[i];
|
|
if (button == null) continue;
|
|
|
|
// Skip buttons that were assigned in the inspector
|
|
var isInitial = System.Array.IndexOf(_initialButtons, button) >= 0;
|
|
if (isInitial) continue;
|
|
|
|
RemoveButton(button);
|
|
}
|
|
}
|
|
|
|
public void SelectIndex(int index, bool instant = false)
|
|
{
|
|
if (_buttons == null || _buttons.Count == 0) return;
|
|
SelectButton(_buttons[Mathf.Clamp(index, 0, _buttons.Count - 1)], instant);
|
|
}
|
|
|
|
public void SelectButton(UnderlineButton button, bool instant = false)
|
|
{
|
|
if (button == null) return;
|
|
if (Current == button && !instant) return;
|
|
|
|
Current = button;
|
|
RefreshAllButtonStates();
|
|
SyncEventSystemSelection(button);
|
|
}
|
|
|
|
public void ClearSelection()
|
|
{
|
|
Current = null;
|
|
RefreshAllButtonStates();
|
|
}
|
|
|
|
// ── Internal ──────────────────────────────────────────────────
|
|
|
|
private void RefreshAllButtonStates()
|
|
{
|
|
foreach (var button in _buttons)
|
|
{
|
|
if (button == null) continue;
|
|
|
|
var isSelected = button == Current;
|
|
var isHovered = button == CurrentHovered;
|
|
|
|
var showUnderline = isSelected
|
|
? CurrentHovered == null || isHovered
|
|
: isHovered;
|
|
|
|
button.SetSelectedState(isSelected);
|
|
button.SetUnderline(showUnderline);
|
|
}
|
|
}
|
|
|
|
private void HandleSelectionRequested(UnderlineButton button)
|
|
{
|
|
SelectButton(button);
|
|
}
|
|
|
|
private void HandleHoverEntered(UnderlineButton button)
|
|
{
|
|
CurrentHovered = button;
|
|
RefreshAllButtonStates();
|
|
}
|
|
|
|
private void HandleHoverExited(UnderlineButton button)
|
|
{
|
|
if (CurrentHovered == button)
|
|
CurrentHovered = null;
|
|
|
|
RefreshAllButtonStates();
|
|
}
|
|
|
|
private void SyncEventSystemSelection(UnderlineButton button)
|
|
{
|
|
if (!_syncEventSystemSelection || EventSystem.current == null) return;
|
|
|
|
var go = button.GetSelectableGameObject();
|
|
if (go == null || EventSystem.current.currentSelectedGameObject == go) return;
|
|
EventSystem.current.SetSelectedGameObject(go);
|
|
}
|
|
}
|
|
} |