126 lines
4.8 KiB
C#
126 lines
4.8 KiB
C#
#nullable enable
|
|
|
|
using System;
|
|
using TMPro;
|
|
using UnityEngine;
|
|
using UnityEngine.EventSystems;
|
|
using UnityEngine.UI;
|
|
|
|
namespace ReplacedPerson.Runtime
|
|
{
|
|
[RequireComponent(typeof(RectTransform), typeof(Button))]
|
|
public sealed class ReplacedCardDragHandler : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
|
|
{
|
|
private Canvas? _canvas;
|
|
private RectTransform? _ghost;
|
|
private ReplacedCardMotion? _motion;
|
|
private Action<Vector2>? _dropAction;
|
|
private Vector2 _dragStart;
|
|
private bool _hasDragStart;
|
|
|
|
public void Configure(Action<Vector2> dropAction)
|
|
{
|
|
_dropAction = dropAction;
|
|
}
|
|
|
|
public void OnBeginDrag(PointerEventData eventData)
|
|
{
|
|
CleanupGhost();
|
|
_dragStart = eventData.position;
|
|
_hasDragStart = true;
|
|
_canvas = GetComponentInParent<Canvas>()?.rootCanvas;
|
|
if (_canvas == null) return;
|
|
_motion = GetComponent<ReplacedCardMotion>();
|
|
_motion?.SetDragging(true);
|
|
_ghost = CreateGhost(_canvas.transform);
|
|
UpdateGhostPosition(eventData);
|
|
}
|
|
|
|
public void OnDrag(PointerEventData eventData)
|
|
{
|
|
UpdateGhostPosition(eventData);
|
|
}
|
|
|
|
public void OnEndDrag(PointerEventData eventData)
|
|
{
|
|
var moved = _hasDragStart && (eventData.position - _dragStart).sqrMagnitude >= 100f;
|
|
_motion?.SetDragging(false);
|
|
_motion = null;
|
|
CleanupGhost();
|
|
_hasDragStart = false;
|
|
if (moved && _dropAction != null) _dropAction(eventData.position);
|
|
else GetComponent<Button>().onClick.Invoke();
|
|
}
|
|
|
|
private RectTransform CreateGhost(Transform canvasRoot)
|
|
{
|
|
var sourceRect = (RectTransform)transform;
|
|
var sourceImage = GetComponent<Image>();
|
|
var sourceLabel = transform.Find("Label")?.GetComponent<TMP_Text>();
|
|
var ghostObject = new GameObject("CardDragGhost", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image), typeof(CanvasGroup));
|
|
var ghostRect = ghostObject.GetComponent<RectTransform>();
|
|
ghostRect.SetParent(canvasRoot, false);
|
|
ghostRect.anchorMin = ghostRect.anchorMax = new Vector2(.5f, .5f);
|
|
ghostRect.pivot = sourceRect.pivot;
|
|
ghostRect.sizeDelta = sourceRect.rect.size;
|
|
ghostRect.localScale = Vector3.one * 1.06f;
|
|
ghostRect.SetAsLastSibling();
|
|
|
|
var ghostImage = ghostObject.GetComponent<Image>();
|
|
ghostImage.sprite = sourceImage.sprite;
|
|
ghostImage.type = sourceImage.type;
|
|
ghostImage.color = sourceImage.color;
|
|
ghostImage.material = sourceImage.material;
|
|
ghostImage.raycastTarget = false;
|
|
var group = ghostObject.GetComponent<CanvasGroup>();
|
|
group.alpha = .94f;
|
|
group.blocksRaycasts = false;
|
|
group.interactable = false;
|
|
|
|
if (sourceLabel != null)
|
|
{
|
|
var labelObject = new GameObject("Label", typeof(RectTransform), typeof(CanvasRenderer), typeof(TextMeshProUGUI));
|
|
var labelRect = labelObject.GetComponent<RectTransform>();
|
|
labelRect.SetParent(ghostRect, false);
|
|
labelRect.anchorMin = Vector2.zero;
|
|
labelRect.anchorMax = Vector2.one;
|
|
labelRect.offsetMin = new Vector2(8, 4);
|
|
labelRect.offsetMax = new Vector2(-8, -4);
|
|
var label = labelObject.GetComponent<TextMeshProUGUI>();
|
|
label.font = sourceLabel.font;
|
|
label.fontSize = sourceLabel.fontSize;
|
|
label.alignment = sourceLabel.alignment;
|
|
label.color = sourceLabel.color;
|
|
label.text = sourceLabel.text;
|
|
label.enableWordWrapping = sourceLabel.enableWordWrapping;
|
|
label.raycastTarget = false;
|
|
}
|
|
return ghostRect;
|
|
}
|
|
|
|
private void UpdateGhostPosition(PointerEventData eventData)
|
|
{
|
|
if (_ghost == null || _canvas == null) return;
|
|
var canvasRect = (RectTransform)_canvas.transform;
|
|
var camera = _canvas.renderMode == RenderMode.ScreenSpaceOverlay ? null : _canvas.worldCamera;
|
|
if (RectTransformUtility.ScreenPointToLocalPointInRectangle(canvasRect, eventData.position, camera, out var localPoint))
|
|
_ghost.anchoredPosition = localPoint;
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
_motion?.SetDragging(false);
|
|
_motion = null;
|
|
_hasDragStart = false;
|
|
CleanupGhost();
|
|
}
|
|
|
|
private void CleanupGhost()
|
|
{
|
|
if (_ghost != null) Destroy(_ghost.gameObject);
|
|
_ghost = null;
|
|
_canvas = null;
|
|
}
|
|
}
|
|
}
|