#nullable enable
using System;
using UnityEngine;
using UnityEngine.UIElements;
namespace UVC.UIToolkit
{
///
/// 툴바 버튼 VisualElement의 추상 베이스 클래스입니다.
/// 아이콘(Material Icon / Image), 텍스트, 활성화 상태의 공통 UI를 제공합니다.
///
public abstract partial class UTKToolBarButtonBase : VisualElement, IDisposable
{
#region Fields
/// 아이콘 요소 (Material Icon Label 또는 Image)
protected UTKLabel? _iconLabel;
/// 텍스트 라벨
protected UTKLabel? _textLabel;
/// 루트 버튼 요소
protected VisualElement? _rootButton;
/// 바인딩된 데이터
protected UTKToolBarButtonData? _data;
/// 클릭 콜백
protected EventCallback? _onClickCallback;
/// 정리 여부
protected bool _disposed;
/// UXML 리소스 경로
protected string _uxmlPath = "";
/// USS 리소스 경로
protected string _ussPath = "";
#endregion
#region Events
/// 버튼 클릭 이벤트 (데이터 전달)
public event Action? OnButtonClicked;
#endregion
#region Constructor
///
/// UTKToolBarButtonBase의 새 인스턴스를 초기화합니다.
///
protected UTKToolBarButtonBase()
{
// 1. 테마 적용
UTKThemeManager.Instance.ApplyThemeToElement(this);
// 2. 테마 구독
SubscribeToThemeChanges();
}
#endregion
#region Setup
///
/// UI를 생성합니다. UXML 로드 또는 코드 Fallback.
///
protected virtual void CreateUI()
{
// USS 로드
if (!string.IsNullOrEmpty(_ussPath))
{
var uss = Resources.Load(_ussPath);
if (uss != null)
{
styleSheets.Add(uss);
}
}
// UXML 로드
if (!string.IsNullOrEmpty(_uxmlPath))
{
var asset = Resources.Load(_uxmlPath);
if (asset != null)
{
CreateUIFromUxml(asset);
return;
}
}
// Fallback
CreateUIFallback();
}
///
/// UXML에서 UI를 생성합니다.
///
/// UXML 에셋
protected virtual void CreateUIFromUxml(VisualTreeAsset asset)
{
var root = asset.Instantiate();
_rootButton = root.Q("button-root");
_iconLabel = root.Q("icon");
_textLabel = root.Q("label");
_textLabel.Size = UTKLabel.LabelSize.Caption; // UXML에서 기본 크기를 설정하므로 코드에서 다시 지정
// TemplateContainer가 아이콘 정렬을 방해하지 않도록 설정
root.style.flexGrow = 1;
root.style.alignItems = Align.Stretch;
root.style.justifyContent = Justify.Center;
Add(root);
// 클릭 이벤트 등록
if (_rootButton != null)
{
_onClickCallback = OnClick;
_rootButton.RegisterCallback(_onClickCallback);
}
}
///
/// 코드 Fallback으로 UI를 생성합니다.
///
protected virtual void CreateUIFallback()
{
_rootButton = new VisualElement();
_rootButton.AddToClassList("utk-toolbar-btn");
_iconLabel = new UTKLabel();
_iconLabel.AddToClassList("utk-toolbar-btn__icon");
_rootButton.Add(_iconLabel);
_textLabel = new UTKLabel();
_textLabel.AddToClassList("utk-toolbar-btn__label");
_textLabel.Size = UTKLabel.LabelSize.Caption;
_rootButton.Add(_textLabel);
Add(_rootButton);
// 클릭 이벤트 등록
_onClickCallback = OnClick;
_rootButton.RegisterCallback(_onClickCallback);
}
#endregion
#region Data Binding
///
/// 데이터를 바인딩합니다. OnStateChanged 이벤트를 구독합니다.
///
/// 바인딩할 데이터
public virtual void BindData(UTKToolBarButtonData data)
{
// 기존 바인딩 해제
UnbindData();
_data = data;
_data.OnStateChanged += OnDataStateChanged;
// 초기 UI 업데이트
UpdateIcon(_data.IconPath, _data.UseMaterialIcon);
UpdateText(_data.Text);
UpdateEnabled(_data.IsEnabled);
UpdateTooltip(_data.Tooltip);
}
///
/// 데이터 바인딩을 해제합니다.
///
public virtual void UnbindData()
{
if (_data != null)
{
_data.OnStateChanged -= OnDataStateChanged;
_data = null;
}
}
/// 바인딩된 데이터 (읽기 전용)
public UTKToolBarButtonData? BoundData => _data;
///
/// 바인딩된 데이터의 활성화 상태를 변경합니다.
///
/// 활성화 여부
public void SetDataEnabled(bool isEnabled)
{
if (_data != null)
{
_data.IsEnabled = isEnabled;
}
}
#endregion
#region UI Update
///
/// 아이콘을 업데이트합니다.
///
/// 아이콘 경로 (Material Icon 유니코드 또는 Resources 경로)
/// Material Icon 사용 여부
protected void UpdateIcon(string? iconPath, bool useMaterialIcon)
{
if (_iconLabel == null) return;
if (string.IsNullOrEmpty(iconPath))
{
_iconLabel.style.display = DisplayStyle.None;
return;
}
_iconLabel.style.display = DisplayStyle.Flex;
if (useMaterialIcon)
{
// Material Icon (폰트 기반) - UTKLabel의 SetMaterialIcon 사용
_iconLabel.RemoveFromClassList("utk-toolbar-btn__icon--image");
_iconLabel.SetMaterialIcon(iconPath);
}
else
{
// 이미지 아이콘 - UTKLabel의 SetImageIcon 사용
_iconLabel.AddToClassList("utk-toolbar-btn__icon--image");
_iconLabel.SetImageIcon(iconPath);
}
}
///
/// 텍스트를 업데이트합니다.
///
/// 버튼 텍스트
protected void UpdateText(string text)
{
if (_textLabel != null)
{
_textLabel.Text = text;
_textLabel.style.display = string.IsNullOrEmpty(text) ? DisplayStyle.None : DisplayStyle.Flex;
}
}
///
/// 툴팁을 업데이트합니다.
///
/// 툴팁 텍스트
protected void UpdateTooltip(string? tooltipText)
{
if (_rootButton == null) return;
if (string.IsNullOrEmpty(tooltipText))
{
UTKTooltipManager.Instance.DetachTooltip(_rootButton);
}
else
{
UTKTooltipManager.Instance.UpdateTooltip(_rootButton, tooltipText);
}
}
///
/// 활성화 상태를 업데이트합니다.
///
/// 활성화 여부
protected void UpdateEnabled(bool isEnabled)
{
if (_rootButton != null)
{
if (isEnabled)
{
_rootButton.RemoveFromClassList("utk-toolbar-btn--disabled");
}
else
{
_rootButton.AddToClassList("utk-toolbar-btn--disabled");
}
}
}
///
/// 모델 상태 변경 핸들러. 바인딩된 데이터의 시각 상태를 UI에 반영합니다.
///
protected virtual void OnDataStateChanged()
{
if (_data == null) return;
UpdateIcon(_data.IconPath, _data.UseMaterialIcon);
UpdateText(_data.Text);
UpdateEnabled(_data.IsEnabled);
UpdateTooltip(_data.Tooltip);
}
#endregion
#region Click Handler
///
/// 클릭 이벤트 핸들러.
///
/// 클릭 이벤트
protected virtual void OnClick(ClickEvent evt)
{
if (_data == null || !_data.IsEnabled) return;
_data.ExecuteClick();
RaiseOnButtonClicked(_data);
}
///
/// OnButtonClicked 이벤트를 발생시킵니다. 파생 클래스에서 사용합니다.
///
/// 버튼 데이터
protected void RaiseOnButtonClicked(UTKToolBarButtonData data)
{
OnButtonClicked?.Invoke(data);
}
#endregion
#region Theme
///
/// 테마 변경 이벤트를 구독합니다.
///
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 IDisposable
///
/// 리소스를 정리합니다.
///
public virtual void Dispose()
{
if (_disposed) return;
_disposed = true;
// 데이터 바인딩 해제
UnbindData();
// 툴팁 해제
if (_rootButton != null)
{
UTKTooltipManager.Instance.DetachTooltip(_rootButton);
}
// 테마 구독 해제
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
UnregisterCallback(OnAttachToPanelForTheme);
UnregisterCallback(OnDetachFromPanelForTheme);
// 클릭 이벤트 해제
if (_rootButton != null && _onClickCallback != null)
{
_rootButton.UnregisterCallback(_onClickCallback);
}
// 참조 정리
OnButtonClicked = null;
_rootButton = null;
_iconLabel = null;
_textLabel = null;
_onClickCallback = null;
}
#endregion
}
}