// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik // #if UNITY_EDITOR using System; using System.IO; using UnityEditor; using UnityEngine; using static Animancer.Editor.AnimancerGUI; using Object = UnityEngine.Object; namespace Animancer.Editor { /// [Editor-Only] A custom Inspector for fields. /// https://kybernetik.com.au/animancer/api/Animancer.Editor/StringAssetDrawer [CustomPropertyDrawer(typeof(StringAsset), true)] public class StringAssetDrawer : PropertyDrawer { /************************************************************************************************************************/ /// public override float GetPropertyHeight(SerializedProperty property, GUIContent label) => LineHeight; /************************************************************************************************************************/ /// public override void OnGUI(Rect area, SerializedProperty property, GUIContent label) { label = EditorGUI.BeginProperty(area, label, property); property.objectReferenceValue = DrawGUI( area, label, property, out var exitGUI); if (exitGUI) { property.serializedObject.ApplyModifiedProperties(); GUIUtility.ExitGUI(); } EditorGUI.EndProperty(); } /************************************************************************************************************************/ private static readonly Func GetCurrentPropertyValues = () => _CurrentProperty != null ? Serialization.GetValues(_CurrentProperty) : null; private static SerializedProperty _CurrentProperty; /// Draws the GUI for a . public static Object DrawGUI( Rect area, GUIContent label, SerializedProperty property, out bool exitGUI) { var showMixedValue = EditorGUI.showMixedValue; if (property != null && property.hasMultipleDifferentValues) EditorGUI.showMixedValue = true; _CurrentProperty = property; var value = DrawGUI( area, label, property?.objectReferenceValue, property?.serializedObject?.targetObject, out exitGUI, GetCurrentPropertyValues); _CurrentProperty = null; EditorGUI.showMixedValue = showMixedValue; return value; } /************************************************************************************************************************/ private static readonly int ButtonHash = "Button".GetHashCode(); private static readonly int ObjectFieldHash = "s_ObjectFieldHash".GetHashCode(); /// Draws the GUI for a . public static Object DrawGUI( Rect area, GUIContent label, Object value, Object context, out bool exitGUI, Func getAllValues = null) { var currentEvent = Event.current; if (currentEvent.type == EventType.Repaint && !area.Contains(currentEvent.mousePosition) && !IsDraggingStringAsset()) { GUIUtility.GetControlID(ButtonHash, FocusType.Passive);// Button. GUIUtility.GetControlID(ButtonHash, FocusType.Passive);// Button. GUIUtility.GetControlID(DragHint, FocusType.Passive, area);// DragAndDrop. var iconSize = EditorGUIUtility.GetIconSize(); var newIconSize = LineHeight * 2 / 3; EditorGUIUtility.SetIconSize(new(newIconSize, newIconSize)); var controlID = GUIUtility.GetControlID(ObjectFieldHash, FocusType.Keyboard, area);// Object. var valueArea = EditorGUI.PrefixLabel(area, controlID, label); using (var content = PooledGUIContent.Acquire()) { if (value != null) { content.text = value.name; content.image = AnimancerIcons.ScriptableObject; } else { content.text = ""; content.image = AssetPreview.GetMiniTypeThumbnail(typeof(StringAsset)); } EditorStyles.objectField.Draw(valueArea, content, false, false, false, false); } EditorGUIUtility.SetIconSize(iconSize); exitGUI = false; return value; } if (value == null) { var buttonArea = StealFromRight(ref area, area.height, StandardSpacing); var content = AnimancerIcons.AddIcon("Create and save a new String Asset"); if (GUI.Button(buttonArea, content, NoPaddingButtonStyle)) { exitGUI = true; return CreateNewInstance(label.text, context); } GUIUtility.GetControlID(ButtonHash, FocusType.Passive); } else { var clearArea = StealFromRight(ref area, area.height, StandardSpacing); var copyArea = StealFromRight(ref area, area.height, StandardSpacing); var content = AnimancerIcons.CopyIcon("Copy string to clipboard"); if (GUI.Button(copyArea, content, NoPaddingButtonStyle)) GUIUtility.systemCopyBuffer = value.name; content = AnimancerIcons.ClearIcon("Clear reference"); if (GUI.Button(clearArea, content, NoPaddingButtonStyle)) value = null; } HandleDragAndDrop(area, currentEvent, value, getAllValues); exitGUI = false; return EditorGUI.ObjectField(area, label, value, typeof(StringAsset), false); } /************************************************************************************************************************/ private static readonly int DragHint = "Drag".GetHashCode(); private static void HandleDragAndDrop( Rect area, Event currentEvent, Object value, Func getAllValues) { var id = GUIUtility.GetControlID(DragHint, FocusType.Passive, area); switch (currentEvent.type) { // Drag out of object field. case EventType.MouseDrag: if (GUIUtility.keyboardControl == id + 1 && currentEvent.button == 0 && area.Contains(currentEvent.mousePosition) && value != null) { var values = getAllValues?.Invoke() ?? new Object[] { value }; DragAndDrop.PrepareStartDrag(); DragAndDrop.objectReferences = values; DragAndDrop.StartDrag("Objects"); currentEvent.Use(); } break; } } /// Is a currently being dragged? private static bool IsDraggingStringAsset() { var dragging = DragAndDrop.objectReferences; if (dragging.IsNullOrEmpty()) return false; for (int i = 0; i < dragging.Length; i++) if (dragging[i] is not StringAsset) return false; return true; } /************************************************************************************************************************/ private const string FolderPathKey = nameof(StringAsset) + ".FolderPath"; /// Asks where to save a new . private static Object CreateNewInstance(string name, Object targetObject) { var folderPath = GetSaveFolder(targetObject); var path = EditorUtility.SaveFilePanelInProject( "Create String Asset", name, "asset", "Where yould you like to save the new String Asset?", folderPath); if (string.IsNullOrEmpty(path)) return null; EditorPrefs.SetString(FolderPathKey, Path.GetDirectoryName(path)); var instance = ScriptableObject.CreateInstance(); AssetDatabase.CreateAsset(instance, path); return instance; } private static string GetSaveFolder(Object targetObject) { var getActiveFolderPath = typeof(ProjectWindowUtil).GetMethod( "GetActiveFolderPath", AnimancerReflection.StaticBindings, null, Type.EmptyTypes, null); if (getActiveFolderPath != null && getActiveFolderPath.ReturnType == typeof(string)) { var activeFolderPath = getActiveFolderPath.Invoke(null, Array.Empty())?.ToString(); if (!string.IsNullOrEmpty(activeFolderPath)) return activeFolderPath; } var folderPath = AssetDatabase.GetAssetPath(targetObject); if (!string.IsNullOrEmpty(folderPath)) folderPath = Path.GetDirectoryName(folderPath); if (string.IsNullOrEmpty(folderPath)) { folderPath = EditorPrefs.GetString(FolderPathKey); if (folderPath == null || !folderPath.StartsWith("Assets/")) folderPath = "Assets/"; } return folderPath; } /************************************************************************************************************************/ } } #endif