Restructured for new direction.

This commit is contained in:
2026-05-12 12:01:09 +01:00
parent 0439b6c1d2
commit c203f836b1
1134 changed files with 125569 additions and 213519 deletions

View 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);
}
}
}