You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
CrowdControl/Assets/Plugins/AssetUsageDetector/Editor/AssetUsageDetectorWindow.cs

812 lines
33 KiB
C#

// Asset Usage Detector - by Suleyman Yasir KULA (yasirkula@gmail.com)
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System.Reflection;
using Object = UnityEngine.Object;
#if UNITY_2021_2_OR_NEWER
using PrefabStage = UnityEditor.SceneManagement.PrefabStage;
using PrefabStageUtility = UnityEditor.SceneManagement.PrefabStageUtility;
#elif UNITY_2018_3_OR_NEWER
using PrefabStage = UnityEditor.Experimental.SceneManagement.PrefabStage;
using PrefabStageUtility = UnityEditor.Experimental.SceneManagement.PrefabStageUtility;
#endif
namespace AssetUsageDetectorNamespace
{
public enum Phase { Setup, Processing, Complete };
public class AssetUsageDetectorWindow : EditorWindow, IHasCustomMenu
{
private enum WindowFilter { AlwaysReturnActive, ReturnActiveIfNotLocked, AlwaysReturnNew };
private const string PREFS_SEARCH_SCENES = "AUD_SceneSearch";
private const string PREFS_SEARCH_SCENE_LIGHTING_SETTINGS = "AUD_LightingSettingsSearch";
private const string PREFS_SEARCH_ASSETS = "AUD_AssetsSearch";
private const string PREFS_SEARCH_PROJECT_SETTINGS = "AUD_ProjectSettingsSearch";
private const string PREFS_DONT_SEARCH_SOURCE_ASSETS = "AUD_AssetsExcludeSrc";
private const string PREFS_SEARCH_DEPTH_LIMIT = "AUD_Depth";
private const string PREFS_SEARCH_FIELDS = "AUD_Fields";
private const string PREFS_SEARCH_PROPERTIES = "AUD_Properties";
private const string PREFS_SEARCH_NON_SERIALIZABLES = "AUD_NonSerializables";
private const string PREFS_SEARCH_UNUSED_MATERIAL_PROPERTIES = "AUD_SearchUnusedMaterialProps";
private const string PREFS_LAZY_SCENE_SEARCH = "AUD_LazySceneSearch";
private const string PREFS_ADDRESSABLES_SUPPORT = "AUD_AddressablesSupport";
private const string PREFS_CALCULATE_UNUSED_OBJECTS = "AUD_FindUnusedObjs";
private const string PREFS_HIDE_DUPLICATE_ROWS = "AUD_HideDuplicates";
private const string PREFS_HIDE_REDUNDANT_PREFAB_VARIANT_LINKS = "AUD_HideRedundantPVariantLinks";
private const string PREFS_SHOW_PROGRESS = "AUD_Progress";
private static readonly GUIContent windowTitle = new GUIContent( "Asset Usage Detector" );
private static readonly Vector2 windowMinSize = new Vector2( 325f, 220f );
private static readonly GUILayoutOption GL_WIDTH_12 = GUILayout.Width( 12f );
private GUIStyle lockButtonStyle;
private readonly AssetUsageDetector core = new AssetUsageDetector();
private SearchResult searchResult; // Overall search results
// This isn't readonly so that it can be serialized
private List<ObjectToSearch> objectsToSearch = new List<ObjectToSearch>() { new ObjectToSearch( null ) };
#pragma warning disable 0649
[SerializeField] // Since titleContent persists between Editor sessions, so should the IsLocked property because otherwise, "[L]" in title becomes confusing when the EditorWindow isn't actually locked
private bool m_isLocked;
private bool IsLocked
{
get { return m_isLocked; }
set
{
if( m_isLocked != value )
{
m_isLocked = value;
titleContent = value ? new GUIContent( "[L] " + windowTitle.text, EditorGUIUtility.IconContent( "InspectorLock" ).image ) : windowTitle;
}
}
}
#pragma warning restore 0649
private Phase currentPhase = Phase.Setup;
private bool searchInOpenScenes = true; // Scenes currently open in Hierarchy view
private bool searchInScenesInBuild = true; // Scenes in build
private bool searchInScenesInBuildTickedOnly = true; // Scenes in build (ticked only or not)
private bool searchInAllScenes = true; // All scenes (including scenes that are not in build)
private bool searchInSceneLightingSettings = true; // Window-Rendering-Lighting settings
private bool searchInAssetsFolder = true; // Assets in Project window
private bool dontSearchInSourceAssets = true; // objectsToSearch won't be searched for internal references
private bool searchInProjectSettings = true; // Player Settings, Graphics Settings etc.
private List<Object> searchInAssetsSubset = new List<Object>() { null }; // If not empty, only these assets are searched for references
private List<Object> excludedAssets = new List<Object>() { null }; // These assets won't be searched for references
private List<Object> excludedScenes = new List<Object>() { null }; // These scenes won't be searched for references
private int searchDepthLimit = 4; // Depth limit for recursively searching variables of objects
private bool lazySceneSearch = true;
#if ASSET_USAGE_ADDRESSABLES
private bool addressablesSupport = false;
#endif
private bool searchNonSerializableVariables = true;
private bool searchUnusedMaterialProperties = true;
private bool calculateUnusedObjects = false;
private bool hideDuplicateRows = true;
private bool hideReduntantPrefabVariantLinks = true;
private bool noAssetDatabaseChanges = false;
private bool showDetailedProgressBar = true;
private BindingFlags fieldModifiers, propertyModifiers;
private SearchRefactoring searchRefactoring = null; // Its value can be assigned via ShowAndSearch
private readonly ObjectToSearchListDrawer objectsToSearchDrawer = new ObjectToSearchListDrawer();
private readonly ObjectListDrawer searchInAssetsSubsetDrawer = new ObjectListDrawer( "Search following asset(s) only:", false );
private readonly ObjectListDrawer excludedAssetsDrawer = new ObjectListDrawer( "Don't search following asset(s):", false );
private readonly ObjectListDrawer excludedScenesDrawer = new ObjectListDrawer( "Don't search in following scene(s):", false );
private bool drawObjectsToSearchSection = true;
private Vector2 scrollPosition = Vector2.zero;
private bool shouldRepositionSelf;
private Rect windowTargetPosition;
void IHasCustomMenu.AddItemsToMenu( GenericMenu contextMenu )
{
contextMenu.AddItem( new GUIContent( "Lock" ), IsLocked, () => IsLocked = !IsLocked );
contextMenu.AddSeparator( "" );
#if UNITY_2018_3_OR_NEWER
contextMenu.AddItem( new GUIContent( "Settings" ), false, () => SettingsService.OpenProjectSettings( "Project/yasirkula/Asset Usage Detector" ) );
#else
contextMenu.AddItem( new GUIContent( "Settings" ), false, () =>
{
System.Type preferencesWindowType = typeof( EditorWindow ).Assembly.GetType( "UnityEditor.PreferencesWindow" );
preferencesWindowType.GetMethod( "ShowPreferencesWindow", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static ).Invoke( null, null );
EditorWindow preferencesWindow = GetWindow( preferencesWindowType );
if( (bool) preferencesWindowType.GetField( "m_RefreshCustomPreferences", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance ).GetValue( preferencesWindow ) )
{
preferencesWindowType.GetMethod( "AddCustomSections", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance ).Invoke( preferencesWindow, null );
preferencesWindowType.GetField( "m_RefreshCustomPreferences", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance ).SetValue( preferencesWindow, false );
}
int targetSectionIndex = -1;
System.Collections.IList sections = (System.Collections.IList) preferencesWindowType.GetField( "m_Sections", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance ).GetValue( preferencesWindow );
for( int i = 0; i < sections.Count; i++ )
{
if( ( (GUIContent) sections[i].GetType().GetField( "content", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance ).GetValue( sections[i] ) ).text == "Asset Usage Detector" )
{
targetSectionIndex = i;
break;
}
}
if( targetSectionIndex >= 0 )
preferencesWindowType.GetProperty( "selectedSectionIndex", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance ).SetValue( preferencesWindow, targetSectionIndex, null );
} );
#endif
if( currentPhase == Phase.Setup )
{
contextMenu.AddSeparator( "" );
contextMenu.AddItem( new GUIContent( "Refresh Sub-Assets of Searched Objects" ), false, () =>
{
for( int i = objectsToSearch.Count - 1; i >= 0; i-- )
objectsToSearch[i].RefreshSubAssets();
} );
}
else if( currentPhase == Phase.Complete )
{
if( searchResult != null && searchResult.NumberOfGroups > 0 )
{
contextMenu.AddSeparator( "" );
contextMenu.AddItem( new GUIContent( "Collapse All" ), false, searchResult.CollapseAllSearchResultGroups );
}
}
}
// Shows lock button at the top-right corner
// Credit: http://leahayes.co.uk/2013/04/30/adding-the-little-padlock-button-to-your-editorwindow.html
private void ShowButton( Rect position )
{
if( lockButtonStyle == null )
lockButtonStyle = "IN LockButton";
IsLocked = GUI.Toggle( position, IsLocked, GUIContent.none, lockButtonStyle );
}
private static AssetUsageDetectorWindow GetWindow( WindowFilter filter )
{
AssetUsageDetectorWindow[] windows = Resources.FindObjectsOfTypeAll<AssetUsageDetectorWindow>();
AssetUsageDetectorWindow window = System.Array.Find( windows, ( w ) => w && !w.IsLocked );
if( !window )
window = System.Array.Find( windows, ( w ) => w );
if( window && ( filter == WindowFilter.AlwaysReturnActive || ( !window.IsLocked && filter == WindowFilter.ReturnActiveIfNotLocked ) ) )
{
window.Show();
window.Focus();
return window;
}
Rect? windowTargetPosition = null;
if( window )
{
Rect position = window.position;
position.position += new Vector2( 50f, 50f );
windowTargetPosition = position;
}
window = CreateInstance<AssetUsageDetectorWindow>();
window.titleContent = windowTitle;
window.minSize = windowMinSize;
if( windowTargetPosition.HasValue )
{
window.shouldRepositionSelf = true;
window.windowTargetPosition = windowTargetPosition.Value;
}
window.Show( true );
window.Focus();
return window;
}
[MenuItem( "Window/Asset Usage Detector/Active Window" )]
private static void OpenActiveWindow()
{
GetWindow( WindowFilter.AlwaysReturnActive );
}
[MenuItem( "Window/Asset Usage Detector/New Window" )]
private static void OpenNewWindow()
{
GetWindow( WindowFilter.AlwaysReturnNew );
}
// Quickly initiate search for the selected assets
[MenuItem( "GameObject/Search for References/This Object Only", priority = 49 )]
[MenuItem( "Assets/Search for References", priority = 1000 )]
private static void SearchSelectedAssetReferences( MenuCommand command )
{
// This happens when this button is clicked via hierarchy's right click context menu
// and is called once for each object in the selection. We don't want that, we want
// the function to be called only once
if( command.context )
{
EditorApplication.update -= CallSearchSelectedAssetReferencesOnce;
EditorApplication.update += CallSearchSelectedAssetReferencesOnce;
}
else
ShowAndSearch( Selection.objects );
}
[MenuItem( "GameObject/Search for References/Include Children", priority = 49 )]
private static void SearchSelectedAssetReferencesWithChildren( MenuCommand command )
{
if( command.context )
{
EditorApplication.update -= CallSearchSelectedAssetReferencesWithChildrenOnce;
EditorApplication.update += CallSearchSelectedAssetReferencesWithChildrenOnce;
}
else
ShowAndSearch( Selection.objects, true );
}
// Show the menu item only if there is a selection in the Editor
[MenuItem( "GameObject/Search for References/This Object Only", validate = true )]
[MenuItem( "GameObject/Search for References/Include Children", validate = true )]
[MenuItem( "Assets/Search for References", validate = true )]
private static bool SearchSelectedAssetReferencesValidate( MenuCommand command )
{
return Selection.objects.Length > 0;
}
// Quickly show the AssetUsageDetector window and initiate a search
public static void ShowAndSearch( IEnumerable<Object> searchObjects, bool? shouldSearchChildren = null )
{
GetWindow( WindowFilter.ReturnActiveIfNotLocked ).ShowAndSearchInternal( searchObjects, null, shouldSearchChildren );
}
// Quickly show the AssetUsageDetector window and initiate a search
public static void ShowAndSearch( AssetUsageDetector.Parameters searchParameters, bool? shouldSearchChildren = null )
{
if( searchParameters == null )
{
Debug.LogError( "searchParameters can't be null!" );
return;
}
GetWindow( WindowFilter.ReturnActiveIfNotLocked ).ShowAndSearchInternal( searchParameters.objectsToSearch, searchParameters, shouldSearchChildren );
}
private static void CallSearchSelectedAssetReferencesOnce()
{
EditorApplication.update -= CallSearchSelectedAssetReferencesOnce;
SearchSelectedAssetReferences( new MenuCommand( null ) );
}
private static void CallSearchSelectedAssetReferencesWithChildrenOnce()
{
EditorApplication.update -= CallSearchSelectedAssetReferencesWithChildrenOnce;
SearchSelectedAssetReferencesWithChildren( new MenuCommand( null ) );
}
private void ShowAndSearchInternal( IEnumerable<Object> searchObjects, AssetUsageDetector.Parameters searchParameters, bool? shouldSearchChildren )
{
if( !ReturnToSetupPhase() )
{
Debug.LogError( "Need to reset the previous search first!" );
return;
}
objectsToSearch.Clear();
if( searchObjects != null )
{
foreach( Object obj in searchObjects )
objectsToSearch.Add( new ObjectToSearch( obj, shouldSearchChildren ) );
}
if( searchParameters != null )
{
ParseSceneSearchMode( searchParameters.searchInScenes );
searchInSceneLightingSettings = searchParameters.searchInSceneLightingSettings;
searchInAssetsFolder = searchParameters.searchInAssetsFolder;
dontSearchInSourceAssets = searchParameters.dontSearchInSourceAssets;
searchInProjectSettings = searchParameters.searchInProjectSettings;
searchDepthLimit = searchParameters.searchDepthLimit;
fieldModifiers = searchParameters.fieldModifiers;
propertyModifiers = searchParameters.propertyModifiers;
searchNonSerializableVariables = searchParameters.searchNonSerializableVariables;
searchUnusedMaterialProperties = searchParameters.searchUnusedMaterialProperties;
searchRefactoring = searchParameters.searchRefactoring;
lazySceneSearch = searchParameters.lazySceneSearch;
#if ASSET_USAGE_ADDRESSABLES
addressablesSupport = searchParameters.addressablesSupport;
#endif
calculateUnusedObjects = searchParameters.calculateUnusedObjects;
hideDuplicateRows = searchParameters.hideDuplicateRows;
hideReduntantPrefabVariantLinks = searchParameters.hideReduntantPrefabVariantLinks;
noAssetDatabaseChanges = searchParameters.noAssetDatabaseChanges;
showDetailedProgressBar = searchParameters.showDetailedProgressBar;
searchInAssetsSubset.Clear();
if( searchParameters.searchInAssetsSubset != null )
{
foreach( Object obj in searchParameters.searchInAssetsSubset )
searchInAssetsSubset.Add( obj );
}
excludedAssets.Clear();
if( searchParameters.excludedAssetsFromSearch != null )
{
foreach( Object obj in searchParameters.excludedAssetsFromSearch )
excludedAssets.Add( obj );
}
excludedScenes.Clear();
if( searchParameters.excludedScenesFromSearch != null )
{
foreach( Object obj in searchParameters.excludedScenesFromSearch )
excludedScenes.Add( obj );
}
}
InitiateSearch();
Repaint();
}
private void Awake()
{
LoadPrefs();
}
private void OnEnable()
{
if( currentPhase == Phase.Complete && AssetUsageDetectorSettings.ShowCustomTooltip )
wantsMouseMove = wantsMouseEnterLeaveWindow = true; // These values aren't preserved during domain reload on Unity 2020.3.0f1
#if UNITY_2018_3_OR_NEWER
PrefabStage.prefabStageClosing -= ReplacePrefabStageObjectsWithAssets;
PrefabStage.prefabStageClosing += ReplacePrefabStageObjectsWithAssets;
#endif
}
private void OnDisable()
{
#if UNITY_2018_3_OR_NEWER
PrefabStage.prefabStageClosing -= ReplacePrefabStageObjectsWithAssets;
#endif
SearchResultTooltip.Hide();
}
private void OnDestroy()
{
if( core != null )
core.SaveCache();
SavePrefs();
if( searchResult != null && currentPhase == Phase.Complete )
searchResult.RestoreInitialSceneSetup();
}
private void SavePrefs()
{
EditorPrefs.SetInt( PREFS_SEARCH_SCENES, (int) GetSceneSearchMode( false ) );
EditorPrefs.SetBool( PREFS_SEARCH_SCENE_LIGHTING_SETTINGS, searchInSceneLightingSettings );
EditorPrefs.SetBool( PREFS_SEARCH_ASSETS, searchInAssetsFolder );
EditorPrefs.SetBool( PREFS_DONT_SEARCH_SOURCE_ASSETS, dontSearchInSourceAssets );
EditorPrefs.SetBool( PREFS_SEARCH_PROJECT_SETTINGS, searchInProjectSettings );
EditorPrefs.SetInt( PREFS_SEARCH_DEPTH_LIMIT, searchDepthLimit );
EditorPrefs.SetInt( PREFS_SEARCH_FIELDS, (int) fieldModifiers );
EditorPrefs.SetInt( PREFS_SEARCH_PROPERTIES, (int) propertyModifiers );
EditorPrefs.SetBool( PREFS_SEARCH_NON_SERIALIZABLES, searchNonSerializableVariables );
EditorPrefs.SetBool( PREFS_SEARCH_UNUSED_MATERIAL_PROPERTIES, searchUnusedMaterialProperties );
EditorPrefs.SetBool( PREFS_LAZY_SCENE_SEARCH, lazySceneSearch );
#if ASSET_USAGE_ADDRESSABLES
EditorPrefs.SetBool( PREFS_ADDRESSABLES_SUPPORT, addressablesSupport );
#endif
EditorPrefs.SetBool( PREFS_CALCULATE_UNUSED_OBJECTS, calculateUnusedObjects );
EditorPrefs.SetBool( PREFS_HIDE_DUPLICATE_ROWS, hideDuplicateRows );
EditorPrefs.SetBool( PREFS_HIDE_REDUNDANT_PREFAB_VARIANT_LINKS, hideReduntantPrefabVariantLinks );
EditorPrefs.SetBool( PREFS_SHOW_PROGRESS, showDetailedProgressBar );
}
private void LoadPrefs()
{
ParseSceneSearchMode( (SceneSearchMode) EditorPrefs.GetInt( PREFS_SEARCH_SCENES, (int) ( SceneSearchMode.OpenScenes | SceneSearchMode.ScenesInBuildSettingsTickedOnly | SceneSearchMode.AllScenes ) ) );
searchInSceneLightingSettings = EditorPrefs.GetBool( PREFS_SEARCH_SCENE_LIGHTING_SETTINGS, true );
searchInAssetsFolder = EditorPrefs.GetBool( PREFS_SEARCH_ASSETS, true );
dontSearchInSourceAssets = EditorPrefs.GetBool( PREFS_DONT_SEARCH_SOURCE_ASSETS, true );
searchInProjectSettings = EditorPrefs.GetBool( PREFS_SEARCH_PROJECT_SETTINGS, true );
searchDepthLimit = EditorPrefs.GetInt( PREFS_SEARCH_DEPTH_LIMIT, 4 );
fieldModifiers = (BindingFlags) EditorPrefs.GetInt( PREFS_SEARCH_FIELDS, (int) ( BindingFlags.Public | BindingFlags.NonPublic ) );
propertyModifiers = (BindingFlags) EditorPrefs.GetInt( PREFS_SEARCH_PROPERTIES, (int) ( BindingFlags.Public | BindingFlags.NonPublic ) );
searchNonSerializableVariables = EditorPrefs.GetBool( PREFS_SEARCH_NON_SERIALIZABLES, true );
searchUnusedMaterialProperties = EditorPrefs.GetBool( PREFS_SEARCH_UNUSED_MATERIAL_PROPERTIES, true );
lazySceneSearch = EditorPrefs.GetBool( PREFS_LAZY_SCENE_SEARCH, true );
#if ASSET_USAGE_ADDRESSABLES
addressablesSupport = EditorPrefs.GetBool( PREFS_ADDRESSABLES_SUPPORT, false );
#endif
calculateUnusedObjects = EditorPrefs.GetBool( PREFS_CALCULATE_UNUSED_OBJECTS, false );
hideDuplicateRows = EditorPrefs.GetBool( PREFS_HIDE_DUPLICATE_ROWS, true );
hideReduntantPrefabVariantLinks = EditorPrefs.GetBool( PREFS_HIDE_REDUNDANT_PREFAB_VARIANT_LINKS, true );
showDetailedProgressBar = EditorPrefs.GetBool( PREFS_SHOW_PROGRESS, true );
}
private SceneSearchMode GetSceneSearchMode( bool hideOptionsInPlayMode )
{
SceneSearchMode sceneSearchMode = SceneSearchMode.None;
if( searchInOpenScenes )
sceneSearchMode |= SceneSearchMode.OpenScenes;
if( !hideOptionsInPlayMode || !EditorApplication.isPlaying )
{
if( searchInScenesInBuild )
sceneSearchMode |= searchInScenesInBuildTickedOnly ? SceneSearchMode.ScenesInBuildSettingsTickedOnly : SceneSearchMode.ScenesInBuildSettingsAll;
if( searchInAllScenes )
sceneSearchMode |= SceneSearchMode.AllScenes;
}
return sceneSearchMode;
}
private void ParseSceneSearchMode( SceneSearchMode sceneSearchMode )
{
searchInOpenScenes = ( sceneSearchMode & SceneSearchMode.OpenScenes ) == SceneSearchMode.OpenScenes;
searchInScenesInBuild = ( sceneSearchMode & SceneSearchMode.ScenesInBuildSettingsAll ) == SceneSearchMode.ScenesInBuildSettingsAll || ( sceneSearchMode & SceneSearchMode.ScenesInBuildSettingsTickedOnly ) == SceneSearchMode.ScenesInBuildSettingsTickedOnly;
searchInScenesInBuildTickedOnly = ( sceneSearchMode & SceneSearchMode.ScenesInBuildSettingsAll ) != SceneSearchMode.ScenesInBuildSettingsAll;
searchInAllScenes = ( sceneSearchMode & SceneSearchMode.AllScenes ) == SceneSearchMode.AllScenes;
}
private void Update()
{
if( shouldRepositionSelf )
{
shouldRepositionSelf = false;
position = windowTargetPosition;
}
}
private void OnGUI()
{
// Make the window scrollable
scrollPosition = EditorGUILayout.BeginScrollView( scrollPosition, Utilities.GL_EXPAND_WIDTH, Utilities.GL_EXPAND_HEIGHT );
GUILayout.BeginVertical();
if( currentPhase == Phase.Processing )
{
// If we are stuck at this phase, then we have encountered an exception
GUILayout.Label( ". . . Search in progress or something went wrong (check console) . . ." );
if( GUILayout.Button( "RETURN", Utilities.GL_HEIGHT_30 ) )
{
ReturnToSetupPhase();
GUIUtility.ExitGUI();
}
}
else if( currentPhase == Phase.Setup )
{
DrawObjectsToSearchSection();
GUILayout.Space( 10f );
Color c = GUI.backgroundColor;
GUI.backgroundColor = AssetUsageDetectorSettings.SettingsHeaderColor;
GUILayout.Box( "<b>SEARCH IN</b>", Utilities.BoxGUIStyle, Utilities.GL_EXPAND_WIDTH );
GUI.backgroundColor = c;
searchInAssetsFolder = WordWrappingToggleLeft( "Project window (Assets folder)", searchInAssetsFolder );
if( searchInAssetsFolder )
{
GUILayout.BeginHorizontal();
GUILayout.Space( 35f );
GUILayout.BeginVertical();
searchInAssetsSubsetDrawer.Draw( searchInAssetsSubset );
excludedAssetsDrawer.Draw( excludedAssets );
GUILayout.EndVertical();
GUILayout.EndHorizontal();
}
GUILayout.Space( 5f );
dontSearchInSourceAssets = WordWrappingToggleLeft( "Don't search \"SEARCHED OBJECTS\" themselves for references", dontSearchInSourceAssets );
searchUnusedMaterialProperties = WordWrappingToggleLeft( "Search unused material properties (e.g. normal map of a material that no longer uses normal mapping)", searchUnusedMaterialProperties );
Utilities.DrawSeparatorLine();
if( searchInAllScenes && !EditorApplication.isPlaying )
GUI.enabled = false;
searchInOpenScenes = WordWrappingToggleLeft( "Currently open (loaded) scene(s)", searchInOpenScenes );
if( !EditorApplication.isPlaying )
{
searchInScenesInBuild = WordWrappingToggleLeft( "Scenes in Build Settings", searchInScenesInBuild );
if( searchInScenesInBuild )
{
GUILayout.BeginHorizontal();
GUILayout.Space( 35f );
searchInScenesInBuildTickedOnly = EditorGUILayout.ToggleLeft( "Ticked only", searchInScenesInBuildTickedOnly, Utilities.GL_WIDTH_100 );
searchInScenesInBuildTickedOnly = !EditorGUILayout.ToggleLeft( "All", !searchInScenesInBuildTickedOnly, Utilities.GL_WIDTH_100 );
GUILayout.EndHorizontal();
}
GUI.enabled = true;
searchInAllScenes = WordWrappingToggleLeft( "All scenes in the project", searchInAllScenes );
}
GUILayout.BeginHorizontal();
GUILayout.Space( 35f );
GUILayout.BeginVertical();
excludedScenesDrawer.Draw( excludedScenes );
GUILayout.EndVertical();
GUILayout.EndHorizontal();
EditorGUI.BeginDisabledGroup( !searchInOpenScenes && !searchInScenesInBuild && !searchInAllScenes );
searchInSceneLightingSettings = WordWrappingToggleLeft( "Scene Lighting Settings (WARNING: This may change the active scene during search)", searchInSceneLightingSettings );
EditorGUI.EndDisabledGroup();
Utilities.DrawSeparatorLine();
searchInProjectSettings = WordWrappingToggleLeft( "Project Settings (Player Settings, Graphics Settings etc.)", searchInProjectSettings );
GUILayout.Space( 10f );
GUI.backgroundColor = AssetUsageDetectorSettings.SettingsHeaderColor;
GUILayout.Box( "<b>SETTINGS</b>", Utilities.BoxGUIStyle, Utilities.GL_EXPAND_WIDTH );
GUI.backgroundColor = c;
#if ASSET_USAGE_ADDRESSABLES
EditorGUI.BeginDisabledGroup( addressablesSupport );
#endif
lazySceneSearch = WordWrappingToggleLeft( "Lazy scene search: scenes are searched in detail only when they are manually refreshed (faster search)", lazySceneSearch );
#if ASSET_USAGE_ADDRESSABLES
EditorGUI.EndDisabledGroup();
addressablesSupport = WordWrappingToggleLeft( "Addressables support (Experimental) (WARNING: 'Lazy scene search' will be disabled) (slower search)", addressablesSupport );
#endif
calculateUnusedObjects = WordWrappingToggleLeft( "Calculate unused objects", calculateUnusedObjects );
hideDuplicateRows = WordWrappingToggleLeft( "Hide duplicate rows in search results", hideDuplicateRows );
#if UNITY_2018_3_OR_NEWER
hideReduntantPrefabVariantLinks = WordWrappingToggleLeft( "Hide redundant prefab variant links (when the same value is assigned to the same Component of a prefab and its variant(s))", hideReduntantPrefabVariantLinks );
#endif
noAssetDatabaseChanges = WordWrappingToggleLeft( "I haven't modified any assets/scenes since the last search (faster search)", noAssetDatabaseChanges );
showDetailedProgressBar = WordWrappingToggleLeft( "Update search progress bar more often (cancelable search) (slower search)", showDetailedProgressBar );
GUILayout.Space( 10f );
// Don't let the user press the GO button without any valid search location
if( !searchInAllScenes && !searchInOpenScenes && !searchInScenesInBuild && !searchInAssetsFolder && !searchInProjectSettings )
GUI.enabled = false;
if( GUILayout.Button( "GO!", Utilities.GL_HEIGHT_30 ) )
{
InitiateSearch();
GUIUtility.ExitGUI();
}
GUILayout.Space( 5f );
}
else if( currentPhase == Phase.Complete )
{
// Draw the results of the search
GUI.enabled = false;
DrawObjectsToSearchSection();
if( drawObjectsToSearchSection )
GUILayout.Space( 10f );
GUI.enabled = true;
if( GUILayout.Button( "Reset Search", Utilities.GL_HEIGHT_30 ) )
{
ReturnToSetupPhase();
GUIUtility.ExitGUI();
}
if( searchResult == null )
{
EditorGUILayout.HelpBox( "ERROR: searchResult is null", MessageType.Error );
return;
}
else if( !searchResult.SearchCompletedSuccessfully )
EditorGUILayout.HelpBox( "ERROR: search was interrupted, check the logs for more info", MessageType.Error );
if( searchResult.NumberOfGroups == 0 )
{
GUILayout.Space( 10f );
GUILayout.Box( "No references found...", Utilities.BoxGUIStyle, Utilities.GL_EXPAND_WIDTH );
}
else
{
noAssetDatabaseChanges = WordWrappingToggleLeft( "I haven't modified any assets/scenes since the last search (faster Refresh)", noAssetDatabaseChanges );
EditorGUILayout.Space();
scrollPosition.y = searchResult.DrawOnGUI( this, scrollPosition.y, noAssetDatabaseChanges );
}
}
if( Event.current.type == EventType.MouseLeaveWindow )
{
SearchResultTooltip.Hide();
if( searchResult != null )
searchResult.CancelDelayedTreeViewTooltip();
}
GUILayout.EndVertical();
EditorGUILayout.EndScrollView();
}
private void DrawObjectsToSearchSection()
{
Color c = GUI.backgroundColor;
GUI.backgroundColor = AssetUsageDetectorSettings.SettingsHeaderColor;
GUILayout.Box( "<b>SEARCHED OBJECTS</b>", Utilities.BoxGUIStyle, Utilities.GL_EXPAND_WIDTH );
GUI.backgroundColor = c;
Rect searchedObjectsHeaderRect = GUILayoutUtility.GetLastRect();
searchedObjectsHeaderRect.x += 5f;
searchedObjectsHeaderRect.yMin += ( searchedObjectsHeaderRect.height - EditorGUIUtility.singleLineHeight ) * 0.5f;
searchedObjectsHeaderRect.height = EditorGUIUtility.singleLineHeight;
drawObjectsToSearchSection = EditorGUI.Foldout( searchedObjectsHeaderRect, drawObjectsToSearchSection, GUIContent.none, true );
if( drawObjectsToSearchSection )
objectsToSearchDrawer.Draw( objectsToSearch );
}
public static bool WordWrappingToggleLeft( string label, bool value )
{
GUILayout.BeginHorizontal();
bool result = EditorGUILayout.ToggleLeft( GUIContent.none, value, GL_WIDTH_12 );
if( GUILayout.Button( label, EditorStyles.wordWrappedLabel ) )
{
GUI.FocusControl( null );
result = !value;
}
GUILayout.EndHorizontal();
return result;
}
private void InitiateSearch()
{
currentPhase = Phase.Processing;
SavePrefs();
#if UNITY_2018_3_OR_NEWER
ReplacePrefabStageObjectsWithAssets( PrefabStageUtility.GetCurrentPrefabStage() );
#endif
// Start searching
searchResult = core.Run( new AssetUsageDetector.Parameters()
{
objectsToSearch = !objectsToSearch.IsEmpty() ? new ObjectToSearchEnumerator( objectsToSearch ).ToArray() : null,
searchInScenes = GetSceneSearchMode( true ),
searchInSceneLightingSettings = searchInSceneLightingSettings,
searchInAssetsFolder = searchInAssetsFolder,
searchInAssetsSubset = !searchInAssetsSubset.IsEmpty() ? searchInAssetsSubset.ToArray() : null,
excludedAssetsFromSearch = !excludedAssets.IsEmpty() ? excludedAssets.ToArray() : null,
dontSearchInSourceAssets = dontSearchInSourceAssets,
excludedScenesFromSearch = !excludedScenes.IsEmpty() ? excludedScenes.ToArray() : null,
searchInProjectSettings = searchInProjectSettings,
//fieldModifiers = fieldModifiers,
//propertyModifiers = propertyModifiers,
//searchDepthLimit = searchDepthLimit,
//searchNonSerializableVariables = searchNonSerializableVariables,
searchUnusedMaterialProperties = searchUnusedMaterialProperties,
searchRefactoring = searchRefactoring,
#if ASSET_USAGE_ADDRESSABLES
lazySceneSearch = lazySceneSearch && !addressablesSupport,
addressablesSupport = addressablesSupport,
#else
lazySceneSearch = lazySceneSearch,
#endif
calculateUnusedObjects = calculateUnusedObjects,
hideDuplicateRows = hideDuplicateRows,
hideReduntantPrefabVariantLinks = hideReduntantPrefabVariantLinks,
noAssetDatabaseChanges = noAssetDatabaseChanges,
showDetailedProgressBar = showDetailedProgressBar
} );
currentPhase = Phase.Complete;
// We really don't want SearchRefactoring to affect next searches unless the search is initiated via ShowAndSearch again
searchRefactoring = null;
if( AssetUsageDetectorSettings.ShowCustomTooltip )
wantsMouseMove = wantsMouseEnterLeaveWindow = true;
}
#if UNITY_2018_3_OR_NEWER
// Try replacing searched objects who are part of currently open prefab stage with their corresponding prefab assets
public void ReplacePrefabStageObjectsWithAssets( PrefabStage prefabStage )
{
if( prefabStage == null || !prefabStage.stageHandle.IsValid() )
return;
#if UNITY_2020_1_OR_NEWER
GameObject prefabAsset = AssetDatabase.LoadAssetAtPath<GameObject>( prefabStage.assetPath );
#else
GameObject prefabAsset = AssetDatabase.LoadAssetAtPath<GameObject>( prefabStage.prefabAssetPath );
#endif
if( prefabAsset == null || prefabAsset.Equals( null ) )
return;
for( int i = 0; i < objectsToSearch.Count; i++ )
{
Object obj = objectsToSearch[i].obj;
if( obj != null && !obj.Equals( null ) && obj is GameObject && prefabStage.IsPartOfPrefabContents( (GameObject) obj ) )
{
GameObject prefabStageObjectSource = ( (GameObject) obj ).FollowSymmetricHierarchy( prefabStage.prefabContentsRoot, prefabAsset );
if( prefabStageObjectSource != null )
objectsToSearch[i].obj = prefabStageObjectSource;
List<ObjectToSearch.SubAsset> subAssets = objectsToSearch[i].subAssets;
for( int j = 0; j < subAssets.Count; j++ )
{
obj = subAssets[j].subAsset;
if( obj != null && !obj.Equals( null ) && obj is GameObject && prefabStage.IsPartOfPrefabContents( (GameObject) obj ) )
{
prefabStageObjectSource = ( (GameObject) obj ).FollowSymmetricHierarchy( prefabStage.prefabContentsRoot, prefabAsset );
if( prefabStageObjectSource != null )
subAssets[j].subAsset = prefabStageObjectSource;
}
}
}
}
}
#endif
private bool ReturnToSetupPhase()
{
if( searchResult != null && !EditorApplication.isPlaying && !searchResult.RestoreInitialSceneSetup() )
return false;
searchResult = null;
currentPhase = Phase.Setup;
wantsMouseMove = wantsMouseEnterLeaveWindow = false;
SearchResultTooltip.Hide();
return true;
}
internal void OnSettingsChanged( bool highlightedSearchTextColorChanged = false, bool tooltipDescriptionsColorChanged = false )
{
if( searchResult == null )
return;
wantsMouseMove = wantsMouseEnterLeaveWindow = AssetUsageDetectorSettings.ShowCustomTooltip;
for( int i = searchResult.NumberOfGroups - 1; i >= 0; i-- )
{
if( searchResult[i].treeView != null )
{
searchResult[i].treeView.rowHeight = EditorGUIUtility.singleLineHeight + AssetUsageDetectorSettings.ExtraRowHeight;
searchResult[i].treeView.OnSettingsChanged( highlightedSearchTextColorChanged, tooltipDescriptionsColorChanged );
}
}
}
}
}