버그 수정 중. 이동 후 남겨지는 경우가 있음
This commit is contained in:
@@ -6,106 +6,74 @@ using System.Collections.Generic;
|
||||
namespace UVC.UI.List.Tree
|
||||
{
|
||||
/// <summary>
|
||||
/// 트리 구조 리스트에서 각 아이템이 갖는 데이터 클래스입니다.
|
||||
/// 트리 리스트 아이템의 데이터 모델.
|
||||
///
|
||||
/// 트리 구조란? 폴더-파일처럼 상위(부모)와 하위(자식) 관계를 가진 계층 구조입니다.
|
||||
/// 예: 📁 폴더
|
||||
/// ├─ 📄 파일1
|
||||
/// ├─ 📄 파일2
|
||||
/// └─ 📁 하위폴더
|
||||
/// └─ 📄 파일3
|
||||
/// 목적:
|
||||
/// - 이름/옵션/선택/펼침 상태 및 계층(부모-자식) 보유
|
||||
/// - 상태 변경 시 UI에 알릴 이벤트 제공(데이터 변경/선택 변경)
|
||||
/// - 자식 추가/삽입/복제/재배치/초기화 등 계층 편집 기능
|
||||
///
|
||||
/// 이 클래스는 InfiniteScrollData를 상속하여 UI 스크롤 리스트와 연동됩니다.
|
||||
/// 이벤트 설계:
|
||||
/// - OnDataChanged(ChangedType type, TreeListItemData changed, int index):
|
||||
/// 데이터/구조 변경 통지. changed는 변동 주체(보통 this 또는 대상 자식),
|
||||
/// index는 삽입/이동 시 사용(해당 없으면 -1).
|
||||
/// - OnSelectionChanged(TreeListItemData data, bool isSelected):
|
||||
/// 선택 상태 변경 통지(선택 UI/로직 처리용).
|
||||
///
|
||||
/// 동등성/해시:
|
||||
/// - 이름(Name) 기반 비교/해시 코드. 이름이 같으면 같은 개체로 간주(대소문자 구분).
|
||||
/// - 해시/Equals가 이름에 의존하므로 Name 변경 시 컬렉션 키로 사용 중이면 주의.
|
||||
///
|
||||
/// 주의사항:
|
||||
/// - ClearChildren은 각 자식에 Dispose를 호출하여 이벤트/내부 상태를 정리한 뒤
|
||||
/// 리스트를 비웁니다(연결만 끊는 수준을 넘어 자식도 정리됨).
|
||||
/// - AddClone계열은 전달된 원본 child를 Dispose한 뒤 복제본을 추가합니다(파괴적).
|
||||
/// </summary>
|
||||
public class TreeListItemData: IDisposable
|
||||
{
|
||||
#region 이벤트 (Events)
|
||||
|
||||
/// <summary>
|
||||
/// 데이터가 변경되었을 때 발생하는 이벤트입니다.
|
||||
///
|
||||
/// 용도: 이 데이터의 속성(Name, Option 등)이 변경되면
|
||||
/// UI에 자동으로 반영되도록 통지합니다.
|
||||
///
|
||||
/// 사용 예:
|
||||
/// treeItem.OnDataChanged += (data) => Debug.Log("데이터 변경됨!");
|
||||
/// 데이터/구조 변경 통지 이벤트.
|
||||
/// 시그니처: (ChangedType type, TreeListItemData changed, int index)
|
||||
/// - type: 변경 종류
|
||||
/// - changed: 변경 대상(보통 this 또는 특정 자식)
|
||||
/// - index: 위치 관련 변경 시 사용(해당 없으면 -1)
|
||||
/// </summary>
|
||||
public Action<ChangedType, TreeListItemData, int>? OnDataChanged;
|
||||
|
||||
/// <summary>
|
||||
/// 선택 상태가 변경되었을 때 발생하는 이벤트입니다.
|
||||
///
|
||||
/// 용도: 사용자가 이 아이템을 클릭해서 선택 또는 선택 해제했을 때
|
||||
/// 다른 시스템(예: 옵션 창)에 알려줍니다.
|
||||
///
|
||||
/// 매개변수:
|
||||
/// - TreeListItemData: 변경된 아이템 자신
|
||||
/// - bool: true면 선택됨, false면 선택 해제됨
|
||||
///
|
||||
/// 사용 예:
|
||||
/// treeItem.OnSelectionChanged += (data, isSelected) => {
|
||||
/// Debug.Log($"{data.Name}이 {(isSelected ? "선택됨" : "해제됨")}");
|
||||
/// };
|
||||
/// 선택 상태 변경 통지 이벤트.
|
||||
/// 시그니처: (TreeListItemData data, bool isSelected)
|
||||
/// </summary>
|
||||
public Action<TreeListItemData, bool>? OnSelectionChanged;
|
||||
|
||||
/// <summary>
|
||||
/// 아이템 클릭 시 실행할 사용자 정의 동작.
|
||||
/// (예: 속성 패널 열기, 포커스 이동 등. 확장/축소와는 별개)
|
||||
/// </summary>
|
||||
public Action<TreeListItemData>? OnClickAction;
|
||||
|
||||
#endregion
|
||||
|
||||
#region 내부 필드 (Private Fields)
|
||||
|
||||
/// <summary>
|
||||
/// 아이템의 이름을 저장하는 비공개 필드입니다.
|
||||
///
|
||||
/// '_' 접두사를 붙인 이유:
|
||||
/// 실제 데이터는 여기 저장하고, public 프로퍼티(Name)를 통해
|
||||
/// 접근을 제어합니다. (캡슐화)
|
||||
///
|
||||
/// 예: _name = "폴더", Name 프로퍼티로 접근
|
||||
/// </summary>
|
||||
/// <summary>아이템 이름.</summary>
|
||||
private string _name = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 아이템의 추가 옵션 정보를 저장합니다.
|
||||
///
|
||||
/// 용도: 읽기 전용, 숨김 속성 등의 추가 설정 정보
|
||||
/// 예: "readonly", "hidden", "locked" 등
|
||||
/// </summary>
|
||||
/// <summary>추가 옵션 문자열.</summary>
|
||||
private string _option = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 이 아이템의 자식들이 펼쳐져 있는지 여부를 나타냅니다.
|
||||
///
|
||||
/// true: 자식들이 표시됨 (▼ 펼침 상태)
|
||||
/// false: 자식들이 숨겨짐 (▶ 접혀있는 상태)
|
||||
///
|
||||
/// 자식이 없으면 이 값은 의미가 없습니다.
|
||||
/// </summary>
|
||||
/// <summary>자식 펼침 여부.</summary>
|
||||
private bool _isExpanded = false;
|
||||
|
||||
/// <summary>
|
||||
/// 현재 아이템이 사용자에게 선택되어 있는지를 나타냅니다.
|
||||
///
|
||||
/// true: 선택됨 (보통 배경색이 다르게 표시)
|
||||
/// false: 선택 안 됨 (기본 상태)
|
||||
///
|
||||
/// 예: 파일 탐색기에서 파일을 클릭했을 때 그 파일의 _isSelected = true
|
||||
/// </summary>
|
||||
/// <summary>선택 여부.</summary>
|
||||
private bool _isSelected = false;
|
||||
|
||||
/// <summary>부모</summary>
|
||||
private TreeListItemData? _parent;
|
||||
|
||||
/// <summary>
|
||||
/// 이 아이템의 하위 아이템들을 모두 저장하는 리스트입니다.
|
||||
///
|
||||
/// 트리 구조 예:
|
||||
/// 부모 (이 객체)
|
||||
/// ├─ 자식1
|
||||
/// ├─ 자식2
|
||||
/// └─ 자식3
|
||||
///
|
||||
/// _children 리스트에 [자식1, 자식2, 자식3]이 저장됩니다.
|
||||
/// 자식이 없으면 빈 리스트입니다.
|
||||
/// </summary>
|
||||
/// <summary>자식 리스트.</summary>
|
||||
private List<TreeListItemData> _children = new List<TreeListItemData>();
|
||||
|
||||
#endregion
|
||||
@@ -113,20 +81,8 @@ namespace UVC.UI.List.Tree
|
||||
#region 공개 프로퍼티 (Public Properties)
|
||||
|
||||
/// <summary>
|
||||
/// 아이템의 이름을 가져오거나 설정합니다.
|
||||
///
|
||||
/// 동작:
|
||||
/// - 가져올 때(get): _name의 값을 반환합니다.
|
||||
/// - 설정할 때(set):
|
||||
/// 1. 기존 값과 비교해서 정말 달라졌는지 확인
|
||||
/// 2. 다르면 새 값으로 변경
|
||||
/// 3. OnDataChanged 이벤트를 발생시켜 UI에 알림
|
||||
///
|
||||
/// 이렇게 하는 이유: 같은 값으로 변경되는 불필요한 갱신을 피합니다.
|
||||
///
|
||||
/// 사용 예:
|
||||
/// treeItem.Name = "새로운 이름"; // 자동으로 UI 업데이트
|
||||
/// string currentName = treeItem.Name; // 이름 읽기
|
||||
/// 아이템 이름.
|
||||
/// 변경 시 OnDataChanged(Name) 발생.
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
@@ -142,13 +98,8 @@ namespace UVC.UI.List.Tree
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 아이템의 추가 옵션을 가져오거나 설정합니다.
|
||||
///
|
||||
/// 동작: Name 프로퍼티와 동일하게 작동합니다.
|
||||
/// 변경될 때마다 OnDataChanged 이벤트를 발생시킵니다.
|
||||
///
|
||||
/// 사용 예:
|
||||
/// treeItem.Option = "readonly"; // 읽기 전용으로 설정
|
||||
/// 옵션 문자열.
|
||||
/// 변경 시 OnDataChanged(Option) 발생.
|
||||
/// </summary>
|
||||
public string Option
|
||||
{
|
||||
@@ -164,17 +115,8 @@ namespace UVC.UI.List.Tree
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 이 아이템의 자식들이 펼쳐져 있는지 여부를 가져오거나 설정합니다.
|
||||
///
|
||||
/// internal 접근제한자 이유:
|
||||
/// 이것은 UI 시스템에서만 관리해야 하므로 외부에서 직접 접근할 수 없습니다.
|
||||
/// (같은 어셈블리 내부에서만 접근 가능)
|
||||
///
|
||||
/// true: 자식들이 표시됨 (트리 펼침)
|
||||
/// false: 자식들이 숨겨짐 (트리 접힘)
|
||||
///
|
||||
/// 사용 예: (UI 시스템에서만)
|
||||
/// treeItem.IsExpanded = true; // 자식들을 표시
|
||||
/// 펼침 상태(같은 어셈블리 내 전용).
|
||||
/// 변경 시 OnDataChanged(Expanded) 발생.
|
||||
/// </summary>
|
||||
internal bool IsExpanded
|
||||
{
|
||||
@@ -190,16 +132,8 @@ namespace UVC.UI.List.Tree
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 이 아이템이 선택되어 있는지 여부를 가져오거나 설정합니다.
|
||||
///
|
||||
/// 중요한 차이점: 다른 프로퍼티는 OnDataChanged를 호출하지만,
|
||||
/// 이것은 OnSelectionChanged를 호출합니다.
|
||||
/// 왜? 선택 상태는 UI 갱신이 아니라
|
||||
/// 선택 이벤트 처리가 필요하기 때문입니다.
|
||||
///
|
||||
/// 사용 예:
|
||||
/// treeItem.IsSelected = true; // 아이템 선택
|
||||
/// if (treeItem.IsSelected) { ... } // 선택 여부 확인
|
||||
/// 선택 상태.
|
||||
/// 변경 시 OnSelectionChanged 발생.
|
||||
/// </summary>
|
||||
public bool IsSelected
|
||||
{
|
||||
@@ -216,21 +150,9 @@ namespace UVC.UI.List.Tree
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 사용자가 확장/축소 버튼을 클릭했을 때 호출될 함수입니다.
|
||||
///
|
||||
/// 용도: 트리의 화살표(▼/▶) 버튼을 클릭했을 때
|
||||
/// IsExpanded 상태를 변경하는 로직을 실행합니다.
|
||||
///
|
||||
/// 누가 등록하나? UI 시스템 (TreeListItem 클래스)
|
||||
///
|
||||
/// 사용 예:
|
||||
/// treeItem.OnClickAction = (data) => {
|
||||
/// data.IsExpanded = !data.IsExpanded; // 펼침/접힘 토글
|
||||
/// };
|
||||
/// </summary>
|
||||
public Action<TreeListItemData>? OnClickAction;
|
||||
|
||||
|
||||
/// <summary>부모 데이터(내부 전용).</summary>
|
||||
internal TreeListItemData? Parent
|
||||
{
|
||||
get => _parent;
|
||||
@@ -238,22 +160,8 @@ namespace UVC.UI.List.Tree
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 이 아이템의 모든 자식 아이템들을 가져오거나 설정합니다.
|
||||
///
|
||||
/// internal 접근제한자 이유:
|
||||
/// 자식 리스트는 AddChild, RemoveChild, ClearChildren 메서드로만
|
||||
/// 수정되어야 데이터 일관성이 보장됩니다.
|
||||
///
|
||||
/// 동작:
|
||||
/// - 가져올 때(get): _children 리스트 반환
|
||||
/// - 설정할 때(set):
|
||||
/// 1. null이면 빈 리스트로 설정 (null 방지)
|
||||
/// 2. OnDataChanged 이벤트 발생
|
||||
///
|
||||
/// ?? 연산자 설명:
|
||||
/// childrenItemData ?? new List<>()
|
||||
/// = childrenItemData가 null이면 빈 리스트 사용
|
||||
/// null이 아니면 childrenItemData 사용
|
||||
/// 자식 컬렉션(내부 전용).
|
||||
/// set 시 null을 빈 리스트로 대체하고 OnDataChanged(ResetChildren) 발생.
|
||||
/// </summary>
|
||||
internal List<TreeListItemData> Children
|
||||
{
|
||||
@@ -270,47 +178,23 @@ namespace UVC.UI.List.Tree
|
||||
#region 생성자 (Constructors)
|
||||
|
||||
/// <summary>
|
||||
/// 빈 TreeListItemData를 생성합니다.
|
||||
///
|
||||
/// 초기값:
|
||||
/// - Name: 빈 문자열
|
||||
/// - Option: 빈 문자열
|
||||
/// - IsExpanded: false (접혀있음)
|
||||
/// - Children: 빈 리스트 (자식 없음)
|
||||
///
|
||||
/// 사용 예:
|
||||
/// var item = new TreeListItemData();
|
||||
/// item.Name = "새 폴더";
|
||||
/// 기본 생성자.
|
||||
/// 초기값: 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>
|
||||
/// 이름과 선택적으로 자식 목록을 지정하여 TreeListItemData를 생성합니다.
|
||||
///
|
||||
/// 매개변수:
|
||||
/// - generalName: 아이템의 이름 (필수)
|
||||
/// - childrenItemData: 초기 자식 목록 (선택사항, null 가능)
|
||||
///
|
||||
/// 초기값:
|
||||
/// - Name: generalName
|
||||
/// - Option: 빈 문자열
|
||||
/// - IsExpanded: false
|
||||
/// - Children: childrenItemData (null이면 빈 리스트)
|
||||
///
|
||||
/// 사용 예:
|
||||
/// // 간단한 아이템 생성
|
||||
/// var item1 = new TreeListItemData("폴더");
|
||||
///
|
||||
/// // 자식을 포함해서 생성
|
||||
/// var children = new List<TreeListItemData> { item1 };
|
||||
/// var parent = new TreeListItemData("부모 폴더", children);
|
||||
/// 이름과 초기 자식으로 생성합니다.
|
||||
/// </summary>
|
||||
/// <param name="generalName">아이템 이름(필수).</param>
|
||||
/// <param name="childrenItemData">초기 자식 목록(null 허용).</param>
|
||||
public TreeListItemData(string generalName, List<TreeListItemData>? childrenItemData = null)
|
||||
{
|
||||
_name = generalName;
|
||||
@@ -324,29 +208,21 @@ namespace UVC.UI.List.Tree
|
||||
#region 자식 관리 메서드 (Child Management Methods)
|
||||
|
||||
/// <summary>
|
||||
/// 이 아이템에 자식 아이템을 추가합니다.
|
||||
///
|
||||
/// 동작:
|
||||
/// 1. 자식을 _children 리스트에 추가
|
||||
/// 2. OnDataChanged 이벤트 발생 (UI 트리 구조 갱신)
|
||||
///
|
||||
/// 사용 예:
|
||||
/// parent.AddChild(child); // 부모에 자식 추가
|
||||
///
|
||||
/// 트리 구조 변화:
|
||||
/// 변경 전: 변경 후:
|
||||
/// 부모 부모
|
||||
/// ├─ 자식1 ├─ 자식1
|
||||
/// └─ 자식2 ├─ 자식2
|
||||
/// └─ 새자식
|
||||
/// 자식을 끝에 추가합니다.
|
||||
/// </summary>
|
||||
/// <param name="child">추가할 자식.</param>
|
||||
public void AddChild(TreeListItemData child)
|
||||
{
|
||||
child._parent = this;
|
||||
child.Parent = this;
|
||||
_children.Add(child);
|
||||
NotifyDataChanged(ChangedType.AddChild, child); // UI에 트리 구조 변경 알림
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 자식을 지정 인덱스에 삽입합니다.
|
||||
/// </summary>
|
||||
/// <param name="child">삽입할 자식.</param>
|
||||
/// <param name="index">삽입 인덱스(0 기반).</param>
|
||||
public void AddChildAt(TreeListItemData child, int index)
|
||||
{
|
||||
child._parent = this;
|
||||
@@ -355,64 +231,51 @@ namespace UVC.UI.List.Tree
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 이 아이템에서 지정된 자식 아이템을 자식 목록에서 제거하지만 아이템 자체는 삭제하지 않습니다.
|
||||
///
|
||||
/// 동작:
|
||||
/// 1. 자식을 _children 리스트에서 제거
|
||||
/// 2. OnDataChanged 이벤트 발생
|
||||
///
|
||||
/// 주의: 같은 이름의 첫 번째 자식만 제거됩니다.
|
||||
/// (TreeListItemData의 == 연산자가 Name으로 비교하기 때문)
|
||||
///
|
||||
/// 사용 예:
|
||||
/// parent.RemoveChild(child); // 부모에서 자식 제거
|
||||
///
|
||||
/// 트리 구조 변화:
|
||||
/// 변경 전: 변경 후:
|
||||
/// 부모 부모
|
||||
/// ├─ 자식1 ├─ 자식1
|
||||
/// ├─ 자식2
|
||||
/// └─ 자식3 └─ 자식3
|
||||
/// (자식2 제거)
|
||||
/// 전달된 자식을 복제하여 추가합니다.
|
||||
/// 주의: 원본 child는 Dispose됩니다(파괴적).
|
||||
/// </summary>
|
||||
/// <param name="child">복제 및 대체할 원본 자식.</param>
|
||||
public void AddCloneChild(TreeListItemData child)
|
||||
{
|
||||
NotifyDataChanged(ChangedType.AddCloneChild, child); // UI에 트리 구조 변경 알림
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 전달된 자식을 복제하여 지정 인덱스에 삽입합니다.
|
||||
/// 주의: 원본 child는 Dispose됩니다(파괴적).
|
||||
/// </summary>
|
||||
/// <param name="child">복제 및 대체할 원본 자식.</param>
|
||||
/// <param name="index">삽입 인덱스(0 기반).</param>
|
||||
public void AddCloneAtChild(TreeListItemData child, int index)
|
||||
{
|
||||
NotifyDataChanged(ChangedType.AddCloneAtChild, child, index); // UI에 트리 구조 변경 알림
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 지정 자식을 지정 인덱스 위치에 삽입합니다.
|
||||
/// 주의: 기존 위치에서 제거하지 않으므로 "이동"이 아닌 "삽입" 동작입니다.
|
||||
/// (호출 측에서 기존 위치 정리는 별도로 해야 합니다)
|
||||
/// </summary>
|
||||
/// <param name="child">삽입할 기존 자식 참조.</param>
|
||||
/// <param name="index">삽입 인덱스(0 기반).</param>
|
||||
public void SwapChild(TreeListItemData child, int index)
|
||||
{
|
||||
_children.Insert(index, child);
|
||||
NotifyDataChanged(ChangedType.SwapChild, child, index); // UI에 트리 구조 변경 알림
|
||||
}
|
||||
|
||||
public void RemoveChild(TreeListItemData child)
|
||||
{
|
||||
child._parent = null;
|
||||
int index = _children.IndexOf(child);
|
||||
_children.Remove(child);
|
||||
NotifyDataChanged(ChangedType.RemoveChild, child, index); // UI에 트리 구조 변경 알림
|
||||
if (_children.Remove(child))
|
||||
{
|
||||
child._parent = null;
|
||||
NotifyDataChanged(ChangedType.RemoveChild, child); // UI에 트리 구조 변경 알림
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 이 아이템에서 지정된 자식 아이템을 자식 목록에서 제거하고 아이템 자체도 삭제합니다.
|
||||
/// </summary>
|
||||
/// <param name="child"></param>
|
||||
public void DeleteChild(TreeListItemData child)
|
||||
{
|
||||
RemoveChild(child);
|
||||
child.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 이 아이템의 모든 자식을 한 번에 제거합니다.
|
||||
///
|
||||
/// 동작:
|
||||
/// 1. _children 리스트를 완전히 비움
|
||||
/// 2. OnDataChanged 이벤트 발생
|
||||
///
|
||||
/// 주의: 자식들이 메모리에서 삭제되는 것은 아니고,
|
||||
/// 이 아이템과의 연결만 끊어집니다.
|
||||
/// (C#의 가비지 컬렉션이 필요 없으면 나중에 정리)
|
||||
///
|
||||
/// 사용 예:
|
||||
/// parent.ClearChildren(); // 모든 자식 제거
|
||||
///
|
||||
/// 트리 구조 변화:
|
||||
/// 변경 전: 변경 후:
|
||||
/// 부모 부모
|
||||
/// ├─ 자식1 (모든 자식 제거됨)
|
||||
/// ├─ 자식2
|
||||
/// └─ 자식3
|
||||
/// 모든 자식을 제거하고 정리합니다.
|
||||
/// 각 자식에 대해 Parent=null 및 Dispose()를 호출한 뒤 리스트를 비웁니다.
|
||||
/// </summary>
|
||||
public void ClearChildren()
|
||||
{
|
||||
@@ -430,19 +293,11 @@ namespace UVC.UI.List.Tree
|
||||
#region 내부 메서드 (Internal Methods)
|
||||
|
||||
/// <summary>
|
||||
/// 데이터가 변경되었음을 UI 시스템에 알립니다.
|
||||
///
|
||||
/// 동작: OnDataChanged 이벤트를 발생시킵니다.
|
||||
/// 이를 통해 UI는 자동으로 이 아이템의 정보를 갱신합니다.
|
||||
///
|
||||
/// 호출되는 시점:
|
||||
/// - Name이나 Option이 변경될 때
|
||||
/// - 자식이 추가/제거될 때
|
||||
/// - IsExpanded 상태가 변경될 때
|
||||
///
|
||||
/// 왜 protected인가?
|
||||
/// 이 클래스를 상속받은 자식 클래스에서도 호출할 수 있도록 하기 위함입니다.
|
||||
/// 변경을 구독자에게 알립니다.
|
||||
/// </summary>
|
||||
/// <param name="changedType">변경 종류.</param>
|
||||
/// <param name="target">변경 대상(null이면 this).</param>
|
||||
/// <param name="index">위치 관련 인덱스(없으면 -1).</param>
|
||||
internal void NotifyDataChanged(ChangedType changedType, TreeListItemData? target = null, int index = -1)
|
||||
{
|
||||
// OnDataChanged가 등록되어 있으면 실행
|
||||
@@ -455,28 +310,7 @@ namespace UVC.UI.List.Tree
|
||||
#region 비교 연산자 (Comparison Operators)
|
||||
|
||||
/// <summary>
|
||||
/// 두 TreeListItemData 객체가 같은지 비교합니다. (== 연산자)
|
||||
///
|
||||
/// 비교 기준: Name (아이템의 이름)
|
||||
/// 즉, 이름이 같으면 같은 아이템으로 간주합니다.
|
||||
///
|
||||
/// 비교 로직:
|
||||
/// 1. 같은 객체인가? (메모리 주소가 같음) → true
|
||||
/// 2. 둘 다 null이거나 하나가 null? → false (둘 다 null이면 true인데, 1번에서 처리)
|
||||
/// 3. Name이 같은가? → true/false
|
||||
///
|
||||
/// 사용 예:
|
||||
/// var item1 = new TreeListItemData("파일");
|
||||
/// var item2 = new TreeListItemData("파일");
|
||||
/// var item3 = new TreeListItemData("폴더");
|
||||
///
|
||||
/// item1 == item2 // true (Name이 "파일"로 같음)
|
||||
/// item1 == item3 // false (Name이 다름)
|
||||
/// item1 == null // false
|
||||
///
|
||||
/// 주의: 같은 이름이면 같은 아이템으로 취급되므로,
|
||||
/// 실제로 다른 객체임에도 true가 될 수 있습니다.
|
||||
/// 이는 의도된 설계입니다.
|
||||
/// 이름(대소문자 구분, Ordinal) 기반 동등 비교.
|
||||
/// </summary>
|
||||
public static bool operator ==(TreeListItemData? left, TreeListItemData? right)
|
||||
{
|
||||
@@ -497,12 +331,7 @@ namespace UVC.UI.List.Tree
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 두 TreeListItemData 객체가 다른지 비교합니다. (!= 연산자)
|
||||
///
|
||||
/// 동작: == 연산자의 결과를 반대(!)로 반환합니다.
|
||||
///
|
||||
/// 사용 예:
|
||||
/// if (item1 != item2) { ... } // 다른 아이템이면 실행
|
||||
/// 이름 기반 비동등 비교.
|
||||
/// </summary>
|
||||
public static bool operator !=(TreeListItemData? left, TreeListItemData? right)
|
||||
{
|
||||
@@ -514,23 +343,7 @@ namespace UVC.UI.List.Tree
|
||||
#region 객체 메서드 (Object Methods)
|
||||
|
||||
/// <summary>
|
||||
/// 이 객체의 고유한 해시 코드를 반환합니다.
|
||||
///
|
||||
/// 용도: 이 객체를 Dictionary나 HashSet 같은 컬렉션에 저장할 때 사용합니다.
|
||||
///
|
||||
/// 해시 코드란? 객체를 빠르게 비교/검색하기 위한 고유 숫자입니다.
|
||||
/// 같은 내용이면 같은 해시 코드를 반환해야 합니다.
|
||||
///
|
||||
/// 우리의 기준:
|
||||
/// Name의 해시 코드 = 이 객체의 해시 코드
|
||||
/// 왜? == 연산자에서 Name으로 비교하기 때문입니다.
|
||||
///
|
||||
/// 사용 예:
|
||||
/// int hash = item.GetHashCode();
|
||||
///
|
||||
/// // Dictionary에 저장
|
||||
/// Dictionary<TreeListItemData, string> dict = new();
|
||||
/// dict[item] = "값"; // 내부적으로 GetHashCode() 사용
|
||||
/// 이름 기반 해시 코드 반환.
|
||||
/// </summary>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
@@ -538,29 +351,9 @@ namespace UVC.UI.List.Tree
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 이 객체가 다른 객체와 같은지 비교합니다. (Equals 메서드)
|
||||
///
|
||||
/// 용도: 모든 C# 객체는 Equals 메서드를 가집니다.
|
||||
/// 이 메서드를 오버라이드하여 우리의 비교 로직을 정의합니다.
|
||||
///
|
||||
/// 동작:
|
||||
/// 1. 다른 객체가 TreeListItemData 타입인지 확인
|
||||
/// 2. 맞으면 == 연산자로 비교 (Name으로 비교)
|
||||
/// 3. 아니면 false 반환
|
||||
///
|
||||
/// 사용 예:
|
||||
/// var item1 = new TreeListItemData("파일");
|
||||
/// var item2 = new TreeListItemData("파일");
|
||||
///
|
||||
/// item1.Equals(item2) // true
|
||||
/// item1.Equals("파일") // false (문자열은 다른 타입)
|
||||
/// item1 == item2 // true (== 연산자와 동일)
|
||||
///
|
||||
/// GetHashCode()와 Equals의 관계:
|
||||
/// - 같은 객체면 같은 해시 코드를 가져야 함
|
||||
/// - 우리의 경우: Name이 같으면 Equals는 true, 해시 코드도 같음
|
||||
/// - 이는 일관성 있게 설계되어 있습니다.
|
||||
/// 이름 기반 동등성 비교(== 사용).
|
||||
/// </summary>
|
||||
/// <param name="obj">비교 대상.</param>
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
// obj가 TreeListItemData 타입인지 확인
|
||||
@@ -574,24 +367,49 @@ namespace UVC.UI.List.Tree
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 이벤트/참조/자식 컬렉션을 정리합니다.
|
||||
/// - Parent=null, 이벤트 핸들러 해제, Children.Clear()
|
||||
/// - 하위 항목을 재귀적으로 Dispose하지는 않습니다
|
||||
/// (재귀 정리는 ClearChildren에서 수행).
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if(OnDataChanged != null) OnDataChanged = null;
|
||||
if (_parent != null && _parent.Children.Contains(this)) _parent.Children.Remove(this);
|
||||
_parent = null;
|
||||
if (OnDataChanged != null) OnDataChanged = null;
|
||||
if(OnSelectionChanged != null) OnSelectionChanged = null;
|
||||
if(OnClickAction != null) OnClickAction = null;
|
||||
if(Children != null)
|
||||
{
|
||||
//foreach(var child in Children)
|
||||
//{
|
||||
// child.Dispose();
|
||||
//}
|
||||
Children.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 깊은 복제본을 생성합니다(자식까지 재귀 복제).
|
||||
/// </summary>
|
||||
/// <returns>복제된 새 인스턴스.</returns>
|
||||
public TreeListItemData Clone()
|
||||
{
|
||||
TreeListItemData clone = new TreeListItemData();
|
||||
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.Clone());
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 데이터 변경 종류 열거형.
|
||||
/// </summary>
|
||||
public enum ChangedType
|
||||
{
|
||||
Name,
|
||||
@@ -599,7 +417,10 @@ namespace UVC.UI.List.Tree
|
||||
Expanded,
|
||||
ResetChildren,
|
||||
AddChild,
|
||||
RemoveChild,
|
||||
AddAtChild,
|
||||
RemoveChild
|
||||
AddCloneChild,
|
||||
AddCloneAtChild,
|
||||
SwapChild,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user