#nullable enable using System; using UnityEngine; using UnityEngine.UIElements; using UVC.Locale; namespace UVC.UIToolkit { /// /// UIToolkit 기반 메뉴 아이템의 추상 베이스 클래스입니다. /// 공통 로직을 제공하고, UI 생성은 하위 클래스에서 구현합니다. /// [UxmlElement] public abstract partial class UTKMenuItemBase : VisualElement, IDisposable { #region Fields protected bool _disposed; protected Button? _button; protected VisualElement? _arrow; protected UTKMenuItemData? _data; protected LocalizationManager? _locManager; protected EventCallback? _onClickCallback; protected string _ussPath = ""; #endregion #region UXML Attributes /// 메뉴 아이템 ID [UxmlAttribute("item-id")] public string ItemId { get; set; } = ""; /// 표시 이름 (다국어 키 또는 이미지 경로) [UxmlAttribute("display-name")] public string DisplayName { get; set; } = ""; /// 활성화 상태 [UxmlAttribute("is-enabled")] public bool IsEnabled { get => _button?.enabledSelf ?? true; set { if (_button != null) _button.SetEnabled(value); } } /// 단축키 [UxmlAttribute("shortcut")] public string Shortcut { get; set; } = ""; #endregion #region Events /// 메뉴 아이템 클릭 이벤트 public event Action? OnClicked; #endregion #region Constructor /// /// UTKMenuItemBase의 새 인스턴스를 초기화합니다. /// protected UTKMenuItemBase() { // 1. 테마 적용 UTKThemeManager.Instance.ApplyThemeToElement(this); // 2. USS 로드 (하위 클래스에서 _ussPath 설정 필요) LoadStyleSheet(); // 3. UI 생성 (추상 메서드, 하위 클래스 구현) CreateUI(); // 4. 테마 변경 구독 SubscribeToThemeChanges(); // 5. LocalizationManager 가져오기 _locManager = LocalizationManager.Instance; } #endregion #region Setup /// /// USS 스타일시트를 로드합니다. /// protected virtual void LoadStyleSheet() { if (string.IsNullOrEmpty(_ussPath)) return; var uss = Resources.Load(_ussPath); if (uss != null) { styleSheets.Add(uss); } } /// /// UI를 생성합니다. 하위 클래스에서 구현해야 합니다. /// protected abstract void CreateUI(); /// /// 테마 변경 이벤트를 구독합니다. /// private void SubscribeToThemeChanges() { UTKThemeManager.Instance.OnThemeChanged += OnThemeChanged; RegisterCallback(OnAttachToPanelForTheme); RegisterCallback(OnDetachFromPanelForTheme); } /// /// 패널에 붙을 때 호출됩니다. /// private void OnAttachToPanelForTheme(AttachToPanelEvent evt) { UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged; UTKThemeManager.Instance.OnThemeChanged += OnThemeChanged; UTKThemeManager.Instance.ApplyThemeToElement(this); } /// /// 패널에서 분리될 때 호출됩니다. /// private void OnDetachFromPanelForTheme(DetachFromPanelEvent evt) { UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged; } /// /// 테마 변경 시 호출됩니다. /// /// 새로운 테마 private void OnThemeChanged(UTKTheme theme) { UTKThemeManager.Instance.ApplyThemeToElement(this); } #endregion #region Public Methods /// /// 메뉴 아이템 데이터를 설정합니다. /// /// 메뉴 아이템 데이터 /// data가 null인 경우 public virtual void SetData(UTKMenuItemData data) { if (data == null) throw new ArgumentNullException(nameof(data), "메뉴 아이템 데이터가 null입니다."); _data = data; ItemId = data.ItemId; DisplayName = data.DisplayName; IsEnabled = data.IsEnabled; Shortcut = data.Shortcut ?? ""; UpdateUI(); } /// /// 활성화 상태를 업데이트합니다. /// /// 활성화 여부 public void UpdateEnabled(bool enabled) { IsEnabled = enabled; if (_data != null) _data.IsEnabled = enabled; UpdateOpacity(); } /// /// 단축키를 업데이트합니다. /// /// 단축키 문자열 public void UpdateShortcut(string shortcut) { Shortcut = shortcut ?? ""; if (_data != null) _data.Shortcut = shortcut; } /// /// 하위 메뉴 화살표를 표시합니다. /// /// 하위 메뉴 존재 여부 public void ShowArrow(bool hasSubMenu) { if (_arrow != null) { _arrow.style.display = hasSubMenu ? DisplayStyle.Flex : DisplayStyle.None; } } #endregion #region Protected Methods /// /// UI를 업데이트합니다. 하위 클래스에서 오버라이드하여 구현합니다. /// protected abstract void UpdateUI(); /// /// 활성화 상태에 따라 투명도를 업데이트합니다. 하위 클래스에서 오버라이드 가능합니다. /// protected virtual void UpdateOpacity() { // 하위 클래스에서 구현 } /// /// 버튼 클릭 시 호출됩니다. /// /// 클릭 이벤트 protected virtual void OnButtonClicked(ClickEvent evt) { if (_data != null && IsEnabled) { OnClicked?.Invoke(_data); } } #endregion #region IDisposable /// /// 리소스를 정리합니다. /// public virtual void Dispose() { if (_disposed) return; _disposed = true; // 이벤트 구독 해제 UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged; UnregisterCallback(OnAttachToPanelForTheme); UnregisterCallback(OnDetachFromPanelForTheme); // 버튼 이벤트 해제 if (_button != null && _onClickCallback != null) { _button.UnregisterCallback(_onClickCallback); } // 참조 정리 OnClicked = null; _button = null; _arrow = null; _data = null; _onClickCallback = null; } #endregion } }