#nullable enable using System; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; namespace UVC.UI.List.Draggable { /// /// 리스트 아이템의 드래그와 비주얼을 관리하는 컴포넌트입니다. /// DragBehavior와 함께 사용되어 드래그 시 시각적 효과를 처리합니다. /// /// 필수 설정: /// 1. 이 컴포넌트가 있는 GameObject에 또는 자식에 DragBehavior를 함께 추가해야 합니다. /// 2. CanvasGroup이 없으면 자동으로 추가됩니다. /// /// Inspector 설정: /// - Drag Alpha: 드래그 중 투명도 (0~1, 기본값 0.6) /// /// 사용 예시: /// GameObject listItem = Instantiate(itemPrefab); /// listItem.AddComponent(); /// listItem.AddComponent(); /// public class ListItemController : MonoBehaviour { // 컴포넌트 참조들 private CanvasGroup? canvasGroup; private ListReorderHandler? reorderHandler; private GameObject? placeholder; [Header("DragBehavior 설정")] [SerializeField] private DragBehavior? dragBehavior; [Header("비주얼 설정")] [SerializeField, Range(0f, 1f)] [Tooltip("드래그 중일 때의 투명도 값입니다. 0은 완전 투명, 1은 완전 불투명")] private float dragAlpha = 0.6f; /// /// 현재 생성된 플레이스홀더를 가져옵니다 /// public GameObject? Placeholder => placeholder; /// /// 원래 순서(인덱스)를 가져옵니다 /// public int OriginalIndex => dragBehavior?.OriginalIndex ?? -1; /// /// 컴포넌트 초기화 - Unity가 자동으로 호출 /// void Awake() { // 필수 컴포넌트들을 가져옵니다 if(dragBehavior == null) dragBehavior = GetComponentInChildren(); // CanvasGroup이 없으면 추가합니다 (알파값 조절을 위해 필요) canvasGroup = GetComponent(); if (canvasGroup == null) { canvasGroup = gameObject.AddComponent(); Debug.Log($"[ListItemController] CanvasGroup을 자동으로 추가했습니다 - {gameObject.name}"); } // 드래그 이벤트에 핸들러 메서드들을 연결합니다 if (dragBehavior != null) { dragBehavior.OnDragStarted += HandleDragStart; dragBehavior.OnDragging += HandleDragging; dragBehavior.OnDragEnded += HandleDragEnd; } else { Debug.LogError($"[ListItemController] DragBehavior를 찾을 수 없습니다! - {gameObject.name}"); } } /// /// Start는 Awake 이후에 호출되며, 다른 오브젝트의 컴포넌트를 찾을 때 사용 /// void Start() { // 부모에서 ListReorderHandler를 찾습니다 reorderHandler = GetComponentInParent(); if (reorderHandler == null) { Debug.LogWarning($"[ListItemController] ListReorderHandler를 찾을 수 없습니다. 재정렬 기능이 작동하지 않을 수 있습니다. - {gameObject.name}"); } } /// /// 드래그가 시작될 때 호출되는 핸들러 /// private void HandleDragStart(PointerEventData eventData) { Debug.Log($"[ListItemController] 드래그 시작 처리 - {gameObject.name}"); // 1. 플레이스홀더(빈 공간 표시)를 생성합니다 CreatePlaceholder(); // 2. 드래그 중인 아이템을 반투명하게 만듭니다 if (canvasGroup != null) { canvasGroup.alpha = dragAlpha; canvasGroup.blocksRaycasts = false; // 드래그 중에는 클릭을 받지 않습니다 } // 3. 아이템을 최상위 Canvas로 이동시켜 다른 UI 위에 표시되도록 합니다 Canvas? rootCanvas = GetComponentInParent(); if (rootCanvas != null) { transform.SetParent(rootCanvas.transform); transform.SetAsLastSibling(); // 가장 위에 표시 } // 4. 리오더 핸들러에 드래그 시작을 알립니다 reorderHandler?.StartReorder(this); } /// /// 드래그 중일 때 계속 호출되는 핸들러 /// private void HandleDragging(PointerEventData eventData) { // 리오더 핸들러가 현재 마우스 위치에서 적절한 드롭 위치를 계산하도록 합니다 reorderHandler?.UpdateReorderPosition(this, eventData.position); } /// /// 드래그가 끝날 때 호출되는 핸들러 /// private void HandleDragEnd(PointerEventData eventData) { //Debug.Log($"[ListItemController] 드래그 종료 처리 - {gameObject.name}"); // 1. 아이템을 원래 투명도로 복원합니다 if (canvasGroup != null) { canvasGroup.alpha = 1f; canvasGroup.blocksRaycasts = true; // 다시 클릭을 받을 수 있도록 } // 2. 리오더 핸들러가 최종 위치를 결정하도록 합니다 reorderHandler?.EndReorder(this); // 3. 플레이스홀더를 제거합니다 if (placeholder != null) { Destroy(placeholder); placeholder = null; } // 중요: 드래그 종료 후 새로운 위치 정보를 업데이트 dragBehavior?.UpdatePositionInfo(); } /// /// 플레이스홀더(빈 공간 표시용 오브젝트)를 생성합니다 /// 드래그 중인 아이템이 원래 있던 자리를 표시하는 용도입니다 /// private void CreatePlaceholder() { // 이미 플레이스홀더가 있다면 생성하지 않습니다 if (placeholder != null) return; // 새 GameObject를 만들어 플레이스홀더로 사용합니다 placeholder = new GameObject("Placeholder"); // RectTransform 컴포넌트를 추가하고 설정합니다 var rect = placeholder.AddComponent(); if (dragBehavior?.OriginalParent != null) { rect.SetParent(dragBehavior.OriginalParent); rect.SetSiblingIndex(dragBehavior.OriginalIndex); // 원본과 같은 크기로 설정합니다 var originalRect = GetComponent(); if (originalRect != null) { rect.sizeDelta = originalRect.sizeDelta; } } // 시각적으로 구분할 수 있도록 반투명한 회색 이미지를 추가합니다 var image = placeholder.AddComponent(); image.color = new Color(0.5f, 0.5f, 0.5f, 0.3f); Debug.Log($"[ListItemController] 플레이스홀더 생성됨 - {gameObject.name}"); } /// /// 컴포넌트가 파괴될 때 이벤트 구독을 해제합니다 /// void OnDestroy() { // 메모리 누수를 방지하기 위해 이벤트 구독을 해제합니다 if (dragBehavior != null) { dragBehavior.OnDragStarted -= HandleDragStart; dragBehavior.OnDragging -= HandleDragging; dragBehavior.OnDragEnded -= HandleDragEnd; } } } }