Eflatun.SceneReference
Eflatun.SceneReference copied to clipboard
Add support for dragging and dropping multiple scene files on lists (& other collections)
The issue:
The package doesn't allow populating a List of SceneReferences with multiple scene files in the inspector (via box selecting multiple files and dragging and dropping in the component's inspector).
Expected behavior:
Dragging and dropping multiple scene files into an list of SceneReferences should allow populating that list with the dropped files.
Steps to reproduce:
- Open Unity with any project containing the package
- Create an empty object and add a script to it
- Inside that script, add a variable of type
List<SceneReference>
; - Back in the Unity inspector, select multiple scene files and try dragging them into the newly-created List variable; This should not be possible, even though it "would" be expected (as it is a pretty common QoL improvement)
Other
https://github.com/starikcetin/Eflatun.SceneReference/assets/27467821/27c7e6a5-2d0a-493c-95c9-0f57752aeb97
This can be a bit tricky, but I'll see what we can do.
Fortunately, Unity does provide some tools for working with drag and drop on the editor - here's a link to the DragAndDrop class docs.
From what I've read, you can't implement PropertyDrawer scripts directy to generic classes such as List<>
, which makes things even more complicated (in a personal project I'm using a wrapper for the list, and a drawer script for that wrapper). Don't know what happens to the list drawer if you implement the DragAndDrop operations directly to the SceneReference drawer script though.
Most implementations I've seen iterate over the DragAndDrop.objectReferences
variable, casting and adding objects to the list in question, but you don't have access to the list inside the PropertDrawer script, so that's another issue too.
The drag and drop logic (probably) will look something like this:
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
if (position.Contains(Event.current.mousePosition))
{
if (Event.current.type == EventType.DragUpdated
|| Event.current.type == EventType.DragPerform)
{
DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
if (Event.current.type == EventType.DragPerform)
{
DragAndDrop.AcceptDrag();
foreach (Object draggedObj in DragAndDrop.objectReferences)
{
Debug.Log(draggedObj as SceneAsset);
// From here do whatever you want with the draggedObj.
}
}
Event.current.Use();
}
}
}
I could not devise a pretty way to do this consistently. I am leaving the issue open until I devise a good approach, or someone smarter than me decides to take a stab at it. I am happy to accept PRs.
I'll take a swing at it pretty soon enough. I got my editor script working on instancing my wrapper classes, so I'll see if I can work around making a property drawer for List<SceneReference>
types, and adapting my script to the package. Did you happen to find anything about that (making drawers for lists)?
I'll take a swing at it pretty soon enough. I got my editor script working on instancing my wrapper classes, so I'll see if I can work around making a property drawer for
List<SceneReference>
types, and adapting my script to the package. Did you happen to find anything about that (making drawers for lists)?
Let me run you through what I have thought of so far. I rejected all the options in my head for the reasons given:
- A dummy wrapper class
SceneReferenceList : List<SceneReference>
to be used instead ofList<SceneReference>
. Then we would write a property drawer for the wrapper. This is the sanest option, but has the obvious drawback of introducing another type to the hierarchy. - Custom editor for
UnityEngine.Object
. This has the very high risk of colliding with other custom editors. - Tapping into Unity's internals with reflection or IL patching. This is very brittle and dangerous.
- Doing it in the existing
SceneReference
property drawer as you suggested. This has a couple of issues. First is we don't have a way to access the drawing rect of the list header, we can only approximate it. Second, if the list doesn't already have anySceneReference
s in it, then the property drawer doesn't even run, which is inconsistent behavior.
As far as I know, it is not possible to overwrite List
drawing with property drawers, but that might have changed since the last time I tried it.
Gonna give a go at 4; I was flirting around with the constants in EditorGUIUtility
(singeLineHeight
, standardVerticalSpacing
and so on) and with the idea of getting the list via the serialized object. If I come up with something, I'll open up a PR.
I took a stab at this and reached the same conclusion as @starikcetin; i.e that there seems to be no elegant way of doing this without a (a) wrapper on List<SceneReference>, or (b) dangerous touching of shared classes like Object or ReorderableLists or (c) forcing property drawers to do something it was not meant to do, potentially breaking things in unexpected ways or inconsistent behaviour between unity versions.
IMHO, best solution might be to paste a link to an example in the documentation to a blob with code to a custom inspector that implements this functionality via an approach similar to: https://gist.github.com/bzgeb/3800350. That way users that need it can create their own inspector.