- Home /
Why does my custom property drawer's DragAndDrop add items twice?
I've got a custom property drawer, with this code in it's OnGui:
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
SerializedProperty serialisedList = property.FindPropertyRelative("_serialisedName");
switch (Event.current.type)
{
case EventType.DragUpdated:
DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
Event.current.Use();
break;
case EventType.DragPerform:
DragAndDrop.AcceptDrag();
foreach (Object draggedObject in DragAndDrop.objectReferences)
{
if (draggedObject is GameObject addedGameObject)
{
MyClass addedItem = addedGameObject.GetComponent<MyClass>();
if (addedItem != null)
{
int index = serialisedList.arraySize;
serialisedList.InsertArrayElementAtIndex(index);
serialisedList.GetArrayElementAtIndex(index).objectReferenceValue = addedItem;
}
}
}
break;
}
This works perfectly when dragging an item onto the "frame" of the list (i.e. not onto an item slot in the list). However, if I drag an item onto an item in the list, the above code is executed, but the item is also added at the end of the list. Is there a way to either:
a) Detect that the drag is onto an item, and avoid executing the code above.
b) Somehow avoid that the item assignment is performed. I'm not sure what's causing that.
I would prefer solution a)
, as that would still allow assigning items to specific slots, effectively overwriting the existing entries, but b)
would also be acceptable. The advantage of b)
is that it's not as easy to accidentally overwrite an item, when you really meant to just add one. It would mean that dragging an item onto the list would always have the same effect, regardless of where it's dragged (but it would work differently from the standard lists).
EDIT: Also, if I drop elements onto other lists in the same component, it ALSO adds them to this list.
Answer by Bunny83 · Jul 13, 2019 at 09:03 AM
You don't check the mouse position at all. So currently you should be able to drop your item anywhere in the inspector window, not only on your list. We don't know for what element you designed your property drawer for. A propertydrawer is meant to handle the drawing of that one SerializedProperty. You can't get access to other properties as you don't know where they are drawn inside the inspector. So it highly depends on how your actual serialized data looks like, to which property this drawer is attached to and how you draw the properties.
In any way you have to do this for the whole list and mask the events for the list / array header. You can do rect.Contains(Event.current.mousePosition)
to see if the mouse is inside that rect. Keep in mind that you have to do this in both cases. So for DragUpdated to actually show the correct visual and in DragPerform to restrict the dropping to that area. But again, which rect you should use depends on which property this drawer belongs to. By default a single "line" has a height of 15 pixel which is provided in EditorGUIUtility.singleLineHeight.
For reference you may want to have a look at my I$$anonymous$$GUI crash course
O$$anonymous$$, so I manually have to check the mouse position to know when the dragging is actually happening on my property's area. Oh, and moving the above code below the actual displaying seems to fix the adding-twice when dropping into an element :)
(I've added another answer as well, just to put the code in, in case it's useful to anyone else)
Answer by svendkiloo · Jul 15, 2019 at 07:14 AM
With the help from @Bunny83 and some trial and error I found this solution to both problems:
Adding the check on mouse position against the rect of the property drawer.
Moving the drag-handling code below the displaying code.
So, what I ended up with looks a bit like this:
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
Rect position = originalPosition; // Saving the originalPosition value, as my displaying code updates position
... // I've left out the displaying code here
SerializedProperty serialisedList = property.FindPropertyRelative("_serialisedName");
if (originalPosition.Contains(Event.current.mousePosition))
{
switch (Event.current.type)
{
case EventType.DragUpdated:
DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
Event.current.Use();
break;
case EventType.DragPerform:
DragAndDrop.AcceptDrag();
foreach (Object draggedObject in DragAndDrop.objectReferences)
{
if (draggedObject is GameObject addedGameObject)
{
MyClass addedItem = addedGameObject.GetComponent<MyClass>();
if (addedItem != null)
{
int index = serialisedList.arraySize;
serialisedList.InsertArrayElementAtIndex(index);
serialisedList.GetArrayElementAtIndex(index).objectReferenceValue = addedItem;
}
}
}
break;
}
}