using System.IO; using System.Linq; using SubAssetsToolbox.Utilities; using UnityEditor; using UnityEngine; using Object = UnityEngine.Object; namespace SubAssetsToolbox.Editor { internal static class DragAndDropAddon { #if UNITY_6000_3_OR_NEWER [InitializeOnLoadMethod] public static void AddDragProjectHandler() { DragAndDrop.AddDropHandlerV2(ProjectWindowHandler); } private static DragAndDropVisualMode ProjectWindowHandler(EntityId id, string destinationPath, bool released) { #else [InitializeOnLoadMethod] public static void AddDragProjectHandler() => DragAndDrop.AddDropHandler(ProjectWindowHandler); private static DragAndDropVisualMode ProjectWindowHandler(int id, string destinationPath, bool released) { #endif if (DragAndDrop.paths.Length == 0 || DragAndDrop.objectReferences.Length == 0) // Was not dragging from another window return DragAndDropVisualMode.None; bool destinationIsFolder = AssetDatabase.IsValidFolder(destinationPath); if (released) { bool isDraggingSubAssets = DragAndDrop.objectReferences.Any(AssetDatabase.IsSubAsset); foreach (string path in DragAndDrop.paths) { if (string.Equals(path, destinationPath)) return DragAndDropVisualMode.None; if (destinationIsFolder) continue; if (isDraggingSubAssets) continue; if (!TypeChecker.ValidateSubAssetType(path)) return DragAndDropVisualMode.Rejected; if (!TypeChecker.ValidateDestinationType(destinationPath)) return DragAndDropVisualMode.Rejected; } if (!destinationIsFolder) { // Adding sub-asset(s) // (drag destination is an asset) string objectName = Path.GetFileNameWithoutExtension(destinationPath); bool isOne = DragAndDrop.objectReferences.Length == 1; int choice = EditorUtility.DisplayDialogComplex( isOne ? "Add as Sub-Asset" : "Add as Sub-Assets", isOne ? string.Format(Constants.ConfirmAddAsSubAsset, DragAndDrop.objectReferences[0].name, objectName) : string.Format(Constants.ConfirmAddAsSubAssets, DragAndDrop.objectReferences[0].name, DragAndDrop.objectReferences[1].name, objectName), isOne ? "Add" : "Add all", "Cancel", isOne ? "Add and Keep Original" : "Add and Keep Originals"); int redirectRefChoice = -1; if (choice == 0) { ReferencePatchingMode mode = SubAssetsToolboxSettings.PatchReferences.value; if (mode == ReferencePatchingMode.AutoApply) redirectRefChoice = 0; else if (mode == ReferencePatchingMode.AskEachTime) redirectRefChoice = EditorUtility.DisplayDialogComplex("Patch references", Constants.PatchRefsToSubAssetPrompt, "Patch References", "Don't Patch", "Cancel Moving Assets"); } // Cancelled the operation if (choice == 1 || redirectRefChoice == 2) return DragAndDropVisualMode.Rejected; foreach (Object draggedObject in DragAndDrop.objectReferences) { string draggedObjectPath = AssetDatabase.GetAssetPath(draggedObject); string draggedObjectName = draggedObject.name; bool wasMainAsset = AssetDatabase.IsMainAsset(draggedObject); // TODO: preserve selection if the object selected was the one being destroyed SubAssetsToolbox.AddSubAsset(destinationPath, draggedObject, out Object newSubasset); if (choice == 2) continue; // Keep previous objects if (redirectRefChoice == 0) { AssetDatabase.SaveAssets(); if (!AssetReferenceRedirector.RedirectToSubAsset(draggedObject, newSubasset)) { Debug.LogWarning(string.Format(Constants.RedirectionFailed, draggedObjectName)); continue; } } // Do not keep previous if (wasMainAsset) { #if ADDRESSABLES_INSTALLED AddressableEntryMigrator.MigrateOrRemoveEntry( AssetDatabase.AssetPathToGUID(draggedObjectPath), AssetDatabase.AssetPathToGUID(destinationPath), objectName); #endif AssetDatabase.DeleteAsset(draggedObjectPath); } else { // After redirect, Refresh() may have invalidated the original reference. // Reload the sub-asset from disk if needed. Object objectToRemove = draggedObject; if (objectToRemove == null) { Object[] subAssets = AssetDatabase.LoadAllAssetRepresentationsAtPath(draggedObjectPath); objectToRemove = subAssets.FirstOrDefault(a => a.name == draggedObjectName); } if (objectToRemove != null) SubAssetsToolbox.RemoveSubAsset(draggedObjectPath, objectToRemove); } } AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); return DragAndDropVisualMode.Link; } // Removing sub-asset(s) // (drag destination is a folder or an empty space) if (!DragAndDrop.objectReferences.Any(AssetDatabase.IsSubAsset)) return Path.GetDirectoryName(DragAndDrop.paths[0]) == destinationPath ? DragAndDropVisualMode.Move : DragAndDropVisualMode.None; ReferencePatchingMode redirectMode = SubAssetsToolboxSettings.PatchReferences.value; int redirectRefsChoice; if (redirectMode == ReferencePatchingMode.AutoApply) redirectRefsChoice = 0; else if (redirectMode == ReferencePatchingMode.AskEachTime) redirectRefsChoice = EditorUtility.DisplayDialogComplex("Patch references", Constants.PatchRefsToStandalonePrompt, "Patch References", "Don't Patch", "Cancel Sub-Asset Removal"); else redirectRefsChoice = 1; if (redirectRefsChoice == 2) return DragAndDropVisualMode.Rejected; int rejected = 0; foreach (Object draggedObject in DragAndDrop.objectReferences) { string draggedObjectPath = AssetDatabase.GetAssetPath(draggedObject); if (AssetDatabase.IsSubAsset(draggedObject)) { // Was dragging a sub-asset: un-parent // Check if its parent is in selection too Object mainAssetAtPath = AssetDatabase.LoadMainAssetAtPath(draggedObjectPath); string fileExtension = ExtensionMap.GetFileExtensionByType(draggedObject.GetType()); if (DragAndDrop.objectReferences.Contains(mainAssetAtPath)) continue; // Verify if there's an object with the same name string desiredName = Path.Combine(destinationPath, $"{draggedObject.name}.{fileExtension}"); if (AssetDatabase.LoadAssetAtPath(desiredName) != null) { Debug.LogWarning(string.Format(Constants.ImpossibleToExtract, draggedObject.name)); rejected++; continue; } string draggedObjectName = draggedObject.name; Object newObject = Object.Instantiate(draggedObject); newObject.name = draggedObjectName; AssetDatabase.CreateAsset(newObject, desiredName); if (redirectRefsChoice == 0) { AssetDatabase.SaveAssets(); if (!AssetReferenceRedirector.RedirectToStandaloneAsset(draggedObject, newObject)) { Debug.LogWarning(string.Format(Constants.RedirectionFailed, draggedObjectName)); continue; } } // After redirect, Refresh() may have invalidated the original reference. // Reload the sub-asset from disk if needed. Object objectToRemove = draggedObject; if (objectToRemove == null) { Object[] subAssets = AssetDatabase.LoadAllAssetRepresentationsAtPath(draggedObjectPath); objectToRemove = subAssets.FirstOrDefault(a => a.name == draggedObjectName); } if (objectToRemove != null) SubAssetsToolbox.RemoveSubAsset(draggedObjectPath, objectToRemove); } } AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); if (rejected != 0) return DragAndDropVisualMode.Rejected; if (Path.GetDirectoryName(DragAndDrop.paths[0]) != destinationPath) return DragAndDropVisualMode.None; // Allows relocation of main assets return DragAndDropVisualMode.Move; // Avoid confirmation popup when drag/dropping in the same folder } // While dragging if (destinationIsFolder) return DragAndDropVisualMode.Generic; if (DragAndDrop.objectReferences.Any(AssetDatabase.IsSubAsset)) return DragAndDropVisualMode.Link; foreach (string path in DragAndDrop.paths) { if (string.Equals(path, destinationPath)) return DragAndDropVisualMode.None; if (!TypeChecker.ValidateSubAssetType(path, true)) return DragAndDropVisualMode.Rejected; if (!TypeChecker.ValidateDestinationType(destinationPath, true)) return DragAndDropVisualMode.Rejected; } return DragAndDropVisualMode.Link; } } }