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(); [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 _buttons = new(); public UnderlineButton Current { get; private set; } public UnderlineButton CurrentHovered { get; private set; } private void Reset() { _initialButtons = GetComponentsInChildren(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 ──────────────────────────────────────────────── /// /// Registers a button with the group at runtime. /// Safe to call multiple times — ignores already-registered buttons. /// 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; } /// /// Unregisters a button from the group. /// Safe to call on buttons not in the group. /// 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(); } /// Removes all dynamically added buttons, leaving inspector-assigned ones. 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); } } }