개발 중
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
#nullable enable
|
||||
using Cysharp.Threading.Tasks;
|
||||
using DG.Tweening;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
@@ -46,7 +48,7 @@ namespace UVC.UI.List.Tree
|
||||
/// (private이지만 Unity가 특별히 접근 가능)
|
||||
/// </summary>
|
||||
[SerializeField]
|
||||
protected TreeList control;
|
||||
protected TreeList treeList;
|
||||
|
||||
/// <summary>
|
||||
/// 이 아이템의 이름을 표시하는 텍스트 UI입니다.
|
||||
@@ -224,7 +226,7 @@ namespace UVC.UI.List.Tree
|
||||
///
|
||||
/// 매개변수:
|
||||
/// - data: 표시할 데이터
|
||||
/// - control: 부모 TreeList (선택 관리, 클릭 처리 등)
|
||||
/// - treeList: 부모 TreeList (선택 관리, 클릭 처리 등)
|
||||
///
|
||||
/// 사용 예:
|
||||
/// var item = Instantiate(prefab);
|
||||
@@ -233,40 +235,29 @@ namespace UVC.UI.List.Tree
|
||||
public void Init(TreeListItemData data, TreeList control, TreeListDragDropManager dragDropManager)
|
||||
{
|
||||
// 1. 기본 정보 할당
|
||||
this.control = control;
|
||||
this.treeList = control;
|
||||
this.data = data;
|
||||
|
||||
gameObject.name = "TreeListItem_" + data.Name;
|
||||
|
||||
// 2. 아이템 이름을 UI에 표시
|
||||
valueText.text = data.Name;
|
||||
|
||||
// 3. 자식 아이템들을 UI로 생성
|
||||
Debug.Log("Creating Children for " + data.Name + ", " + data.Children.Count);
|
||||
|
||||
// 자식이 없는 경우
|
||||
if (data.Children.Count == 0)
|
||||
{
|
||||
// 펼침 버튼 숨기기 (자식이 없으니까 펼칠 게 없음)
|
||||
childExpand.gameObject.SetActive(false);
|
||||
|
||||
// 자식 컨테이너도 숨기기
|
||||
childContainer.SetActive(false);
|
||||
}
|
||||
// 자식이 있는 경우
|
||||
else
|
||||
{
|
||||
if (data.Children.Count > 0)
|
||||
{
|
||||
// 각 자식 데이터에 대해 UI 생성
|
||||
foreach (var childData in data.Children)
|
||||
{
|
||||
CreateItem(childData); // 재귀적으로 트리 구조 생성
|
||||
}
|
||||
|
||||
// 펼침 버튼과 컨테이너 표시
|
||||
childExpand.gameObject.SetActive(true);
|
||||
childContainer.SetActive(true);
|
||||
|
||||
// 화살표 방향 설정 (초기에는 펼쳐짐)
|
||||
SetExpand();
|
||||
|
||||
}
|
||||
// 화살표 방향 설정 (초기에는 펼쳐짐)
|
||||
SetExpand();
|
||||
|
||||
// 4. 데이터 변경 감지 구독
|
||||
// 데이터의 이름, 자식 목록 등이 변경되면 OnDataChanged 호출
|
||||
@@ -323,118 +314,93 @@ namespace UVC.UI.List.Tree
|
||||
/// 데이터가 변경되면 UI가 자동으로 업데이트되는 패턴입니다.
|
||||
/// 이렇게 하면 데이터와 UI를 동기화 유지하기 쉽습니다.
|
||||
/// </summary>
|
||||
private void OnDataChanged(TreeListItemData changedData)
|
||||
private void OnDataChanged(ChangedType changedType, TreeListItemData changedData, int index)
|
||||
{
|
||||
if (data == null) return;
|
||||
|
||||
// 이름이 변경된 경우
|
||||
if (valueText.text != data.Name)
|
||||
if (changedType == ChangedType.Expanded)
|
||||
{
|
||||
valueText.text = data.Name;
|
||||
childContainer.SetActive(data.IsExpanded);
|
||||
// 펼침/접힘 상태 변경 처리
|
||||
SetExpand();
|
||||
return;
|
||||
}
|
||||
|
||||
// 자식 목록이 변경된 경우 (추가/제거/순서 변경)
|
||||
// 1️. 현재 UI의 모든 자식 TreeListItem을 수집
|
||||
var currentUIChildren = new List<TreeListItem>();
|
||||
for (int i = 0; i < childRoot.childCount; i++)
|
||||
{
|
||||
var childItem = childRoot.GetChild(i).GetComponent<TreeListItem>();
|
||||
if (childItem != null && childItem.Data != null)
|
||||
|
||||
if (changedType == ChangedType.Name)
|
||||
{
|
||||
// 이름이 변경된 경우
|
||||
if (valueText.text != data.Name)
|
||||
{
|
||||
currentUIChildren.Add(childItem);
|
||||
valueText.text = data.Name;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 2️. 데이터와 UI의 자식 개수가 다르거나 순서가 다르면 동기화
|
||||
bool needsReorder = false;
|
||||
|
||||
// 2-1️. 기본적인 개수 확인
|
||||
if (currentUIChildren.Count != data.Children.Count)
|
||||
if (changedType == ChangedType.ResetChildren)
|
||||
{
|
||||
needsReorder = true;
|
||||
}
|
||||
// 2-2️. 개수가 같으면 순서 확인
|
||||
else if (currentUIChildren.Count > 0)
|
||||
{
|
||||
for (int i = 0; i < data.Children.Count; i++)
|
||||
// 전체 리셋 처리
|
||||
// 자식 모두 삭제 후 재생성
|
||||
for (int i = childRoot.childCount - 1; i >= 0; i--)
|
||||
{
|
||||
// UI 아이템의 데이터와 데이터 리스트의 순서가 다르면 재정렬 필요
|
||||
if (currentUIChildren[i].Data != data.Children[i])
|
||||
var childItem = childRoot.GetChild(i).GetComponent<TreeListItem>();
|
||||
if (childItem != null)
|
||||
{
|
||||
needsReorder = true;
|
||||
break;
|
||||
childItem.Delete(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3️. 재정렬이 필요하면 전체 재구성
|
||||
if (needsReorder)
|
||||
{
|
||||
// 데이터에는 있지만 UI에는 없는 자식 추가
|
||||
var childrenToAdd = new List<TreeListItemData>();
|
||||
foreach (var childData in data.Children)
|
||||
{
|
||||
bool uiChildExists = currentUIChildren.Any(ui => ui.Data == childData);
|
||||
if (!uiChildExists)
|
||||
{
|
||||
childrenToAdd.Add(childData);
|
||||
}
|
||||
}
|
||||
|
||||
// UI에는 있지만 데이터에는 없는 자식 제거
|
||||
var childrenToDestroy = new List<TreeListItem>();
|
||||
foreach (var uiChild in currentUIChildren)
|
||||
{
|
||||
if (uiChild.Data != null && !data.Children.Contains(uiChild.Data))
|
||||
{
|
||||
childrenToDestroy.Add(uiChild);
|
||||
}
|
||||
}
|
||||
|
||||
// 수집한 자식들 삭제
|
||||
foreach (var child in childrenToDestroy)
|
||||
{
|
||||
child.Delete();
|
||||
}
|
||||
|
||||
// 새로운 자식 추가
|
||||
foreach (var childData in childrenToAdd)
|
||||
{
|
||||
CreateItem(childData);
|
||||
}
|
||||
|
||||
// 4️. 자식들을 데이터 순서대로 재정렬
|
||||
// 현재 UI 자식들을 다시 수집 (추가/삭제 후이므로)
|
||||
var updatedUIChildren = new List<TreeListItem>();
|
||||
for (int i = 0; i < childRoot.childCount; i++)
|
||||
treeList.UpdateFlattenedItemDataList();
|
||||
}
|
||||
else if (changedType == ChangedType.AddChild)
|
||||
{
|
||||
TreeListItem? item = treeList.AllItemFlattened.FirstOrDefault(x => x.Data == changedData);
|
||||
if (item != null)
|
||||
{
|
||||
var childItem = childRoot.GetChild(i).GetComponent<TreeListItem>();
|
||||
if (childItem != null && childItem.Data != null)
|
||||
{
|
||||
updatedUIChildren.Add(childItem);
|
||||
}
|
||||
item.transform.SetParent(childRoot);
|
||||
item.SetExpand();
|
||||
}
|
||||
else
|
||||
{
|
||||
CreateItem(changedData);
|
||||
}
|
||||
UniTask.DelayFrame(1).ContinueWith(() =>
|
||||
{
|
||||
if (item != null) item.transform.SetAsLastSibling();
|
||||
treeList.UpdateFlattenedItemDataList();
|
||||
});
|
||||
}
|
||||
else if (changedType == ChangedType.AddAtChild)
|
||||
{
|
||||
TreeListItem? item = treeList.AllItemFlattened.FirstOrDefault(x => x.Data == changedData);
|
||||
if (item != null)
|
||||
{
|
||||
item.transform.SetParent(childRoot);
|
||||
item.SetExpand();
|
||||
}
|
||||
else
|
||||
{
|
||||
item = CreateItem(changedData);
|
||||
}
|
||||
|
||||
// 데이터 순서에 맞게 UI 자식들을 정렬
|
||||
for (int i = 0; i < data.Children.Count; i++)
|
||||
UniTask.DelayFrame(1).ContinueWith(() =>
|
||||
{
|
||||
var targetData = data.Children[i];
|
||||
|
||||
// 현재 위치의 UI 자식이 목표 데이터와 다르면 이동
|
||||
if (updatedUIChildren[i].Data != targetData)
|
||||
item.transform.SetSiblingIndex(index);
|
||||
treeList.UpdateFlattenedItemDataList();
|
||||
});
|
||||
}
|
||||
else if (changedType == ChangedType.RemoveChild)
|
||||
{
|
||||
var childItem = childRoot.GetChild(index).GetComponent<TreeListItem>();
|
||||
if (childItem != null)
|
||||
{
|
||||
childItem.Delete(false);
|
||||
UniTask.DelayFrame(1).ContinueWith(() =>
|
||||
{
|
||||
// 목표 데이터에 해당하는 UI 자식을 찾기
|
||||
var targetUIChild = updatedUIChildren.FirstOrDefault(ui => ui.Data == targetData);
|
||||
if (targetUIChild != null)
|
||||
{
|
||||
// 목표 위치로 이동 (SetSiblingIndex: 형제 중 순서 변경)
|
||||
targetUIChild.transform.SetSiblingIndex(i);
|
||||
|
||||
// 로컬 리스트에서도 위치 업데이트
|
||||
updatedUIChildren.Remove(targetUIChild);
|
||||
updatedUIChildren.Insert(i, targetUIChild);
|
||||
}
|
||||
}
|
||||
treeList.UpdateFlattenedItemDataList();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -655,7 +621,7 @@ namespace UVC.UI.List.Tree
|
||||
// 4️. 부모 TreeList에 클릭 정보 전달
|
||||
// TreeList는 이 정보를 받아서 선택 로직을 처리합니다.
|
||||
// (단일 선택 / 다중 선택 / 범위 선택 등)
|
||||
control.OnItemClicked(data, ctrlPressed, shiftPressed);
|
||||
treeList.OnItemClicked(data, ctrlPressed, shiftPressed);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -690,12 +656,6 @@ namespace UVC.UI.List.Tree
|
||||
// ! 연산자: 반대로 변경 (true → false, false → true)
|
||||
data!.IsExpanded = !data.IsExpanded;
|
||||
|
||||
// 3️. 자식 컨테이너 표시/숨김
|
||||
// IsExpanded가 true면 표시, false면 숨김
|
||||
childContainer.SetActive(data.IsExpanded);
|
||||
|
||||
// 4️. 0.3초에 걸쳐 펼침/접힘 애니메이션 실행
|
||||
SetExpand(0.3f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -719,34 +679,23 @@ namespace UVC.UI.List.Tree
|
||||
/// 애니메이션이 완료되면 호출되는 콜백입니다.
|
||||
/// 람다 식(=>)으로 익명 함수를 정의합니다.
|
||||
/// </summary>
|
||||
private void SetExpand(float duration = 0.0f)
|
||||
internal void SetExpand(float duration = 0.0f)
|
||||
{
|
||||
// 1️. 자식이 있는지 확인해서 펼침 버튼 표시 여부 결정
|
||||
childExpand.gameObject.SetActive(childRoot.childCount > 0);
|
||||
|
||||
// 2️. 자식이 있는 경우에만 애니메이션 실행
|
||||
if (childRoot.childCount > 0)
|
||||
{
|
||||
// UI 상태와 데이터 동기화
|
||||
if (data != null) data.IsExpanded = childContainer.activeSelf == true;
|
||||
// 3️. 화살표 회전 애니메이션 실행
|
||||
// DORotate(목표 각도, 지속 시간)
|
||||
// IsExpanded가 true면 0도 (▼), false면 90도 (▶)
|
||||
childExpand.transform.DOKill();
|
||||
childExpand.transform.DORotate(new Vector3(0, 0, data.IsExpanded ? 0 : 90), duration)
|
||||
.OnComplete(() =>
|
||||
{
|
||||
// 4️. 애니메이션이 완료되면 플래그 리셋
|
||||
// 이제 다시 ToggleChild() 호출 가능
|
||||
isAnimating = false;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
// 자식이 없으면 IsExpanded는 항상 false
|
||||
if (data != null) data.IsExpanded = false;
|
||||
}
|
||||
// 펼침 버튼
|
||||
childExpand.gameObject.SetActive(data!.Children.Count > 0);
|
||||
childContainer.SetActive(data!.Children.Count > 0 && data!.IsExpanded);
|
||||
|
||||
// DORotate(목표 각도, 지속 시간)
|
||||
// IsExpanded가 true면 0도 (▼), false면 90도 (▶)
|
||||
childExpand.transform.DOKill();
|
||||
childExpand.transform.DORotate(new Vector3(0, 0, data!.IsExpanded ? 0 : 90), duration)
|
||||
.OnComplete(() =>
|
||||
{
|
||||
// 4️. 애니메이션이 완료되면 플래그 리셋
|
||||
// 이제 다시 ToggleChild() 호출 가능
|
||||
isAnimating = false;
|
||||
});
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -794,7 +743,7 @@ namespace UVC.UI.List.Tree
|
||||
/// - AddChild()에서 새 자식을 추가할 때
|
||||
///
|
||||
/// 동작:
|
||||
/// 1. control.ItemPrefab를 childRoot 아래에 인스턴스화
|
||||
/// 1. treeList.ItemPrefab를 childRoot 아래에 인스턴스화
|
||||
/// 2. 생성된 item의 Init() 메서드 호출
|
||||
/// 3. 생성된 item 반환
|
||||
///
|
||||
@@ -816,15 +765,15 @@ namespace UVC.UI.List.Tree
|
||||
{
|
||||
// 1️. 프리팹을 복제해서 새로운 TreeListItem 생성
|
||||
// Instantiate<T>(원본, 부모, 옵션)
|
||||
// control.ItemPrefab: UI 아이템 템플릿
|
||||
// treeList.ItemPrefab: UI 아이템 템플릿
|
||||
// childRoot: 새 아이템의 부모 Transform
|
||||
TreeListItem item = GameObject.Instantiate<TreeListItem>(
|
||||
control.ItemPrefab, // 복제할 프리팹
|
||||
treeList.ItemPrefab, // 복제할 프리팹
|
||||
childRoot // 부모로 배치할 위치
|
||||
);
|
||||
|
||||
// 2️. 생성된 아이템 초기화
|
||||
item.Init(data, control, control.DragDropManager);
|
||||
item.Init(data, treeList, treeList.DragDropManager);
|
||||
|
||||
// 3️. 생성된 아이템 반환
|
||||
return item;
|
||||
@@ -856,7 +805,7 @@ namespace UVC.UI.List.Tree
|
||||
/// 게임이 계속 실행되면서 메모리 사용량이 증가해서
|
||||
/// 결국 게임이 느려지거나 충돌할 수 있습니다.
|
||||
/// </summary>
|
||||
public void Delete()
|
||||
public void Delete(bool deleteData = false)
|
||||
{
|
||||
// 1️. 데이터 변경 이벤트 구독 해제
|
||||
if (data != null)
|
||||
@@ -865,6 +814,8 @@ namespace UVC.UI.List.Tree
|
||||
data.OnDataChanged -= OnDataChanged;
|
||||
data.OnSelectionChanged -= OnSelectionChanged;
|
||||
data.Parent = null;
|
||||
if(deleteData) data.Dispose();
|
||||
data = null;
|
||||
}
|
||||
|
||||
// 2️. 버튼 클릭 이벤트 구독 해제
|
||||
@@ -908,6 +859,9 @@ namespace UVC.UI.List.Tree
|
||||
{
|
||||
data.OnDataChanged -= OnDataChanged;
|
||||
data.OnSelectionChanged -= OnSelectionChanged;
|
||||
data.Parent = null;
|
||||
data.Dispose();
|
||||
data = null;
|
||||
}
|
||||
|
||||
// 2️. 버튼 클릭 이벤트 구독 해제
|
||||
|
||||
Reference in New Issue
Block a user