드래그앤 드랍 개발 중
This commit is contained in:
@@ -154,6 +154,12 @@ namespace UVC.UI.List.Tree
|
||||
/// </summary>
|
||||
protected List<TreeListItemData> allItemsFlattened = new List<TreeListItemData>();
|
||||
|
||||
public List<TreeListItemData> AllItemsFlattened => allItemsFlattened;
|
||||
|
||||
protected List<TreeListItemData> items = new List<TreeListItemData>();
|
||||
|
||||
public List<TreeListItemData> Items => items;
|
||||
|
||||
/// <summary>
|
||||
/// 드래그 & 드롭 매니저입니다.
|
||||
/// </summary>
|
||||
@@ -247,6 +253,7 @@ namespace UVC.UI.List.Tree
|
||||
/// </summary>
|
||||
public void AddItem(TreeListItemData data)
|
||||
{
|
||||
data.Parent = null;
|
||||
// Instantiate(템플릿, 부모 Transform)
|
||||
// = 템플릿을 복제하고 부모의 자식으로 설정
|
||||
TreeListItem item = GameObject.Instantiate<TreeListItem>(ItemPrefab, root);
|
||||
@@ -255,6 +262,56 @@ namespace UVC.UI.List.Tree
|
||||
// 데이터를 UI에 바인딩하고 이벤트 리스너 등록
|
||||
item.Init(data, this, dragDropManager);
|
||||
|
||||
items.Add(data);
|
||||
|
||||
// 범위 선택에 필요한 평탄화 리스트 업데이트
|
||||
UpdateFlattenedItemList();
|
||||
}
|
||||
|
||||
public void AddItemAt(TreeListItemData data, int index)
|
||||
{
|
||||
data.Parent = null;
|
||||
TreeListItem item = GameObject.Instantiate<TreeListItem>(ItemPrefab, root);
|
||||
item.Init(data, this, dragDropManager);
|
||||
items.Insert(index, data);
|
||||
|
||||
//gameObject 순서 조절
|
||||
item.transform.SetSiblingIndex(index);
|
||||
|
||||
// 범위 선택에 필요한 평탄화 리스트 업데이트
|
||||
UpdateFlattenedItemList();
|
||||
}
|
||||
|
||||
public void RemoveItem(TreeListItemData data)
|
||||
{
|
||||
if (data.Parent != null)
|
||||
{
|
||||
data.Parent.RemoveChild(data);
|
||||
}
|
||||
data.Parent = null;
|
||||
items.Remove(data);
|
||||
//gameObject 삭제
|
||||
// 🎯 root가 부모인 TreeListItem을 찾아 Destroy하기
|
||||
// 1️. root의 모든 직접 자식을 순회
|
||||
for (int i = 0; i < root.childCount; i++)
|
||||
{
|
||||
// 자식 Transform 가져오기
|
||||
Transform childTransform = root.GetChild(i);
|
||||
|
||||
// TreeListItem 컴포넌트 검색
|
||||
TreeListItem treeListItem = childTransform.GetComponent<TreeListItem>();
|
||||
|
||||
// TreeListItem이 있고, 그 데이터가 제거할 데이터와 같으면 삭제
|
||||
if (treeListItem != null && treeListItem.Data == data)
|
||||
{
|
||||
// TreeListItem의 Delete() 메서드로 정리 작업 수행
|
||||
// (이벤트 구독 해제 및 GameObject 삭제)
|
||||
treeListItem.Delete();
|
||||
|
||||
break; // 해당 아이템을 찾았으므로 루프 종료
|
||||
}
|
||||
}
|
||||
|
||||
// 범위 선택에 필요한 평탄화 리스트 업데이트
|
||||
UpdateFlattenedItemList();
|
||||
}
|
||||
@@ -292,17 +349,10 @@ namespace UVC.UI.List.Tree
|
||||
|
||||
// root의 모든 직접 자식을 순회
|
||||
// (손자, 증손자는 재귀로 처리됨)
|
||||
foreach (Transform child in root)
|
||||
{
|
||||
// 자식 게임 오브젝트에서 TreeListItem 컴포넌트 찾기
|
||||
TreeListItem item = child.GetComponent<TreeListItem>();
|
||||
|
||||
// null 체크: 컴포넌트가 있고, 데이터도 있는지 확인
|
||||
if (item != null && item.Data != null)
|
||||
{
|
||||
// 이 아이템과 자식들을 재귀적으로 평탄화 리스트에 추가
|
||||
AddItemToFlattened(item.Data);
|
||||
}
|
||||
foreach (TreeListItemData itemData in items)
|
||||
{
|
||||
// 이 아이템과 자식들을 재귀적으로 평탄화 리스트에 추가
|
||||
AddItemToFlattened(itemData);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#nullable enable
|
||||
using DG.Tweening;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
@@ -331,42 +333,120 @@ namespace UVC.UI.List.Tree
|
||||
valueText.text = data.Name;
|
||||
}
|
||||
|
||||
// 자식 목록이 변경된 경우
|
||||
// UI의 자식 개수와 데이터의 자식 개수 비교
|
||||
int currentChildCount = childRoot.childCount;
|
||||
if (currentChildCount != data.Children.Count)
|
||||
// 자식 목록이 변경된 경우 (추가/제거/순서 변경)
|
||||
// 1️. 현재 UI의 모든 자식 TreeListItem을 수집
|
||||
var currentUIChildren = new List<TreeListItem>();
|
||||
for (int i = 0; i < childRoot.childCount; i++)
|
||||
{
|
||||
// 자식이 추가된 경우
|
||||
if (data.Children.Count > currentChildCount)
|
||||
var childItem = childRoot.GetChild(i).GetComponent<TreeListItem>();
|
||||
if (childItem != null && childItem.Data != null)
|
||||
{
|
||||
// 새로운 자식들을 UI로 생성
|
||||
for (int i = currentChildCount; i < data.Children.Count; i++)
|
||||
{
|
||||
CreateItem(data.Children[i]);
|
||||
}
|
||||
}
|
||||
// 자식이 제거된 경우
|
||||
else if (data.Children.Count < currentChildCount)
|
||||
{
|
||||
// 필요에 따라 UI 아이템 제거 처리
|
||||
while (childRoot.childCount > data.Children.Count)
|
||||
{
|
||||
// 마지막 자식부터 하나씩 삭제
|
||||
Destroy(childRoot.GetChild(childRoot.childCount - 1).gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
// 펼침 버튼 표시 여부 결정
|
||||
// 자식이 있으면 버튼 표시, 없으면 숨김
|
||||
childExpand.gameObject.SetActive(data.Children.Count > 0);
|
||||
|
||||
// 자식이 있고 컨테이너가 비활성화 상태면 활성화
|
||||
if (data.Children.Count > 0 && !childContainer.activeSelf)
|
||||
{
|
||||
childContainer.SetActive(true);
|
||||
SetExpand();
|
||||
currentUIChildren.Add(childItem);
|
||||
}
|
||||
}
|
||||
|
||||
// 2️. 데이터와 UI의 자식 개수가 다르거나 순서가 다르면 동기화
|
||||
bool needsReorder = false;
|
||||
|
||||
// 2-1️. 기본적인 개수 확인
|
||||
if (currentUIChildren.Count != data.Children.Count)
|
||||
{
|
||||
needsReorder = true;
|
||||
}
|
||||
// 2-2️. 개수가 같으면 순서 확인
|
||||
else if (currentUIChildren.Count > 0)
|
||||
{
|
||||
for (int i = 0; i < data.Children.Count; i++)
|
||||
{
|
||||
// UI 아이템의 데이터와 데이터 리스트의 순서가 다르면 재정렬 필요
|
||||
if (currentUIChildren[i].Data != data.Children[i])
|
||||
{
|
||||
needsReorder = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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++)
|
||||
{
|
||||
var childItem = childRoot.GetChild(i).GetComponent<TreeListItem>();
|
||||
if (childItem != null && childItem.Data != null)
|
||||
{
|
||||
updatedUIChildren.Add(childItem);
|
||||
}
|
||||
}
|
||||
|
||||
// 데이터 순서에 맞게 UI 자식들을 정렬
|
||||
for (int i = 0; i < data.Children.Count; i++)
|
||||
{
|
||||
var targetData = data.Children[i];
|
||||
|
||||
// 현재 위치의 UI 자식이 목표 데이터와 다르면 이동
|
||||
if (updatedUIChildren[i].Data != targetData)
|
||||
{
|
||||
// 목표 데이터에 해당하는 UI 자식을 찾기
|
||||
var targetUIChild = updatedUIChildren.FirstOrDefault(ui => ui.Data == targetData);
|
||||
if (targetUIChild != null)
|
||||
{
|
||||
// 목표 위치로 이동 (SetSiblingIndex: 형제 중 순서 변경)
|
||||
targetUIChild.transform.SetSiblingIndex(i);
|
||||
|
||||
// 로컬 리스트에서도 위치 업데이트
|
||||
updatedUIChildren.Remove(targetUIChild);
|
||||
updatedUIChildren.Insert(i, targetUIChild);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 5️. 펼침 버튼 표시 여부 결정
|
||||
childExpand.gameObject.SetActive(data.Children.Count > 0);
|
||||
|
||||
// 확장 상태 변경 않함
|
||||
//if (data.Children.Count > 0 && !childContainer.activeSelf)
|
||||
//{
|
||||
// childContainer.SetActive(true);
|
||||
// SetExpand();
|
||||
//}
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -652,6 +732,7 @@ namespace UVC.UI.List.Tree
|
||||
// 3️. 화살표 회전 애니메이션 실행
|
||||
// DORotate(목표 각도, 지속 시간)
|
||||
// IsExpanded가 true면 0도 (▼), false면 90도 (▶)
|
||||
childExpand.transform.DOKill();
|
||||
childExpand.transform.DORotate(new Vector3(0, 0, data.IsExpanded ? 0 : 90), duration)
|
||||
.OnComplete(() =>
|
||||
{
|
||||
@@ -783,6 +864,7 @@ namespace UVC.UI.List.Tree
|
||||
// -= 연산자: 이벤트에서 리스너 제거
|
||||
data.OnDataChanged -= OnDataChanged;
|
||||
data.OnSelectionChanged -= OnSelectionChanged;
|
||||
data.Parent = null;
|
||||
}
|
||||
|
||||
// 2️. 버튼 클릭 이벤트 구독 해제
|
||||
@@ -791,6 +873,11 @@ namespace UVC.UI.List.Tree
|
||||
itemButton.onClick.RemoveListener(OnItemClicked);
|
||||
}
|
||||
|
||||
if (childExpand != null)
|
||||
{
|
||||
childExpand.transform.DOKill(); // ✅ 진행 중인 회전 애니메이션 중단
|
||||
}
|
||||
|
||||
// 3️. 이 GameObject 삭제
|
||||
// 게임 실행 중에 오브젝트를 제거합니다.
|
||||
GameObject.Destroy(gameObject);
|
||||
|
||||
@@ -92,6 +92,8 @@ namespace UVC.UI.List.Tree
|
||||
/// </summary>
|
||||
private bool _isSelected = false;
|
||||
|
||||
private TreeListItemData? _parent;
|
||||
|
||||
/// <summary>
|
||||
/// 이 아이템의 하위 아이템들을 모두 저장하는 리스트입니다.
|
||||
///
|
||||
@@ -229,6 +231,12 @@ namespace UVC.UI.List.Tree
|
||||
/// </summary>
|
||||
public Action<TreeListItemData>? OnClickAction;
|
||||
|
||||
internal TreeListItemData? Parent
|
||||
{
|
||||
get => _parent;
|
||||
set => _parent = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 이 아이템의 모든 자식 아이템들을 가져오거나 설정합니다.
|
||||
///
|
||||
@@ -334,6 +342,7 @@ namespace UVC.UI.List.Tree
|
||||
/// </summary>
|
||||
public void AddChild(TreeListItemData child)
|
||||
{
|
||||
child._parent = this;
|
||||
_children.Add(child);
|
||||
NotifyDataChanged(); // UI에 트리 구조 변경 알림
|
||||
}
|
||||
@@ -361,6 +370,7 @@ namespace UVC.UI.List.Tree
|
||||
/// </summary>
|
||||
public void RemoveChild(TreeListItemData child)
|
||||
{
|
||||
child._parent = null;
|
||||
_children.Remove(child);
|
||||
NotifyDataChanged(); // UI에 트리 구조 변경 알림
|
||||
}
|
||||
@@ -388,6 +398,10 @@ namespace UVC.UI.List.Tree
|
||||
/// </summary>
|
||||
public void ClearChildren()
|
||||
{
|
||||
foreach (var child in _children)
|
||||
{
|
||||
child._parent = null;
|
||||
}
|
||||
_children.Clear();
|
||||
NotifyDataChanged(); // UI에 트리 구조 변경 알림
|
||||
}
|
||||
|
||||
@@ -80,6 +80,7 @@ namespace UVC.UI.List.Tree
|
||||
private Transform? originalParent;
|
||||
private int originalSiblingIndex;
|
||||
|
||||
private RectTransform? treeListRootParent;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
@@ -99,6 +100,7 @@ namespace UVC.UI.List.Tree
|
||||
treeListItem = item;
|
||||
treeList = list;
|
||||
dragDropManager = manager;
|
||||
treeListRootParent = list.Root.parent as RectTransform;
|
||||
Debug.Log($"[TreeListItemDragHandler] 드래그 핸들러 설정: {item.Data?.Name ?? "Unknown"}");
|
||||
}
|
||||
|
||||
@@ -122,7 +124,7 @@ namespace UVC.UI.List.Tree
|
||||
|
||||
// 드래그 시작 준비: 마우스 위치와 아이템 위치의 오프셋 계산
|
||||
RectTransformUtility.ScreenPointToLocalPointInRectangle(
|
||||
rectTransform,
|
||||
treeListRootParent,//rectTransform,
|
||||
eventData.position,
|
||||
null,
|
||||
out var localPoint);
|
||||
@@ -173,7 +175,7 @@ namespace UVC.UI.List.Tree
|
||||
}
|
||||
|
||||
// 아이템이 마우스를 따라다니도록 위치 업데이트
|
||||
UpdateDragPosition(eventData);
|
||||
//UpdateDragPosition(eventData);
|
||||
|
||||
// 마우스 위의 드롭 대상 찾기
|
||||
var targetItem = GetItemAtMousePosition(eventData.position);
|
||||
@@ -268,7 +270,7 @@ namespace UVC.UI.List.Tree
|
||||
|
||||
// 스크린 좌표를 캔버스 로컬 좌표로 변환
|
||||
if (RectTransformUtility.ScreenPointToLocalPointInRectangle(
|
||||
canvasRect,
|
||||
treeListRootParent, //canvasRect,
|
||||
eventData.position,
|
||||
null,
|
||||
out var canvasLocalPoint))
|
||||
@@ -288,14 +290,28 @@ namespace UVC.UI.List.Tree
|
||||
/// </summary>
|
||||
private void CreateDropIndicator()
|
||||
{
|
||||
Debug.Log($"[CreateDropIndicator] dropIndicator != null:{dropIndicator != null}");
|
||||
if (dropIndicator != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//기존에 생성된 DropIndicator가 있는지 확인
|
||||
var existingDropIndicator = FindDropIndicatorInRoot();
|
||||
if (existingDropIndicator != null)
|
||||
{
|
||||
// 기존 DropIndicator 사용
|
||||
dropIndicator = existingDropIndicator.GetComponent<Image>();
|
||||
dropIndicatorRect = existingDropIndicator.GetComponent<RectTransform>();
|
||||
dropIndicatorParent = treeListRootParent;
|
||||
|
||||
Debug.Log("[CreateDropIndicator] 기존 DropIndicator를 재사용합니다");
|
||||
return;
|
||||
}
|
||||
|
||||
// ✅ Root(root)를 부모로 설정
|
||||
// 계층 구조: TreeList > ScrollView > Viewport > Content > Root > TreeListItem
|
||||
dropIndicatorParent = treeList!.Root.parent as RectTransform;// rectTransform?.parent as RectTransform;
|
||||
dropIndicatorParent = treeListRootParent;// rectTransform?.parent as RectTransform;
|
||||
|
||||
if (dropIndicatorParent == null)
|
||||
{
|
||||
@@ -303,6 +319,7 @@ namespace UVC.UI.List.Tree
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 새로운 GameObject 생성
|
||||
var indicatorGo = new GameObject("DropIndicator");
|
||||
indicatorGo.transform.SetParent(dropIndicatorParent, false);
|
||||
@@ -315,10 +332,10 @@ namespace UVC.UI.List.Tree
|
||||
dropIndicatorRect = indicatorGo.GetComponent<RectTransform>();
|
||||
if (dropIndicatorRect != null)
|
||||
{
|
||||
dropIndicatorRect.anchorMin = new Vector2(0, 0.5f);
|
||||
dropIndicatorRect.anchorMax = new Vector2(1, 0.5f);
|
||||
dropIndicatorRect.pivot = new Vector2(0, 0.5f);
|
||||
dropIndicatorRect.sizeDelta = new Vector2(0, 3f); // 높이 3
|
||||
dropIndicatorRect.anchorMin = new Vector2(0, 1f);
|
||||
dropIndicatorRect.anchorMax = new Vector2(1, 1f);
|
||||
dropIndicatorRect.pivot = new Vector2(0, 1f);
|
||||
dropIndicatorRect.sizeDelta = new Vector2(0, 2f); // 높이 3
|
||||
}
|
||||
|
||||
dropIndicator.raycastTarget = false;
|
||||
@@ -360,45 +377,68 @@ namespace UVC.UI.List.Tree
|
||||
Vector3[] targetCorners = new Vector3[4];
|
||||
targetRect.GetWorldCorners(targetCorners);
|
||||
|
||||
// 🎯 아이템의 월드 X 좌표 (왼쪽 끝)
|
||||
float targetWorldX = targetCorners[0].x;
|
||||
|
||||
// 아이템의 월드 Y 좌표
|
||||
float targetWorldY = targetCorners[0].y;
|
||||
|
||||
// 부모(EntryRoot)의 월드 좌표
|
||||
// 부모(Root)의 월드 좌표
|
||||
Vector3[] parentCorners = new Vector3[4];
|
||||
dropIndicatorParent.GetWorldCorners(parentCorners);
|
||||
float parentWorldY = parentCorners[0].y;
|
||||
float parentWorldX = parentCorners[0].x;
|
||||
float parentHeight = parentCorners[1].y - parentCorners[0].y;
|
||||
float parentWidth = parentCorners[3].x - parentCorners[0].x;
|
||||
|
||||
// 🎯 월드 X를 부모 기준 로컬 X로 변환
|
||||
float relativeX = targetWorldX - parentWorldX;
|
||||
|
||||
// 월드 Y를 부모 기준 로컬 Y로 변환
|
||||
float relativeY = targetWorldY - parentWorldY;
|
||||
|
||||
// 부모의 pivot을 고려한 로컬 Y 계산
|
||||
float pivotAdjustedY = relativeY - (parentHeight * dropIndicatorParent.pivot.y);
|
||||
// 🎯 부모의 pivot을 고려한 로컬 X 계산
|
||||
float pivotAdjustedX = relativeX - (parentWidth * dropIndicatorParent.pivot.x);
|
||||
|
||||
// 부모의 pivot을 고려한 로컬 Y 계산
|
||||
float pivotAdjustedY = relativeY - (parentHeight * dropIndicatorParent.pivot.y) + targetRect.rect.height / 2;
|
||||
|
||||
float indicatorX = 0;
|
||||
float indicatorY = 0;
|
||||
float indicatorHeight = 3f; // 기본 높이
|
||||
|
||||
switch (dropPosition)
|
||||
{
|
||||
case DropPosition.Above:
|
||||
// 대상 아이템 위 (아이템 높이의 절반)
|
||||
indicatorX = pivotAdjustedX;
|
||||
indicatorY = pivotAdjustedY + (targetRect.rect.height / 2);
|
||||
indicatorHeight = 2f; // 얇은 선
|
||||
break;
|
||||
|
||||
case DropPosition.Below:
|
||||
// 대상 아이템 아래
|
||||
indicatorX = pivotAdjustedX;
|
||||
indicatorY = pivotAdjustedY - (targetRect.rect.height / 2);
|
||||
indicatorHeight = 2f; // 얇은 선
|
||||
break;
|
||||
|
||||
case DropPosition.InsideAsChild:
|
||||
// 대상 아이템 중앙
|
||||
indicatorY = pivotAdjustedY;
|
||||
indicatorX = pivotAdjustedX;
|
||||
indicatorY = pivotAdjustedY + (targetRect.rect.height / 2); //indicatorHeight 크기 때문에 반영
|
||||
// 🎯 targetRect의 높이만큼 설정
|
||||
indicatorHeight = targetRect.rect.height;
|
||||
break;
|
||||
}
|
||||
|
||||
// ✅ DropIndicator 위치 설정
|
||||
dropIndicatorRect.anchoredPosition = new Vector2(0, indicatorY);
|
||||
dropIndicatorRect.anchoredPosition = new Vector2(indicatorX, indicatorY);
|
||||
// 🎯 DropIndicator 높이 설정
|
||||
dropIndicatorRect.sizeDelta = new Vector2(dropIndicatorRect.sizeDelta.x, indicatorHeight);
|
||||
//dropIndicatorRect의 x 시작 위치를 targetRect.rect의 위치와 맞춤
|
||||
|
||||
Debug.Log($"[UpdateDropIndicator] 위치: {dropPosition}, targetY: {targetWorldY}, parentY: {parentWorldY}, indicatorY: {indicatorY}");
|
||||
Debug.Log($"[UpdateDropIndicator] {targetItem?.Data?.Name} 위치: {dropPosition}, targetY: {targetWorldY}, parentY: {parentWorldY}, indicatorY: {indicatorY}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -412,6 +452,35 @@ namespace UVC.UI.List.Tree
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// treeListRootParent의 자식 중에서 "DropIndicator"라는 이름의 GameObject를 찾습니다.
|
||||
/// </summary>
|
||||
/// <returns>찾은 GameObject (없으면 null)</returns>
|
||||
private GameObject? FindDropIndicatorInRoot()
|
||||
{
|
||||
if (treeListRootParent == null)
|
||||
{
|
||||
Debug.LogWarning("[FindDropIndicatorInRoot] treeListRootParent가 null입니다");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 1️. treeListRootParent의 모든 직접 자식을 순회
|
||||
for (int i = 0; i < treeListRootParent.childCount; i++)
|
||||
{
|
||||
Transform child = treeListRootParent.GetChild(i);
|
||||
|
||||
// 2️. 자식의 이름이 "DropIndicator"인지 확인
|
||||
if (child.name == "DropIndicator")
|
||||
{
|
||||
Debug.Log("[FindDropIndicatorInRoot] DropIndicator를 찾았습니다");
|
||||
return child.gameObject;
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Log("[FindDropIndicatorInRoot] DropIndicator를 찾지 못했습니다");
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 주어진 스크린 좌표에 있는 TreeListItem을 찾습니다.
|
||||
/// </summary>
|
||||
@@ -438,7 +507,7 @@ namespace UVC.UI.List.Tree
|
||||
foreach (var result in results)
|
||||
{
|
||||
var item = result.gameObject.GetComponentInParent<TreeListItem>();
|
||||
if (item != null && item != treeListItem)
|
||||
if (item != null)// && item != treeListItem)
|
||||
{
|
||||
Debug.Log($"[GetItemAtMousePosition] 찾은 아이템: {item.Data?.Name ?? "Unknown"}");
|
||||
return item;
|
||||
@@ -479,7 +548,7 @@ namespace UVC.UI.List.Tree
|
||||
// 드롭 위치 판단: 대상의 위/아래 또는 자식으로
|
||||
var dropPosition = GetDropPosition(targetItem.GetComponent<RectTransform>());
|
||||
|
||||
Debug.Log($"[HandleDropSuccess] 드롭 위치: {dropPosition}");
|
||||
Debug.Log($"[HandleDropSuccess] 드롭 위치: {targetItem?.Data?.Name} {dropPosition}");
|
||||
|
||||
switch (dropPosition)
|
||||
{
|
||||
@@ -515,8 +584,8 @@ namespace UVC.UI.List.Tree
|
||||
// 하위 1/3: 아래
|
||||
|
||||
var height = targetRect.rect.height;
|
||||
var thresholdUpper = height * 0.33f;
|
||||
var thresholdLower = height * 0.67f;
|
||||
var thresholdUpper = height * 0.2f;
|
||||
var thresholdLower = height * 0.8f;
|
||||
|
||||
// 월드 좌표에서 로컬 좌표로 변환
|
||||
RectTransformUtility.ScreenPointToLocalPointInRectangle(
|
||||
@@ -550,8 +619,69 @@ namespace UVC.UI.List.Tree
|
||||
// 기존 부모에서 제거
|
||||
RemoveFromParent(draggedData);
|
||||
Debug.Log($"[MoveToRoot] {draggedData.Name}을(를) 루트로 이동");
|
||||
// 루트 레벨에 추가 (트리 구조 데이터에만 적용)
|
||||
// UI 업데이트는 이벤트 구독자가 처리
|
||||
|
||||
// ✅ 루트 레벨에 추가
|
||||
// TreeList의 Root는 직접 자식들을 포함하는 컨테이너
|
||||
// Root의 자식 TreeListItem들이 실제 루트 레벨 아이템
|
||||
// 데이터 구조에서 루트 아이템을 찾기 위해 모든 루트 아이템들을 순회
|
||||
//
|
||||
if (treeList == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var root = treeList.Root;
|
||||
if (root == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 1️. 현재 마우스 위치(드래그 대상)를 기반으로 루트 아이템의 인덱스 계산
|
||||
// 2️. AddItemAt()을 호출하여 정확한 위치에 아이템 추가
|
||||
|
||||
// 루트 아이템 중 마우스 위의 아이템 찾기
|
||||
var targetItem = GetItemAtMousePosition(Input.mousePosition);
|
||||
|
||||
if (targetItem != null && targetItem.Data != null)
|
||||
{
|
||||
// 드롭 위치 판단
|
||||
var dropPosition = GetDropPosition(targetItem.GetComponent<RectTransform>());
|
||||
var targetData = targetItem.Data;
|
||||
|
||||
// 대상 아이템이 루트 레벨의 아이템인지 확인
|
||||
if (targetData.Parent == null)
|
||||
{
|
||||
// 루트 레벨 아이템이라면 해당 위치에 삽입
|
||||
int targetIndex = treeList.Items.IndexOf(targetData);
|
||||
|
||||
if (targetIndex >= 0)
|
||||
{
|
||||
// Above: 대상 아이템 앞에 삽입
|
||||
if (dropPosition == DropPosition.Above)
|
||||
{
|
||||
treeList.AddItemAt(draggedData, targetIndex);
|
||||
}
|
||||
// Below: 대상 아이템 뒤에 삽입
|
||||
else if (dropPosition == DropPosition.Below)
|
||||
{
|
||||
treeList.AddItemAt(draggedData, targetIndex + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// InsideAsChild: 루트 레벨에서는 아래에 추가
|
||||
treeList.AddItemAt(draggedData, targetIndex + 1);
|
||||
}
|
||||
|
||||
Debug.Log($"[MoveToRoot] {draggedData.Name}을(를) 루트 레벨의 인덱스 {targetIndex}에 추가");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 루트 레벨에 아이템이 없거나 유효한 위치를 찾지 못한 경우 끝에 추가
|
||||
treeList.AddItem(draggedData);
|
||||
Debug.Log($"[MoveToRoot] {draggedData.Name}을(를) 루트 레벨의 끝에 추가");
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -582,6 +712,21 @@ namespace UVC.UI.List.Tree
|
||||
Debug.Log($"[MoveBefore] {draggedData.Name}을(를) {targetData.Name} 앞으로 이동");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 루트 레벨인 경우
|
||||
var treeListItems = treeList?.Items;
|
||||
if (treeListItems != null)
|
||||
{
|
||||
var targetIndex = treeListItems.IndexOf(targetData);
|
||||
if (targetIndex >= 0)
|
||||
{
|
||||
treeList?.AddItemAt(draggedData, targetIndex);
|
||||
Debug.Log($"[MoveBefore] {draggedData.Name}을(를) {targetData.Name} 앞으로 이동 (루트 레벨)");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -602,18 +747,41 @@ namespace UVC.UI.List.Tree
|
||||
Debug.Log($"[MoveAfter] {draggedData.Name}을(를) {targetData.Name} 뒤로 이동");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 루트 레벨인 경우
|
||||
var treeListItems = treeList?.Items;
|
||||
if (treeListItems != null)
|
||||
{
|
||||
var targetIndex = treeListItems.IndexOf(targetData);
|
||||
if (targetIndex >= 0)
|
||||
{
|
||||
treeList?.AddItemAt(draggedData, targetIndex + 1);
|
||||
Debug.Log($"[MoveAfter] {draggedData.Name}을(를) {targetData.Name} 뒤로 이동 (루트 레벨)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 아이템을 현재 부모에서 제거합니다.
|
||||
/// </summary>
|
||||
private void RemoveFromParent(TreeListItemData item)
|
||||
{
|
||||
|
||||
var parent = FindParentOfItem(item);
|
||||
Debug.Log($"[RemoveFromParent] {item.Name}을(를) 부모 {parent == null}에서 제거");
|
||||
if (parent != null)
|
||||
{
|
||||
parent.RemoveChild(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 루트 레벨에서 제거
|
||||
treeList?.RemoveItem(item);
|
||||
}
|
||||
treeList?.UpdateFlattenedItemList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -626,62 +794,17 @@ namespace UVC.UI.List.Tree
|
||||
return null;
|
||||
}
|
||||
|
||||
// 루트 아이템들 확인
|
||||
foreach (Transform child in treeList.GetComponent<RectTransform>())
|
||||
foreach (TreeListItemData data in treeList!.AllItemsFlattened)
|
||||
{
|
||||
var childItem = child.GetComponent<TreeListItem>();
|
||||
if (childItem?.Data == item)
|
||||
if (data == item)
|
||||
{
|
||||
return null; // 루트 레벨
|
||||
return data.Parent;
|
||||
}
|
||||
}
|
||||
|
||||
// 전체 트리 순회해서 부모 찾기
|
||||
return FindParentRecursive(item, null);
|
||||
}
|
||||
|
||||
private TreeListItemData? FindParentRecursive(TreeListItemData target, TreeListItemData? currentParent)
|
||||
{
|
||||
if (currentParent != null)
|
||||
{
|
||||
foreach (var child in currentParent.Children)
|
||||
{
|
||||
if (child == target)
|
||||
{
|
||||
return currentParent;
|
||||
}
|
||||
|
||||
var found = FindParentRecursive(target, child);
|
||||
if (found != null)
|
||||
{
|
||||
return found;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 루트 레벨의 모든 아이템 확인
|
||||
if (treeList != null)
|
||||
{
|
||||
var root = treeList.GetComponent<RectTransform>();
|
||||
foreach (Transform child in root)
|
||||
{
|
||||
var childItem = child.GetComponent<TreeListItem>();
|
||||
if (childItem?.Data != null)
|
||||
{
|
||||
var found = FindParentRecursive(target, childItem.Data);
|
||||
if (found != null)
|
||||
{
|
||||
return found;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 드롭 위치를 나타내는 열거형입니다.
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user