Files
XRLib/Assets/Scripts/UVC/UI/List/Tree/TreeListItemDragHandler.cs

697 lines
25 KiB
C#
Raw Normal View History

2025-10-28 20:10:51 +09:00
#nullable enable
2025-11-03 19:07:04 +09:00
using System.Collections.Generic;
2025-10-28 20:10:51 +09:00
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
namespace UVC.UI.List.Tree
{
/// <summary>
/// TreeListItem의 드래그 & 드롭 입력을 처리하는 컴포넌트입니다.
/// </summary>
public class TreeListItemDragHandler : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{
2025-11-03 19:07:04 +09:00
private static readonly List<RaycastResult> s_RaycastResults = new List<RaycastResult>(32);
/// <summary>부모 TreeListItem.</summary>
2025-10-28 20:10:51 +09:00
private TreeListItem? treeListItem;
/// <summary>부모 TreeList.</summary>
2025-10-28 20:10:51 +09:00
private TreeList? treeList;
/// <summary>드래그 & 드롭 매니저.</summary>
2025-10-28 20:10:51 +09:00
private TreeListDragDropManager? dragDropManager;
private RectTransform? rectTransform;
/// <summary>드래그 시각 피드백용 CanvasGroup.</summary>
2025-10-28 20:10:51 +09:00
private CanvasGroup? canvasGroup;
/// <summary>드래그 시작 전 원본 알파값.</summary>
2025-10-28 20:10:51 +09:00
private float originalAlpha = 1f;
private Vector2 dragOffset = Vector2.zero;
/// <summary>드래그 중 적용할 알파값.</summary>
2025-10-28 20:10:51 +09:00
[SerializeField]
private float dragAlpha = 0.5f;
/// <summary>드래그 활성화 여부.</summary>
2025-10-28 20:10:51 +09:00
[SerializeField]
private bool enableDragDrop = true;
/// <summary>드롭 위치 표시 라인/블록.</summary>
2025-10-28 20:10:51 +09:00
private Image? dropIndicator;
private RectTransform? dropIndicatorRect;
/// <summary>드롭 표시기의 부모(RectTransform).</summary>
2025-10-28 20:10:51 +09:00
private RectTransform? dropIndicatorParent;
/// <summary>드래그 시작 시의 원본 부모/순서.</summary>
2025-10-28 20:10:51 +09:00
private Transform? originalParent;
private int originalSiblingIndex;
2025-10-29 20:12:11 +09:00
private RectTransform? treeListRootParent;
2025-10-28 20:10:51 +09:00
2025-11-03 19:07:04 +09:00
/// <summary>
/// 드롭 위치의 상/하 경계 비율.0.3이면 상하30%는 위/아래, 가운데40%는 자식으로 판단.
/// </summary>
[SerializeField, Range(0.05f, 0.45f)]
private float dropEdgeRatio = 0.3f;
// Auto-scroll settings
[Header("Auto Scroll While Dragging")]
[SerializeField]
private bool autoScrollWhileDragging = true;
[SerializeField, Tooltip("픽셀 단위 초당 스크롤 속도")]
private float autoScrollPixelsPerSecond = 800f;
[SerializeField, Tooltip("뷰포트 상/하단 가장자리에서 오토 스크롤을 시작하는 패딩(픽셀)")]
private float autoScrollEdgePadding = 24f;
private Vector2 _lastPointerScreenPos;
2025-10-28 20:10:51 +09:00
private void Awake()
{
rectTransform = GetComponent<RectTransform>();
// CanvasGroup 가져오기 (없으면 생성)
canvasGroup = GetComponent<CanvasGroup>();
if (canvasGroup == null)
{
canvasGroup = gameObject.AddComponent<CanvasGroup>();
}
originalAlpha = canvasGroup.alpha;
}
/// <summary>
/// 외부에서 드래그 매니저 및 상위 컨텍스트를 주입합니다.
/// </summary>
2025-10-28 20:10:51 +09:00
public void SetDragDropManager(TreeListItem item, TreeList list, TreeListDragDropManager manager)
{
treeListItem = item;
treeList = list;
dragDropManager = manager;
2025-10-29 20:12:11 +09:00
treeListRootParent = list.Root.parent as RectTransform;
2025-11-14 17:02:38 +09:00
//Debug.Log($"[TreeListItemDragHandler] 드래그 핸들러 설정: {item.Data?.Name ?? "Unknown"}");
2025-10-28 20:10:51 +09:00
}
public void OnBeginDrag(PointerEventData eventData)
{
if (!enableDragDrop || treeListItem?.Data == null || dragDropManager == null)
{
return;
}
if (eventData.button != PointerEventData.InputButton.Left)
{
return;
}
RectTransformUtility.ScreenPointToLocalPointInRectangle(
2025-11-03 19:07:04 +09:00
treeListRootParent,
eventData.position,
null,
out var localPoint);
2025-10-28 20:10:51 +09:00
dragOffset = localPoint;
2025-11-03 19:07:04 +09:00
_lastPointerScreenPos = eventData.position;
2025-10-28 20:10:51 +09:00
2025-11-14 17:02:38 +09:00
//Debug.Log($"[OnPointerDown] {treeListItem.Data.Name}에 포인터 다운, offset: {dragOffset}");
2025-10-28 20:10:51 +09:00
}
public void OnDrag(PointerEventData eventData)
{
if (!enableDragDrop || dragDropManager == null || treeListItem?.Data == null || treeList == null || rectTransform == null)
{
Debug.LogWarning("[OnDrag] 필수 컴포넌트 누락 또는 비활성화됨");
return;
}
if (eventData.button != PointerEventData.InputButton.Left)
{
return;
}
2025-11-03 19:07:04 +09:00
_lastPointerScreenPos = eventData.position;
2025-10-28 20:10:51 +09:00
2025-11-14 17:02:38 +09:00
//Debug.Log($"[OnDrag] {treeListItem.Data.Name} 드래그 중");
2025-10-28 20:10:51 +09:00
if (!dragDropManager.IsDragging)
{
dragDropManager.StartDrag(treeListItem.Data);
originalParent = rectTransform.parent;
originalSiblingIndex = rectTransform.GetSiblingIndex();
if (canvasGroup != null)
{
canvasGroup.alpha = dragAlpha;
canvasGroup.blocksRaycasts = false;
}
CreateDropIndicator();
2025-11-14 17:02:38 +09:00
//Debug.Log($"[OnDrag] {treeListItem.Data.Name} 드래그 시작");
2025-10-28 20:10:51 +09:00
}
// 마우스 위의 드롭 대상 찾기
var targetItem = GetItemAtMousePosition(eventData.position);
2025-11-03 19:07:04 +09:00
if (targetItem != null)
{
var targetRect = targetItem.GetComponent<RectTransform>();
if (targetRect != null)
{
var pos = GetDropPosition(targetRect);
dragDropManager.OnDragOver(targetItem.Data, pos);
}
else
{
dragDropManager.OnDragOver(targetItem.Data);
}
}
else
{
dragDropManager.OnDragOver((TreeListItemData?)null);
}
2025-10-28 20:10:51 +09:00
// 드롭 위치 표시 갱신(위/아래/자식)
2025-10-28 20:10:51 +09:00
UpdateDropIndicator(targetItem);
}
2025-11-03 19:07:04 +09:00
private void Update()
{
// 지속 오토 스크롤 및 표시기 갱신(마우스가 움직이지 않아도 동작)
if (!autoScrollWhileDragging || dragDropManager == null || !dragDropManager.IsDragging) return;
if (treeListItem?.Data == null || dragDropManager.DraggedItem != treeListItem.Data) return;
AutoScrollIfNeeded();
// 스크롤로 콘텐츠가 움직이면 표시기도 갱신
var targetItem = GetItemAtMousePosition(_lastPointerScreenPos);
UpdateDropIndicator(targetItem);
}
2025-10-28 20:10:51 +09:00
public void OnEndDrag(PointerEventData eventData)
{
if (!enableDragDrop || dragDropManager == null)
{
return;
}
2025-11-14 17:02:38 +09:00
//Debug.Log("[OnPointerUp] 드래그 완료");
2025-10-28 20:10:51 +09:00
if (canvasGroup != null)
{
canvasGroup.alpha = originalAlpha;
canvasGroup.blocksRaycasts = true;
}
if (originalParent != null)
{
rectTransform?.SetParent(originalParent);
if (rectTransform != null)
{
rectTransform.SetSiblingIndex(originalSiblingIndex);
}
}
HideDropIndicator();
2025-11-03 19:07:04 +09:00
// 드래그가 시작되지 않았으면 종료 처리만
2025-10-28 20:10:51 +09:00
if (!dragDropManager.IsDragging)
{
dragDropManager.EndDrag();
return;
}
var targetItem = GetItemAtMousePosition(eventData.position);
if (treeListItem?.Data != null)
{
var result = dragDropManager.TryDrop(targetItem?.Data);
2025-11-14 17:02:38 +09:00
//Debug.Log($"[OnPointerUp] 드롭 결과: {(result ? "성공" : "실패")}");
2025-10-28 20:10:51 +09:00
if (result) HandleDropSuccess(treeListItem.Data, targetItem);
2025-10-28 20:10:51 +09:00
}
2025-11-03 19:07:04 +09:00
// 계약상 TryDrop은 EndDrag를 호출하지 않음 → 여기서 종료
2025-10-28 20:10:51 +09:00
dragDropManager.EndDrag();
}
2025-11-03 19:07:04 +09:00
private void AutoScrollIfNeeded()
{
var sr = treeList != null ? treeList.GetComponent<ScrollRect>() : null;
if (sr == null || sr.content == null) return;
var viewport = sr.viewport != null ? sr.viewport : sr.transform as RectTransform;
if (viewport == null) return;
// 콘텐츠가 스크롤 가능하지 않으면 종료
float scrollableHeight = Mathf.Max(0f, sr.content.rect.height - viewport.rect.height);
if (scrollableHeight <= 1f)
{
return;
}
// 마우스의 Y 위치와 뷰포트 상/하단 비교(월드 좌표)
Vector3[] corners = new Vector3[4];
viewport.GetWorldCorners(corners);
float bottomY = corners[0].y;
float topY = corners[1].y;
float mouseY = _lastPointerScreenPos.y;
float dt = Time.unscaledDeltaTime;
float pixelsPerSec = autoScrollPixelsPerSecond;
if (mouseY > topY - autoScrollEdgePadding)
{
// 위로 스크롤(정규화 값 증가)
float factor = Mathf.Clamp01((mouseY - (topY - autoScrollEdgePadding)) / autoScrollEdgePadding);
float deltaNorm = (pixelsPerSec * dt / scrollableHeight) * factor;
sr.verticalNormalizedPosition = Mathf.Clamp01(sr.verticalNormalizedPosition + deltaNorm);
}
else if (mouseY < bottomY + autoScrollEdgePadding)
{
// 아래로 스크롤(정규화 값 감소)
float factor = Mathf.Clamp01(((bottomY + autoScrollEdgePadding) - mouseY) / autoScrollEdgePadding);
float deltaNorm = (pixelsPerSec * dt / scrollableHeight) * factor;
sr.verticalNormalizedPosition = Mathf.Clamp01(sr.verticalNormalizedPosition - deltaNorm);
}
}
2025-10-28 20:10:51 +09:00
private void UpdateDragPosition(PointerEventData eventData)
{
if (rectTransform == null || treeList == null)
{
return;
}
var canvasRect = treeList.GetComponent<RectTransform>();
if (canvasRect == null)
{
return;
}
if (RectTransformUtility.ScreenPointToLocalPointInRectangle(
2025-11-03 19:07:04 +09:00
treeListRootParent,
eventData.position,
null,
out var canvasLocalPoint))
2025-10-28 20:10:51 +09:00
{
var currentPosition = rectTransform.anchoredPosition;
rectTransform.anchoredPosition = new Vector2(
2025-11-03 19:07:04 +09:00
currentPosition.x,
canvasLocalPoint.y - dragOffset.y
2025-10-28 20:10:51 +09:00
);
}
}
private void CreateDropIndicator()
{
if (dropIndicator != null)
{
return;
}
2025-10-29 20:12:11 +09:00
var existingDropIndicator = FindDropIndicatorInRoot();
if (existingDropIndicator != null)
{
dropIndicator = existingDropIndicator.GetComponent<Image>();
dropIndicatorRect = existingDropIndicator.GetComponent<RectTransform>();
dropIndicatorParent = treeListRootParent;
2025-11-14 17:02:38 +09:00
//Debug.Log("[CreateDropIndicator] 기존 DropIndicator를 재사용합니다");
2025-10-29 20:12:11 +09:00
return;
}
2025-11-03 19:07:04 +09:00
dropIndicatorParent = treeListRootParent;
2025-10-28 20:10:51 +09:00
if (dropIndicatorParent == null)
{
Debug.LogError("[CreateDropIndicator] EntryRoot/Content를 찾을 수 없습니다");
return;
}
var indicatorGo = new GameObject("DropIndicator");
indicatorGo.transform.SetParent(dropIndicatorParent, false);
dropIndicator = indicatorGo.AddComponent<Image>();
2025-11-03 19:07:04 +09:00
dropIndicator.color = new Color(0.2f, 0.8f, 1f, 0.5f);
2025-10-28 20:10:51 +09:00
dropIndicatorRect = indicatorGo.GetComponent<RectTransform>();
if (dropIndicatorRect != null)
{
2025-10-29 20:12:11 +09:00
dropIndicatorRect.anchorMin = new Vector2(0, 1f);
dropIndicatorRect.anchorMax = new Vector2(1, 1f);
dropIndicatorRect.pivot = new Vector2(0, 1f);
2025-11-03 19:07:04 +09:00
dropIndicatorRect.sizeDelta = new Vector2(0, 2f);
2025-10-28 20:10:51 +09:00
}
dropIndicator.raycastTarget = false;
indicatorGo.SetActive(false);
2025-11-14 17:02:38 +09:00
//Debug.Log("[CreateDropIndicator] 드롭 위치 표시 막대 생성됨");
2025-10-28 20:10:51 +09:00
}
private void UpdateDropIndicator(TreeListItem? targetItem)
{
if (dropIndicator == null || dropIndicatorRect == null || dropIndicatorParent == null)
{
return;
}
if (targetItem == null)
{
dropIndicator.gameObject.SetActive(false);
return;
}
var targetRect = targetItem.GetComponent<RectTransform>();
if (targetRect == null)
{
dropIndicator.gameObject.SetActive(false);
return;
}
var dropPosition = GetDropPosition(targetRect);
dropIndicator.gameObject.SetActive(true);
Vector3[] targetCorners = new Vector3[4];
targetRect.GetWorldCorners(targetCorners);
2025-10-29 20:12:11 +09:00
float targetWorldX = targetCorners[0].x;
2025-10-28 20:10:51 +09:00
float targetWorldY = targetCorners[0].y;
Vector3[] parentCorners = new Vector3[4];
dropIndicatorParent.GetWorldCorners(parentCorners);
float parentWorldY = parentCorners[0].y;
2025-10-29 20:12:11 +09:00
float parentWorldX = parentCorners[0].x;
2025-10-28 20:10:51 +09:00
float parentHeight = parentCorners[1].y - parentCorners[0].y;
2025-10-29 20:12:11 +09:00
float parentWidth = parentCorners[3].x - parentCorners[0].x;
float relativeX = targetWorldX - parentWorldX;
2025-10-28 20:10:51 +09:00
float relativeY = targetWorldY - parentWorldY;
2025-10-29 20:12:11 +09:00
float pivotAdjustedX = relativeX - (parentWidth * dropIndicatorParent.pivot.x);
float pivotAdjustedY = relativeY - (parentHeight * dropIndicatorParent.pivot.y) + targetRect.rect.height / 2;
2025-10-28 20:10:51 +09:00
2025-10-29 20:12:11 +09:00
float indicatorX = 0;
2025-10-28 20:10:51 +09:00
float indicatorY = 0;
2025-11-03 19:07:04 +09:00
float indicatorHeight = 3f;
2025-10-28 20:10:51 +09:00
switch (dropPosition)
{
2025-11-03 19:07:04 +09:00
case TreeDropPosition.Above:
2025-10-29 20:12:11 +09:00
indicatorX = pivotAdjustedX;
2025-10-28 20:10:51 +09:00
indicatorY = pivotAdjustedY + (targetRect.rect.height / 2);
2025-11-03 19:07:04 +09:00
indicatorHeight = 2f;
2025-10-28 20:10:51 +09:00
break;
2025-11-03 19:07:04 +09:00
case TreeDropPosition.Below:
2025-10-29 20:12:11 +09:00
indicatorX = pivotAdjustedX;
2025-10-28 20:10:51 +09:00
indicatorY = pivotAdjustedY - (targetRect.rect.height / 2);
2025-11-03 19:07:04 +09:00
indicatorHeight = 2f;
2025-10-28 20:10:51 +09:00
break;
2025-11-03 19:07:04 +09:00
case TreeDropPosition.InsideAsChild:
2025-10-29 20:12:11 +09:00
indicatorX = pivotAdjustedX;
2025-11-03 19:07:04 +09:00
indicatorY = pivotAdjustedY + (targetRect.rect.height / 2);
2025-10-29 20:12:11 +09:00
indicatorHeight = targetRect.rect.height;
2025-10-28 20:10:51 +09:00
break;
}
2025-10-29 20:12:11 +09:00
dropIndicatorRect.anchoredPosition = new Vector2(indicatorX, indicatorY);
dropIndicatorRect.sizeDelta = new Vector2(dropIndicatorRect.sizeDelta.x, indicatorHeight);
2025-10-28 20:10:51 +09:00
2025-11-14 17:02:38 +09:00
//Debug.Log($"[UpdateDropIndicator] {targetItem?.Data?.Name} 위치: {dropPosition}, targetY: {targetWorldY}, parentY: {parentWorldY}, indicatorY: {indicatorY}");
2025-10-28 20:10:51 +09:00
}
private void HideDropIndicator()
{
if (dropIndicator != null)
{
dropIndicator.gameObject.SetActive(false);
}
}
2025-10-29 20:12:11 +09:00
private GameObject? FindDropIndicatorInRoot()
{
if (treeListRootParent == null)
{
Debug.LogWarning("[FindDropIndicatorInRoot] treeListRootParent가 null입니다");
return null;
}
for (int i = 0; i < treeListRootParent.childCount; i++)
{
Transform child = treeListRootParent.GetChild(i);
if (child.name == "DropIndicator")
{
return child.gameObject;
}
}
return null;
}
2025-10-28 20:10:51 +09:00
private TreeListItem? GetItemAtMousePosition(Vector2 screenPosition)
{
if (treeList == null)
{
return null;
}
2025-11-03 19:07:04 +09:00
var es = EventSystem.current;
if (es == null)
{
return null;
}
s_RaycastResults.Clear();
var eventData = new PointerEventData(es)
2025-10-28 20:10:51 +09:00
{
position = screenPosition
};
2025-11-03 19:07:04 +09:00
es.RaycastAll(eventData, s_RaycastResults);
2025-10-28 20:10:51 +09:00
2025-11-14 17:02:38 +09:00
//Debug.Log($"[GetItemAtMousePosition] Raycast 결과: {s_RaycastResults.Count}개");
2025-10-28 20:10:51 +09:00
2025-11-03 19:07:04 +09:00
for (int i = 0; i < s_RaycastResults.Count; i++)
2025-10-28 20:10:51 +09:00
{
2025-11-03 19:07:04 +09:00
var result = s_RaycastResults[i];
2025-10-28 20:10:51 +09:00
var item = result.gameObject.GetComponentInParent<TreeListItem>();
2025-11-03 19:07:04 +09:00
if (item != null)
2025-10-28 20:10:51 +09:00
{
2025-11-14 17:02:38 +09:00
//Debug.Log($"[GetItemAtMousePosition] 찾은 아이템: {item.Data?.Name ?? "Unknown"}");
2025-10-28 20:10:51 +09:00
return item;
}
}
return null;
}
private void HandleDropSuccess(TreeListItemData? draggedData, TreeListItem? targetItem)
{
if (draggedData == null || treeList == null)
{
return;
}
if (targetItem == null)
{
MoveToRoot(draggedData);
2025-11-03 19:07:04 +09:00
treeList.ScheduleFlattenedUpdate();
2025-10-28 20:10:51 +09:00
return;
}
var targetData = targetItem.Data;
if (targetData == null)
{
return;
}
var dropPosition = GetDropPosition(targetItem.GetComponent<RectTransform>());
2025-11-14 17:02:38 +09:00
//Debug.Log($"[HandleDropSuccess] 드롭 위치: {targetItem?.Data?.Name} {dropPosition}");
2025-10-28 20:10:51 +09:00
switch (dropPosition)
{
2025-11-03 19:07:04 +09:00
case TreeDropPosition.InsideAsChild:
2025-10-28 20:10:51 +09:00
MoveAsChild(draggedData, targetData);
break;
2025-11-03 19:07:04 +09:00
case TreeDropPosition.Above:
2025-10-28 20:10:51 +09:00
MoveBefore(draggedData, targetData);
break;
2025-11-03 19:07:04 +09:00
case TreeDropPosition.Below:
2025-10-28 20:10:51 +09:00
MoveAfter(draggedData, targetData);
break;
}
2025-11-03 19:07:04 +09:00
treeList.ScheduleFlattenedUpdate();
2025-10-28 20:10:51 +09:00
}
2025-11-03 19:07:04 +09:00
private TreeDropPosition GetDropPosition(RectTransform targetRect)
2025-10-28 20:10:51 +09:00
{
var height = targetRect.rect.height;
2025-11-03 19:07:04 +09:00
var thresholdUpper = height * dropEdgeRatio;
var thresholdLower = height * (1f - dropEdgeRatio);
2025-10-28 20:10:51 +09:00
RectTransformUtility.ScreenPointToLocalPointInRectangle(
2025-11-03 19:07:04 +09:00
targetRect,
Input.mousePosition,
null,
out var localMousePos);
2025-10-28 20:10:51 +09:00
2025-11-03 19:07:04 +09:00
float relativeY = localMousePos.y + height / 2f; //0..height 기준
2025-10-30 18:36:26 +09:00
2025-11-03 19:07:04 +09:00
if (relativeY <= thresholdUpper)
2025-10-28 20:10:51 +09:00
{
2025-11-03 19:07:04 +09:00
return TreeDropPosition.Below; // 좌표계 보정에 따라 아래/위 반전 가능 → 아래쪽 영역
2025-10-28 20:10:51 +09:00
}
2025-11-03 19:07:04 +09:00
if (relativeY >= thresholdLower)
2025-10-28 20:10:51 +09:00
{
2025-11-03 19:07:04 +09:00
return TreeDropPosition.Above; // 위쪽 영역
2025-10-28 20:10:51 +09:00
}
2025-11-03 19:07:04 +09:00
return TreeDropPosition.InsideAsChild;
2025-10-28 20:10:51 +09:00
}
private void MoveToRoot(TreeListItemData draggedData)
{
2025-11-14 17:02:38 +09:00
//Debug.Log($"[MoveToRoot] {draggedData.Name}을(를) 루트로 이동");
2025-10-29 20:12:11 +09:00
if (treeList == null)
{
return;
}
var root = treeList.Root;
if (root == null)
{
return;
}
if (draggedData.Parent == null)
2025-10-29 20:12:11 +09:00
{
treeList.Items.Remove(draggedData);
treeList.SwapItem(draggedData, treeList.Items.Count);
}
else
{
treeList.AddCloneItem(draggedData);
2025-10-29 20:12:11 +09:00
}
2025-11-14 17:02:38 +09:00
//Debug.Log($"[MoveToRoot] {draggedData.Name}을(를) 루트 레벨의 끝에 추가");
2025-10-28 20:10:51 +09:00
}
private void MoveAsChild(TreeListItemData draggedData, TreeListItemData targetData)
{
2025-12-08 21:06:05 +09:00
if (treeList == null) return;
treeList.AddCloneChild(targetData, draggedData);
2025-11-14 17:02:38 +09:00
//Debug.Log($"[MoveAsChild] {draggedData.Name}을(를) {targetData.Name}의 자식으로 이동");
2025-10-28 20:10:51 +09:00
}
private void MoveBefore(TreeListItemData draggedData, TreeListItemData targetData)
{
2025-12-08 21:06:05 +09:00
if (treeList == null) return;
if (draggedData.Parent == targetData.Parent)
2025-10-28 20:10:51 +09:00
{
if (draggedData.Parent != null)
2025-10-28 20:10:51 +09:00
{
draggedData.Parent.Children.Remove(draggedData);
var targetIndex = draggedData.Parent.Children.IndexOf(targetData);
if (targetIndex >= 0)
{
2025-12-08 21:06:05 +09:00
treeList.SwapChild(draggedData.Parent, draggedData, targetIndex);
2025-11-14 17:02:38 +09:00
//Debug.Log($"[MoveBefore] {draggedData.Name}을(를) {targetData.Name} 앞으로 {targetIndex} 순서 변경");
}
}
else
{
2025-12-08 21:06:05 +09:00
var treeListItems = treeList.Items;
treeListItems.Remove(draggedData);
var targetIndex = treeListItems.IndexOf(targetData);
if (targetIndex >= 0)
{
2025-12-08 21:06:05 +09:00
treeList.SwapItem(draggedData, targetIndex);
//Debug.Log($"[MoveBefore] {draggedData.Name}을(를) {targetData.Name} 앞으로 {targetIndex} 순서 변경(루트레벨)");
}
2025-10-28 20:10:51 +09:00
}
}
2025-10-29 20:12:11 +09:00
else
{
2025-11-03 19:07:04 +09:00
var parentData = targetData.Parent;
if (parentData != null)
2025-10-29 20:12:11 +09:00
{
var targetIndex = parentData.Children.IndexOf(targetData);
2025-10-29 20:12:11 +09:00
if (targetIndex >= 0)
{
2025-12-08 21:06:05 +09:00
treeList.AddCloneChildAt(parentData, draggedData, targetIndex);
2025-11-14 17:02:38 +09:00
//Debug.Log($"[MoveBefore] {draggedData.Name}을(를) {targetData.Name} 앞으로 {targetIndex} 이동");
2025-10-29 20:12:11 +09:00
}
}
else
{
2025-12-08 21:06:05 +09:00
var treeListItems = treeList.Items;
var targetIndex = treeListItems.IndexOf(targetData);
if (targetIndex >= 0)
{
2025-12-08 21:06:05 +09:00
treeList.AddCloneItemAt(draggedData, targetIndex);
//Debug.Log($"[MoveBefore] {draggedData.Name}을(를) {targetData.Name} 앞으로 {targetIndex} 이동 (루트 레벨)");
}
}
2025-10-29 20:12:11 +09:00
}
2025-10-28 20:10:51 +09:00
}
private void MoveAfter(TreeListItemData draggedData, TreeListItemData targetData)
{
2025-12-08 21:06:05 +09:00
if (treeList == null) return;
if (draggedData.Parent == targetData.Parent)
2025-10-28 20:10:51 +09:00
{
if (draggedData.Parent != null)
2025-10-28 20:10:51 +09:00
{
draggedData.Parent.Children.Remove(draggedData);
var targetIndex = draggedData.Parent.Children.IndexOf(targetData);
if (targetIndex >= 0)
{
2025-12-08 21:06:05 +09:00
treeList.SwapChild(draggedData.Parent, draggedData, targetIndex + 1);
2025-11-14 17:02:38 +09:00
//Debug.Log($"[MoveAfter] {draggedData.Name}을(를) {targetData.Name} 뒤로 {targetIndex} 순서 변경");
}
2025-10-28 20:10:51 +09:00
}
else
2025-10-29 20:12:11 +09:00
{
2025-12-08 21:06:05 +09:00
var treeListItems = treeList.Items;
treeListItems.Remove(draggedData);
var targetIndex = treeListItems.IndexOf(targetData);
if (targetIndex >= 0)
2025-10-29 20:12:11 +09:00
{
2025-12-08 21:06:05 +09:00
treeList.SwapItem(draggedData, targetIndex + 1);
//Debug.Log($"[MoveAfter] {draggedData.Name}을(를) {targetData.Name} 뒤로 {targetIndex} 순서 변경(루트레벨)");
2025-10-29 20:12:11 +09:00
}
}
}
else
{
var parentData = targetData.Parent;
2025-10-28 20:10:51 +09:00
if (parentData != null)
{
var targetIndex = parentData.Children.IndexOf(targetData);
if (targetIndex >= 0)
{
2025-12-08 21:06:05 +09:00
treeList.AddCloneChildAt(parentData, draggedData, targetIndex + 1);
2025-11-14 17:02:38 +09:00
//Debug.Log($"[MoveAfter] {draggedData.Name}을(를) {targetData.Name} 뒤로 {targetIndex} 이동");
}
}
else
2025-10-28 20:10:51 +09:00
{
2025-12-08 21:06:05 +09:00
var treeListItems = treeList.Items;
var targetIndex = treeListItems.IndexOf(targetData);
if (targetIndex >= 0)
{
2025-12-08 21:06:05 +09:00
treeList.AddCloneItemAt(draggedData, targetIndex + 1);
//Debug.Log($"[MoveAfter] {draggedData.Name}을(를) {targetData.Name} 뒤로 {targetIndex} 이동 (루트 레벨)");
}
2025-10-28 20:10:51 +09:00
}
}
}
}
}