Skip to content

Commit

Permalink
0.5.6 RefTo: drag and drop
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexeyTaranov committed Feb 16, 2025
1 parent 144c960 commit dbe9641
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 48 deletions.
2 changes: 1 addition & 1 deletion Editor/Layouts/RefTo.uxml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<ui:Label text="Label" name="RefName" style="-unity-font-style: bold; color: rgb(255, 255, 255); background-color: rgb(88, 88, 88); padding-right: 6px; padding-left: 6px;" />
<ui:VisualElement style="flex-grow: 1; max-width: 8px;" />
<ui:Label text="Host:" style="max-width: 120px; color: rgb(196, 196, 196);" />
<uie:ObjectField name="Host" enabled="false" style="color: rgb(196, 196, 196);" />
<uie:ObjectField name="Host" enabled="true" style="color: rgb(196, 196, 196);" />
</ui:GroupBox>
</ui:VisualElement>
</ui:UXML>
12 changes: 4 additions & 8 deletions Editor/RefTo/RefToContextMenu.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,16 @@ private static void ShowRefToContextMenu(GenericMenu menu, SerializedProperty pr
{
var copy = property.Copy();

if (RefToExtensions.TryGetRefType(copy, out var targetType))
if (property.isArray == false && RefToExtensions.TryGetRefType(copy, out var targetType, out _))
{
menu.AddItem(new GUIContent("RefTo: Reset"), false,
(_) => { RefToExtensions.ResetRefTo(copy); },
null);

var isCanWriteToRef =
_copy.referenceType != null && targetType.IsAssignableFrom(_copy.referenceType);
var isSameType = _copy.referenceType != null && targetType.IsAssignableFrom(_copy.referenceType);
var typeName = _copy.referenceType?.Name;

var typeName = _copy.referenceType == null
? string.Empty
: ObjectNames.NicifyVariableName(_copy.referenceType.Name);

if (isCanWriteToRef)
if (isSameType)
{
menu.AddItem(new GUIContent($"RefTo: Paste Serialize Reference: {typeName}"), false,
(_) => { PasteToProperty(copy); },
Expand Down
73 changes: 71 additions & 2 deletions Editor/RefTo/RefToExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ public static class RefToExtensions
private const string HostPropertyName = "_host";
private const string ReferenceIdName = "_referenceId";

public static bool TryGetRefType(SerializedProperty property, out Type refToType)
public static bool TryGetRefType(SerializedProperty property, out Type refToType, out Type hostType)
{
var toType = property.boxedValue?.GetType();
refToType = null;
hostType = null;
if (IsGenericTypeOf(toType, typeof(RefTo<,>)))
{
refToType = toType.GenericTypeArguments[0];
hostType = toType.GenericTypeArguments[1];
return true;
}

Expand All @@ -40,7 +42,7 @@ public static void WriteRefToFromPropertyToProperty(SerializedProperty fromPrope
toProperty.serializedObject.Update();
}

public static (Object host, long id) GetRefToFieldsFromProperty(SerializedProperty property)
private static (Object host, long id) GetRefToFieldsFromProperty(SerializedProperty property)
{
var targetObject = property.boxedValue;
if (targetObject != null)
Expand Down Expand Up @@ -76,6 +78,73 @@ private static object GetValue(object target, string fieldName)
var field = type.GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic);
return field?.GetValue(target);
}

public static bool TraverseProperty(SerializedProperty inProperty, string path,
Func<SerializedProperty, bool> isCompleteFunc)
{
using var currentProperty = inProperty.Copy();

do
{
var propertyPath = string.IsNullOrEmpty(path) ? currentProperty.name : $"{path}.{currentProperty.name}";

if (currentProperty.isArray && currentProperty.propertyType != SerializedPropertyType.String)
{
for (int i = 0; i < currentProperty.arraySize; i++)
{
var element = currentProperty.GetArrayElementAtIndex(i);
if (TraverseProperty(element, $"{propertyPath}[{i}]", isCompleteFunc))
{
return true;
}
}
}
else if (currentProperty.hasVisibleChildren && currentProperty.propertyType == SerializedPropertyType.Generic)
{
if (TraverseProperty(currentProperty, propertyPath, isCompleteFunc))
{
return true;
}
}
else
{
if (currentProperty.propertyType == SerializedPropertyType.ManagedReference)
{
if (isCompleteFunc.Invoke(currentProperty))
{
return true;
}
}
}
}
while (currentProperty.NextVisible(false));

return false;
}

public static (Type refType, Type targetType, Type hostType, Object host, bool isSameType) GetInspectorValues(
SerializedProperty property)
{
var (host, id) = GetRefToFieldsFromProperty(property);
TryGetRefType(property, out var targetType, out var hostType);
var isSameType = false;
Type refType = null;
if (host != null)
{
var reference = UnityEngine.Serialization.ManagedReferenceUtility.GetManagedReference(host, id);
if (reference != null)
{
refType = reference.GetType();
isSameType = targetType.IsAssignableFrom(refType);
}
}
else
{
isSameType = true;
}

return (refType, targetType, hostType, host, isSameType);
}
}
}
#endif
97 changes: 61 additions & 36 deletions Editor/RefTo/RefToPropertyDrawer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,30 +29,41 @@ public override float GetPropertyHeight(SerializedProperty property, GUIContent
public override void OnGUI(Rect rect, SerializedProperty property, GUIContent label)
{
var propertyRect = rect;
var (refType, targetType, host, isSameType) = GetInspectorValues(property);
var tooltip = $"Reference type: {TypeToName(refType)} \nRefTo Type: {TypeToName(targetType)}";
label.tooltip = tooltip;
var (refType, targetType, hostType, host, isSameType) = RefToExtensions.GetInspectorValues(property);
label.tooltip = $"Target \nType - {targetType.Name} \nNamespace - {targetType.Namespace}";
EditorGUI.BeginProperty(rect, label, property);
var labelWidth = EditorGUIUtility.labelWidth > 90 ? 90 : EditorGUIUtility.labelWidth;
var labelWidth = EditorGUIUtility.labelWidth / 2;
propertyRect.width = labelWidth;
EditorGUI.LabelField(propertyRect, label);


var height = EditorGUIUtility.singleLineHeight;

var fieldSize = (rect.width - labelWidth) * 0.3f;
var fieldSize = (rect.width - labelWidth) * 0.5f;
var labelRect = new Rect(rect.position + new Vector2(labelWidth, 0),
new Vector2(rect.width - fieldSize - labelWidth, height));
var fieldRect = new Rect(labelRect.position + new Vector2(labelRect.width, 0),
new Vector2(fieldSize, height));

var style = isSameType ? EditorStyles.boldLabel : ErrorStyle;
var refLabel = $"RefTo: {TypeToName(refType)} [{TypeToName(targetType)}]";
EditorGUI.LabelField(labelRect, new GUIContent(refLabel), style);
var refLabel = $" R: {refType?.Name}";
EditorGUI.LabelField(labelRect,
new GUIContent(refLabel, $"Reference \nType - {refType?.Name} \nNamespace - {refType?.Namespace}"),
style);

var newValue = EditorGUI.ObjectField(fieldRect, host, hostType, true);
if (host != newValue)
{
if (newValue != null)
{
TryApplyRefToValue(newValue, property.serializedObject.targetObject, targetType,
property.propertyPath);
}
else
{
RefToExtensions.ResetRefTo(property);
}
}

EditorGUI.BeginDisabledGroup(true);
EditorGUI.ObjectField(fieldRect, host, typeof(UnityEngine.Object), true);
EditorGUI.EndDisabledGroup();
EditorGUI.EndProperty();
}

Expand All @@ -61,55 +72,69 @@ private void DrawUIToolkit(VisualElement root, SerializedProperty property)
var uiToolkitLayoutPath = "Packages/com.alexeytaranov.serializereferencedropdown/Editor/Layouts/RefTo.uxml";
var visualTreeAsset = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(uiToolkitLayoutPath);
root.Add(visualTreeAsset.Instantiate());

var propertyField = root.Q<PropertyField>();

propertyField.BindProperty(property);
var (_, targetType, _, _) = GetInspectorValues(property);
var (_, targetType, hostType, _, _) = RefToExtensions.GetInspectorValues(property);

propertyField.tooltip =
$"Field: {property.name} \nTarget Type: {targetType.Name} \nNamespace: {targetType.Namespace}";

propertyField.tooltip = $"Field: {property.name} \nTarget Type: {targetType.Name} \nNamespace: {targetType.Namespace}";

var propertyPath = property.propertyPath;
root.TrackSerializedObjectValue(property.serializedObject, RefreshDynamic);
var objectField = root.Q<ObjectField>();
objectField.objectType = hostType;
var targetObject = property.serializedObject.targetObject;
objectField.RegisterValueChangedCallback(ApplyRefToFromObject);
RefreshDynamic(property.serializedObject);

void RefreshDynamic(SerializedObject so)
{
using var localProperty = so.FindProperty(propertyPath);
var (refType, _, host, isSameType) = GetInspectorValues(localProperty);
var (refType, _, _, host, isSameType) = RefToExtensions.GetInspectorValues(localProperty);
var refLabel = root.Q<Label>("RefName");
var refTypeName = refType == null ? "null" : refType.Name;
refLabel.text = $"R:{refTypeName}";
refLabel.tooltip = $"Reference \nType: {refType?.Name} \nNamespace: {refType?.Namespace}";
refLabel.style.color = isSameType ? new StyleColor(Color.white) : new StyleColor(Color.red);
root.Q<ObjectField>().value = host;
root.Q<ObjectField>().SetValueWithoutNotify(host);
}
}

private (Type refType, Type targetType, Object host, bool isSameType) GetInspectorValues(
SerializedProperty property)
{
var (host, id) = RefToExtensions.GetRefToFieldsFromProperty(property);
RefToExtensions.TryGetRefType(property, out var targetType);
var isSameType = false;
Type refType = null;
if (host != null)
void ApplyRefToFromObject(ChangeEvent<Object> evt)
{
var reference = UnityEngine.Serialization.ManagedReferenceUtility.GetManagedReference(host, id);
if (reference != null)
var newValue = evt.newValue;
if (TryApplyRefToValue(newValue, targetObject, targetType, propertyPath) == false &&
newValue != null)
{
refType = reference.GetType();
isSameType = targetType.IsAssignableFrom(refType);
objectField.SetValueWithoutNotify(evt.previousValue);
}
}
else
}

private bool TryApplyRefToValue(Object newValue, Object targetObject, Type targetType,
string targetPropertyPath)
{
using var newValueSo = new SerializedObject(newValue);
using var newValueIteratorProperty = newValueSo.GetIterator();
newValueIteratorProperty.NextVisible(true);
return RefToExtensions.TraverseProperty(newValueIteratorProperty, string.Empty, TryWriteToRefTo);


bool TryWriteToRefTo(SerializedProperty refProperty)
{
isSameType = true;
}
var refType = TypeUtils.ExtractTypeFromString(refProperty.managedReferenceFullTypename);
if (targetType.IsAssignableFrom(refType))
{
using var targetSo = new SerializedObject(targetObject);
using var copyProperty = targetSo.FindProperty(targetPropertyPath);
RefToExtensions.WriteRefToFromPropertyToProperty(refProperty, copyProperty);
return true;
}

return (refType, targetType, host, isSameType);
return false;
}
}

string TypeToName(Type type) => ObjectNames.NicifyVariableName(type?.Name);
}
}
#endif
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "com.alexeytaranov.serializereferencedropdown",
"version": "0.5.5",
"version": "0.5.6",
"displayName": "SerializeReferenceDropdown",
"description": "Editor dropdown for SerializeReference Attribute",
"unity": "2019.3",
Expand Down

0 comments on commit dbe9641

Please sign in to comment.