Skip to content

Commit

Permalink
Adding serialized ref selector
Browse files Browse the repository at this point in the history
  • Loading branch information
ahmed-shariff committed Sep 29, 2024
1 parent 92c3271 commit c8a8db0
Show file tree
Hide file tree
Showing 11 changed files with 657 additions and 1 deletion.
8 changes: 8 additions & 0 deletions Editor/SerializeReferenceSelector.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

89 changes: 89 additions & 0 deletions Editor/SerializeReferenceSelector/AdvancedTypePopup.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
using System;
using System.Linq;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEditor.IMGUI.Controls;

namespace ubco.ovilab.HPUI.Editor
{
/// <summary>
/// Items used in <see cref="AdvancedTypePopup"/>
/// </summary>
public class AdvancedTypePopupItem : AdvancedDropdownItem
{
/// <summary>
/// The <see cref="Type"/> representing the item.
/// </summary>
public Type Type { get; }

public AdvancedTypePopupItem (Type type,string name) : base(name)
{
Type = type;
}
}

/// <summary>
/// A type popup with a fuzzy finder.
/// </summary>
/// This is taken from https://github.com/mackysoft/Unity-SerializeReferenceExtensions
public class AdvancedTypePopup : AdvancedDropdown
{
private string emptyDisplayName;
private Type[] types;

public event Action<AdvancedTypePopupItem> OnItemSelected;

public AdvancedTypePopup (IEnumerable<Type> types, int maxLineCount, string emptyDisplayName, AdvancedDropdownState state) : base(state)
{
this.emptyDisplayName = emptyDisplayName;
this.types = types.ToArray();
minimumSize = new Vector2(minimumSize.x,EditorGUIUtility.singleLineHeight * maxLineCount + EditorGUIUtility.singleLineHeight * 2f);
}

/// <inheritdoc />
protected override AdvancedDropdownItem BuildRoot ()
{
AdvancedDropdownItem root = new AdvancedDropdownItem("Select Type");
int itemCount = 0;

// Add null item.
AdvancedTypePopupItem nullItem = new AdvancedTypePopupItem(null, emptyDisplayName)
{
id = itemCount++
};

root.AddChild(nullItem);

// Add type items.
foreach (Type type in types)
{
AdvancedDropdownItem parent = root;

string typeDisplayName = ObjectNames.NicifyVariableName(type.Name);
if (!string.IsNullOrEmpty(type.Namespace))
{
typeDisplayName += $" ({type.Namespace})";
}

// Add type item.
AdvancedTypePopupItem item = new AdvancedTypePopupItem(type, typeDisplayName)
{
id = itemCount++
};
parent.AddChild(item);
}
return root;
}

/// <inheritdoc />
protected override void ItemSelected (AdvancedDropdownItem item)
{
base.ItemSelected(item);
if (item is AdvancedTypePopupItem typePopupItem)
{
OnItemSelected?.Invoke(typePopupItem);
}
}
}
}
11 changes: 11 additions & 0 deletions Editor/SerializeReferenceSelector/AdvancedTypePopup.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
using System;
using UnityEditor;
using UnityEngine;

namespace ubco.ovilab.HPUI.Editor
{
public static class ManagedReferenceContextualPropertyMenu
{
const string kCopiedPropertyPathKey = "SerializeReferenceExtensions.CopiedPropertyPath";
const string kClipboardKey = "SerializeReferenceExtensions.CopyAndPasteProperty";

static readonly GUIContent kPasteContent = new GUIContent("Paste Property");
static readonly GUIContent kNewInstanceContent = new GUIContent("New Instance");
static readonly GUIContent kResetAndNewInstanceContent = new GUIContent("Reset and New Instance");

[InitializeOnLoadMethod]
static void Initialize ()
{
EditorApplication.contextualPropertyMenu += OnContextualPropertyMenu;
}

static void OnContextualPropertyMenu (GenericMenu menu, SerializedProperty property)
{
if (property.propertyType == SerializedPropertyType.ManagedReference)
{
// NOTE: When the callback function is called, the SerializedProperty is rewritten to the property that was being moused over at the time,
// so a new SerializedProperty instance must be created.
SerializedProperty clonedProperty = property.Copy();

menu.AddItem(new GUIContent($"Copy \"{property.propertyPath}\" property"), false, Copy, clonedProperty);

string copiedPropertyPath = SessionState.GetString(kCopiedPropertyPathKey, string.Empty);
if (!string.IsNullOrEmpty(copiedPropertyPath))
{
menu.AddItem(new GUIContent($"Paste \"{copiedPropertyPath}\" property"), false, Paste, clonedProperty);
}
else
{
menu.AddDisabledItem(kPasteContent);
}

menu.AddSeparator("");

bool hasInstance = clonedProperty.managedReferenceValue != null;
if (hasInstance)
{
menu.AddItem(kNewInstanceContent, false, NewInstance, clonedProperty);
menu.AddItem(kResetAndNewInstanceContent, false, ResetAndNewInstance, clonedProperty);
}
else
{
menu.AddDisabledItem(kNewInstanceContent);
menu.AddDisabledItem(kResetAndNewInstanceContent);
}
}
}

static void Copy (object customData)
{
SerializedProperty property = (SerializedProperty)customData;
string json = JsonUtility.ToJson(property.managedReferenceValue);
SessionState.SetString(kCopiedPropertyPathKey, property.propertyPath);
SessionState.SetString(kClipboardKey, json);
}

static void Paste (object customData)
{
SerializedProperty property = (SerializedProperty)customData;
string json = SessionState.GetString(kClipboardKey, string.Empty);
if (string.IsNullOrEmpty(json))
{
return;
}

Undo.RecordObject(property.serializedObject.targetObject, "Paste Property");
JsonUtility.FromJsonOverwrite(json, property.managedReferenceValue);
property.serializedObject.ApplyModifiedProperties();
}

static void NewInstance (object customData)
{
SerializedProperty property = (SerializedProperty)customData;
string json = JsonUtility.ToJson(property.managedReferenceValue);

Undo.RecordObject(property.serializedObject.targetObject, "New Instance");
property.managedReferenceValue = JsonUtility.FromJson(json, property.managedReferenceValue.GetType());
property.serializedObject.ApplyModifiedProperties();

Debug.Log($"Create new instance of \"{property.propertyPath}\".");
}

static void ResetAndNewInstance (object customData)
{
SerializedProperty property = (SerializedProperty)customData;

Undo.RecordObject(property.serializedObject.targetObject, "Reset and New Instance");
property.managedReferenceValue = Activator.CreateInstance(property.managedReferenceValue.GetType());
property.serializedObject.ApplyModifiedProperties();

Debug.Log($"Reset property and created new instance of \"{property.propertyPath}\".");
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit c8a8db0

Please sign in to comment.