mirror of
https://github.com/maxartz15/ScriptableData.git
synced 2024-11-21 13:55:37 +01:00
ExpandableScriptableObjectDrawer
This commit is contained in:
parent
10d751c21e
commit
91648de3d9
@ -1,5 +1,6 @@
|
|||||||
# Change Log:
|
# Change Log:
|
||||||
|
|
||||||
## 1.0.0
|
## 1.0.0
|
||||||
|
|
||||||
- Base project.
|
- Base project.
|
||||||
|
|
||||||
|
## 1.1.0
|
||||||
|
- ExstendableScriptableObjectDrawer.
|
||||||
|
8
Editor.meta
Normal file
8
Editor.meta
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d458e753979e51e45b177e41f406d936
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
8
Editor/Drawers.meta
Normal file
8
Editor/Drawers.meta
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d838f79c4b7c4b94080dbdb3d4d7c434
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
218
Editor/Drawers/ExtendedScriptableObjectDrawer.cs
Normal file
218
Editor/Drawers/ExtendedScriptableObjectDrawer.cs
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
// References:
|
||||||
|
// https://gist.github.com/tomkail/ba4136e6aa990f4dc94e0d39ec6a058c
|
||||||
|
// Developed by Tom Kail at Inkle
|
||||||
|
// Released under the MIT Licence as held at https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor;
|
||||||
|
using Object = UnityEngine.Object;
|
||||||
|
|
||||||
|
namespace ScriptableData.Editor
|
||||||
|
{
|
||||||
|
[CustomPropertyDrawer(typeof(ScriptableObject), true)]
|
||||||
|
public class ExtendedScriptableObjectDrawer : PropertyDrawer
|
||||||
|
{
|
||||||
|
private static readonly string[] ignoreClassFullNames = new string[] { "TMPro.TMP_FontAsset" };
|
||||||
|
private const int buttonWidth = 20;
|
||||||
|
|
||||||
|
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
|
||||||
|
{
|
||||||
|
float totalHeight = EditorGUIUtility.singleLineHeight;
|
||||||
|
if (property.objectReferenceValue == null || !HasVisableSubProperties(property) || fieldInfo.HasAttribute<NonExtendableAttribute>())
|
||||||
|
{
|
||||||
|
return totalHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (property.isExpanded)
|
||||||
|
{
|
||||||
|
ScriptableObject data = property.objectReferenceValue as ScriptableObject;
|
||||||
|
|
||||||
|
if (data == null)
|
||||||
|
{
|
||||||
|
return EditorGUIUtility.singleLineHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
SerializedObject serializedObject = new SerializedObject(data);
|
||||||
|
SerializedProperty prop = serializedObject.GetIterator();
|
||||||
|
if (prop.NextVisible(true))
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (prop.name == "m_Script")
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
SerializedProperty subProp = serializedObject.FindProperty(prop.name);
|
||||||
|
float height = EditorGUI.GetPropertyHeight(subProp, null, true) + EditorGUIUtility.standardVerticalSpacing;
|
||||||
|
totalHeight += height;
|
||||||
|
}
|
||||||
|
while (prop.NextVisible(false));
|
||||||
|
}
|
||||||
|
// Add a tiny bit of height if open for the background
|
||||||
|
totalHeight += EditorGUIUtility.standardVerticalSpacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
|
||||||
|
{
|
||||||
|
EditorGUI.BeginProperty(position, label, property);
|
||||||
|
|
||||||
|
Type fieldType = EditorUtils.GetFieldType(fieldInfo);
|
||||||
|
|
||||||
|
if (fieldType == null || HasIgnoredClassName(fieldType) || fieldInfo.HasAttribute<NonExtendableAttribute>())
|
||||||
|
{
|
||||||
|
EditorGUI.PropertyField(position, property, label);
|
||||||
|
EditorGUI.EndProperty();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScriptableObject propertySO = null;
|
||||||
|
if (!property.hasMultipleDifferentValues && property.serializedObject.targetObject != null && property.serializedObject.targetObject is ScriptableObject)
|
||||||
|
{
|
||||||
|
propertySO = property.serializedObject.targetObject as ScriptableObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
GUIContent guiContent = new GUIContent(property.displayName);
|
||||||
|
Rect foldoutRect = new Rect(position.x, position.y, EditorGUIUtility.labelWidth, EditorGUIUtility.singleLineHeight);
|
||||||
|
if (property.objectReferenceValue != null && HasVisableSubProperties(property))
|
||||||
|
{
|
||||||
|
property.isExpanded = EditorGUI.Foldout(foldoutRect, property.isExpanded, guiContent, true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foldoutRect.x += 10;
|
||||||
|
EditorGUI.Foldout(foldoutRect, property.isExpanded, guiContent, true, EditorStyles.label);
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect indentedPosition = EditorGUI.IndentedRect(position);
|
||||||
|
float indentOffset = indentedPosition.x - position.x;
|
||||||
|
Rect propertyRect = new Rect(position.x + (EditorGUIUtility.labelWidth - indentOffset + 2), position.y, position.width - (EditorGUIUtility.labelWidth - indentOffset + 2), EditorGUIUtility.singleLineHeight);
|
||||||
|
|
||||||
|
if (propertySO != null || property.objectReferenceValue == null)
|
||||||
|
{
|
||||||
|
propertyRect.width -= buttonWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUI.BeginChangeCheck();
|
||||||
|
|
||||||
|
Object assignedObject = EditorGUI.ObjectField(propertyRect, GUIContent.none, property.objectReferenceValue, fieldType, false);
|
||||||
|
|
||||||
|
if (EditorGUI.EndChangeCheck())
|
||||||
|
{
|
||||||
|
property.objectReferenceValue = assignedObject;
|
||||||
|
property.serializedObject.ApplyModifiedProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect buttonRect = new Rect(position.x + position.width - buttonWidth, position.y, buttonWidth, EditorGUIUtility.singleLineHeight);
|
||||||
|
|
||||||
|
if (property.propertyType == SerializedPropertyType.ObjectReference && property.objectReferenceValue != null)
|
||||||
|
{
|
||||||
|
if (property.isExpanded)
|
||||||
|
{
|
||||||
|
DrawScriptableObjectChildFields(position, property);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DrawScriptableObjectCreateButton(buttonRect, property, fieldType);
|
||||||
|
}
|
||||||
|
|
||||||
|
property.serializedObject.ApplyModifiedProperties();
|
||||||
|
EditorGUI.EndProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawScriptableObjectChildFields(Rect position, SerializedProperty property)
|
||||||
|
{
|
||||||
|
ScriptableObject scriptableObject = (ScriptableObject)property.objectReferenceValue;
|
||||||
|
|
||||||
|
if (scriptableObject == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw a background that shows us clearly which fields are part of the ScriptableObject
|
||||||
|
GUI.Box(new Rect(position.x, position.y + EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing - 1, position.width, position.height - EditorGUIUtility.singleLineHeight - EditorGUIUtility.standardVerticalSpacing), "");
|
||||||
|
|
||||||
|
EditorGUI.indentLevel++;
|
||||||
|
SerializedObject serializedObject = new SerializedObject(scriptableObject);
|
||||||
|
|
||||||
|
// Iterate over all the values and draw them
|
||||||
|
SerializedProperty prop = serializedObject.GetIterator();
|
||||||
|
float y = position.y + EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
|
||||||
|
|
||||||
|
EditorGUI.BeginChangeCheck();
|
||||||
|
|
||||||
|
if (prop.NextVisible(true))
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
// Don't bother drawing the class file
|
||||||
|
if (prop.name == "m_Script") continue;
|
||||||
|
float height = EditorGUI.GetPropertyHeight(prop, new GUIContent(prop.displayName), true);
|
||||||
|
EditorGUI.PropertyField(new Rect(position.x, y, position.width /*- buttonWidth*/, height), prop, true);
|
||||||
|
y += height + EditorGUIUtility.standardVerticalSpacing;
|
||||||
|
}
|
||||||
|
while (prop.NextVisible(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EditorGUI.EndChangeCheck())
|
||||||
|
{
|
||||||
|
serializedObject.ApplyModifiedProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUI.indentLevel--;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawScriptableObjectCreateButton(Rect position, SerializedProperty property, Type type)
|
||||||
|
{
|
||||||
|
if (GUI.Button(position, "+"))
|
||||||
|
{
|
||||||
|
if (type.IsAbstract)
|
||||||
|
{
|
||||||
|
GenericMenu typeChooser = new GenericMenu();
|
||||||
|
foreach (var elem in type.Assembly.GetTypes().Where(t => type.IsAssignableFrom(t)))
|
||||||
|
{
|
||||||
|
if (elem.IsAbstract) continue;
|
||||||
|
|
||||||
|
typeChooser.AddItem(new GUIContent(elem.Name), false, (elem) => {
|
||||||
|
property.objectReferenceValue = EditorUtils.CreateAssetWithSavePrompt(elem as Type, EditorUtils.GetSelectedAssetPath(property));
|
||||||
|
property.serializedObject.ApplyModifiedProperties();
|
||||||
|
}, elem);
|
||||||
|
}
|
||||||
|
typeChooser.ShowAsContext();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
property.objectReferenceValue = EditorUtils.CreateAssetWithSavePrompt(type, EditorUtils.GetSelectedAssetPath(property));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool HasIgnoredClassName(Type type)
|
||||||
|
{
|
||||||
|
return ignoreClassFullNames.Contains(type.FullName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool HasVisableSubProperties(SerializedProperty property)
|
||||||
|
{
|
||||||
|
ScriptableObject scriptableObject = (ScriptableObject)property.objectReferenceValue;
|
||||||
|
|
||||||
|
if (scriptableObject != null)
|
||||||
|
{
|
||||||
|
SerializedObject serializedObject = new SerializedObject(scriptableObject);
|
||||||
|
SerializedProperty prop = serializedObject.GetIterator();
|
||||||
|
while (prop.NextVisible(true))
|
||||||
|
{
|
||||||
|
if (prop.name == "m_Script") continue;
|
||||||
|
return true; //if theres any visible property other than m_script
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
Editor/Drawers/ExtendedScriptableObjectDrawer.cs.meta
Normal file
11
Editor/Drawers/ExtendedScriptableObjectDrawer.cs.meta
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c9558a1cbd654b5499f5394666f2479d
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
70
Editor/EditorUtils.cs
Normal file
70
Editor/EditorUtils.cs
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor;
|
||||||
|
|
||||||
|
namespace ScriptableData.Editor
|
||||||
|
{
|
||||||
|
public static class EditorUtils
|
||||||
|
{
|
||||||
|
public static string GetSelectedAssetPath(SerializedProperty property)
|
||||||
|
{
|
||||||
|
string selectedAssetPath = "Assets";
|
||||||
|
|
||||||
|
if (property.serializedObject.targetObject is MonoBehaviour behaviour)
|
||||||
|
{
|
||||||
|
MonoScript ms = MonoScript.FromMonoBehaviour(behaviour);
|
||||||
|
selectedAssetPath = System.IO.Path.GetDirectoryName(AssetDatabase.GetAssetPath(ms));
|
||||||
|
}
|
||||||
|
|
||||||
|
return selectedAssetPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ScriptableObject CreateAssetWithSavePrompt(Type type, string path)
|
||||||
|
{
|
||||||
|
path = EditorUtility.SaveFilePanelInProject("Save ScriptableObject", type.Name + ".asset", "asset", "Enter a file name for the ScriptableObject.", path);
|
||||||
|
if (string.IsNullOrWhiteSpace(path)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
ScriptableObject asset = ScriptableObject.CreateInstance(type);
|
||||||
|
AssetDatabase.CreateAsset(asset, path);
|
||||||
|
AssetDatabase.SaveAssets();
|
||||||
|
AssetDatabase.Refresh();
|
||||||
|
AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate);
|
||||||
|
EditorGUIUtility.PingObject(asset);
|
||||||
|
return asset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool HasAttribute<T>(this FieldInfo fieldInfo)
|
||||||
|
{
|
||||||
|
Attribute[] attributes = Attribute.GetCustomAttributes(fieldInfo);
|
||||||
|
|
||||||
|
foreach (Attribute a in attributes)
|
||||||
|
{
|
||||||
|
if (a is T)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Type GetFieldType(FieldInfo fieldInfo)
|
||||||
|
{
|
||||||
|
Type type = fieldInfo.FieldType;
|
||||||
|
|
||||||
|
if (type.IsArray)
|
||||||
|
{
|
||||||
|
type = type.GetElementType();
|
||||||
|
}
|
||||||
|
else if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>))
|
||||||
|
{
|
||||||
|
type = type.GetGenericArguments()[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
Editor/EditorUtils.cs.meta
Normal file
11
Editor/EditorUtils.cs.meta
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 0a7cb0a51d6707b42a7d94c7ad8f42dc
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
18
Editor/VertexColor.ScriptableData.Editor.asmdef
Normal file
18
Editor/VertexColor.ScriptableData.Editor.asmdef
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"name": "VertexColor.ScriptableData.Editor",
|
||||||
|
"rootNamespace": "ScriptableData.Editor",
|
||||||
|
"references": [
|
||||||
|
"GUID:bd2fa452e376b7e4e8356eed741e5eea"
|
||||||
|
],
|
||||||
|
"includePlatforms": [
|
||||||
|
"Editor"
|
||||||
|
],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": false,
|
||||||
|
"overrideReferences": false,
|
||||||
|
"precompiledReferences": [],
|
||||||
|
"autoReferenced": true,
|
||||||
|
"defineConstraints": [],
|
||||||
|
"versionDefines": [],
|
||||||
|
"noEngineReferences": false
|
||||||
|
}
|
7
Editor/VertexColor.ScriptableData.Editor.asmdef.meta
Normal file
7
Editor/VertexColor.ScriptableData.Editor.asmdef.meta
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 67726662be89db44e8fafa5427d4ed5f
|
||||||
|
AssemblyDefinitionImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -33,6 +33,13 @@ public class ScriptableEvent<T0, T1, T2, T3> : ScriptableObject {}
|
|||||||
[CreateAssetMenu(menuName = "ScriptableData/Event/Vector3", order = 147)]
|
[CreateAssetMenu(menuName = "ScriptableData/Event/Vector3", order = 147)]
|
||||||
public class SEVector3 : ScriptableEvent<Vector3> {}
|
public class SEVector3 : ScriptableEvent<Vector3> {}
|
||||||
```
|
```
|
||||||
|
## ExtendedScriptableObjectDrawer
|
||||||
|
If you don't want to use the extended drawer, use the `[NonExpandable]` attribute.
|
||||||
|
### Example:
|
||||||
|
```C#
|
||||||
|
[NonExpandable]
|
||||||
|
public SEVector3 myVector3;
|
||||||
|
```
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
[Installing from a Git URL](https://docs.unity3d.com/Manual/upm-ui-giturl.html)
|
[Installing from a Git URL](https://docs.unity3d.com/Manual/upm-ui-giturl.html)
|
||||||
|
8
Runtime/Attributes.meta
Normal file
8
Runtime/Attributes.meta
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 53eb228a806717b42ad447e290770d87
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
8
Runtime/Attributes/NonExtendableAttribute.cs
Normal file
8
Runtime/Attributes/NonExtendableAttribute.cs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace ScriptableData
|
||||||
|
{
|
||||||
|
[AttributeUsage(AttributeTargets.Field, AllowMultiple = true, Inherited = true)]
|
||||||
|
public class NonExtendableAttribute : PropertyAttribute { }
|
||||||
|
}
|
11
Runtime/Attributes/NonExtendableAttribute.cs.meta
Normal file
11
Runtime/Attributes/NonExtendableAttribute.cs.meta
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f456f876476c1a54b86a231e0271cf82
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -1,11 +1,9 @@
|
|||||||
This package borrows code from various different sources, including:
|
This package borrows code from various different sources, including:
|
||||||
|
|
||||||
# []()
|
## ExtendedScriptableObjectDrawer
|
||||||
|
- [x] Modified
|
||||||
### Relevant Files
|
|
||||||
- []()
|
|
||||||
|
|
||||||
### Credits
|
### Credits
|
||||||
- Author: []()
|
- Author: Tom Kail
|
||||||
- Source: []()
|
- Source: [ExtendedScriptableObjectDrawer](https://gist.github.com/tomkail/ba4136e6aa990f4dc94e0d39ec6a058c)
|
||||||
- License: []()
|
- License: [MIT](https://opensource.org/licenses/MIT)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "com.vertexcolor.scriptabledata",
|
"name": "com.vertexcolor.scriptabledata",
|
||||||
"displayName": "ScriptableData",
|
"displayName": "ScriptableData",
|
||||||
"version": "1.0.0",
|
"version": "1.1.0",
|
||||||
"unity": "2019.1",
|
"unity": "2019.1",
|
||||||
"description": "ScriptableData code base for ScriptableObject workflow.",
|
"description": "ScriptableData code base for ScriptableObject workflow.",
|
||||||
"category": "Tool",
|
"category": "Tool",
|
||||||
|
Loading…
Reference in New Issue
Block a user