diff --git a/Editor/AssetImporters.meta b/Editor/AssetImporters.meta new file mode 100644 index 0000000..f3b210a --- /dev/null +++ b/Editor/AssetImporters.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: adea2f219bc34c5690f613740e7f0ee7 +timeCreated: 1702156747 \ No newline at end of file diff --git a/Editor/AssetImporters/EditorAssetImporterForScriptableObject.cs b/Editor/AssetImporters/EditorAssetImporterForScriptableObject.cs new file mode 100644 index 0000000..0c17b50 --- /dev/null +++ b/Editor/AssetImporters/EditorAssetImporterForScriptableObject.cs @@ -0,0 +1,30 @@ +using UnityEditor; + +namespace Module.Inspector.Editor.AssetImporters +{ + internal sealed class EditorAssetImporterForScriptableObject : AssetPostprocessor + { + public static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths) + { + if (HasAnyScriptableObjects(importedAssets) || + HasAnyScriptableObjects(deletedAssets) || + HasAnyScriptableObjects(movedAssets)) + { + EditorUtilitySearchObject.Clear(); + } + } + + private static bool HasAnyScriptableObjects(string[] paths) + { + for (var i = 0; i < paths.Length; i++) + { + string path = paths[i]; + + if (path.EndsWith(".asset")) + return true; + } + + return false; + } + } +} \ No newline at end of file diff --git a/Editor/AssetImporters/EditorAssetImporterForScriptableObject.cs.meta b/Editor/AssetImporters/EditorAssetImporterForScriptableObject.cs.meta new file mode 100644 index 0000000..71818ba --- /dev/null +++ b/Editor/AssetImporters/EditorAssetImporterForScriptableObject.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a1d618b9d79641b79e5815e258baeecb +timeCreated: 1702156766 \ No newline at end of file diff --git a/Editor/Drawers/PopupFromScriptableObjectTypeAttributeDrawer.cs b/Editor/Drawers/PopupFromScriptableObjectTypeAttributeDrawer.cs new file mode 100644 index 0000000..09baf6d --- /dev/null +++ b/Editor/Drawers/PopupFromScriptableObjectTypeAttributeDrawer.cs @@ -0,0 +1,57 @@ +using System; +using Module.Inspector.Editor.Utilities; +using UnityEditor; +using UnityEngine; + +namespace Module.Inspector.Editor +{ + [CustomPropertyDrawer(typeof(PopupFromScriptableObjectTypeAttribute))] + internal sealed class PopupFromScriptableObjectTypeAttributeDrawer : DrawerPropertyDrawer + { + private static readonly Type ASSIGNABLE_FROM_TYPE = typeof(ScriptableObject); + + public override bool Draw(Rect position, DrawerPropertyAttribute attribute, SerializedProperty property, GUIContent label, EditorPropertyUtility.Result result) + { + if (property.propertyType != SerializedPropertyType.ObjectReference) + return false; + + var att = (PopupFromScriptableObjectTypeAttribute)attribute; + Type targetType = att.type != null ? att.type : property.GetValueType(); + + if (targetType != null && !ASSIGNABLE_FROM_TYPE.IsAssignableFrom(targetType)) + return false; + + EditorGUI.BeginChangeCheck(); + EditorGUI.BeginProperty(position, label, property); + { + const float BUTTON_WIDTH = 20f; + const float BUTTON_PADDING = 3f; + + var rect0 = new Rect(position.x, position.y, position.width - BUTTON_WIDTH, position.height); + var rect1 = new Rect(rect0.xMax + BUTTON_PADDING, rect0.y + 1, BUTTON_WIDTH - BUTTON_PADDING, rect0.height - 3); + + EditorUtilitySearchObject.SearchObject searchObject = EditorUtilitySearchObject.SearchAndCache(targetType); + int index = searchObject.assets.IndexOf(property.objectReferenceValue) + 1; + int newIndex = EditorGUI.Popup(rect0, label, index, searchObject.names); + + if (newIndex != -1 && index != newIndex) + property.objectReferenceValue = newIndex > 0 ? searchObject.assets[newIndex - 1] : null; + + if (GUI.Button(rect1, EditorIcons.GetScriptableObjectIconContent(), GUIStyle.none) && property.objectReferenceValue != null) + EditorGUIUtility.PingObject(property.objectReferenceValue); + } + EditorGUI.EndProperty(); + bool changed = EditorGUI.EndChangeCheck(); + + if (changed) + property.serializedObject.ApplyModifiedProperties(); + + return true; + } + + public override string GetErrorMessage(SerializedProperty property) + { + return "Only supports Scriptable Object types"; + } + } +} \ No newline at end of file diff --git a/Editor/Drawers/PopupFromScriptableObjectTypeAttributeDrawer.cs.meta b/Editor/Drawers/PopupFromScriptableObjectTypeAttributeDrawer.cs.meta new file mode 100644 index 0000000..c84d388 --- /dev/null +++ b/Editor/Drawers/PopupFromScriptableObjectTypeAttributeDrawer.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 7ff046961af547fa913d5febb8176b31 +timeCreated: 1702147997 \ No newline at end of file diff --git a/Editor/Utilities/EditorIcons.cs b/Editor/Utilities/EditorIcons.cs index eaab2d3..ced1a41 100644 --- a/Editor/Utilities/EditorIcons.cs +++ b/Editor/Utilities/EditorIcons.cs @@ -7,6 +7,7 @@ namespace Module.Inspector.Editor.Utilities { private static GUIContent CONTENT_WARNING; private static GUIContent CONTENT_ERROR; + private static GUIContent CONTENT_OBJECT; private static GUIContent GetWarningIconContent() { @@ -23,6 +24,14 @@ namespace Module.Inspector.Editor.Utilities return CONTENT_ERROR; } + + public static GUIContent GetScriptableObjectIconContent() + { + if (CONTENT_ERROR == null) + CONTENT_ERROR = EditorGUIUtility.IconContent("d_ScriptableObject Icon"); + + return CONTENT_ERROR; + } public static void SetWarningIcon(GUIContent content) { diff --git a/Editor/Utilities/EditorUtilitySearchObject.cs b/Editor/Utilities/EditorUtilitySearchObject.cs new file mode 100644 index 0000000..d3822c5 --- /dev/null +++ b/Editor/Utilities/EditorUtilitySearchObject.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; +using Object = UnityEngine.Object; + +namespace Module.Inspector.Editor +{ + internal static class EditorUtilitySearchObject + { + private static readonly Dictionary TYPE_TO_ASSETS = new(); + + public static SearchObject SearchAndCache(Type type) + { + if (TYPE_TO_ASSETS.TryGetValue(type, out SearchObject searchObject)) + return searchObject; + + string[] guids = AssetDatabase.FindAssets($"t:{type.Name}"); + searchObject = new(); + + for (var i = 0; i < guids.Length; i++) + { + string path = AssetDatabase.GUIDToAssetPath(guids[i]); + Object asset = AssetDatabase.LoadAssetAtPath(path, type); + + if (asset != null) + searchObject.assets.Add(asset); + } + + searchObject.CreateNamesFromAssets(); + TYPE_TO_ASSETS.Add(type, searchObject); + return searchObject; + } + + public static void Clear() + { + TYPE_TO_ASSETS.Clear(); + } + + public sealed class SearchObject + { + public readonly List assets = new(); + public GUIContent[] names; + + public void CreateNamesFromAssets() + { + assets.Sort((a0, a1) => string.Compare(a0.name, a1.name, StringComparison.Ordinal)); + + names = new GUIContent[assets.Count + 1]; + names[0] = new GUIContent("None"); + + for (var i = 0; i < assets.Count; i++) + { + names[i + 1] = new GUIContent(assets[i].name); + } + } + } + } +} \ No newline at end of file diff --git a/Editor/Utilities/EditorUtilitySearchObject.cs.meta b/Editor/Utilities/EditorUtilitySearchObject.cs.meta new file mode 100644 index 0000000..7e5af0e --- /dev/null +++ b/Editor/Utilities/EditorUtilitySearchObject.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8083be0f988541b294da46beca2224d9 +timeCreated: 1702156783 \ No newline at end of file diff --git a/README.md b/README.md index 636cdb2..6b488b1 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,8 @@ List of all available drawer attributes: * Convert float value to percentage and back again (1% = 0.01f) * `PopupFromConst` * Adds popup with all const fields of type string in provided type +* `PopupFromScriptableObjectType` + * Adds popup with all `ScriptableObject`s from field or provided type * `QuaternionToEuler` * Converts quaternion value to Euler angles and back again * `ReadableScriptableObject` diff --git a/Runtime/Drawers/PopupFromScriptableObjectTypeAttribute.cs b/Runtime/Drawers/PopupFromScriptableObjectTypeAttribute.cs new file mode 100644 index 0000000..2b9204a --- /dev/null +++ b/Runtime/Drawers/PopupFromScriptableObjectTypeAttribute.cs @@ -0,0 +1,21 @@ +using System; +using UnityEngine.Scripting; + +namespace Module.Inspector +{ + [AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = true)] + public sealed class PopupFromScriptableObjectTypeAttribute : DrawerPropertyAttribute + { + public readonly Type type; + + [Preserve] + public PopupFromScriptableObjectTypeAttribute() + { + } + + public PopupFromScriptableObjectTypeAttribute(Type type) + { + this.type = type; + } + } +} diff --git a/Runtime/Drawers/PopupFromScriptableObjectTypeAttribute.cs.meta b/Runtime/Drawers/PopupFromScriptableObjectTypeAttribute.cs.meta new file mode 100644 index 0000000..7233db8 --- /dev/null +++ b/Runtime/Drawers/PopupFromScriptableObjectTypeAttribute.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b9e4e5e0d934ef14c9e74760997e4916 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/package.json b/package.json index de244dd..ef26098 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "com.module.inspector", - "version": "1.8.3", + "version": "1.8.4", "displayName": "Module.Inspector", "description": "Custom inspector with various useful property drawers", "unity": "2019.2",