using UnityEngine; using UnityEngine.Events; using System.Collections.Generic; using Sirenix.OdinInspector; using UnityEngine.EventSystems; [DisallowMultipleComponent] public class UXGroup : UIBehaviour { [SerializeField] private bool m_AllowSwitchOff; [ReadOnly, SerializeField] private List m_Buttons = new(); private UXButton _current; private readonly HashSet _registeredButtons = new(); public UnityEvent onSelectedChanged = new(); public bool allowSwitchOff { get => m_AllowSwitchOff; set { m_AllowSwitchOff = value; ValidateGroupState(); } } protected override void OnDestroy() { foreach (var button in _registeredButtons) { if (button) button.IsSelected = false; } m_Buttons.Clear(); _registeredButtons.Clear(); base.OnDestroy(); } protected override void Awake() => ValidateGroupState(); public void RegisterButton(UXButton button) { if (!button || _registeredButtons.Contains(button)) return; m_Buttons.Add(button); _registeredButtons.Add(button); if (button.IsSelected) { if (_current && _current != button) _current.IsSelected = false; _current = button; } ValidateGroupState(); } public void UnregisterButton(UXButton button) { if (!button || !_registeredButtons.Contains(button)) return; m_Buttons.Remove(button); _registeredButtons.Remove(button); button.IsSelected = false; if (_current == button) _current = null; } internal void NotifyButtonClicked(UXButton clickedButton) { if (clickedButton.IsSelected) { if (m_AllowSwitchOff) SetSelectedButton(null); } else { SetSelectedButton(clickedButton); } } private void SetSelectedButton(UXButton target) { var previous = _current; _current = null; foreach (var button in m_Buttons) { bool shouldSelect = (button == target); if (button.IsSelected != shouldSelect) button.IsSelected = shouldSelect; if (shouldSelect) _current = button; } if (previous != _current) onSelectedChanged?.Invoke(_current); } private void ValidateGroupState() { bool hasSelected = _current != null && _current.IsSelected; if (!hasSelected && m_Buttons.Count > 0 && !m_AllowSwitchOff) SetSelectedButton(m_Buttons[0]); } public bool AnyOtherSelected(UXButton exclusion) { foreach (var button in m_Buttons) { if (button != exclusion && button.IsSelected) return true; } return false; } public void SelectPrevious() { if (m_Buttons.Count == 0) return; int startIndex = _current ? m_Buttons.IndexOf(_current) : -1; int newIndex = FindSelectableIndex(startIndex, -1); if (newIndex != -1) { SetSelectedButton(m_Buttons[newIndex]); } } public void SelectNext() { if (m_Buttons.Count == 0) return; int startIndex = _current ? m_Buttons.IndexOf(_current) : -1; int newIndex = FindSelectableIndex(startIndex, 1); if (newIndex != -1) { SetSelectedButton(m_Buttons[newIndex]); } } private int FindSelectableIndex(int startIndex, int direction) { if (m_Buttons.Count == 0) return -1; int buttonCount = m_Buttons.Count - 1; int valueIndex = startIndex == -1 ? 0 : startIndex + direction; int fallBackIndex = -1; valueIndex = valueIndex > buttonCount ? 0 : (valueIndex < 0 ? buttonCount : valueIndex); if (valueIndex > buttonCount) { valueIndex = 0; } else if (valueIndex < 0) { valueIndex = buttonCount; } while (valueIndex != startIndex) { UXButton btn = m_Buttons[valueIndex]; if (btn.isActiveAndEnabled && btn.Interactable) { fallBackIndex = valueIndex; break; } valueIndex += direction; if (valueIndex > buttonCount) { valueIndex = 0; } else if (valueIndex < 0) { valueIndex = buttonCount; } } return fallBackIndex; } }