public static void OnProjectWindowItemOnGui(string guid, Rect rect) { if (!Globals <Config> .Value.MarkRed) { return; } var store = Globals <BacklinkStore> .Value; if (!store.Initialized) { return; } var path = AssetDatabase.GUIDToAssetPath(guid); ShowRowQuantity(rect, path, store); long size = 0; var _ = store.UnusedFiles.TryGetValue(path, out size) || store.UnusedScenes.TryGetValue(path, out size); if (SearchUtils.IsUnused(path)) { var buf = GUI.color; { GUI.color = _style.RedHighlight; GUI.Box(rect, string.Empty); } GUI.color = buf; GUI.Label(rect, CommonUtils.BytesToString(size), _style.ProjectViewCounterLabel); } }
public void Run() { if (_from.IsEmpty()) { return; } SearchArgMain.AllDestroy(); ScenePaths.AllDestroy(); FileResultRows.AllDestroy(); SceneResultRows.AllDestroy(); var wd = Globals <WindowData> .Value; if (wd.Window) { wd.Window.Repaint(); } foreach (var i in _from.Out(out var get1, out _)) { var t1 = get1[i]; if (!t1.Target) { continue; } wd.FindFrom = t1.From; try { switch (t1.From) { case FindModeEnum.Scene: World.NewEntityWith(out SearchArg st); SearchUtils.Init(st, t1.Target, t1.Scene); SearchUtils.InScene(st, t1.Scene); break; case FindModeEnum.File: World.NewEntityWith(out SearchArg arg); SearchUtils.Init(arg, t1.Target); SearchUtils.FilesThatReference(arg); SearchUtils.ScenesThatContain(t1.Target); break; } } catch (Exception e) { Debug.LogException(e); } } _from.AllUnset <SelectionChanged>(); }
void RenderRows(WindowData windowData) { // todo show spinner until scene is loaded, if (FileResultRows.GetEntitiesCount() > 0) { windowData.ExpandFiles = EditorGUILayout.Foldout(windowData.ExpandFiles, $"Usages in Project: {FileResultRows.GetEntitiesCount()}"); } if (SearchArgMain.IsEmpty()) { return; } if (windowData.ExpandFiles && windowData.FindFrom == FindModeEnum.File) { foreach (var i in FileResultRows.Out(out var get1, out var get2, out _, out _)) { DrawRowFile(get1[i], get2[i], windowData); } } var sceneMessage = $"Usages in Scenes: {ScenePaths.GetEntitiesCount()}"; if (ScenePaths.GetEntitiesCount() > 0) { windowData.ExpandScenes = EditorGUILayout.Foldout(windowData.ExpandScenes, sceneMessage); } if (!windowData.ExpandScenes) { return; } if (windowData.ExpandScenes && windowData.FindFrom == FindModeEnum.Scene) { foreach (var(grp, indices) in SceneResultRows.Out(out _, out var get2, out _, out _) .GroupBy1(ResultComp.Instance)) { using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox)) { var count = 0; foreach (var i in indices) { if (count++ == 0) { if (GUILayout.Button(get2[i].Label, windowData.Style.RowMainAssetBtn)) { if (windowData.Click.IsDoubleClick(grp.RootGo)) { // _selectionChangedByArrows = false; Selection.activeObject = grp.RootGo; } else { EditorGUIUtility.PingObject(grp.RootGo); } windowData.Click = new PrevClick(grp.RootGo); } } DrawRowScene(get2[i]); } } } } using (new GUILayout.HorizontalScope()) { GUILayout.Space(windowData.Style.SceneIndent1); using (new EditorGUILayout.VerticalScope()) { foreach (var i1 in ScenePaths.Out(out var get1, out var get2, out _)) { windowData.SceneFoldout.text = get1[i1].PathNicified; var details = get2[i1]; details.SearchRequested = details.Scene.isLoaded; details.SearchRequested = EditorGUILayout.Foldout(details.SearchRequested, windowData.SceneFoldout, EditorStyles.foldout); if (details.SearchRequested && details.Scene.isLoaded && !details.SearchDone) { var mainArg = SearchArgMain.GetSingle(); mainArg.Scene = SceneManager.GetSceneByPath(details.Scene.path); SearchUtils.InScene(mainArg, details.Scene); details.SearchDone = true; } if (!details.SearchRequested) { if (!details.Scene.isLoaded) { continue; } if (!details.WasOpened) { // to clean up on selection change AufCtx.World.NewEntityWith(out SceneToClose comp); comp.Scene = details.Scene; comp.ForceClose = true; } foreach (var row in SceneResultRows.Out(out _, out _, out var get3, out var entities)) { if (!get3[row].ScenePath.Eq(details.Path)) { continue; } entities[row].Destroy(); } details.SearchDone = false; } else { if (!details.Scene.isLoaded) { details.Scene = EditorSceneManager.OpenScene(details.Path, OpenSceneMode.Additive); // to clean up on selection change AufCtx.World.NewEntityWith(out SceneToClose comp); comp.Scene = details.Scene; comp.SelectionId = Globals <PersistentUndoRedoState> .Value.Id; #if UNITY_2019_1_OR_NEWER EditorSceneManager.SetSceneCullingMask(details.Scene, 0); #endif details.SearchRequested = true; // todo Scope component #if later if (details.Scene.isLoaded) { EditorSceneManager.CloseScene(details.Scene, false); } #endif } else if (SceneResultRows.IsEmpty()) { EditorGUILayout.LabelField("No in-scene dependencies found."); } else { using (new GUILayout.HorizontalScope()) { GUILayout.Space(windowData.Style.SceneIndent2); using (new EditorGUILayout.VerticalScope()) foreach (var(grp, indices) in SceneResultRows .Out(out var g1, out var g2, out var g3, out _) .GroupBy1(ResultComp.Instance)) { var any = false; foreach (var i3 in indices) { if (!g3[i3].ScenePath.Eq(details.Path)) { continue; } any = true; break; } if (!any) { continue; } using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox)) { var count = 0; foreach (var i2 in indices) { if (!g3[i2].ScenePath.Eq(details.Path)) { continue; } if (count++ == 0) { Result comp = g1[i2]; if (GUILayout.Button(g2[i2].Label, windowData.Style.RowMainAssetBtn)) { if (windowData.Click.IsDoubleClick(grp.RootGo)) { // _selectionChangedByArrows = false; Selection.activeObject = comp.RootGo; } else { EditorGUIUtility.PingObject(comp.RootGo); } windowData.Click = new PrevClick(comp.RootGo); } } DrawRowScene(g2[i2]); } } } } } } } } } }
static bool AskDeleteUnusedScenes(SearchArg arg, List <string> unusedScenes, WindowData windowData) { if (arg == null || unusedScenes == null) { return(false); } var hasUnusedScenes = unusedScenes.Count > 0; var previous = GUI.enabled; GUI.enabled = hasUnusedScenes; var guiContentRemoveScenes = windowData.Style.RemoveScene; if (GUILayout.Button(guiContentRemoveScenes, windowData.Style.RemoveUnusedBtn)) { var choice = EditorUtility.DisplayDialog( title: "Asset Cleaner", message: $"Do you really want to remove {unusedScenes.Count} scene(s)?", ok: "Remove", cancel: "Cancel"); if (choice) { EditorApplication.ExecuteMenuItem("File/Save Project"); var i = 0f; var total = (float)unusedScenes.Count; foreach (var scene in unusedScenes) { var path = Application.dataPath.Replace("Assets", scene); DeleteWithMeta(path); if (total >= _progressBarShowFromLevel) { var percent = i * 100 / total; if (Math.Abs(percent % 5f) < 0.01f) { if (EditorUtility.DisplayCancelableProgressBar( "Please wait...", "Deleting scenes...", percent)) { throw new Exception("Deleting aborted"); } } i++; } } if (total >= _progressBarShowFromLevel) { EditorUtility.ClearProgressBar(); } AssetDatabase.Refresh(); SearchUtils.Upd(arg); } GUI.enabled = previous; return(true); } GUI.enabled = previous; return(false); }
BacklinkStore.UnusedQty ShowObjectName(BacklinkStore store, WindowData windowData, TargetTypeEnum targetTypeEnum, SearchArg arg, string[] selectedGuids) { float TextWidth() { _buf2.text = _contentBuf.text; return(20f + GUI.skin.button.CalcSize(_buf2).x); } if (arg == null || arg.Main == null) { return(new BacklinkStore.UnusedQty()); } bool isMultiSelect = selectedGuids != null && selectedGuids.Length > 1; if (_contentBuf == null) { _contentBuf = new GUIContent { tooltip = $"Click to ping" }; } _contentBuf.image = isMultiSelect ? windowData.Style.MultiSelect.image : AssetPreview.GetMiniThumbnail(arg.Target); _contentBuf.text = string.Empty; _contentBuf.tooltip = string.Empty; if (!isMultiSelect) { switch (targetTypeEnum) { case TargetTypeEnum.Directory: if (!SearchArgMain.IsEmpty()) { _contentBuf.text = $"{arg.Main.name} (Folder)"; if (GUILayout.Button(_contentBuf, windowData.Style.CurrentBtn, GUILayout.MinWidth(TextWidth()))) { EditorGUIUtility.PingObject(arg.Main); } if (AskDeleteUnusedFiles(arg, arg.UnusedAssetsFiltered, windowData)) { return(new BacklinkStore.UnusedQty()); } if (AskDeleteUnusedScenes(arg, arg.UnusedScenesFiltered, windowData)) { return(new BacklinkStore.UnusedQty()); } } break; case TargetTypeEnum.File: _contentBuf.text = $"{arg.Main.name} (File Asset)"; if (GUILayout.Button(_contentBuf, windowData.Style.CurrentBtn, GUILayout.MinWidth(TextWidth()))) { EditorGUIUtility.PingObject(arg.Main); } bool hasUnusedFile = SearchUtils.IsUnused(arg.FilePath); var previous = GUI.enabled; GUI.enabled = hasUnusedFile; if (GUILayout.Button(windowData.Style.RemoveFile, windowData.Style.RemoveUnusedBtn)) { var choice = EditorUtility.DisplayDialog( title: "Asset Cleaner", message: $"Do you really want to remove file: \"{arg.Main.name}\"?", ok: "Remove", cancel: "Cancel"); if (choice) { EditorApplication.ExecuteMenuItem("File/Save Project"); DeleteWithMeta(arg.FilePath); AssetDatabase.Refresh(); SearchUtils.Upd(arg); } } GUI.enabled = previous; break; case TargetTypeEnum.Scene: _contentBuf.text = $"{arg.Main.name} (Scene)"; if (GUILayout.Button(_contentBuf, windowData.Style.CurrentBtn, GUILayout.MinWidth(TextWidth()))) { EditorGUIUtility.PingObject(arg.Main); } bool hasUnusedScene = SearchUtils.IsUnused(arg.FilePath); previous = GUI.enabled; GUI.enabled = hasUnusedScene; if (GUILayout.Button(windowData.Style.RemoveScene, windowData.Style.RemoveUnusedBtn)) { var choice = EditorUtility.DisplayDialog( title: "Asset Cleaner", message: $"Do you really want to remove scene: {arg.Main.name}?", ok: "Remove", cancel: "Cancel"); if (choice) { EditorApplication.ExecuteMenuItem("File/Save Project"); DeleteWithMeta(arg.FilePath); AssetDatabase.Refresh(); SearchUtils.Upd(arg); } } GUI.enabled = previous; break; case TargetTypeEnum.ObjectInScene: _contentBuf.text = $"{arg.Main.name} (Object in Scene)"; if (GUILayout.Button(_contentBuf, windowData.Style.CurrentBtn, GUILayout.MinWidth(TextWidth()))) { EditorGUIUtility.PingObject(arg.Main); } break; case TargetTypeEnum.ObjectInStage: _contentBuf.image = AssetPreview.GetMiniThumbnail(arg.Target); _contentBuf.text = $"{arg.Main.name} (Object in Staging)"; if (GUILayout.Button(_contentBuf, windowData.Style.RemoveUnusedBtn)) { EditorGUIUtility.PingObject(arg.Main); } break; default: if (GUILayout.Button($"{arg.Main.name} (Unknown Object Type)", windowData.Style.RemoveUnusedBtn)) { EditorGUIUtility.PingObject(arg.Main); } break; } } else { var unusedAssets = new List <string>(); var unusedScenes = new List <string>(); foreach (var guid in selectedGuids) { var path = AssetDatabase.GUIDToAssetPath(guid); if (store.UnusedFiles.TryGetValue(path, out _)) { unusedAssets.Add(path); } else if (store.UnusedScenes.TryGetValue(path, out _)) { unusedScenes.Add(path); } if (store.FoldersWithQty.TryGetValue(path, out _)) { SearchArg searchArg = new SearchArg() { FilePath = path, Target = AssetDatabase.LoadAssetAtPath <DefaultAsset>(path), Main = AssetDatabase.LoadAssetAtPath <DefaultAsset>(path) }; SearchUtils.Upd(searchArg); foreach (var unusedAssetPath in searchArg.UnusedAssetsFiltered) { if (store.UnusedFiles.TryGetValue(unusedAssetPath, out _)) { unusedAssets.Add(unusedAssetPath); } } foreach (var unusedScenePath in searchArg.UnusedScenesFiltered) { if (store.UnusedScenes.TryGetValue(unusedScenePath, out _)) { unusedScenes.Add(unusedScenePath); } } } } unusedAssets = unusedAssets.Distinct().ToList(); unusedScenes = unusedScenes.Distinct().ToList(); var assetSize = unusedAssets.Sum(CommonUtils.Size); var sceneSize = unusedScenes.Sum(CommonUtils.Size); _contentBuf.text = $"Assets: {unusedAssets.Count} ({CommonUtils.BytesToString(assetSize)}), Scenes: {unusedScenes.Count} ({CommonUtils.BytesToString(sceneSize)})"; ; GUILayout.Button(_contentBuf, windowData.Style.CurrentBtn, GUILayout.MinWidth(TextWidth())); if (AskDeleteUnusedFiles(arg, unusedAssets, windowData)) { return(new BacklinkStore.UnusedQty()); } if (AskDeleteUnusedScenes(arg, unusedScenes, windowData)) { return(new BacklinkStore.UnusedQty()); } return(new BacklinkStore.UnusedQty(unusedAssets.Count, unusedScenes.Count, assetSize + sceneSize)); } return(new BacklinkStore.UnusedQty()); }
static bool TryGetHelpInfo(SearchArg arg, out string msg, out MessageType msgType) { msgType = MessageType.Info; if (arg == null) { msg = default; return(false); } var path = arg.FilePath; if (string.IsNullOrEmpty(path)) { msg = default; return(false); } if (SearchUtils.IgnoredPaths(path, out var subPath)) { msg = $"Paths containing '{subPath}' are ignored. You could add or remove it in Settings tab"; return(true); } if (SearchUtils.IgnoredNonAssets(path) && !path.Eq("Assets")) { msg = $"Asset is outside of Assets folder"; return(true); } if (IgnoreTypes.Check(path, out var type)) { if (AssetDatabase.IsValidFolder(path)) { var scenes = arg.UnusedScenesFiltered?.Count; var files = arg.UnusedAssetsFiltered?.Count; if (scenes == 0 && files == 0) { msg = default; return(false); } var b = new StringBuilder(); b.Append("This directory contains: "); var any = false; if (files > 0) { any = true; b.Append($"unused files ({files})"); } if (scenes > 0) { if (any) { b.Append(", "); } b.Append($"unused scenes ({scenes})"); } b.Append( ".\nYou could delete them pressing corresponding button to the right.\nIf you don't want it to be inspected, please add it to Ignore list in the Settings tab"); msg = b.ToString(); return(true); } msg = $"Assets of type '{type.Name}' are ignored. Please contact support if you need to change it"; return(true); } // if (Filters.ScenePaths.GetEntitiesCount() == 0 && Filters.FileResultRows.GetEntitiesCount() == 0) { if (SearchUtils.IsUnused(path)) { type = AssetDatabase.GetMainAssetTypeAtPath(path); var name = type.IsAssignableFromInverse(typeof(SceneAsset)) ? "scene" : "file"; msg = $"This {name} has no explicit serialized usages and potentially could be removed. If you don't want it to be inspected, please add the containing folder to ignore list"; return(true); } msgType = default; msg = default; return(false); }