168 lines
5.3 KiB
C#
168 lines
5.3 KiB
C#
#nullable enable
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
namespace UVC.UI.List.Tree
|
|
{
|
|
/// <summary>
|
|
/// 트리 리스트의 드래그 & 드롭 기능을 관리하는 클래스입니다.
|
|
///
|
|
/// 역할:
|
|
/// 1. 드래그 시작/진행/종료 상태 관리
|
|
/// 2. 유효한 드롭 대상 판단 (순환 참조 방지)
|
|
/// 3. 아이템 위치 변경 (형제 아이템 간 순서 변경)
|
|
/// 4. 아이템 계층 구조 변경 (부모-자식 관계 수정)
|
|
///
|
|
/// 기능:
|
|
/// - 드래그할 아이템과 드롭 대상을 추적
|
|
/// - 유효성 검사 (자기 자신에게 드롭 금지, 순환 참조 방지)
|
|
/// - 드롭 완료 후 데이터 동기화
|
|
/// </summary>
|
|
public class TreeListDragDropManager
|
|
{
|
|
/// <summary>
|
|
/// 드래그 중인 아이템의 데이터입니다.
|
|
/// </summary>
|
|
public TreeListItemData? DraggedItem { get; private set; }
|
|
|
|
/// <summary>
|
|
/// 현재 드래그 중인 상태인지 여부입니다.
|
|
/// </summary>
|
|
public bool IsDragging { get; private set; }
|
|
|
|
/// <summary>
|
|
/// 드래그 시작 시 발생하는 이벤트입니다.
|
|
/// </summary>
|
|
public Action<TreeListItemData>? OnDragStarted;
|
|
|
|
/// <summary>
|
|
/// 드래그 진행 중 발생하는 이벤트입니다.
|
|
/// </summary>
|
|
public Action<TreeListItemData, TreeListItemData?>? OnDragEntered;
|
|
|
|
/// <summary>
|
|
/// 드래그 종료 시 발생하는 이벤트입니다.
|
|
/// </summary>
|
|
public Action<TreeListItemData>? OnDragEnded;
|
|
|
|
/// <summary>
|
|
/// 드롭 완료 시 발생하는 이벤트입니다.
|
|
/// </summary>
|
|
public Action<TreeListItemData, TreeListItemData?>? OnDropped;
|
|
|
|
/// <summary>
|
|
/// 드래그를 시작합니다.
|
|
/// </summary>
|
|
/// <param name="draggedItem">드래그할 아이템</param>
|
|
public void StartDrag(TreeListItemData draggedItem)
|
|
{
|
|
if (IsDragging)
|
|
{
|
|
return;
|
|
}
|
|
|
|
DraggedItem = draggedItem;
|
|
IsDragging = true;
|
|
|
|
OnDragStarted?.Invoke(draggedItem);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 드래그 중에 마우스가 다른 아이템 위에 있을 때 호출됩니다.
|
|
/// </summary>
|
|
/// <param name="targetItem">현재 마우스 위에 있는 아이템</param>
|
|
public void OnDragOver(TreeListItemData? targetItem)
|
|
{
|
|
if (!IsDragging || DraggedItem == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
OnDragEntered?.Invoke(DraggedItem, targetItem);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 드래그를 종료합니다.
|
|
/// </summary>
|
|
public void EndDrag()
|
|
{
|
|
if (!IsDragging || DraggedItem == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
OnDragEnded?.Invoke(DraggedItem);
|
|
|
|
IsDragging = false;
|
|
DraggedItem = null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 드래그된 아이템을 대상 아이템에 드롭합니다.
|
|
/// </summary>
|
|
/// <param name="targetItem">드롭 대상 아이템 (null이면 루트 레벨)</param>
|
|
/// <param name="insertIndex">대상 부모 내에서의 삽입 위치 (-1이면 끝에 추가)</param>
|
|
/// <returns>드롭 성공 여부</returns>
|
|
public bool TryDrop(TreeListItemData? targetItem, int insertIndex = -1)
|
|
{
|
|
if (!IsDragging || DraggedItem == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// 자기 자신에게 드롭하는 경우 무시
|
|
if (targetItem != null && targetItem == DraggedItem)
|
|
{
|
|
EndDrag();
|
|
return false;
|
|
}
|
|
|
|
// 순환 참조 검사 (드래그 아이템이 드롭 대상의 부모인 경우)
|
|
if (targetItem != null && IsAncestorOf(DraggedItem, targetItem))
|
|
{
|
|
EndDrag();
|
|
return false;
|
|
}
|
|
|
|
OnDropped?.Invoke(DraggedItem, targetItem);
|
|
|
|
EndDrag();
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 첫 번째 아이템이 두 번째 아이템의 조상인지 확인합니다.
|
|
/// 순환 참조를 방지하기 위해 사용됩니다.
|
|
/// </summary>
|
|
/// <param name="potentialAncestor">조상일 가능성이 있는 아이템</param>
|
|
/// <param name="potentialDescendant">후손일 가능성이 있는 아이템</param>
|
|
/// <returns>조상-후손 관계이면 true</returns>
|
|
public static bool IsAncestorOf(TreeListItemData potentialAncestor, TreeListItemData potentialDescendant)
|
|
{
|
|
var current = potentialDescendant.Parent;
|
|
|
|
while (current != null)
|
|
{
|
|
if (current == potentialAncestor)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
current = current.Parent;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// 모든 드래그 & 드롭 상태를 리셋합니다.
|
|
/// </summary>
|
|
public void Reset()
|
|
{
|
|
IsDragging = false;
|
|
DraggedItem = null;
|
|
}
|
|
}
|
|
} |