implements "Auto DragAndDrop UsageTypeDetection" for reference fields based on PR from @toasterhead-master

- contains only changes regarding to the feature itself without any of the refactoring
    - reduces the amount of boilerplate classes

Closes: #441
Co-authored-by: ToasterHead<toasterhead-master>
This commit is contained in:
Oliver Biwer 2023-11-03 22:33:04 +01:00
parent 37ead24dbb
commit 2ddefb7f4a
10 changed files with 99 additions and 12 deletions

View File

@ -7,7 +7,7 @@ namespace UnityAtoms.BaseAtoms.Editor
/// A custom property drawer for AtomCollectionReference. Makes it possible to choose between a Collection or a Collection Instancer.
/// </summary>
[CustomPropertyDrawer(typeof(AtomCollectionReference), true)]
public class AtomCollectionReferenceDrawer : AtomBaseReferenceDrawer
public class AtomCollectionReferenceDrawer : AtomBaseReferenceDrawer<AtomCollectionInstancer>
{
protected class UsageCollection : UsageData
{

View File

@ -7,7 +7,7 @@ namespace UnityAtoms.BaseAtoms.Editor
/// A custom property drawer for AtomListReference. Makes it possible to choose between a List or a List Instancer.
/// </summary>
[CustomPropertyDrawer(typeof(AtomListReference), true)]
public class AtomListReferenceDrawer : AtomBaseReferenceDrawer
public class AtomListReferenceDrawer : AtomBaseReferenceDrawer<AtomListInstancer>
{
protected class UsageList : UsageData
{

View File

@ -7,7 +7,7 @@ namespace UnityAtoms.BaseAtoms.Editor
/// A custom property drawer for AtomBaseVariable BaseEventReferences. Makes it possible to choose between an Event, Event Instancer, Collection Added, Collection Removed, List Added, List Removed, Collection Instancer Added, Collection Instancer Removed, List Instancer Added or List Instancer Removed.
/// </summary>
[CustomPropertyDrawer(typeof(AtomBaseVariableBaseEventReference), true)]
public class AtomBaseVariableBaseEventReferenceDrawer : AtomBaseReferenceDrawer
public class AtomBaseVariableBaseEventReferenceDrawer : AtomBaseReferenceDrawer<AtomBaseVariableEventInstancer>
{
protected class UsageEvent : UsageData
{

View File

@ -7,7 +7,7 @@ namespace UnityAtoms.BaseAtoms.Editor
/// A custom property drawer for Void BaseEventReferences. Makes it possible to choose between an Event, Event Instancer, Collection Cleared, List Cleared, Collection Instancer Cleared or List Instancer Cleared.
/// </summary>
[CustomPropertyDrawer(typeof(VoidBaseEventReference), true)]
public class VoidBaseEventReferenceDrawer : AtomBaseReferenceDrawer
public class VoidBaseEventReferenceDrawer : AtomBaseReferenceDrawer<VoidEventInstancer>
{
protected class UsageEvent : UsageData
{

View File

@ -8,7 +8,8 @@ namespace UnityAtoms.Editor
/// A custom property drawer for References (Events and regular). Makes it possible to reference a resources (Variable or Event) through multiple options.
/// </summary>
public abstract class AtomBaseReferenceDrawer : PropertyDrawer
public abstract class AtomBaseReferenceDrawer<TInstancerType> : PropertyDrawer
where TInstancerType : MonoBehaviour
{
protected abstract class UsageData
{
@ -16,6 +17,7 @@ namespace UnityAtoms.Editor
public abstract string PropertyName { get; }
public abstract string DisplayName { get; }
}
const string USAGE_PROPERTY_NAME = "_usage";
protected abstract UsageData[] GetUsages(SerializedProperty prop = null);
@ -24,7 +26,7 @@ namespace UnityAtoms.Editor
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
var usageIntVal = property.FindPropertyRelative("_usage").intValue;
var usageIntVal = property.FindPropertyRelative(USAGE_PROPERTY_NAME).intValue;
var usageData = GetUsages(property)[0];
for (var i = 0; i < GetUsages(property).Length; ++i)
{
@ -67,8 +69,9 @@ namespace UnityAtoms.Editor
int indent = EditorGUI.indentLevel;
EditorGUI.indentLevel = 0;
var currentUsage = property.FindPropertyRelative("_usage");
var currentUsage = property.FindPropertyRelative(USAGE_PROPERTY_NAME);
var newUsageValue = EditorGUI.Popup(buttonRect, currentUsage.intValue, GetPopupOptions(property), _popupStyle);
DetermineDragAndDropFieldReferenceType(position, property, ref newUsageValue);
currentUsage.intValue = newUsageValue;
var usageTypePropertyName = GetUsages(property)[newUsageValue].PropertyName;
@ -100,5 +103,63 @@ namespace UnityAtoms.Editor
EditorGUI.indentLevel = indent;
EditorGUI.EndProperty();
}
#region Auto DragAndDrop UsageTypeDetection
private void DetermineDragAndDropFieldReferenceType(in Rect position, SerializedProperty property,
ref int newUsageValue)
{
EventType mouseEventType = Event.current.type;
if (mouseEventType != EventType.DragUpdated && mouseEventType != EventType.DragPerform)
return;
if (!IsMouseHoveringOverProperty(position))
return;
var draggedObjects = DragAndDrop.objectReferences;
if (draggedObjects.Length < 1)
return;
Object draggedObject = draggedObjects[0];
if (draggedObject is GameObject gameObject)
draggedObject = gameObject.GetComponent<TInstancerType>();
UpdateConfigurationOption(property, draggedObject, ref newUsageValue);
}
private void UpdateConfigurationOption(SerializedProperty property, Object draggedObject, ref int newUsageValue)
{
if (!draggedObject)
return;
var usages = GetUsages(property);
for (int index = 0; index < usages.Length; index++)
{
var usage = usages[index];
SerializedProperty fieldProperty = property.FindPropertyRelative(usage.PropertyName);
string draggedObjectType = draggedObject.GetType().Name;
string fieldPropertyType = fieldProperty.type.Replace("PPtr<$", "").Replace(">", "");
if (draggedObjectType == fieldPropertyType)
{
newUsageValue = index;
break;
}
}
}
private static bool IsMouseHoveringOverProperty(Rect rectPosition)
{
const int HEIGHT_OFFSET_TO_AVOID_OVERLAP = 1;
Rect controlRect = rectPosition;
controlRect.height -= HEIGHT_OFFSET_TO_AVOID_OVERLAP;
return controlRect.Contains(Event.current.mousePosition);
}
#endregion
}
}

View File

@ -6,7 +6,7 @@ namespace UnityAtoms.Editor
/// A custom property drawer for Event References. Makes it possible to choose between an Event, Event Instancer, Variable or a Variable Instancer.
/// </summary>
[CustomPropertyDrawer(typeof(AtomBaseEventReference), true)]
public class AtomEventReferenceDrawer : AtomBaseReferenceDrawer
public class AtomEventReferenceDrawer : AtomBaseReferenceDrawer<AtomEventInstancer>
{
protected class UsageEvent : UsageData
{

View File

@ -7,7 +7,7 @@ namespace UnityAtoms.Editor
/// </summary>
[CustomPropertyDrawer(typeof(AtomBaseReference), true)]
public class AtomReferenceDrawer : AtomBaseReferenceDrawer
public class AtomReferenceDrawer : AtomBaseReferenceDrawer<AtomBaseVariableInstancer>
{
protected class UsageValue : UsageData
{

View File

@ -5,6 +5,11 @@ using UnityEngine.Assertions;
namespace UnityAtoms
{
public abstract class AtomEventInstancer : MonoBehaviour
{
}
/// <summary>
/// An Event Instancer is a MonoBehaviour that takes an Event as a base and creates an in memory copy of it on OnEnable.
/// This is handy when you want to use Events for prefabs that are instantiated at runtime.
@ -13,7 +18,7 @@ namespace UnityAtoms
/// <typeparam name="E">Event of type T.</typeparam>
[EditorIcon("atom-icon-sign-blue")]
[DefaultExecutionOrder(Runtime.ExecutionOrder.VARIABLE_INSTANCER)]
public abstract class AtomEventInstancer<T, E> : MonoBehaviour, IGetEvent, ISetEvent
public abstract class AtomEventInstancer<T, E> : AtomEventInstancer, IGetEvent, ISetEvent
where E : AtomEvent<T>
{
public T InspectorRaiseValue { get => _inspectorRaiseValue; }

View File

@ -4,6 +4,11 @@ using UnityEngine.Assertions;
namespace UnityAtoms
{
public abstract class AtomBaseVariableInstancer : MonoBehaviour
{
}
/// <summary>
/// A Variable Instancer is a MonoBehaviour that takes a variable as a base and creates an in memory copy of it OnEnable.
/// This is handy when you want to use atoms for prefabs that are instantiated at runtime. Use together with AtomCollection to
@ -17,7 +22,7 @@ namespace UnityAtoms
/// <typeparam name="F">Function of type T => T</typeparam>
[EditorIcon("atom-icon-hotpink")]
[DefaultExecutionOrder(Runtime.ExecutionOrder.VARIABLE_INSTANCER)]
public abstract class AtomBaseVariableInstancer<T, V> : MonoBehaviour, IVariable<V>
public abstract class AtomBaseVariableInstancer<T, V> : AtomBaseVariableInstancer, IVariable<V>
where V : AtomBaseVariable<T>
{
/// <summary>
@ -25,6 +30,22 @@ namespace UnityAtoms
/// </summary>
public V Variable { get => _inMemoryCopy; }
public void SetSource(V variable)
{
_base = variable;
if (Application.isPlaying)
{
Debug.LogWarning("Setting the underlying variable of an instancers at runtime can result in hard to find issues. be careful.");
if (_inMemoryCopy != null)
{
Destroy(_inMemoryCopy);
_inMemoryCopy = null;
}
OnEnable();
}
}
/// <summary>
/// Getter for retrieving the value of the in memory runtime variable.
/// </summary>

View File

@ -7,7 +7,7 @@ namespace UnityAtoms.FSM.Editor
/// A custom property drawer for FiniteStateMachineReference. Makes it possible to choose between a FSM or a FSM Instancer.
/// </summary>
[CustomPropertyDrawer(typeof(FiniteStateMachineReference), true)]
public class FiniteStateMachineReferenceDrawer : AtomBaseReferenceDrawer
public class FiniteStateMachineReferenceDrawer : AtomBaseReferenceDrawer<FiniteStateMachineInstancer>
{
protected class UsageFSM : UsageData
{