using System; using System.Collections; using System.Collections.Generic; using HeurekaGames.AssetHunterPRO.BaseTreeviewImpl.AssetTreeView; using UnityEngine; using UnityEditor; using System.Linq; using HeurekaGames.AssetHunterPRO.BaseTreeviewImpl; using System.IO; namespace HeurekaGames.AssetHunterPRO { [System.Serializable] public class AH_TreeViewSelectionInfo { public delegate void AssetDeletedHandler(); public static event AssetDeletedHandler OnAssetDeleted; private bool hasSelection; public bool HasSelection { get { return hasSelection; } } public const float Height = 64; private AH_MultiColumnHeader multiColumnHeader; private List selection; internal void Reset() { selection = null; hasSelection = false; } internal void SetSelection(AH_TreeViewWithTreeModel treeview, IList selectedIds) { multiColumnHeader = (AH_MultiColumnHeader)(treeview.multiColumnHeader); selection = new List(); foreach (var itemID in selectedIds) { selection.Add(treeview.treeModel.Find(itemID)); } hasSelection = (selection.Count > 0); //If we have more, select the assets in project view if (hasSelection) { if (selection.Count > 1) { UnityEngine.Object[] selectedObjects = new UnityEngine.Object[selection.Count]; for (int i = 0; i < selection.Count; i++) { selectedObjects[i] = AssetDatabase.LoadMainAssetAtPath(selection[i].RelativePath); } Selection.objects = selectedObjects; } else Selection.activeObject = AssetDatabase.LoadMainAssetAtPath(selection[0].RelativePath); AH_Utils.PingObjectAtPath(selection[selection.Count - 1].RelativePath, false); } } internal void OnGUISelectionInfo(Rect selectionRect) { GUILayout.BeginArea(selectionRect); //TODO MAKE SURE WE DONT DO ALL OF THIS EACH FRAME, BUT CACHE THE SELECTION DATA using (new EditorGUILayout.HorizontalScope()) { if (selection.Count == 1) { drawSingle(); } else { drawMulti(); } } GUILayout.EndArea(); } private void drawSingle() { EditorGUILayout.BeginHorizontal(); EditorGUILayout.BeginVertical(); drawAssetPreview(true); EditorGUILayout.EndVertical(); //Draw info from single asset EditorGUILayout.BeginVertical(); GUILayout.Label(selection[0].RelativePath); if (!selection[0].IsFolder) { GUILayout.Label("(" + selection[0].AssetType + ")"); } EditorGUILayout.EndVertical(); GUILayout.FlexibleSpace(); if (selection[0].IsFolder) DrawDeleteFolderButton(selection[0]); else drawDeleteAssetsButton(); EditorGUILayout.EndHorizontal(); } private void drawMulti() { //Make sure we have not selected folders bool allFolders = selection.All(val => val.IsFolder); bool allFiles = !selection.Any(val => val.IsFolder); var allSameType = selection.All(var => var.AssetType == selection[0].AssetType); //Find if we have selected nested assets or folders var containsNested = selection.Any(file => selection.Any(y => file != y && (y.RelativePath.StartsWith(file.RelativePath) && y.RelativePath.Split(Path.DirectorySeparatorChar).Length != file.RelativePath.Split(Path.DirectorySeparatorChar).Length))); drawAssetPreview(allSameType); EditorGUILayout.BeginHorizontal(); //Draw info from multiple EditorGUILayout.BeginVertical(); //Identical files if (allSameType && allFiles) { GUILayout.Label(selection[0].AssetType.ToString() + " (" + selection.Count() + ")"); } //all folders else if (allSameType) { GUILayout.Label("Folders (" + selection.Count() + ")"); } //Non identical selection else { GUILayout.Label("Items (" + selection.Count() + ")"); } EditorGUILayout.EndVertical(); if (!containsNested) drawDeleteAssetsButton(); EditorGUILayout.EndHorizontal(); } private void drawDeleteAssetsButton() { if (multiColumnHeader.ShowMode != AH_MultiColumnHeader.AssetShowMode.Unused) return; long combinedSize = 0; foreach (var item in selection) { if (item.IsFolder) combinedSize += item.GetFileSizeRecursively(AH_MultiColumnHeader.AssetShowMode.Unused); else combinedSize += item.FileSize; } if (GUILayout.Button("Delete " + (AH_Utils.GetSizeAsString(combinedSize)), GUILayout.Width(160), GUILayout.Height(32))) deleteUnusedAssets(); } private void DrawDeleteFolderButton(AH_TreeviewElement folder) { if (multiColumnHeader.ShowMode != AH_MultiColumnHeader.AssetShowMode.Unused) return; string description = "Delete unused assets from folder"; GUIContent content = new GUIContent("Delete " + (AH_Utils.GetSizeAsString(folder.GetFileSizeRecursively(AH_MultiColumnHeader.AssetShowMode.Unused))), description); GUIStyle style = new GUIStyle(GUI.skin.button); DrawDeleteFolderButton(content, folder, style, description, "Do you want to delete all unused assets from:" + Environment.NewLine + folder.RelativePath, GUILayout.Width(160), GUILayout.Height(32)); } public void DrawDeleteFolderButton(GUIContent content, AH_TreeviewElement folder, GUIStyle style, string dialogHeader, string dialogDescription, params GUILayoutOption[] layout) { if (GUILayout.Button(content, style, layout)) deleteUnusedFromFolder(dialogHeader, dialogDescription, folder); } private void drawAssetPreview(bool bDraw) { GUIContent content = new GUIContent(); //Draw asset preview if (bDraw && !selection[0].IsFolder) { var preview = AssetPreview.GetAssetPreview(AssetDatabase.LoadMainAssetAtPath(selection[0].RelativePath)); content = new GUIContent(preview); } //Draw Folder icon else if (bDraw) content = EditorGUIUtility.IconContent("Folder Icon"); GUILayout.Label(content,GUILayout.Width(Height), GUILayout.Height(Height)); } private void deleteUnusedAssets() { int choice = EditorUtility.DisplayDialogComplex("Delete unused assets", "Do you want to delete the selected assets", "Yes", "Cancel", "Backup"); List affectedAssets = new List(); if (choice == 0)//Delete { foreach (var item in selection) { affectedAssets.Add(item.RelativePath); } deleteMultipleAssets(affectedAssets); } else if (choice == 2)//Backup { foreach (var item in selection) { affectedAssets.Add(item.RelativePath); exportAssetsToPackage("Backup as unitypackage", affectedAssets); } } } private void deleteUnusedFromFolder(AH_TreeviewElement folder) { deleteUnusedFromFolder("Delete unused assets from folder", "Do you want to delete all unused assets from:" + Environment.NewLine + folder.RelativePath, folder); } private void deleteUnusedFromFolder(string header, string description, AH_TreeviewElement folder) { int choice = EditorUtility.DisplayDialogComplex(header, description, "Yes", "Cancel", "Backup (Slow)"); List affectedAssets = new List(); if (choice != 1)//Not Cancel { //Collect affected assets affectedAssets = folder.GetUnusedPathsRecursively(); } if (choice == 0)//Delete { deleteMultipleAssets(affectedAssets); } else if (choice == 2)//Backup { exportAssetsToPackage("Backup as unitypackage", affectedAssets); } } private void exportAssetsToPackage(string header, List affectedAssets) { string filename = Environment.UserName + "_Backup_" + "_" + AH_SerializationHelper.GetDateString(); string savePath = EditorUtility.SaveFilePanel( header, AH_SerializationHelper.GetBackupFolder(), filename, "unitypackage"); if (!string.IsNullOrEmpty(savePath)) { EditorUtility.DisplayProgressBar("Backup", "Creating backup of " + affectedAssets.Count() + " assets", 0f); AssetDatabase.ExportPackage(affectedAssets.ToArray(), savePath, ExportPackageOptions.Default); EditorUtility.ClearProgressBar(); EditorUtility.RevealInFinder(savePath); deleteMultipleAssets(affectedAssets); } } private void deleteMultipleAssets(List affectedAssets) { double startTime = EditorApplication.timeSinceStartup; for (int i = 0; i < affectedAssets.Count(); i++) { EditorUtility.DisplayProgressBar("Deleting unused assets", "Deleting " + i + "/" + affectedAssets.Count() + Environment.NewLine + affectedAssets[i], ((float)i) / ((float)affectedAssets.Count())); } #if UNITY_2020_1_OR_NEWER List failedPaths = new List(); AssetDatabase.DeleteAssets(affectedAssets.ToArray(), failedPaths); #else foreach (var asset in affectedAssets) { AssetDatabase.DeleteAsset(asset); } #endif EditorUtility.ClearProgressBar(); AssetDatabase.Refresh(); if (OnAssetDeleted != null) OnAssetDeleted(); } } }