Namespace cleanup, move some categories out of UI namespace

This commit is contained in:
Sinai
2021-06-30 07:49:58 +10:00
parent 65c4d49274
commit f815a13d9a
55 changed files with 174 additions and 150 deletions

View File

@ -0,0 +1,252 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI;
using UnityExplorer.UI.Models;
using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Widgets;
using UnityExplorer.UI.Widgets.AutoComplete;
namespace UnityExplorer.ObjectExplorer
{
public class ObjectSearch : UIModel
{
public ObjectExplorerPanel Parent { get; }
public ObjectSearch(ObjectExplorerPanel parent)
{
Parent = parent;
}
private SearchContext m_context = SearchContext.UnityObject;
private SceneFilter m_sceneFilter = SceneFilter.Any;
private ChildFilter m_childFilter = ChildFilter.Any;
private string desiredTypeInput;
private string lastCheckedTypeInput;
private bool lastTypeCanHaveGO;
public ButtonListHandler<object, ButtonCell> dataHandler;
private ScrollPool<ButtonCell> resultsScrollPool;
private List<object> currentResults = new List<object>();
public TypeCompleter typeAutocompleter;
public override GameObject UIRoot => uiRoot;
private GameObject uiRoot;
private GameObject sceneFilterRow;
private GameObject childFilterRow;
private GameObject unityObjectClassRow;
private InputFieldRef nameInputField;
private Text resultsLabel;
public List<object> GetEntries() => currentResults;
public void DoSearch()
{
cachedCellTexts.Clear();
if (m_context == SearchContext.Singleton)
currentResults = SearchProvider.SingletonSearch(nameInputField.Text);
else if (m_context == SearchContext.StaticClass)
currentResults = SearchProvider.StaticClassSearch(nameInputField.Text);
else
{
string compType = "";
if (m_context == SearchContext.UnityObject)
compType = this.desiredTypeInput;
currentResults = SearchProvider.UnityObjectSearch(nameInputField.Text, compType, m_context, m_childFilter, m_sceneFilter);
}
dataHandler.RefreshData();
resultsScrollPool.Refresh(true);
resultsLabel.text = $"{currentResults.Count} results";
}
public void Update()
{
if (m_context == SearchContext.UnityObject && lastCheckedTypeInput != desiredTypeInput)
{
lastCheckedTypeInput = desiredTypeInput;
//var type = ReflectionUtility.GetTypeByName(desiredTypeInput);
if (ReflectionUtility.GetTypeByName(desiredTypeInput) is Type cachedType)
{
var type = cachedType;
lastTypeCanHaveGO = typeof(Component).IsAssignableFrom(type) || type == typeof(GameObject);
sceneFilterRow.SetActive(lastTypeCanHaveGO);
childFilterRow.SetActive(lastTypeCanHaveGO);
}
else
{
sceneFilterRow.SetActive(false);
childFilterRow.SetActive(false);
lastTypeCanHaveGO = false;
}
}
}
// UI Callbacks
private void OnContextDropdownChanged(int value)
{
m_context = (SearchContext)value;
lastCheckedTypeInput = null;
sceneFilterRow.SetActive(false);
childFilterRow.SetActive(false);
unityObjectClassRow.SetActive(m_context == SearchContext.UnityObject);
}
private void OnSceneFilterDropChanged(int value) => m_sceneFilter = (SceneFilter)value;
private void OnChildFilterDropChanged(int value) => m_childFilter = (ChildFilter)value;
private void OnTypeInputChanged(string val)
{
desiredTypeInput = val;
if (string.IsNullOrEmpty(val))
{
sceneFilterRow.SetActive(false);
childFilterRow.SetActive(false);
lastCheckedTypeInput = val;
}
}
// Cache the syntax-highlighted text for each search result to reduce allocs.
private static readonly Dictionary<int, string> cachedCellTexts = new Dictionary<int, string>();
public void SetCell(ButtonCell cell, int index)
{
if (!cachedCellTexts.ContainsKey(index))
{
string text;
if (m_context == SearchContext.StaticClass)
text = SignatureHighlighter.Parse(currentResults[index] as Type, true);
else
text = ToStringUtility.ToStringWithType(currentResults[index], currentResults[index]?.GetActualType());
cachedCellTexts.Add(index, text);
}
cell.Button.ButtonText.text = cachedCellTexts[index];
}
private void OnCellClicked(int dataIndex)
{
if (m_context == SearchContext.StaticClass)
InspectorManager.Inspect(currentResults[dataIndex] as Type);
else
InspectorManager.Inspect(currentResults[dataIndex]);
}
private bool ShouldDisplayCell(object arg1, string arg2) => true;
public override void ConstructUI(GameObject parent)
{
uiRoot = UIFactory.CreateVerticalGroup(parent, "ObjectSearch", true, true, true, true, 2, new Vector4(2, 2, 2, 2));
UIFactory.SetLayoutElement(uiRoot, flexibleHeight: 9999);
// Search context row
var contextGroup = UIFactory.CreateHorizontalGroup(uiRoot, "SearchContextRow", false, true, true, true, 2, new Vector4(2, 2, 2, 2));
UIFactory.SetLayoutElement(contextGroup, minHeight: 25, flexibleHeight: 0);
var contextLbl = UIFactory.CreateLabel(contextGroup, "SearchContextLabel", "Searching for:", TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(contextLbl.gameObject, minWidth: 110, flexibleWidth: 0);
var contextDropObj = UIFactory.CreateDropdown(contextGroup, out Dropdown contextDrop, null, 14, OnContextDropdownChanged);
foreach (var name in Enum.GetNames(typeof(SearchContext)))
contextDrop.options.Add(new Dropdown.OptionData(name));
UIFactory.SetLayoutElement(contextDropObj, minHeight: 25, flexibleHeight: 0, flexibleWidth: 9999);
// Unity class input
unityObjectClassRow = UIFactory.CreateHorizontalGroup(uiRoot, "UnityClassRow", false, true, true, true, 2, new Vector4(2, 2, 2, 2));
UIFactory.SetLayoutElement(unityObjectClassRow, minHeight: 25, flexibleHeight: 0);
var unityClassLbl = UIFactory.CreateLabel(unityObjectClassRow, "UnityClassLabel", "Class filter:", TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(unityClassLbl.gameObject, minWidth: 110, flexibleWidth: 0);
var classInputField = UIFactory.CreateInputField(unityObjectClassRow, "ClassInput", "...");
UIFactory.SetLayoutElement(classInputField.UIRoot, minHeight: 25, flexibleHeight: 0, flexibleWidth: 9999);
typeAutocompleter = new TypeCompleter(typeof(UnityEngine.Object), classInputField);
classInputField.OnValueChanged += OnTypeInputChanged;
//unityObjectClassRow.SetActive(false);
// Child filter row
childFilterRow = UIFactory.CreateHorizontalGroup(uiRoot, "ChildFilterRow", false, true, true, true, 2, new Vector4(2, 2, 2, 2));
UIFactory.SetLayoutElement(childFilterRow, minHeight: 25, flexibleHeight: 0);
var childLbl = UIFactory.CreateLabel(childFilterRow, "ChildLabel", "Child filter:", TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(childLbl.gameObject, minWidth: 110, flexibleWidth: 0);
var childDropObj = UIFactory.CreateDropdown(childFilterRow, out Dropdown childDrop, null, 14, OnChildFilterDropChanged);
foreach (var name in Enum.GetNames(typeof(ChildFilter)))
childDrop.options.Add(new Dropdown.OptionData(name));
UIFactory.SetLayoutElement(childDropObj, minHeight: 25, flexibleHeight: 0, flexibleWidth: 9999);
childFilterRow.SetActive(false);
// Scene filter row
sceneFilterRow = UIFactory.CreateHorizontalGroup(uiRoot, "SceneFilterRow", false, true, true, true, 2, new Vector4(2, 2, 2, 2));
UIFactory.SetLayoutElement(sceneFilterRow, minHeight: 25, flexibleHeight: 0);
var sceneLbl = UIFactory.CreateLabel(sceneFilterRow, "SceneLabel", "Scene filter:", TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(sceneLbl.gameObject, minWidth: 110, flexibleWidth: 0);
var sceneDropObj = UIFactory.CreateDropdown(sceneFilterRow, out Dropdown sceneDrop, null, 14, OnSceneFilterDropChanged);
foreach (var name in Enum.GetNames(typeof(SceneFilter)))
sceneDrop.options.Add(new Dropdown.OptionData(name));
UIFactory.SetLayoutElement(sceneDropObj, minHeight: 25, flexibleHeight: 0, flexibleWidth: 9999);
sceneFilterRow.SetActive(false);
// Name filter input
var nameRow = UIFactory.CreateHorizontalGroup(uiRoot, "NameRow", true, true, true, true, 2, new Vector4(2, 2, 2, 2));
UIFactory.SetLayoutElement(nameRow, minHeight: 25, flexibleHeight: 0);
var nameLbl = UIFactory.CreateLabel(nameRow, "NameFilterLabel", "Name contains:", TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(nameLbl.gameObject, minWidth: 110, flexibleWidth: 0);
nameInputField = UIFactory.CreateInputField(nameRow, "NameFilterInput", "...");
UIFactory.SetLayoutElement(nameInputField.UIRoot, minHeight: 25, flexibleHeight: 0, flexibleWidth: 9999);
// Search button
var searchButton = UIFactory.CreateButton(uiRoot, "SearchButton", "Search");
UIFactory.SetLayoutElement(searchButton.Component.gameObject, minHeight: 25, flexibleHeight: 0);
searchButton.OnClick += DoSearch;
// Results count label
var resultsCountRow = UIFactory.CreateHorizontalGroup(uiRoot, "ResultsCountRow", true, true, true, true);
UIFactory.SetLayoutElement(resultsCountRow, minHeight: 25, flexibleHeight: 0);
resultsLabel = UIFactory.CreateLabel(resultsCountRow, "ResultsLabel", "0 results", TextAnchor.MiddleCenter);
// RESULTS SCROLL POOL
dataHandler = new ButtonListHandler<object, ButtonCell>(resultsScrollPool, GetEntries, SetCell, ShouldDisplayCell, OnCellClicked);
resultsScrollPool = UIFactory.CreateScrollPool<ButtonCell>(uiRoot, "ResultsList", out GameObject scrollObj,
out GameObject scrollContent);
resultsScrollPool.Initialize(dataHandler);
UIFactory.SetLayoutElement(scrollObj, flexibleHeight: 9999);
}
}
}

View File

@ -0,0 +1,320 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using UnityExplorer.Core;
using UnityExplorer.UI;
using UnityExplorer.UI.Models;
using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Widgets;
namespace UnityExplorer.ObjectExplorer
{
public class SceneExplorer : UIModel
{
public ObjectExplorerPanel Parent { get; }
public SceneExplorer(ObjectExplorerPanel parent)
{
Parent = parent;
SceneHandler.OnInspectedSceneChanged += SceneHandler_OnInspectedSceneChanged;
SceneHandler.OnLoadedScenesChanged += SceneHandler_OnLoadedScenesChanged;
}
public override GameObject UIRoot => m_uiRoot;
private GameObject m_uiRoot;
/// <summary>
/// Whether to automatically update per auto-update interval or not.
/// </summary>
public bool AutoUpdate = false;
public TransformTree Tree;
private float timeOfLastUpdate = -1f;
private GameObject refreshRow;
private Dropdown sceneDropdown;
private readonly Dictionary<Scene, Dropdown.OptionData> sceneToDropdownOption = new Dictionary<Scene, Dropdown.OptionData>();
private IEnumerable<GameObject> GetRootEntries() => SceneHandler.CurrentRootObjects;
public void Update()
{
if ((AutoUpdate || !SceneHandler.InspectingAssetScene) && timeOfLastUpdate.OccuredEarlierThan(1))
{
timeOfLastUpdate = Time.realtimeSinceStartup;
UpdateTree();
}
}
public void UpdateTree()
{
SceneHandler.Update();
Tree.RefreshData(true);
}
public void JumpToTransform(Transform transform)
{
if (!transform)
return;
UIManager.SetPanelActive(this.Parent, true);
this.Parent.SetTab(0);
// select the transform's scene
var go = transform.gameObject;
if (SceneHandler.SelectedScene != go.scene)
{
int idx = sceneDropdown.options.IndexOf(sceneToDropdownOption[go.scene]);
sceneDropdown.value = idx;
}
// Let the TransformTree handle the rest
Tree.JumpAndExpandToTransform(transform);
}
private void OnDropdownChanged(int value)
{
if (value < 0 || SceneHandler.LoadedScenes.Count <= value)
return;
SceneHandler.SelectedScene = SceneHandler.LoadedScenes[value];
SceneHandler.Update();
Tree.RefreshData(true);
OnSelectedSceneChanged(SceneHandler.SelectedScene.Value);
}
private void SceneHandler_OnInspectedSceneChanged(Scene scene)
{
if (!sceneToDropdownOption.ContainsKey(scene))
PopulateSceneDropdown();
if (sceneToDropdownOption.ContainsKey(scene))
{
var opt = sceneToDropdownOption[scene];
int idx = sceneDropdown.options.IndexOf(opt);
if (sceneDropdown.value != idx)
sceneDropdown.value = idx;
else
sceneDropdown.captionText.text = opt.text;
}
OnSelectedSceneChanged(scene);
}
private void OnSelectedSceneChanged(Scene scene)
{
if (refreshRow)
refreshRow.SetActive(!scene.IsValid());
}
private void SceneHandler_OnLoadedScenesChanged(ReadOnlyCollection<Scene> loadedScenes)
{
PopulateSceneDropdown();
}
private void PopulateSceneDropdown()
{
sceneToDropdownOption.Clear();
sceneDropdown.options.Clear();
foreach (var scene in SceneHandler.LoadedScenes)
{
string name = scene.name?.Trim();
if (!scene.IsValid())
name = "HideAndDontSave";
else if (string.IsNullOrEmpty(name))
name = "<untitled>";
var option = new Dropdown.OptionData(name);
sceneDropdown.options.Add(option);
sceneToDropdownOption.Add(scene, option);
}
}
private void OnFilterInput(string input)
{
if ((!string.IsNullOrEmpty(input) && !Tree.Filtering) || (string.IsNullOrEmpty(input) && Tree.Filtering))
{
Tree.cachedTransforms.Clear();
}
Tree.CurrentFilter = input;
Tree.RefreshData(true, true);
}
private void TryLoadScene(LoadSceneMode mode, Dropdown allSceneDrop)
{
var text = allSceneDrop.options[allSceneDrop.value].text;
if (text == DEFAULT_LOAD_TEXT)
return;
try
{
SceneManager.LoadScene(text, mode);
allSceneDrop.value = 0;
}
catch (Exception ex)
{
ExplorerCore.LogWarning($"Unable to load the Scene! {ex.ReflectionExToString()}");
}
}
public override void ConstructUI(GameObject content)
{
m_uiRoot = UIFactory.CreateUIObject("SceneExplorer", content);
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(m_uiRoot, true, true, true, true, 0, 2, 2, 2, 2);
UIFactory.SetLayoutElement(m_uiRoot, flexibleHeight: 9999);
// Tool bar (top area)
var toolbar = UIFactory.CreateVerticalGroup(m_uiRoot, "Toolbar", true, true, true, true, 2, new Vector4(2, 2, 2, 2),
new Color(0.15f, 0.15f, 0.15f));
// Scene selector dropdown
var dropRow = UIFactory.CreateHorizontalGroup(toolbar, "DropdownRow", true, true, true, true, 5, default, new Color(1, 1, 1, 0));
UIFactory.SetLayoutElement(dropRow, minHeight: 25, flexibleWidth: 9999);
var dropLabel = UIFactory.CreateLabel(dropRow, "SelectorLabel", "Scene:", TextAnchor.MiddleLeft, Color.cyan, false, 15);
UIFactory.SetLayoutElement(dropLabel.gameObject, minHeight: 25, minWidth: 60, flexibleWidth: 0);
var dropdownObj = UIFactory.CreateDropdown(dropRow, out sceneDropdown, "<notset>", 13, OnDropdownChanged);
UIFactory.SetLayoutElement(dropdownObj, minHeight: 25, flexibleHeight: 0, flexibleWidth: 9999);
SceneHandler.Update();
PopulateSceneDropdown();
sceneDropdown.captionText.text = sceneToDropdownOption.First().Value.text;
// Filter row
var filterRow = UIFactory.CreateHorizontalGroup(toolbar, "FilterGroup", true, true, true, true, 2, new Vector4(2, 2, 2, 2));
UIFactory.SetLayoutElement(filterRow, minHeight: 25, flexibleHeight: 0);
//Filter input field
var inputField = UIFactory.CreateInputField(filterRow, "FilterInput", "Search...");
inputField.Component.targetGraphic.color = new Color(0.2f, 0.2f, 0.2f);
RuntimeProvider.Instance.SetColorBlock(inputField.Component, new Color(0.4f, 0.4f, 0.4f), new Color(0.2f, 0.2f, 0.2f),
new Color(0.08f, 0.08f, 0.08f));
UIFactory.SetLayoutElement(inputField.UIRoot, minHeight: 25);
inputField.OnValueChanged += OnFilterInput;
// refresh row
refreshRow = UIFactory.CreateHorizontalGroup(toolbar, "RefreshGroup", true, true, true, true, 2, new Vector4(2, 2, 2, 2));
UIFactory.SetLayoutElement(refreshRow, minHeight: 30, flexibleHeight: 0);
var refreshButton = UIFactory.CreateButton(refreshRow, "RefreshButton", "Update");
UIFactory.SetLayoutElement(refreshButton.Component.gameObject, minWidth: 65, flexibleWidth: 0);
refreshButton.OnClick += UpdateTree;
var refreshToggle = UIFactory.CreateToggle(refreshRow, "RefreshToggle", out Toggle toggle, out Text text);
UIFactory.SetLayoutElement(refreshToggle, flexibleWidth: 9999);
text.text = "Auto-update (1 second)";
text.alignment = TextAnchor.MiddleLeft;
text.color = Color.white;
text.fontSize = 12;
toggle.isOn = false;
toggle.onValueChanged.AddListener((bool val) => AutoUpdate = val);
refreshRow.SetActive(false);
// Transform Tree
var scrollPool = UIFactory.CreateScrollPool<TransformCell>(m_uiRoot, "TransformTree", out GameObject scrollObj,
out GameObject scrollContent, new Color(0.11f, 0.11f, 0.11f));
UIFactory.SetLayoutElement(scrollObj, flexibleHeight: 9999);
UIFactory.SetLayoutElement(scrollContent, flexibleHeight: 9999);
Tree = new TransformTree(scrollPool, GetRootEntries);
Tree.Init();
Tree.RefreshData(true, true);
//scrollPool.Viewport.GetComponent<Mask>().enabled = false;
//UIRoot.GetComponent<Mask>().enabled = false;
// Scene Loader
ConstructSceneLoader();
}
private const string DEFAULT_LOAD_TEXT = "[Select a scene]";
private void ConstructSceneLoader()
{
// Scene Loader
try
{
if (SceneHandler.WasAbleToGetScenesInBuild)
{
var sceneLoaderObj = UIFactory.CreateVerticalGroup(m_uiRoot, "SceneLoader", true, true, true, true);
UIFactory.SetLayoutElement(sceneLoaderObj, minHeight: 25);
//sceneLoaderObj.SetActive(false);
var loaderTitle = UIFactory.CreateLabel(sceneLoaderObj, "SceneLoaderLabel", "Scene Loader", TextAnchor.MiddleLeft, Color.white, true, 14);
UIFactory.SetLayoutElement(loaderTitle.gameObject, minHeight: 25, flexibleHeight: 0);
var allSceneDropObj = UIFactory.CreateDropdown(sceneLoaderObj, out Dropdown allSceneDrop, "", 14, null);
UIFactory.SetLayoutElement(allSceneDropObj, minHeight: 25, minWidth: 150, flexibleWidth: 0, flexibleHeight: 0);
allSceneDrop.options.Add(new Dropdown.OptionData(DEFAULT_LOAD_TEXT));
foreach (var scene in SceneHandler.AllSceneNames)
allSceneDrop.options.Add(new Dropdown.OptionData(Path.GetFileNameWithoutExtension(scene)));
allSceneDrop.value = 1;
allSceneDrop.value = 0;
var buttonRow = UIFactory.CreateHorizontalGroup(sceneLoaderObj, "LoadButtons", true, true, true, true, 4);
var loadButton = UIFactory.CreateButton(buttonRow, "LoadSceneButton", "Load (Single)", new Color(0.1f, 0.3f, 0.3f));
UIFactory.SetLayoutElement(loadButton.Component.gameObject, minHeight: 25, minWidth: 150);
loadButton.OnClick += () =>
{
TryLoadScene(LoadSceneMode.Single, allSceneDrop);
};
var loadAdditiveButton = UIFactory.CreateButton(buttonRow, "LoadSceneButton", "Load (Additive)", new Color(0.1f, 0.3f, 0.3f));
UIFactory.SetLayoutElement(loadAdditiveButton.Component.gameObject, minHeight: 25, minWidth: 150);
loadAdditiveButton.OnClick += () =>
{
TryLoadScene(LoadSceneMode.Additive, allSceneDrop);
};
var disabledColor = new Color(0.24f, 0.24f, 0.24f);
RuntimeProvider.Instance.SetColorBlock(loadButton.Component, disabled: disabledColor);
RuntimeProvider.Instance.SetColorBlock(loadAdditiveButton.Component, disabled: disabledColor);
loadButton.Component.interactable = false;
loadAdditiveButton.Component.interactable = false;
allSceneDrop.onValueChanged.AddListener((int val) =>
{
var text = allSceneDrop.options[val].text;
if (text == DEFAULT_LOAD_TEXT)
{
loadButton.Component.interactable = false;
loadAdditiveButton.Component.interactable = false;
}
else
{
loadButton.Component.interactable = true;
loadAdditiveButton.Component.interactable = true;
}
});
}
}
catch (Exception ex)
{
ExplorerCore.LogWarning($"Could not create the Scene Loader helper! {ex.ReflectionExToString()}");
}
}
}
}

View File

@ -0,0 +1,172 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace UnityExplorer.ObjectExplorer
{
public static class SceneHandler
{
/// <summary>
/// The currently inspected Scene.
/// </summary>
public static Scene? SelectedScene
{
get => selectedScene;
internal set
{
if (selectedScene != null && selectedScene == value)
return;
selectedScene = value;
OnInspectedSceneChanged?.Invoke((Scene)selectedScene);
}
}
private static Scene? selectedScene;
/// <summary>
/// The GameObjects in the currently inspected scene.
/// </summary>
public static ReadOnlyCollection<GameObject> CurrentRootObjects => new ReadOnlyCollection<GameObject>(rootObjects);
private static GameObject[] rootObjects = new GameObject[0];
/// <summary>
/// All currently loaded Scenes.
/// </summary>
public static ReadOnlyCollection<Scene> LoadedScenes => new ReadOnlyCollection<Scene>(allLoadedScenes);
private static readonly List<Scene> allLoadedScenes = new List<Scene>();
private static HashSet<Scene> previousLoadedScenes;
/// <summary>
/// The names of all scenes in the build settings, if they could be retrieved.
/// </summary>
public static ReadOnlyCollection<string> AllSceneNames => new ReadOnlyCollection<string>(allScenesInBuild);
private static readonly List<string> allScenesInBuild = new List<string>();
/// <summary>
/// Whether or not we successfuly retrieved the names of the scenes in the build settings.
/// </summary>
public static bool WasAbleToGetScenesInBuild => gotAllScenesInBuild;
private static bool gotAllScenesInBuild = true;
/// <summary>
/// Invoked when the currently inspected Scene changes. The argument is the new scene.
/// </summary>
public static event Action<Scene> OnInspectedSceneChanged;
/// <summary>
/// Invoked whenever the list of currently loaded Scenes changes. The argument contains all loaded scenes after the change.
/// </summary>
public static event Action<ReadOnlyCollection<Scene>> OnLoadedScenesChanged;
/// <summary>
/// Equivalent to <see cref="SceneManager.sceneCount"/> + 2, to include 'DontDestroyOnLoad' and the 'None' scene.
/// </summary>
public static int LoadedSceneCount => SceneManager.sceneCount + 2;
internal static Scene DontDestroyScene => DontDestroyMe.scene;
internal static int DontDestroyHandle => DontDestroyScene.handle;
internal static GameObject DontDestroyMe
{
get
{
if (!dontDestroyObject)
{
dontDestroyObject = new GameObject("DontDestroyMe");
GameObject.DontDestroyOnLoad(dontDestroyObject);
}
return dontDestroyObject;
}
}
private static GameObject dontDestroyObject;
public static bool InspectingAssetScene => SelectedScene.HasValue && SelectedScene.Value == default;
internal static void Init()
{
// Try to get all scenes in the build settings. This may not work.
try
{
Type sceneUtil = ReflectionUtility.GetTypeByName("UnityEngine.SceneManagement.SceneUtility");
if (sceneUtil == null)
throw new Exception("This version of Unity does not ship with the 'SceneUtility' class, or it was not unstripped.");
var method = sceneUtil.GetMethod("GetScenePathByBuildIndex", ReflectionUtility.FLAGS);
int sceneCount = SceneManager.sceneCountInBuildSettings;
for (int i = 0; i < sceneCount; i++)
{
var scenePath = (string)method.Invoke(null, new object[] { i });
allScenesInBuild.Add(scenePath);
}
}
catch (Exception ex)
{
gotAllScenesInBuild = false;
ExplorerCore.LogWarning($"Unable to generate list of all Scenes in the build: {ex}");
}
}
internal static void Update()
{
// check if the loaded scenes changed. always confirm DontDestroy / HideAndDontSave
int confirmedCount = 2;
bool inspectedExists = SelectedScene == DontDestroyScene || (SelectedScene.HasValue && SelectedScene.Value == default);
allLoadedScenes.Clear();
for (int i = 0; i < SceneManager.sceneCount; i++)
{
Scene scene = SceneManager.GetSceneAt(i);
if (scene == default || !scene.isLoaded)
continue;
// If no changes yet, ensure the previous list contained the scene
if (previousLoadedScenes != null && previousLoadedScenes.Contains(scene))
confirmedCount++;
// If we have not yet confirmed inspectedExists, check if this scene is our currently inspected one.
if (!inspectedExists && scene == SelectedScene)
inspectedExists = true;
allLoadedScenes.Add(scene);
}
bool anyChange = confirmedCount != allLoadedScenes.Count;
allLoadedScenes.Add(DontDestroyScene);
allLoadedScenes.Add(default);
previousLoadedScenes = new HashSet<Scene>(allLoadedScenes);
// Default to first scene if none selected or previous selection no longer exists.
if (!inspectedExists)
{
SelectedScene = allLoadedScenes.First();
}
// Notify on the list changing at all
if (anyChange)
{
OnLoadedScenesChanged?.Invoke(LoadedScenes);
}
// Finally, update the root objects list.
if (SelectedScene != null && ((Scene)SelectedScene).IsValid())
rootObjects = RuntimeProvider.Instance.GetRootGameObjects((Scene)SelectedScene);
else
{
var allObjects = RuntimeProvider.Instance.FindObjectsOfTypeAll(typeof(GameObject));
var objects = new List<GameObject>();
foreach (var obj in allObjects)
{
var go = obj.TryCast<GameObject>();
if (go.transform.parent == null && !go.scene.IsValid())
objects.Add(go);
}
rootObjects = objects.ToArray();
}
}
}
}

View File

@ -0,0 +1,203 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityExplorer.Core;
using UnityExplorer.Core.Runtime;
namespace UnityExplorer.ObjectExplorer
{
public enum SearchContext
{
UnityObject,
// GameObject,
Singleton,
StaticClass
}
public enum ChildFilter
{
Any,
RootObject,
HasParent
}
public enum SceneFilter
{
Any,
ActivelyLoaded,
DontDestroyOnLoad,
HideAndDontSave,
}
public static class SearchProvider
{
private static bool Filter(Scene scene, SceneFilter filter)
{
switch (filter)
{
case SceneFilter.Any:
return true;
case SceneFilter.DontDestroyOnLoad:
return scene == SceneHandler.DontDestroyScene;
case SceneFilter.HideAndDontSave:
return scene == default;
case SceneFilter.ActivelyLoaded:
return scene != SceneHandler.DontDestroyScene && scene != default;
default:
return false;
}
}
internal static List<object> UnityObjectSearch(string input, string customTypeInput, SearchContext context,
ChildFilter childFilter, SceneFilter sceneFilter)
{
var results = new List<object>();
Type searchType = null;
if (!string.IsNullOrEmpty(customTypeInput))
{
if (ReflectionUtility.GetTypeByName(customTypeInput) is Type customType)
{
if (typeof(UnityEngine.Object).IsAssignableFrom(customType))
searchType = customType;
else
ExplorerCore.LogWarning($"Custom type '{customType.FullName}' is not assignable from UnityEngine.Object!");
}
else
ExplorerCore.LogWarning($"Could not find any type by name '{customTypeInput}'!");
}
if (searchType == null)
searchType = typeof(UnityEngine.Object);
var allObjects = RuntimeProvider.Instance.FindObjectsOfTypeAll(searchType);
// perform filter comparers
string nameFilter = null;
if (!string.IsNullOrEmpty(input))
nameFilter = input;
bool shouldFilterGOs = searchType == typeof(GameObject) || typeof(Component).IsAssignableFrom(searchType);
foreach (var obj in allObjects)
{
// name check
if (!string.IsNullOrEmpty(nameFilter) && !obj.name.ContainsIgnoreCase(nameFilter))
continue;
GameObject go = null;
var type = obj.GetActualType();
if (type == typeof(GameObject))
go = obj.TryCast<GameObject>();
else if (typeof(Component).IsAssignableFrom(type))
go = obj.TryCast<Component>()?.gameObject;
if (go)
{
// hide unityexplorer objects
if (go.transform.root.name == "ExplorerCanvas")
continue;
if (shouldFilterGOs)
{
// scene check
if (sceneFilter != SceneFilter.Any)
{
if (!Filter(go.scene, sceneFilter))
continue;
}
if (childFilter != ChildFilter.Any)
{
if (!go)
continue;
// root object check (no parent)
if (childFilter == ChildFilter.HasParent && !go.transform.parent)
continue;
else if (childFilter == ChildFilter.RootObject && go.transform.parent)
continue;
}
}
}
results.Add(obj);
}
return results;
}
internal static List<object> StaticClassSearch(string input)
{
var list = new List<object>();
var nameFilter = "";
if (!string.IsNullOrEmpty(input))
nameFilter = input;
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
{
foreach (var type in asm.TryGetTypes().Where(it => it.IsSealed && it.IsAbstract))
{
if (!string.IsNullOrEmpty(nameFilter) && !type.FullName.ContainsIgnoreCase(nameFilter))
continue;
list.Add(type);
}
}
return list;
}
internal static string[] instanceNames = new string[]
{
"m_instance",
"m_Instance",
"s_instance",
"s_Instance",
"_instance",
"_Instance",
"instance",
"Instance",
"<Instance>k__BackingField",
"<instance>k__BackingField",
};
internal static List<object> SingletonSearch(string input)
{
var instances = new List<object>();
var nameFilter = "";
if (!string.IsNullOrEmpty(input))
nameFilter = input;
var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
{
// Search all non-static, non-enum classes.
foreach (var type in asm.TryGetTypes().Where(it => !(it.IsSealed && it.IsAbstract) && !it.IsEnum))
{
try
{
if (!string.IsNullOrEmpty(nameFilter) && !type.FullName.ContainsIgnoreCase(nameFilter))
continue;
ReflectionUtility.FindSingleton(instanceNames, type, flags, instances);
}
catch { }
}
}
return instances;
}
}
}