232 lines
7.3 KiB
C#
232 lines
7.3 KiB
C#
#nullable enable
|
|
using System;
|
|
using UnityEngine;
|
|
|
|
namespace UVC.UIToolkit
|
|
{
|
|
/// <summary>
|
|
/// 모든 PropertyItem의 기본 추상 클래스입니다.
|
|
/// 순수 데이터 클래스로, UI는 별도의 View 클래스에서 담당합니다.
|
|
///
|
|
/// <para><b>주요 기능:</b></para>
|
|
/// <list type="bullet">
|
|
/// <item>속성 값 관리 (Value, IsReadOnly, IsVisible)</item>
|
|
/// <item>값 변경 이벤트 발생</item>
|
|
/// <item>메타데이터 관리 (Description, Tooltip, GroupId)</item>
|
|
/// </list>
|
|
///
|
|
/// <para><b>View-Data 분리:</b></para>
|
|
/// <list type="bullet">
|
|
/// <item>Data (UTKPropertyItemBase): 이 클래스 - 속성 값과 메타데이터</item>
|
|
/// <item>View (UTKPropertyItemViewBase): UI 표시 및 사용자 상호작용</item>
|
|
/// <item>Factory (UTKPropertyItemViewFactory): Data에 맞는 View 생성</item>
|
|
/// </list>
|
|
/// </summary>
|
|
/// <typeparam name="T">속성 값의 타입</typeparam>
|
|
public abstract class UTKPropertyItemBase<T> : IUTKPropertyItem<T>, IDisposable
|
|
{
|
|
#region Fields
|
|
protected T _value;
|
|
protected bool _isReadOnly;
|
|
protected bool _isVisible = true;
|
|
protected bool _showLabel = true;
|
|
private string? _description;
|
|
protected string? _tooltip;
|
|
private string? _groupId;
|
|
private bool _disposed;
|
|
#endregion
|
|
|
|
#region Properties
|
|
/// <summary>고유 ID</summary>
|
|
public string Id { get; }
|
|
|
|
/// <summary>표시 이름</summary>
|
|
public string Name { get; }
|
|
|
|
/// <summary>표시 이름 (Name과 동일)</summary>
|
|
public string DisplayName => Name;
|
|
|
|
/// <summary>그룹 여부 (항상 false)</summary>
|
|
public bool IsGroup => false;
|
|
|
|
/// <summary>TreeView 내부 ID</summary>
|
|
public int TreeViewId { get; set; }
|
|
|
|
/// <summary>속성 타입</summary>
|
|
public abstract UTKPropertyType PropertyType { get; }
|
|
|
|
/// <summary>현재 값</summary>
|
|
public T Value
|
|
{
|
|
get => _value;
|
|
set
|
|
{
|
|
if (!Equals(_value, value))
|
|
{
|
|
var oldValue = _value;
|
|
_value = value;
|
|
NotifyValueChanged(oldValue, value, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>설명 (부가 정보)</summary>
|
|
public string? Description
|
|
{
|
|
get => _description;
|
|
set => _description = value;
|
|
}
|
|
|
|
/// <summary>툴팁 텍스트</summary>
|
|
public string? Tooltip
|
|
{
|
|
get => _tooltip;
|
|
set => _tooltip = value;
|
|
}
|
|
|
|
/// <summary>읽기 전용 여부</summary>
|
|
public bool IsReadOnly
|
|
{
|
|
get => _isReadOnly;
|
|
set
|
|
{
|
|
if (_isReadOnly == value) return;
|
|
_isReadOnly = value;
|
|
OnStateChanged?.Invoke(this);
|
|
}
|
|
}
|
|
|
|
/// <summary>표시 여부</summary>
|
|
public bool IsVisible
|
|
{
|
|
get => _isVisible;
|
|
set => _isVisible = value;
|
|
}
|
|
|
|
/// <summary>소속 그룹 ID</summary>
|
|
public string? GroupId
|
|
{
|
|
get => _groupId;
|
|
set => _groupId = value;
|
|
}
|
|
|
|
/// <summary>라벨 표시 여부 (false면 value가 전체 너비 사용)</summary>
|
|
public bool ShowLabel
|
|
{
|
|
get => _showLabel;
|
|
set{
|
|
if (_showLabel == value) return;
|
|
_showLabel = value;
|
|
OnStateChanged?.Invoke(this);
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Events
|
|
/// <summary>값 변경 이벤트 (object 타입)</summary>
|
|
public event Action<IUTKPropertyItem, object?, object?, bool>? OnValueChanged;
|
|
|
|
/// <summary>값 변경 이벤트 (제네릭 타입)</summary>
|
|
public event Action<IUTKPropertyItem<T>, T, T>? OnTypedValueChanged;
|
|
|
|
/// <summary>상태(ReadOnly 등) 변경 이벤트</summary>
|
|
public event Action<IUTKPropertyItem>? OnStateChanged;
|
|
#endregion
|
|
|
|
#region Constructor
|
|
/// <summary>
|
|
/// PropertyItem을 생성합니다.
|
|
/// </summary>
|
|
/// <param name="id">고유 ID</param>
|
|
/// <param name="name">표시 이름</param>
|
|
/// <param name="initialValue">초기 값</param>
|
|
protected UTKPropertyItemBase(string id, string name, T initialValue)
|
|
{
|
|
Id = id ?? throw new ArgumentNullException(nameof(id));
|
|
Name = name ?? throw new ArgumentNullException(nameof(name));
|
|
_value = initialValue;
|
|
}
|
|
#endregion
|
|
|
|
#region Public Methods
|
|
/// <summary>현재 값을 object로 반환합니다.</summary>
|
|
public object? GetValue() => _value;
|
|
|
|
/// <summary>값을 설정합니다 (타입 변환 포함).</summary>
|
|
public void SetValue(object? value, bool notifyChangeEvent = false)
|
|
{
|
|
if (value == null)
|
|
{
|
|
if (default(T) == null)
|
|
{
|
|
if (!Equals(_value, value))
|
|
{
|
|
var oldValue = _value;
|
|
_value = default!;
|
|
NotifyValueChanged(oldValue, default!, notifyChangeEvent);
|
|
}
|
|
}
|
|
}
|
|
else if (value is T typedValue)
|
|
{
|
|
if (!Equals(_value, typedValue))
|
|
{
|
|
var oldValue = _value;
|
|
_value = typedValue;
|
|
NotifyValueChanged(oldValue, typedValue, notifyChangeEvent);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
try
|
|
{
|
|
var v = (T)Convert.ChangeType(value, typeof(T));
|
|
if (!Equals(_value, v))
|
|
{
|
|
var oldValue = _value;
|
|
_value = v;
|
|
NotifyValueChanged(oldValue, v, notifyChangeEvent);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.LogWarning($"[UTKPropertyItem] Failed to convert value '{value}' to type {typeof(T)}: {ex.Message}");
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Protected Methods
|
|
/// <summary>값 변경을 알립니다.</summary>
|
|
protected void NotifyValueChanged(T oldValue, T newValue, bool notifyChangeEvent = false)
|
|
{
|
|
OnTypedValueChanged?.Invoke(this, oldValue, newValue);
|
|
OnValueChanged?.Invoke(this, oldValue, newValue, notifyChangeEvent);
|
|
}
|
|
#endregion
|
|
|
|
#region IDisposable
|
|
/// <summary>리소스를 해제합니다.</summary>
|
|
public void Dispose()
|
|
{
|
|
Dispose(true);
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
/// <summary>리소스를 해제합니다.</summary>
|
|
protected virtual void Dispose(bool disposing)
|
|
{
|
|
if (_disposed) return;
|
|
_disposed = true;
|
|
|
|
if (disposing)
|
|
{
|
|
OnValueChanged = null;
|
|
OnTypedValueChanged = null;
|
|
OnStateChanged = null;
|
|
}
|
|
}
|
|
#endregion
|
|
}
|
|
}
|