Restructured for new direction.
This commit is contained in:
175
Assets/Scripts/UI/Menus/Components/UnderlineButtonGroup.cs
Normal file
175
Assets/Scripts/UI/Menus/Components/UnderlineButtonGroup.cs
Normal file
@@ -0,0 +1,175 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user