Firstborn/Library/PackageCache/com.unity.cinemachine@2.8.9/Editor/PropertyDrawers/EmbeddedAssetPropertyDrawer.cs

281 lines
12 KiB
C#
Raw Normal View History

2023-03-28 13:24:16 -04:00
using UnityEngine;
using UnityEditor;
using System;
using Cinemachine.Utility;
using System.Linq;
using System.Collections.Generic;
namespace Cinemachine.Editor
{
[CustomPropertyDrawer(typeof(CinemachineEmbeddedAssetPropertyAttribute))]
internal sealed class EmbeddedAssetPropertyDrawer : PropertyDrawer
{
const float vSpace = 2;
const float kIndentAmount = 15;
const float kBoxMargin = 3;
float HelpBoxHeight { get { return EditorGUIUtility.singleLineHeight * 2.5f; } }
bool mExpanded = false;
bool WarnIfNull
{
get
{
var attr = attribute as CinemachineEmbeddedAssetPropertyAttribute;
return attr == null ? false : attr.WarnIfNull;
}
}
float HeaderHeight { get { return EditorGUIUtility.singleLineHeight * 1.5f; } }
float DrawHeader(Rect rect, string text)
{
float delta = HeaderHeight - EditorGUIUtility.singleLineHeight;
rect.y += delta; rect.height -= delta;
EditorGUI.LabelField(rect, new GUIContent(text), EditorStyles.boldLabel);
return HeaderHeight;
}
string HeaderText(SerializedProperty property)
{
var attrs = property.serializedObject.targetObject.GetType()
.GetCustomAttributes(typeof(HeaderAttribute), false);
if (attrs != null && attrs.Length > 0)
return ((HeaderAttribute)attrs[0]).header;
return null;
}
bool AssetHasCustomEditor(SerializedProperty property)
{
ScriptableObject asset = property.objectReferenceValue as ScriptableObject;
if (asset != null)
return ActiveEditorTracker.HasCustomEditor(asset);
return false;
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
bool hasCustomEditor = AssetHasCustomEditor(property);
float height = base.GetPropertyHeight(property, label);
height += + 2 * (kBoxMargin + vSpace);
if (mExpanded && !hasCustomEditor)
{
height += HelpBoxHeight + kBoxMargin;
ScriptableObject asset = property.objectReferenceValue as ScriptableObject;
if (asset != null)
{
SerializedObject so = new SerializedObject(asset);
var prop = so.GetIterator();
prop.NextVisible(true);
do
{
if (prop.name == "m_Script")
continue;
string header = HeaderText(prop);
if (header != null)
height += HeaderHeight + vSpace;
height += EditorGUI.GetPropertyHeight(prop, false) + vSpace;
}
while (prop.NextVisible(prop.isExpanded));
height += kBoxMargin;
}
}
return height;
}
public override void OnGUI(Rect rect, SerializedProperty property, GUIContent label)
{
bool hasCustomEditor = AssetHasCustomEditor(property);
rect.y += vSpace; rect.height -= 2 * vSpace;
GUI.Box(rect, GUIContent.none, GUI.skin.box);
rect.y += kBoxMargin;
rect.height = EditorGUIUtility.singleLineHeight;
EditorGUIUtility.labelWidth -= kBoxMargin;
AssetFieldWithCreateButton(
new Rect(rect.x + kBoxMargin, rect.y, rect.width - 2 * kBoxMargin, rect.height),
property, WarnIfNull,
property.serializedObject.targetObject.name + " " + label.text);
ScriptableObject asset = property.objectReferenceValue as ScriptableObject;
if (asset != null && !hasCustomEditor)
{
mExpanded = EditorGUI.Foldout(rect, mExpanded, GUIContent.none, true);
if (mExpanded)
{
rect.y += rect.height + kBoxMargin + vSpace;
rect.x += kIndentAmount + kBoxMargin;
rect.width -= kIndentAmount + 2 * kBoxMargin;
EditorGUIUtility.labelWidth -= kIndentAmount;
EditorGUI.HelpBox(
new Rect(rect.x, rect.y, rect.width, HelpBoxHeight),
"This is a shared asset.\n"
+ "Changes made here will apply to all users of this asset",
MessageType.Info);
rect.y += HelpBoxHeight + kBoxMargin;
SerializedObject so = new SerializedObject(property.objectReferenceValue);
var prop = so.GetIterator();
prop.NextVisible(true);
var indent = EditorGUI.indentLevel;
do
{
if (prop.name == "m_Script")
continue;
string header = HeaderText(prop);
if (header != null)
{
DrawHeader(rect, header);
rect.y += HeaderHeight + vSpace;
rect.height -= HeaderHeight + vSpace;
}
rect.height = EditorGUI.GetPropertyHeight(prop, false);
EditorGUI.indentLevel = indent + prop.depth;
EditorGUI.PropertyField(rect, prop);
rect.y += rect.height + vSpace;
} while (prop.NextVisible(prop.isExpanded));
if (GUI.changed)
so.ApplyModifiedProperties();
}
EditorGUIUtility.labelWidth += kIndentAmount;
}
EditorGUIUtility.labelWidth += kBoxMargin;
}
Type EmbeddedAssetType(SerializedProperty property)
{
Type type = property.serializedObject.targetObject.GetType();
var a = property.propertyPath.Split('.');
for (int i = 0; i < a.Length; ++i)
{
var field = type.GetField(a[i],
System.Reflection.BindingFlags.Public
| System.Reflection.BindingFlags.NonPublic
| System.Reflection.BindingFlags.Instance);
if (field == null) continue;
type = field.FieldType;
}
return type;
}
Type[] mAssetTypes = null;
List<ScriptableObject> mAssetPresets;
GUIContent[] mAssetPresetNames;
void RebuildPresetList()
{
if (mAssetPresets != null && mAssetPresetNames != null)
return;
mAssetPresets = new List<ScriptableObject>();
if (mAssetTypes != null)
{
for (int i = 0; i < mAssetTypes.Length; ++i)
InspectorUtility.AddAssetsFromPackageSubDirectory(
mAssetTypes[i], mAssetPresets, "Presets/Noise");
}
List<GUIContent> presetNameList = new List<GUIContent>();
foreach (var n in mAssetPresets)
presetNameList.Add(new GUIContent("Presets/" + n.name));
mAssetPresetNames = presetNameList.ToArray();
}
void AssetFieldWithCreateButton(
Rect r, SerializedProperty property, bool warnIfNull, string defaultName)
{
// Collect all the eligible asset types
Type type = EmbeddedAssetType(property);
if (mAssetTypes == null)
mAssetTypes = ReflectionHelpers.GetTypesInAllDependentAssemblies(
(Type t) => type.IsAssignableFrom(t) && !t.IsAbstract).ToArray();
float iconSize = r.height + 4;
r.width -= iconSize;
GUIContent label = new GUIContent(property.displayName, property.tooltip);
if (warnIfNull && property.objectReferenceValue == null)
label.image = EditorGUIUtility.IconContent("console.warnicon.sml").image;
EditorGUI.PropertyField(r, property, label);
r.x += r.width; r.width = iconSize; r.height = iconSize;
if (GUI.Button(r, EditorGUIUtility.IconContent("_Popup"), GUI.skin.label))
{
GenericMenu menu = new GenericMenu();
if (property.objectReferenceValue != null)
{
menu.AddItem(new GUIContent("Edit"), false, ()
=> Selection.activeObject = property.objectReferenceValue);
menu.AddItem(new GUIContent("Clone"), false, () =>
{
ScriptableObject copyFrom = property.objectReferenceValue as ScriptableObject;
if (copyFrom != null)
{
string title = "Create New " + copyFrom.GetType().Name + " asset";
ScriptableObject asset = CreateAsset(
copyFrom.GetType(), copyFrom, defaultName, title);
if (asset != null)
{
property.objectReferenceValue = asset;
property.serializedObject.ApplyModifiedProperties();
}
}
});
menu.AddItem(new GUIContent("Locate"), false, ()
=> EditorGUIUtility.PingObject(property.objectReferenceValue));
}
RebuildPresetList();
int i = 0;
foreach (var a in mAssetPresets)
{
menu.AddItem(mAssetPresetNames[i++], false, () =>
{
property.objectReferenceValue = a;
property.serializedObject.ApplyModifiedProperties();
});
}
foreach (var t in mAssetTypes)
{
menu.AddItem(new GUIContent("New " + InspectorUtility.NicifyClassName(t.Name)), false, () =>
{
string title = "Create New " + t.Name + " asset";
ScriptableObject asset = CreateAsset(t, null, defaultName, title);
if (asset != null)
{
property.objectReferenceValue = asset;
property.serializedObject.ApplyModifiedProperties();
}
});
}
menu.ShowAsContext();
}
}
ScriptableObject CreateAsset(
Type assetType, ScriptableObject copyFrom, string defaultName, string dialogTitle)
{
ScriptableObject asset = null;
string path = EditorUtility.SaveFilePanelInProject(
dialogTitle, defaultName, "asset", string.Empty);
if (!string.IsNullOrEmpty(path))
{
if (copyFrom != null)
{
string fromPath = AssetDatabase.GetAssetPath(copyFrom);
if (AssetDatabase.CopyAsset(fromPath, path))
asset = AssetDatabase.LoadAssetAtPath(path, assetType) as ScriptableObject;
}
else
{
asset = ScriptableObjectUtility.CreateAt(assetType, path);
}
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}
return asset;
}
}
}