using System; using UnityEngine; using UnityEngine.Events; using UnityEngine.EventSystems; using UnityEngine.UI; namespace NightclubArcadia.UI.Menu { /// /// Selects among menu tabs. Panels are toggled via CanvasGroup (never /// SetActive) so TMP's cached Canvas stays valid. Each tab can fire an /// Inspector-wired UnityEvent on show — used by SkillsPanelView.Refresh /// without a cross-assembly interface. /// public sealed class MenuTabBar : MonoBehaviour { [Serializable] public struct TabEntry { public Button button; public CanvasGroup panel; public UnityEvent onShown; } [SerializeField] TabEntry[] tabs = Array.Empty(); int currentIndex = -1; bool bound; void OnEnable() { BindButtons(); } void BindButtons() { if (bound || tabs == null) { return; } for (var i = 0; i < tabs.Length; i++) { var index = i; if (tabs[i].button != null) { tabs[i].button.onClick.RemoveAllListeners(); tabs[i].button.onClick.AddListener(() => SelectTab(index)); } } bound = tabs.Length > 0; } /// Runtime / bootstrap entry point for assigning tabs after AddComponent. public void SetTabs(TabEntry[] entries) { tabs = entries ?? System.Array.Empty(); bound = false; BindButtons(); } public void SelectTab(int index) { if (tabs == null || tabs.Length == 0) { return; } index = Mathf.Clamp(index, 0, tabs.Length - 1); currentIndex = index; for (var i = 0; i < tabs.Length; i++) { var active = i == index; var panel = tabs[i].panel; if (panel != null) { panel.alpha = active ? 1f : 0f; panel.interactable = active; panel.blocksRaycasts = active; } } tabs[index].onShown?.Invoke(); if (tabs[index].button != null && EventSystem.current != null) { EventSystem.current.SetSelectedGameObject(tabs[index].button.gameObject); } } } }