Skip to content

Commit db267c6

Browse files
committed
Add possibility to drag and drop reference values into arrays/lists
1 parent 73d76f2 commit db267c6

5 files changed

Lines changed: 159 additions & 11 deletions

File tree

Assets/Editor Toolbox/Editor/Internal/PropertyScope.cs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,45 @@ internal class PropertyScope : IDisposable
1212
{
1313
private readonly SerializedProperty property;
1414

15+
1516
public PropertyScope(SerializedProperty property, GUIContent label)
1617
{
1718
this.property = property;
1819
var rowHeight = EditorGUIUtility.singleLineHeight;
1920
var labelRect = EditorGUILayout.GetControlRect(true, rowHeight);
2021
label = EditorGUI.BeginProperty(labelRect, label, property);
21-
property.isExpanded = EditorGUI.Foldout(labelRect, property.isExpanded, label, true);
22+
HandleEvents(labelRect);
23+
TryDrawLabel(labelRect, label);
24+
}
25+
26+
27+
private void HandleEvents(Rect rect)
28+
{
29+
if (property.isArray)
30+
{
31+
DragAndDropUtility.DoDragAndDropForProperty(rect, property);
32+
}
2233
}
2334

35+
private void TryDrawLabel(Rect rect, GUIContent label)
36+
{
37+
if (property.hasChildren)
38+
{
39+
property.isExpanded = EditorGUI.Foldout(rect, property.isExpanded, label, true);
40+
}
41+
else
42+
{
43+
EditorGUI.LabelField(rect, label);
44+
}
45+
}
46+
47+
2448
public void Dispose()
2549
{
2650
EditorGUI.EndProperty();
2751
}
2852

53+
2954
public bool IsVisible => property.isExpanded;
3055
}
3156
}

Assets/Editor Toolbox/Editor/Internal/ReorderableListBase.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,7 @@ protected virtual bool DoListHeader(Rect headerRect)
301301
}
302302
}
303303

304+
HandleHeaderEvents(headerRect);
304305
//apply the padding to get the internal rect
305306
headerRect.xMin += Style.padding;
306307
headerRect.xMax -= Style.padding;
@@ -397,6 +398,9 @@ protected virtual bool IsMouseInActiveElement()
397398
return GetCoveredElementIndex(Event.current.mousePosition.y) == Index;
398399
}
399400

401+
protected virtual void HandleHeaderEvents(Rect rect)
402+
{ }
403+
400404
protected abstract int GetCoveredElementIndex(float localY);
401405

402406
protected abstract int GetCoveredElementIndex(Vector2 mousePosition);

Assets/Editor Toolbox/Editor/Internal/ToolboxEditorList.cs

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ private void DrawElementRow(int index, bool isActive, bool isTarget, bool hasFoc
8787
}
8888

8989
//create additional space between item and right margin
90-
DrawEmptySpace(Style.spacing);
90+
CreateSpace(Style.spacing);
9191
}
9292
}
9393

@@ -133,16 +133,19 @@ private void DrawElement(int index)
133133
}
134134

135135
/// <summary>
136-
/// Creates empty space in a layout-based structure.
136+
/// Preserves additional space for the dragging handle.
137137
/// </summary>
138-
private void DrawEmptySpace(float space)
138+
private void DrawHandleArea()
139139
{
140-
GuiLayoutUtility.CreateSpace(space);
140+
CreateSpace(Style.dragAreaWidth);
141141
}
142142

143-
private void DrawHandleArea()
143+
/// <summary>
144+
/// Creates empty space in a layout-based structure.
145+
/// </summary>
146+
private void CreateSpace(float pixels)
144147
{
145-
DrawEmptySpace(Style.dragAreaWidth);
148+
GuiLayoutUtility.CreateSpace(pixels);
146149
}
147150

148151
/// <summary>
@@ -226,7 +229,7 @@ protected override void DoListMiddle(Rect middleRect)
226229

227230
var upperPadding = Style.padding - ElementSpacing;
228231
var lowerPadding = Style.padding;
229-
DrawEmptySpace(upperPadding);
232+
CreateSpace(upperPadding);
230233
//if there are elements, we need to draw them - we will do
231234
//this differently depending on if we are dragging or not
232235
for (var i = 0; i < arraySize; i++)
@@ -235,17 +238,16 @@ protected override void DoListMiddle(Rect middleRect)
235238
var isActive = (i == Index);
236239
var hasFocus = (i == Index && HasKeyboardFocus());
237240
var isTarget = (i == lastCoveredIndex && !isActive);
238-
var isEnding = (i == arraySize - 1);
239241

240-
DrawEmptySpace(ElementSpacing);
242+
CreateSpace(ElementSpacing);
241243
DrawElementRow(i, isActive, isTarget, hasFocus);
242244
if (isTarget)
243245
{
244246
DrawTargetGap(i, Index);
245247
}
246248
}
247249

248-
DrawEmptySpace(lowerPadding);
250+
CreateSpace(lowerPadding);
249251
}
250252

251253
protected override bool DoListHeader()
@@ -325,6 +327,12 @@ protected override int GetCoveredElementIndex(Vector2 mousePosition)
325327
return middleRect.Contains(mousePosition) ? GetCoveredElementIndex(mousePosition.y) : -1;
326328
}
327329

330+
protected override void HandleHeaderEvents(Rect rect)
331+
{
332+
base.HandleHeaderEvents(rect);
333+
DragAndDropUtility.DoDragAndDropForProperty(rect, List);
334+
}
335+
328336

329337
/// <inheritdoc/>
330338
public override void DoList()
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
using System;
2+
using System.Reflection;
3+
using UnityEditor;
4+
using UnityEngine;
5+
using Object = UnityEngine.Object;
6+
7+
namespace Toolbox.Editor
8+
{
9+
public static class DragAndDropUtility
10+
{
11+
static DragAndDropUtility()
12+
{
13+
validateAssignmentMethod = typeof(EditorGUI)
14+
.GetMethod("ValidateObjectFieldAssignment",
15+
BindingFlags.NonPublic | BindingFlags.Static);
16+
appendFoldoutValueMethod = typeof(SerializedProperty)
17+
.GetMethod("AppendFoldoutPPtrValue",
18+
BindingFlags.NonPublic | BindingFlags.Instance);
19+
}
20+
21+
22+
private static readonly MethodInfo validateAssignmentMethod;
23+
private static readonly MethodInfo appendFoldoutValueMethod;
24+
25+
private static readonly int dragAndDropHash = "customDragAndDrop".GetHashCode();
26+
27+
28+
public static Object ValidateAssignment(Object[] references, SerializedProperty property, Type type, bool exactType)
29+
{
30+
#if UNITY_2017_1_OR_NEWER
31+
var methodParams = new object[4];
32+
methodParams[3] = exactType ? 1 : 0;
33+
#else
34+
var methodParams = new object[3];
35+
#endif
36+
methodParams[0] = references;
37+
methodParams[1] = type;
38+
methodParams[2] = property;
39+
return validateAssignmentMethod?.Invoke(null, methodParams) as Object;
40+
}
41+
42+
public static void AppendDragAndDropValue(Object value, SerializedProperty property)
43+
{
44+
property.serializedObject.Update();
45+
appendFoldoutValueMethod?.Invoke(property, new object[] { value });
46+
property.serializedObject.ApplyModifiedProperties();
47+
}
48+
49+
public static void DoDragAndDropForProperty(Rect rect, SerializedProperty property)
50+
{
51+
var controlId = GUIUtility.GetControlID(dragAndDropHash, FocusType.Passive, rect);
52+
var currentEvent = Event.current;
53+
switch (currentEvent.type)
54+
{
55+
case EventType.DragExited:
56+
if (GUI.enabled)
57+
{
58+
HandleUtility.Repaint();
59+
}
60+
61+
break;
62+
case EventType.DragUpdated:
63+
case EventType.DragPerform:
64+
if (GUI.enabled && rect.Contains(currentEvent.mousePosition))
65+
{
66+
var references = DragAndDrop.objectReferences;
67+
var candidates = new Object[1];
68+
var dragAccepted = true;
69+
foreach (var o in references)
70+
{
71+
candidates[0] = o;
72+
var validatedObject = ValidateAssignment(candidates, property, null, false);
73+
if (validatedObject)
74+
{
75+
DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
76+
if (currentEvent.type == EventType.DragPerform)
77+
{
78+
AppendDragAndDropValue(validatedObject, property);
79+
dragAccepted = true;
80+
DragAndDrop.activeControlID = 0;
81+
}
82+
else
83+
{
84+
DragAndDrop.activeControlID = controlId;
85+
}
86+
}
87+
}
88+
89+
if (dragAccepted)
90+
{
91+
GUI.changed = true;
92+
DragAndDrop.AcceptDrag();
93+
}
94+
}
95+
96+
break;
97+
}
98+
}
99+
}
100+
}

Assets/Editor Toolbox/Editor/Utilities/DragAndDropUtility.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)