Files
XRLib/Assets/Scripts/UVC/UI/List/Tree/TreeListItemData.cs
2025-12-22 19:49:36 +09:00

356 lines
11 KiB
C#

#nullable enable
using System;
using System.Collections.Generic;
namespace UVC.UI.List.Tree
{
/// <summary>
/// 트리 리스트 아이템의 순수 데이터 모델.
///
/// 목적:
/// - 이름/옵션/선택/펼침 상태 및 계층(부모-자식) 보유
/// - 자식 추가/삽입/복제/재배치/초기화 등 계층 편집 기능
///
/// 동등성/해시:
/// - Id(Guid) 기반 비교/해시 코드.
///
/// 주의사항:
/// - ClearChildren은 각 자식에 Dispose를 호출하여 내부 상태를 정리한 뒤
/// 리스트를 비웁니다(연결만 끊는 수준을 넘어 자식도 정리됨).
/// - AddClone계열은 전달된 원본 child를 Dispose한 뒤 복제본을 추가합니다(파괴적).
///
/// 이벤트 처리:
/// - 이벤트는 TreeList에서 중앙 관리됩니다.
/// - 데이터 변경 알림은 TreeList.NotifyDataChanged()를 통해 처리합니다.
/// </summary>
public class TreeListItemData : IDisposable
{
#region (protected Fields)
/// <summary>고유 식별자(Id).</summary>
protected Guid _id = Guid.NewGuid();
/// <summary>아이템 이름.</summary>
protected string _name = string.Empty;
/// <summary>추가 옵션 문자열.</summary>
protected string _option = string.Empty;
/// <summary>자식 펼침 여부.</summary>
protected bool _isExpanded = false;
/// <summary>선택 여부.</summary>
protected bool _isSelected = false;
/// <summary>부모</summary>
protected TreeListItemData? _parent;
/// <summary>자식 리스트.</summary>
protected List<TreeListItemData> _children = new List<TreeListItemData>();
#endregion
#region (Public Properties)
/// <summary>
/// 아이템 이름.
/// UI 갱신은 TreeList.SetItemName()을 통해 처리합니다.
/// </summary>
public string Name
{
get => _name;
set => _name = value;
}
/// <summary>
/// 옵션 문자열.
/// UI 갱신은 TreeList.SetItemOption()을 통해 처리합니다.
/// </summary>
public string Option
{
get => _option;
set => _option = value;
}
/// <summary>
/// 펼침 상태.
/// UI 갱신은 TreeList.SetItemExpanded()를 통해 처리합니다.
/// </summary>
public bool IsExpanded
{
get => _isExpanded;
set => _isExpanded = value;
}
/// <summary>
/// 선택 상태.
/// UI 갱신은 TreeList.SelectItem()/DeselectItem()을 통해 처리합니다.
/// </summary>
public bool IsSelected
{
get => _isSelected;
set => _isSelected = value;
}
/// <summary>부모 데이터(내부 전용).</summary>
internal TreeListItemData? Parent
{
get => _parent;
set => _parent = value;
}
/// <summary>
/// 자식 컬렉션(내부 전용).
/// </summary>
internal List<TreeListItemData> Children
{
get => _children;
set => _children = value ?? new List<TreeListItemData>();
}
/// <summary>고유 식별자(Id).</summary>
public Guid Id => _id;
/// <summary>
/// 리스트/모델 가시성 상태
/// </summary>
public bool IsVisible { get; set; } = true;
/// <summary>
/// 외부와의 안정 매핑용 키
/// </summary>
public string ExternalKey { get; set; } = string.Empty;
/// <summary>
/// 외부에서 사용하기 위한 사용자 정의 데이터 태그
/// Transform, GameObject 등 임의 객체 참조에 사용
/// </summary>
public object? Tag { get; set; }
#endregion
#region (Constructors)
/// <summary>
/// 기본 생성자.
/// 초기값: Name="", Option="", IsExpanded=false, IsSelected=false, Children=[]
/// </summary>
public TreeListItemData()
{
_name = string.Empty;
_option = string.Empty;
_isExpanded = false;
_isSelected = false;
_children = new List<TreeListItemData>();
}
/// <summary>
/// 이름과 초기 자식으로 생성합니다.
/// </summary>
/// <param name="generalName">아이템 이름(필수).</param>
/// <param name="childrenItemData">초기 자식 목록(null 허용).</param>
public TreeListItemData(string generalName, List<TreeListItemData>? childrenItemData = null)
{
_name = generalName;
_option = string.Empty;
_isExpanded = false;
_children = childrenItemData ?? new List<TreeListItemData>();
}
#endregion
#region (Child Management Methods)
/// <summary>
/// 자식을 끝에 추가합니다.
/// UI 갱신은 TreeList를 통해 처리해야 합니다.
/// </summary>
/// <param name="child">추가할 자식.</param>
public void AddChild(TreeListItemData child)
{
child.Parent = this;
_children.Add(child);
}
/// <summary>
/// 자식을 지정 인덱스에 삽입합니다.
/// UI 갱신은 TreeList를 통해 처리해야 합니다.
/// </summary>
/// <param name="child">삽입할 자식.</param>
/// <param name="index">삽입 인덱스(0 기반).</param>
public void AddChildAt(TreeListItemData child, int index)
{
child._parent = this;
_children.Insert(index, child);
}
/// <summary>
/// 지정 자식을 지정 인덱스 위치에 삽입합니다.
/// 주의: 기존 위치에서 제거하지 않으므로 "이동"이 아닌 "삽입" 동작입니다.
/// </summary>
/// <param name="child">삽입할 기존 자식 참조.</param>
/// <param name="index">삽입 인덱스(0 기반).</param>
public void SwapChild(TreeListItemData child, int index)
{
_children.Insert(index, child);
}
/// <summary>
/// 자식을 제거합니다.
/// UI 갱신은 TreeList를 통해 처리해야 합니다.
/// </summary>
public bool RemoveChild(TreeListItemData child)
{
if (_children.Remove(child))
{
child._parent = null;
return true;
}
return false;
}
/// <summary>
/// 모든 자식을 제거하고 정리합니다.
/// 각 자식에 대해 Parent=null 및 Dispose()를 호출한 뒤 리스트를 비웁니다.
/// </summary>
public void ClearChildren()
{
foreach (var child in _children)
{
child._parent = null;
child.Dispose();
}
_children.Clear();
}
#endregion
#region (Comparison Operators)
/// <summary>
/// 이름(대소문자 구분, Ordinal) 기반 동등 비교.
/// </summary>
public static bool operator ==(TreeListItemData? left, TreeListItemData? right)
{
// 같은 객체인지 확인 (메모리 주소 비교)
if (ReferenceEquals(left, right))
{
return true;
}
// 하나 이상이 null이면 false (ReferenceEquals에서 둘 다 null인 경우는 true 반환)
if (left is null || right is null)
{
return false;
}
// 고유 식별자(Id)로 비교
return left.Id == right.Id;
}
/// <summary>
/// 이름 기반 비동등 비교.
/// </summary>
public static bool operator !=(TreeListItemData? left, TreeListItemData? right)
{
return !(left == right);
}
#endregion
#region (Object Methods)
/// <summary>
/// 이름 기반 해시 코드 반환.
/// </summary>
public override int GetHashCode()
{
return Id.GetHashCode();
}
/// <summary>
/// 이름 기반 동등성 비교(== 사용).
/// </summary>
/// <param name="obj">비교 대상.</param>
public override bool Equals(object? obj)
{
// obj가 TreeListItemData 타입인지 확인
if (obj is TreeListItemData other)
{
// TreeListItemData면 == 연산자로 비교
return this == other;
}
// 다른 타입이면 false
return false;
}
/// <summary>
/// 참조/자식 컬렉션을 정리합니다.
/// - Parent=null, Children.Clear()
/// - 하위 항목을 재귀적으로 Dispose하지는 않습니다
/// (재귀 정리는 ClearChildrenInternal에서 수행).
/// </summary>
public void Dispose()
{
if (_parent != null && _parent.Children.Contains(this)) _parent.Children.Remove(this);
_parent = null;
Children?.Clear();
}
/// <summary>
/// 깊은 복제본을 생성합니다(자식까지 재귀 복제).
/// </summary>
/// <returns>복제된 새 인스턴스.</returns>
public TreeListItemData CloneWithChild()
{
TreeListItemData clone = new TreeListItemData();
clone._id = this.Id;
clone.Name = this.Name;
clone.Option = this.Option;
clone.IsExpanded = this.IsExpanded;
clone.IsSelected = this.IsSelected;
foreach (var child in this.Children)
{
clone.AddChild(child.CloneWithChild());
}
return clone;
}
/// <summary>
/// 현재 인스턴스의 복사본인 <see cref="TreeListItemData"/>의 새 인스턴스를 생성합니다.
/// </summary>
/// <returns>현재 인스턴스와 동일한 속성 값을 가진 새 <see cref="TreeListItemData"/> 객체를 생성합니다.</returns>
public virtual TreeListItemData Clone()
{
TreeListItemData clone = new TreeListItemData();
clone._id = this.Id;
clone.Name = this.Name;
clone.Option = this.Option;
clone.IsExpanded = this.IsExpanded;
clone.IsSelected = this.IsSelected;
return clone;
}
#endregion
}
/// <summary>
/// 데이터 변경 종류 열거형.
/// </summary>
public enum ChangedType
{
Name,
Option,
Expanded,
ResetChildren,
AddChild,
RemoveChild,
AddAtChild,
AddCloneChild,
AddCloneAtChild,
SwapChild,
TailButtons,// 향후 버튼 관련 변경 추가 가능
Delete, // 아이템 삭제 (Delete 키로 삭제 시)
}
}