Files
EnglewoodLAB/Assets/Scripts/UVC/UIToolkit/Menu/UTKMenuItemBase.cs

269 lines
7.9 KiB
C#

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