Files
XRLib/Assets/Scripts/UVC/UI/List/DraggableListItem.cs
2025-07-30 20:16:21 +09:00

192 lines
5.7 KiB
C#

#nullable enable
using System;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
namespace UVC.UI.List
{
/// <summary>
/// 개별 드래그 가능한 아이템의 UI 컴포넌트
/// 드래그 동작과 시각적 피드백을 담당
/// </summary>
public class DraggableListItem : MonoBehaviour
{
[Header("UI 컴포넌트")]
[SerializeField] protected CanvasGroup? canvasGroup;
[SerializeField] protected RectTransform? rectTransform;
[SerializeField] protected DraggableItem? dragAnchor;
[SerializeField] protected TMP_InputField? inputField;
[Header("드래그 설정")]
[SerializeField] protected float dragAlpha = 0.6f;
[SerializeField] protected bool blockRaycastsWhileDragging = false;
// 프로퍼티
public DraggableItemData? Data { get; private set; }
public RectTransform? RectTransform => rectTransform;
public bool IsDragging { get; private set; }
public event Action<DraggableListItem>? OnBeginDragEvent;
public event Action<DraggableListItem, Vector2>? OnDragEvent;
public event Action<DraggableListItem>? OnEndDragEvent;
private Vector2 originalPosition;
private Transform? originalParent;
private int originalSiblingIndex;
/// <summary>
/// 컴포넌트 초기화
/// </summary>
private void Awake()
{
// null 체크 및 자동 할당
if (rectTransform == null)
rectTransform = GetComponent<RectTransform>();
if (canvasGroup == null)
canvasGroup = GetComponent<CanvasGroup>();
// CanvasGroup이 없으면 추가
if (canvasGroup == null)
canvasGroup = gameObject.AddComponent<CanvasGroup>();
if (dragAnchor == null)
{
Debug.LogError("Drag Anchor is not assigned. Please assign it in the inspector.");
return;
}
dragAnchor.OnBeginDragEvent += OnBeginDrag;
dragAnchor.OnDragEvent += OnDrag;
dragAnchor.OnEndDragEvent += OnEndDrag;
}
/// <summary>
/// 아이템 데이터로 UI 업데이트
/// </summary>
/// <param name="data">표시할 데이터</param>
public void SetData(DraggableItemData? data)
{
if (data == null) return;
Data = data;
UpdateUI();
}
/// <summary>
/// UI 요소들을 데이터에 맞게 업데이트
/// </summary>
protected virtual void UpdateUI()
{
if (Data == null) return;
if(inputField != null)
{
inputField.text = Data.Id;
}
}
/// <summary>
/// 드래그 시작 시 호출
/// </summary>
public void OnBeginDrag(PointerEventData eventData)
{
if (rectTransform == null) return;
IsDragging = true;
// 원래 위치와 부모 저장
originalPosition = rectTransform.anchoredPosition;
originalParent = transform.parent;
originalSiblingIndex = transform.GetSiblingIndex();
// 시각적 피드백 적용
ApplyDragVisuals(true);
// 이벤트 발생
OnBeginDragEvent?.Invoke(this);
}
/// <summary>
/// 드래그 중 호출
/// </summary>
public void OnDrag(PointerEventData eventData)
{
if (rectTransform == null) return;
// 마우스 위치로 아이템 이동
rectTransform.anchoredPosition += new Vector2(0, eventData.delta.y);//eventData.delta
// 이벤트 발생
OnDragEvent?.Invoke(this, rectTransform.anchoredPosition);
}
/// <summary>
/// 드래그 종료 시 호출
/// </summary>
public void OnEndDrag(PointerEventData eventData)
{
IsDragging = false;
// 시각적 피드백 복원
ApplyDragVisuals(false);
// 이벤트 발생
OnEndDragEvent?.Invoke(this);
}
/// <summary>
/// 드래그 시각적 효과 적용/해제
/// </summary>
/// <param name="isDragging">드래그 중인지 여부</param>
private void ApplyDragVisuals(bool isDragging)
{
if (canvasGroup == null) return;
if (isDragging)
{
canvasGroup.alpha = dragAlpha;
canvasGroup.blocksRaycasts = blockRaycastsWhileDragging;
}
else
{
canvasGroup.alpha = 1f;
canvasGroup.blocksRaycasts = true;
}
}
/// <summary>
/// 원래 위치로 되돌리기 (드래그 취소 시 사용)
/// </summary>
public void ResetToOriginalPosition()
{
if (rectTransform == null || originalParent == null) return;
transform.SetParent(originalParent);
transform.SetSiblingIndex(originalSiblingIndex);
rectTransform.anchoredPosition = originalPosition;
}
public void OnDestroy()
{
// 이벤트 구독 해제
if (dragAnchor != null)
{
dragAnchor.OnBeginDragEvent -= OnBeginDrag;
dragAnchor.OnDragEvent -= OnDrag;
dragAnchor.OnEndDragEvent -= OnEndDrag;
}
OnBeginDragEvent = null;
OnDragEvent = null;
OnEndDragEvent = null;
// 리소스 정리
canvasGroup = null;
rectTransform = null;
dragAnchor = null;
}
}
}