// UI Draggable Item|UI|80030
|
|
namespace VRTK
|
|
{
|
|
using UnityEngine;
|
|
using UnityEngine.EventSystems;
|
|
|
|
/// <summary>
|
|
/// Event Payload
|
|
/// </summary>
|
|
/// <param name="target">The target the item is dragged onto.</param>
|
|
public struct UIDraggableItemEventArgs
|
|
{
|
|
public GameObject target;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Event Payload
|
|
/// </summary>
|
|
/// <param name="sender">this object</param>
|
|
/// <param name="e"><see cref="UIDraggableItemEventArgs"/></param>
|
|
public delegate void UIDraggableItemEventHandler(object sender, UIDraggableItemEventArgs e);
|
|
|
|
/// <summary>
|
|
/// Denotes a Unity UI Element as being draggable on the UI Canvas.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// > If a UI Draggable item is set to `Restrict To Drop Zone = true` then the UI Draggable item must be a child of an element that has the VRTK_UIDropZone script applied to it to ensure it starts in a valid drop zone.
|
|
///
|
|
/// **Script Usage:**
|
|
/// * Place the `VRTK_UIDraggableItem` script on the Unity UI element that is to be dragged.
|
|
/// </remarks>
|
|
/// <example>
|
|
/// `VRTK/Examples/034_Controls_InteractingWithUnityUI` demonstrates a collection of UI elements that are draggable
|
|
/// </example>
|
|
[RequireComponent(typeof(CanvasGroup))]
|
|
[AddComponentMenu("VRTK/Scripts/UI/VRTK_UIDraggableItem")]
|
|
public class VRTK_UIDraggableItem : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
|
|
{
|
|
[Tooltip("If checked then the UI element can only be dropped in valid a VRTK_UIDropZone object and must start as a child of a VRTK_UIDropZone object. If unchecked then the UI element can be dropped anywhere on the canvas.")]
|
|
public bool restrictToDropZone = false;
|
|
[Tooltip("If checked then the UI element can only be dropped on the original parent canvas. If unchecked the UI element can be dropped on any valid VRTK_UICanvas.")]
|
|
public bool restrictToOriginalCanvas = false;
|
|
[Tooltip("The offset to bring the UI element forward when it is being dragged.")]
|
|
public float forwardOffset = 0.1f;
|
|
|
|
/// <summary>
|
|
/// The current valid drop zone the dragged element is hovering over.
|
|
/// </summary>
|
|
[HideInInspector]
|
|
public GameObject validDropZone;
|
|
|
|
/// <summary>
|
|
/// Emitted when the draggable item is successfully dropped.
|
|
/// </summary>
|
|
public event UIDraggableItemEventHandler DraggableItemDropped;
|
|
/// <summary>
|
|
/// Emitted when the draggable item is reset.
|
|
/// </summary>
|
|
public event UIDraggableItemEventHandler DraggableItemReset;
|
|
|
|
protected RectTransform dragTransform;
|
|
protected Vector3 startPosition;
|
|
protected Quaternion startRotation;
|
|
protected GameObject startDropZone;
|
|
protected Transform startParent;
|
|
protected Canvas startCanvas;
|
|
protected CanvasGroup canvasGroup;
|
|
|
|
public virtual void OnDraggableItemDropped(UIDraggableItemEventArgs e)
|
|
{
|
|
if (DraggableItemDropped != null)
|
|
{
|
|
DraggableItemDropped(this, e);
|
|
}
|
|
}
|
|
|
|
public virtual void OnDraggableItemReset(UIDraggableItemEventArgs e)
|
|
{
|
|
if (DraggableItemReset != null)
|
|
{
|
|
DraggableItemReset(this, e);
|
|
}
|
|
}
|
|
|
|
public virtual void OnBeginDrag(PointerEventData eventData)
|
|
{
|
|
startPosition = transform.position;
|
|
startRotation = transform.rotation;
|
|
startParent = transform.parent;
|
|
startCanvas = GetComponentInParent<Canvas>();
|
|
canvasGroup.blocksRaycasts = false;
|
|
|
|
if (restrictToDropZone)
|
|
{
|
|
startDropZone = GetComponentInParent<VRTK_UIDropZone>().gameObject;
|
|
validDropZone = startDropZone;
|
|
}
|
|
|
|
SetDragPosition(eventData);
|
|
VRTK_UIPointer pointer = GetPointer(eventData);
|
|
if (pointer != null)
|
|
{
|
|
pointer.OnUIPointerElementDragStart(pointer.SetUIPointerEvent(pointer.pointerEventData.pointerPressRaycast, gameObject));
|
|
}
|
|
}
|
|
|
|
public virtual void OnDrag(PointerEventData eventData)
|
|
{
|
|
SetDragPosition(eventData);
|
|
}
|
|
|
|
public virtual void OnEndDrag(PointerEventData eventData)
|
|
{
|
|
canvasGroup.blocksRaycasts = true;
|
|
dragTransform = null;
|
|
transform.position += (transform.forward * forwardOffset);
|
|
bool validDragEnd = true;
|
|
if (restrictToDropZone)
|
|
{
|
|
if (validDropZone != null && validDropZone != startDropZone)
|
|
{
|
|
transform.SetParent(validDropZone.transform);
|
|
}
|
|
else
|
|
{
|
|
ResetElement();
|
|
validDragEnd = false;
|
|
}
|
|
}
|
|
|
|
Canvas destinationCanvas = (eventData.pointerEnter != null ? eventData.pointerEnter.GetComponentInParent<Canvas>() : null);
|
|
if (restrictToOriginalCanvas)
|
|
{
|
|
if (destinationCanvas != null && destinationCanvas != startCanvas)
|
|
{
|
|
ResetElement();
|
|
validDragEnd = false;
|
|
}
|
|
}
|
|
|
|
if (destinationCanvas == null)
|
|
{
|
|
//We've been dropped off of a canvas
|
|
ResetElement();
|
|
validDragEnd = false;
|
|
}
|
|
|
|
if (validDragEnd)
|
|
{
|
|
VRTK_UIPointer pointer = GetPointer(eventData);
|
|
if (pointer != null)
|
|
{
|
|
pointer.OnUIPointerElementDragEnd(pointer.SetUIPointerEvent(pointer.pointerEventData.pointerPressRaycast, gameObject));
|
|
}
|
|
OnDraggableItemDropped(SetEventPayload(validDropZone));
|
|
}
|
|
|
|
validDropZone = null;
|
|
startParent = null;
|
|
startCanvas = null;
|
|
}
|
|
|
|
protected virtual void OnEnable()
|
|
{
|
|
canvasGroup = GetComponent<CanvasGroup>();
|
|
if (restrictToDropZone && GetComponentInParent<VRTK_UIDropZone>() == null)
|
|
{
|
|
enabled = false;
|
|
VRTK_Logger.Error(VRTK_Logger.GetCommonMessage(VRTK_Logger.CommonMessageKeys.REQUIRED_COMPONENT_MISSING_FROM_GAMEOBJECT, "VRTK_UIDraggableItem", "VRTK_UIDropZone", "the parent", " if `freeDrop = false`"));
|
|
}
|
|
}
|
|
|
|
protected virtual VRTK_UIPointer GetPointer(PointerEventData eventData)
|
|
{
|
|
GameObject controller = VRTK_DeviceFinder.GetControllerByIndex((uint)eventData.pointerId, false);
|
|
return (controller != null ? controller.GetComponent<VRTK_UIPointer>() : null);
|
|
}
|
|
|
|
protected virtual void SetDragPosition(PointerEventData eventData)
|
|
{
|
|
if (eventData.pointerEnter != null && eventData.pointerEnter.transform as RectTransform != null)
|
|
{
|
|
dragTransform = eventData.pointerEnter.transform as RectTransform;
|
|
}
|
|
|
|
Vector3 pointerPosition;
|
|
if (dragTransform != null && RectTransformUtility.ScreenPointToWorldPointInRectangle(dragTransform, eventData.position, eventData.pressEventCamera, out pointerPosition))
|
|
{
|
|
transform.position = pointerPosition - (transform.forward * forwardOffset);
|
|
transform.rotation = dragTransform.rotation;
|
|
}
|
|
}
|
|
|
|
protected virtual void ResetElement()
|
|
{
|
|
transform.position = startPosition;
|
|
transform.rotation = startRotation;
|
|
transform.SetParent(startParent);
|
|
OnDraggableItemReset(SetEventPayload(startParent.gameObject));
|
|
}
|
|
|
|
protected virtual UIDraggableItemEventArgs SetEventPayload(GameObject target)
|
|
{
|
|
UIDraggableItemEventArgs e;
|
|
e.target = target;
|
|
return e;
|
|
}
|
|
}
|
|
}
|