From dc341ff33011c0b545cc46e82c49629c5c488e07 Mon Sep 17 00:00:00 2001 From: Hubert Mattusch Date: Sat, 5 Sep 2026 02:16:56 +0200 Subject: [PATCH 1/2] Implement drag and drop for InterfaceRefPropertyDrawer --- InterfaceRefPropertyDrawer.cs | 97 +++++++++++++++++++++++++++++++++-- 1 file changed, 94 insertions(+), 3 deletions(-) diff --git a/InterfaceRefPropertyDrawer.cs b/InterfaceRefPropertyDrawer.cs index a6fc52f..bc1ef32 100644 --- a/InterfaceRefPropertyDrawer.cs +++ b/InterfaceRefPropertyDrawer.cs @@ -1,8 +1,10 @@ #if UNITY_EDITOR using UnityEditor; using UnityEngine; +using Object = UnityEngine.Object; #if UNITY_2022_2_OR_NEWER +using System; using UnityEngine.UIElements; using UnityEditor.UIElements; #endif @@ -13,18 +15,107 @@ namespace KBCore.Refs public class InterfaceRefPropertyDrawer : PropertyDrawer { private const string IMPLEMENTER_PROP = "_implementer"; - + // unity 2022.2 makes UIToolkit the default for inspectors #if UNITY_2022_2_OR_NEWER public override VisualElement CreatePropertyGUI(SerializedProperty property) { - return new PropertyField(property.FindPropertyRelative(IMPLEMENTER_PROP), property.displayName); + var field = new PropertyField(property.FindPropertyRelative(IMPLEMENTER_PROP), property.displayName); + field.RegisterCallback(DragPerformEvent, property, + TrickleDown.TrickleDown); + field.RegisterCallback(DragUpdatedEvent, property, + TrickleDown.TrickleDown); + + return field; + } + + private void DragUpdatedEvent(DragUpdatedEvent evt, SerializedProperty serializedProperty) + { + evt.StopImmediatePropagation(); + if (!TryGetDraggedComponent(serializedProperty, out _)) + { + DragAndDrop.visualMode = DragAndDropVisualMode.Rejected; + return; + } + + DragAndDrop.visualMode = DragAndDropVisualMode.Generic; + } + + private void DragPerformEvent(DragPerformEvent evt, SerializedProperty serializedProperty) + { + if (!TryGetDraggedComponent(serializedProperty, out var component)) + return; + + DragAndDrop.AcceptDrag(); + + serializedProperty.FindPropertyRelative(IMPLEMENTER_PROP).objectReferenceValue = component; + serializedProperty.serializedObject.ApplyModifiedProperties(); + + evt.StopImmediatePropagation(); } + #endif public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { - EditorGUI.PropertyField(position, property.FindPropertyRelative(IMPLEMENTER_PROP), label, true); + EditorGUI.BeginProperty(position, label, property); + + EditorGUI.ObjectField(position, property.FindPropertyRelative(IMPLEMENTER_PROP), label); + HandleIMGUIObjectDrag(position, property); + + EditorGUI.EndProperty(); + } + + private void HandleIMGUIObjectDrag(Rect position, SerializedProperty property) + { + var evt = Event.current; + + if ((evt.type != EventType.DragUpdated && evt.type != EventType.DragPerform) || !position.Contains(evt.mousePosition)) + return; + + evt.Use(); + + if (!TryGetDraggedComponent(property, out var component)) + { + DragAndDrop.visualMode = DragAndDropVisualMode.Rejected; + return; + } + + DragAndDrop.visualMode = DragAndDropVisualMode.Generic; + + if (evt.type == EventType.DragPerform) + { + DragAndDrop.AcceptDrag(); + + property.FindPropertyRelative(IMPLEMENTER_PROP).objectReferenceValue = component; + property.serializedObject.ApplyModifiedProperties(); + + GUI.changed = true; + } + + evt.Use(); + } + + private static bool TryGetDraggedComponent(SerializedProperty property, out Component component) + { + component = null; + + if (DragAndDrop.objectReferences.Length != 1) + return false; + + var targetType = ((ISerializableRef)property.boxedValue).RefType; + Object checkedObject = DragAndDrop.objectReferences[0]; + + if (checkedObject is Component draggedComponent && targetType.IsAssignableFrom(draggedComponent.GetType())) + { + component = draggedComponent; + return true; + } + + if (checkedObject is GameObject go && go.TryGetComponent(targetType, out component)) + return true; + + return false; } } } From 819433f34217f2d8d4a7603b0fe0478adae72253 Mon Sep 17 00:00:00 2001 From: Hubert Mattusch Date: Sat, 5 Sep 2026 02:41:49 +0200 Subject: [PATCH 2/2] Override Equals to avoid marking scenes dirty --- InterfaceRef.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/InterfaceRef.cs b/InterfaceRef.cs index da87c61..a47b4db 100644 --- a/InterfaceRef.cs +++ b/InterfaceRef.cs @@ -8,7 +8,7 @@ namespace KBCore.Refs /// /// Component type to find and serialize. [Serializable] - public class InterfaceRef : ISerializableRef + public class InterfaceRef : ISerializableRef, IEquatable> where T : class { @@ -57,5 +57,12 @@ void ISerializableRef.Clear() this._value = null; this._implementer = null; } + + public override bool Equals(object obj) => obj is InterfaceRef casted && Equals(casted); + + public bool Equals(InterfaceRef other) + { + return other != null && _implementer == other._implementer; + } } } \ No newline at end of file