//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
//**************** Copyright (c) 2016 Marko Pintera (marko.pintera@gmail.com). All rights reserved. **********************//
using System;
using bs;
namespace bs.Editor
{
/** @addtogroup Library
* @{
*/
///
/// Handles various types of drag and drop operations used by the library window.
///
public class LibraryDropTarget
{
private const int DragStartDistancePx = 3;
///
/// Triggered when a resource drag and drop operation occured over the drop target.
///
public Action OnDropResource;
///
/// Triggered when a scene object drag and drop operation occured over the drop target.
///
public Action OnDropSceneObject;
///
/// Triggered when a local drag and drop operation is starting (pointer has been pressed and is being dragged while
/// within the drop target).
///
public Action OnStart;
///
/// Triggered when a pointer enters the drop target area while a drag operation is in progress.
///
public Action OnEnter;
///
/// Triggered when a pointer leaves the drop target area while a drag operation is in progress.
///
public Action OnLeave;
///
/// Triggered when a pointer moves within the drop target area while a drag operation is in progress.
///
public Action OnDrag;
///
/// Triggered when a locally initiated (started within the drop target) drag and drop operation ends.
///
public Action OnEnd;
private readonly EditorWindow parentWindow;
private OSDropTarget dropTargetOS;
private Rect2I bounds;
private bool isMouseDown;
private bool isLocalDragInProgress;
private bool triggerStartLocalDrag;
private bool triggerEndLocalDrag;
private bool isDragInBounds;
private bool isOSDragActive;
private Vector2I mouseDownScreenPos;
private Vector2I lastDragWindowPos;
///
/// Area inside which drag and drop operations are allowed to occurr. Relative to the parent library window.
///
public Rect2I Bounds
{
get { return bounds; }
set
{
bounds = value;
dropTargetOS.Bounds = bounds;
}
}
///
/// Creates a new library drop target.
///
/// Window the drop target is part of.
public LibraryDropTarget(EditorWindow window)
{
parentWindow = window;
dropTargetOS = new OSDropTarget(window);
dropTargetOS.OnDrag += DoOnOSDrag;
dropTargetOS.OnDrop += DoOnOSDrop;
dropTargetOS.OnEnter += DoOnOSDragEnter;
dropTargetOS.OnLeave += DoOnOSDragLeave;
EditorInput.OnPointerPressed += Input_OnPointerPressed;
EditorInput.OnPointerReleased += Input_OnPointerReleased;
EditorInput.OnPointerMoved += Input_OnPointerMoved;
}
///
/// Triggered when the pointer moved.
///
/// Data about the pointer move event.
void Input_OnPointerMoved(PointerEvent ev)
{
Vector2I currentWindowPos = parentWindow.ScreenToWindowPos(ev.ScreenPos);
if (isMouseDown && !isLocalDragInProgress)
{
Vector2I startWindowPos = parentWindow.ScreenToWindowPos(mouseDownScreenPos);
if (!Bounds.Contains(startWindowPos))
return;
int distance = Vector2I.Distance(startWindowPos, currentWindowPos);
if (distance >= DragStartDistancePx)
triggerStartLocalDrag = true;
}
}
///
/// Triggered when the pointer is released.
///
/// Data about the pointer release event.
void Input_OnPointerReleased(PointerEvent ev)
{
if (isLocalDragInProgress)
triggerEndLocalDrag = true;
isLocalDragInProgress = false;
isMouseDown = false;
isDragInBounds = false;
triggerStartLocalDrag = false;
}
///
/// Triggered when the pointer is pressed.
///
/// Data about the pointer press event.
void Input_OnPointerPressed(PointerEvent ev)
{
Vector2I currentWindowPos = parentWindow.ScreenToWindowPos(ev.ScreenPos);
if (Bounds.Contains(currentWindowPos))
{
isMouseDown = true;
mouseDownScreenPos = ev.ScreenPos;
}
}
///
/// Triggers events and queries for changes in drag and drop operations. Should be called every frame.
///
public void Update()
{
Vector2I currentWindowPos = parentWindow.ScreenToWindowPos(Input.PointerPosition);
if (triggerStartLocalDrag)
{
isLocalDragInProgress = true;
triggerStartLocalDrag = false;
if (OnStart != null)
OnStart(currentWindowPos);
}
if (triggerEndLocalDrag)
{
triggerEndLocalDrag = false;
if (OnEnd != null)
OnEnd(currentWindowPos);
}
if (isOSDragActive)
return;
bool externalDragInProgress = DragDrop.DragInProgress && DragDrop.Type == DragDropType.SceneObject;
if (isLocalDragInProgress || externalDragInProgress)
{
if (lastDragWindowPos != currentWindowPos)
{
if (!isDragInBounds)
{
if (Bounds.Contains(currentWindowPos))
{
isDragInBounds = true;
if (OnEnter != null)
OnEnter(currentWindowPos);
}
}
if (OnDrag != null)
OnDrag(currentWindowPos);
if (isDragInBounds)
{
if (!Bounds.Contains(currentWindowPos))
{
isDragInBounds = false;
if (OnLeave != null)
OnLeave();
}
}
lastDragWindowPos = currentWindowPos;
}
}
if (DragDrop.DropInProgress && Bounds.Contains(currentWindowPos))
{
if (DragDrop.Type == DragDropType.Resource)
{
if (OnDropResource != null)
{
ResourceDragDropData resourceDragDrop = (ResourceDragDropData)DragDrop.Data;
OnDropResource(currentWindowPos, resourceDragDrop.Paths);
}
}
else if (DragDrop.Type == DragDropType.SceneObject)
{
if (OnDropSceneObject != null)
{
SceneObjectDragDropData sceneDragDrop = (SceneObjectDragDropData)DragDrop.Data;
OnDropSceneObject(currentWindowPos, sceneDragDrop.Objects);
}
}
isDragInBounds = false;
}
}
///
/// Destroy the drop target. You should call this when done with the drop target.
///
public void Destroy()
{
dropTargetOS.Destroy();
dropTargetOS = null;
}
///
/// Triggered when an OS drag event has entered the drop target area.
///
/// X coordinate of the pointer relative to the parent window.
/// Y coordinate of the pointer relative to the parent window.
private void DoOnOSDragEnter(int x, int y)
{
isOSDragActive = true;
if (OnEnter != null)
OnEnter(new Vector2I(x, y));
}
///
/// Triggered when an OS drag event has left the parent window.
///
private void DoOnOSDragLeave()
{
isOSDragActive = false;
if (OnLeave != null)
OnLeave();
}
///
/// Triggered when a pointer moves while an OS drag event is occuring over the drop target area.
///
/// X coordinate of the pointer relative to the parent window.
/// Y coordinate of the pointer relative to the parent window.
private void DoOnOSDrag(int x, int y)
{
if (OnDrag != null)
OnDrag(new Vector2I(x, y));
}
///
/// Triggered when the pointer was released during an OS drag and drop operation while over the drop target area.
///
/// X coordinate of the pointer relative to the parent window.
/// Y coordinate of the pointer relative to the parent window.
private void DoOnOSDrop(int x, int y)
{
isOSDragActive = false;
if (OnDropResource != null)
OnDropResource(new Vector2I(x, y), dropTargetOS.FilePaths);
}
}
/** @} */
}