Skip to content

Instantly share code, notes, and snippets.

@Baste-RainGames
Last active May 31, 2022 06:04
Show Gist options
  • Save Baste-RainGames/2aed9b5ce2b1b414fd65aa72c66d7f3b to your computer and use it in GitHub Desktop.
Save Baste-RainGames/2aed9b5ce2b1b414fd65aa72c66d7f3b to your computer and use it in GitHub Desktop.
Interaction with Unity's DragAndDropService
using System;
using System.Reflection;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
// This class gives access to Unity's internal DragAndDropService class through reflection, and exposes the same methods.
// See https://github.com/Unity-Technologies/UnityCsReference/blob/f50ab75c509cab05254e0ff2f06eb74f5ecd30da/Editor/Mono/DragAndDropService.cs
namespace RainDragAndDropService {
public static class DragAndDropService {
public static int kProjectBrowserDropDstId = "ProjectBrowser".GetHashCode();
public static int kSceneDropDstId = "SceneView".GetHashCode();
public static int kHierarchyDropDstId = "Hierarchy".GetHashCode();
public static int kInspectorDropDstId = "Inspector".GetHashCode();
public delegate DragAndDropVisualMode ProjectBrowserDropHandler(int dragInstanceId, string dropUponPath, bool perform);
public delegate DragAndDropVisualMode SceneDropHandler(UnityEngine.Object dropUpon, Vector3 worldPosition, Vector2 viewportPosition,
Transform parentForDraggedObjects, bool perform);
public delegate DragAndDropVisualMode HierarchyDropHandler(int dropTargetInstanceID, InternalEditorUtility.HierarchyDropMode dropMode,
Transform parentForDraggedObjects, bool perform);
public delegate DragAndDropVisualMode InspectorDropHandler(UnityEngine.Object[] targets, bool perform);
public static Action AddDropHandler(ProjectBrowserDropHandler handler) {
return AddDropHandler(kProjectBrowserDropDstId, handler);
}
public static Action AddDropHandler(SceneDropHandler handler) {
return AddDropHandler(kSceneDropDstId, handler);
}
public static Action AddDropHandler(HierarchyDropHandler handler) {
return AddDropHandler(kHierarchyDropDstId, handler);
}
public static Action AddDropHandler(InspectorDropHandler handler) {
return AddDropHandler(kInspectorDropDstId, handler);
}
public static Action AddDropHandler(int dropDstId, Delegate handler) {
twoArgsArray[0] = dropDstId;
twoArgsArray[1] = handler;
return (Action) AddDropHandlerMethod.Invoke(null, twoArgsArray);
}
public static bool HasHandler(int dropDstId, Delegate handler) {
twoArgsArray[0] = dropDstId;
twoArgsArray[1] = handler;
return (bool) HasHandlerMethod.Invoke(null, twoArgsArray);
}
public static void RemoveDropHandler(int dropDstId, Delegate handler) {
twoArgsArray[0] = dropDstId;
twoArgsArray[1] = handler;
RemoveDropHandlerMethod.Invoke(null, twoArgsArray);
}
public static DragAndDropVisualMode Drop(int dropDstId, params object[] args) {
twoArgsArray[0] = dropDstId;
twoArgsArray[1] = args;
return (DragAndDropVisualMode) DropMethod.Invoke(null, twoArgsArray);
}
#region Reflection
private static object[] twoArgsArray = new object[2];
private static Type _dragAndDropServiceType;
private static Type DragAndDropServiceType {
get {
if (_dragAndDropServiceType == null)
_dragAndDropServiceType = Assembly.GetAssembly(typeof(Editor)).GetType("UnityEditor.DragAndDropService");
return _dragAndDropServiceType;
}
}
private static MethodInfo _addDropHandlerMethod;
private static MethodInfo AddDropHandlerMethod {
get {
if (_addDropHandlerMethod == null)
_addDropHandlerMethod = DragAndDropServiceType.GetMethod("AddDropHandler", BindingFlags.Public | BindingFlags.Static, null,
new[] {typeof(int), typeof(Delegate)}, null);
return _addDropHandlerMethod;
}
}
private static MethodInfo _hasHandlerMethod;
private static MethodInfo HasHandlerMethod {
get {
if (_hasHandlerMethod == null)
_hasHandlerMethod = DragAndDropServiceType.GetMethod("HasHandler");
return _hasHandlerMethod;
}
}
private static MethodInfo _removeDropHandlerMethod;
private static MethodInfo RemoveDropHandlerMethod {
get {
if (_removeDropHandlerMethod == null)
_removeDropHandlerMethod = DragAndDropServiceType.GetMethod("RemoveDropHandler");
return _removeDropHandlerMethod;
}
}
private static MethodInfo _dropMethod;
private static MethodInfo DropMethod {
get {
if (_dropMethod == null)
_dropMethod = DragAndDropServiceType.GetMethod("Drop");
return _dropMethod;
}
}
#endregion
}
}
// Example use case: make dragging sprites onto GameObjects add them as SpriteRenderers:
using System.Collections.Generic;
using RainDragAndDropService;
using UnityEditor;
using UnityEngine;
public static class SpriteDragAndDrop {
[InitializeOnLoadMethod]
public static void HookUpDragAndDropToInspector() {
var dropHandler = (DragAndDropService.InspectorDropHandler) OnInspectorDrop;
if (!DragAndDropService.HasHandler(DragAndDropService.kHierarchyDropDstId, dropHandler))
DragAndDropService.AddDropHandler(dropHandler);
}
private static List<GameObject> targetGameObjects = new List<GameObject>();
private static DragAndDropVisualMode OnInspectorDrop(Object[] targets, bool perform) {
targetGameObjects.Clear();
foreach (var target in targets)
if (target is GameObject gameObject && gameObject.GetComponent<SpriteRenderer>() == null)
targetGameObjects.Add(gameObject);
if (targetGameObjects.Count == 0)
return DragAndDropVisualMode.None; // this allows other drag and drop handlers to handle the drag
Sprite sprite = null;
foreach (var obj in DragAndDrop.objectReferences)
if (obj is Sprite s) {
sprite = s;
break;
}
if (sprite == null) {
foreach (var obj in DragAndDrop.objectReferences)
if (obj is Texture t) {
sprite = AssetDatabase.LoadAssetAtPath<Sprite>(AssetDatabase.GetAssetPath(t));
if (sprite != null)
break;
}
}
if (sprite == null)
return DragAndDropVisualMode.None;
if (perform) {
foreach (var gameObject in targetGameObjects)
Undo.AddComponent<SpriteRenderer>(gameObject).sprite = sprite;
}
return DragAndDropVisualMode.Link;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment