using System;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using UVC.Extention;
using UVC.UI.Tooltip;
namespace UVC.UI.Window.PropertyWindow
{
///
/// 모든 PropertyUI의 공통 베이스 클래스입니다.
/// 공통 기능을 제공하고 IPropertyUI, IPoolable, IDisposable을 구현합니다.
///
[RequireComponent(typeof(LayoutElement))]
public abstract class PropertyUIBase : MonoBehaviour, IPropertyUI, IPoolable, IDisposable
where T : class, IPropertyItem
{
[Header("Common UI Elements")]
[SerializeField] protected TextMeshProUGUI _nameLabel;
[SerializeField] protected TextMeshProUGUI _descriptionLabel;
///
/// 현재 표시 중인 속성 아이템
///
protected T _propertyItem;
///
/// 상호작용할 컨트롤러
///
protected PropertyWindow _controller;
///
/// 이미 해제되었는지 여부
///
protected bool _isDisposed = false;
#region IPropertyUI Implementation
///
/// PropertyView에 의해 호출되어 UI를 초기화합니다.
///
public virtual void Setup(IPropertyItem item, PropertyWindow controller)
{
// 기존 상태 정리
Cleanup();
// 타입 체크
if (!(item is T typedItem))
{
Debug.LogError($"[{GetType().Name}] 잘못된 타입의 PropertyItem이 전달되었습니다. 예상: {typeof(T).Name}, 실제: {item.GetType().Name}");
return;
}
_propertyItem = typedItem;
_controller = controller;
_isDisposed = false;
// 공통 UI 설정
SetupCommonUI();
// 파생 클래스의 추가 설정
SetupValueUI();
// 이벤트 구독
SubscribeEvents();
}
///
/// UI의 읽기 전용 상태를 설정합니다.
/// 파생 클래스에서 재정의하여 구체적인 UI 요소에 적용해야 합니다.
///
/// 읽기 전용 여부 (true: 비활성화, false: 활성화)
public virtual void SetReadOnly(bool isReadOnly)
{
if (_propertyItem != null)
{
_propertyItem.IsReadOnly = isReadOnly;
}
// 파생 클래스에서 구체적인 UI 요소 비활성화 로직을 구현
ApplyReadOnlyStateToUI(isReadOnly);
}
///
/// 파생 클래스에서 구체적인 UI 요소에 읽기 전용 상태를 적용합니다.
///
/// 읽기 전용 여부
protected virtual void ApplyReadOnlyStateToUI(bool isReadOnly)
{
// 기본 구현 없음 - 파생 클래스에서 필요시 구현
}
#endregion
#region Common UI Setup
///
/// 공통 UI 요소들을 설정합니다.
///
protected virtual void SetupCommonUI()
{
if (_propertyItem == null) return;
// 이름 설정
if (_nameLabel != null)
{
_nameLabel.text = _propertyItem.Name;
// 툴팁 설정
var tooltipHandler = _nameLabel.GetComponent();
if (tooltipHandler != null && !_propertyItem.Tooltip.IsNullOrEmpty())
{
tooltipHandler.Tooltip = _propertyItem.Tooltip;
}
}
// 설명 설정
if (_descriptionLabel != null)
{
if (_propertyItem.Description.IsNullOrEmpty())
{
_descriptionLabel.gameObject.SetActive(false);
}
else
{
_descriptionLabel.gameObject.SetActive(true);
_descriptionLabel.text = _propertyItem.Description;
}
}
}
///
/// 파생 클래스에서 값 관련 UI를 설정합니다.
///
protected abstract void SetupValueUI();
#endregion
#region Event Management
///
/// 이벤트를 구독합니다. 파생 클래스에서 재정의할 수 있습니다.
///
protected virtual void SubscribeEvents()
{
// 기본 구현 없음 - 파생 클래스에서 필요시 구현
}
///
/// 이벤트 구독을 해제합니다. 파생 클래스에서 재정의할 수 있습니다.
///
protected virtual void UnsubscribeEvents()
{
// 기본 구현 없음 - 파생 클래스에서 필요시 구현
}
#endregion
#region Value Update
///
/// 컨트롤러를 통해 값 변경을 통지합니다.
///
/// 새로운 값
protected void NotifyValueChanged(object newValue)
{
if (_controller == null || _propertyItem == null) return;
// 값이 변경되지 않았으면 무시
var oldValue = _propertyItem.GetValue();
if (Equals(oldValue, newValue)) return;
_controller.UpdatePropertyValue(_propertyItem.Id, _propertyItem.PropertyType, newValue);
}
///
/// 읽기 전용 상태를 설정합니다.
///
/// UI 요소
protected void ApplyReadOnlyState(Selectable interactable)
{
if (interactable != null && _propertyItem != null)
{
interactable.interactable = !_propertyItem.IsReadOnly;
}
}
///
/// 읽기 전용 상태를 설정합니다 (TMP_InputField용).
///
/// 입력 필드
protected void ApplyReadOnlyState(TMP_InputField inputField)
{
if (inputField != null && _propertyItem != null)
{
inputField.interactable = !_propertyItem.IsReadOnly;
}
}
#endregion
#region Cleanup and Disposal
///
/// 상태를 정리합니다.
///
protected virtual void Cleanup()
{
UnsubscribeEvents();
_propertyItem = null;
_controller = null;
}
///
/// IDisposable 구현
///
public void Dispose()
{
if (_isDisposed) return;
Cleanup();
_isDisposed = true;
}
#endregion
#region IPoolable Implementation
///
/// 풀에서 가져올 때 호출됩니다.
///
public virtual void OnGetFromPool()
{
_isDisposed = false;
}
///
/// 풀에 반환될 때 호출됩니다.
///
public virtual void OnReturnToPool()
{
Cleanup();
}
#endregion
#region Unity Lifecycle
protected virtual void OnDestroy()
{
Dispose();
}
#endregion
}
}