diff --git a/src/Core/CSharp/Suggestion.cs b/src/Core/CSharp/Suggestion.cs deleted file mode 100644 index e452ff8..0000000 --- a/src/Core/CSharp/Suggestion.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using UnityEngine; -using UnityExplorer.Core; - -namespace UnityExplorer.Core.CSharp -{ - public struct Suggestion - { - public enum Contexts - { - Namespace, - Keyword, - Other - } - - // ~~~~ Instance ~~~~ - - public readonly string Prefix; - public readonly string Addition; - public readonly Contexts Context; - - public string Full => Prefix + Addition; - - public Color TextColor => GetTextColor(); - - public Suggestion(string addition, string prefix, Contexts type) - { - Addition = addition; - Prefix = prefix; - Context = type; - } - - private Color GetTextColor() - { - switch (Context) - { - case Contexts.Namespace: return Color.grey; - case Contexts.Keyword: return keywordColor; - default: return Color.white; - } - } - - // ~~~~ Static ~~~~ - - public static HashSet Namespaces => m_namespaces ?? GetNamespaces(); - private static HashSet m_namespaces; - - public static HashSet Keywords => throw new NotImplementedException("TODO!"); // m_keywords ?? (m_keywords = new HashSet(CSLexerHighlighter.validKeywordMatcher.Keywords)); - //private static HashSet m_keywords; - - private static readonly Color keywordColor = new Color(80f / 255f, 150f / 255f, 215f / 255f); - - private static HashSet GetNamespaces() - { - HashSet set = new HashSet( - AppDomain.CurrentDomain.GetAssemblies() - .SelectMany(GetTypes) - .Where(x => x.IsPublic && !string.IsNullOrEmpty(x.Namespace)) - .Select(x => x.Namespace)); - - return m_namespaces = set; - - IEnumerable GetTypes(Assembly asm) => asm.TryGetTypes(); - } - } -} diff --git a/src/Core/ReflectionUtility.cs b/src/Core/ReflectionUtility.cs index ab582a7..1cd09ef 100644 --- a/src/Core/ReflectionUtility.cs +++ b/src/Core/ReflectionUtility.cs @@ -120,7 +120,7 @@ namespace UnityExplorer } // cache for GetBaseTypes - internal static readonly Dictionary s_cachedTypeInheritance = new Dictionary(); + internal static readonly Dictionary s_cachedBaseTypes = new Dictionary(); /// /// Get all base types of the provided Type, including itself. @@ -137,7 +137,7 @@ namespace UnityExplorer var name = type.AssemblyQualifiedName; - if (s_cachedTypeInheritance.TryGetValue(name, out Type[] ret)) + if (s_cachedBaseTypes.TryGetValue(name, out Type[] ret)) return ret; List list = new List(); @@ -150,11 +150,52 @@ namespace UnityExplorer ret = list.ToArray(); - s_cachedTypeInheritance.Add(name, ret); + s_cachedBaseTypes.Add(name, ret); return ret; } + // cache for GetImplementationsOf + internal static readonly Dictionary> s_cachedTypeInheritance = new Dictionary>(); + internal static int s_lastAssemblyCount; + + /// + /// Get all non-abstract implementations of the provided type (include itself, if not abstract) in the current AppDomain. + /// + /// The base type, which can optionally be abstract / interface. + /// All implementations of the type in the current AppDomain. + public static HashSet GetImplementationsOf(this Type baseType, bool allowAbstract) + { + var assemblies = AppDomain.CurrentDomain.GetAssemblies(); + + if (!s_cachedTypeInheritance.ContainsKey(baseType) || assemblies.Length != s_lastAssemblyCount) + { + if (assemblies.Length != s_lastAssemblyCount) + { + s_cachedTypeInheritance.Clear(); + s_lastAssemblyCount = assemblies.Length; + } + + var set = new HashSet(); + + if (!baseType.IsAbstract && !baseType.IsInterface) + set.Add(baseType); + + foreach (var asm in assemblies) + { + foreach (var t in asm.TryGetTypes().Where(t => allowAbstract || (!t.IsAbstract && !t.IsInterface))) + { + if (baseType.IsAssignableFrom(t) && !set.Contains(t)) + set.Add(t); + } + } + + s_cachedTypeInheritance.Add(baseType, set); + } + + return s_cachedTypeInheritance[baseType]; + } + /// /// Safely get all valid Types inside an Assembly. /// diff --git a/src/Core/Search/ChildFilter.cs b/src/Core/Search/ChildFilter.cs index 59b63a3..081641b 100644 --- a/src/Core/Search/ChildFilter.cs +++ b/src/Core/Search/ChildFilter.cs @@ -5,7 +5,7 @@ using System.Text; namespace UnityExplorer.Core.Search { - internal enum ChildFilter + public enum ChildFilter { Any, RootObject, diff --git a/src/Core/Search/SceneFilter.cs b/src/Core/Search/SceneFilter.cs index 3373b4e..5310e08 100644 --- a/src/Core/Search/SceneFilter.cs +++ b/src/Core/Search/SceneFilter.cs @@ -5,11 +5,11 @@ using System.Text; namespace UnityExplorer.Core.Search { - internal enum SceneFilter + public enum SceneFilter { Any, - Asset, + ActivelyLoaded, DontDestroyOnLoad, - Explicit, + HideAndDontSave, } } diff --git a/src/Core/Search/SearchContext.cs b/src/Core/Search/SearchContext.cs index 982be2f..6cb463c 100644 --- a/src/Core/Search/SearchContext.cs +++ b/src/Core/Search/SearchContext.cs @@ -5,7 +5,7 @@ using System.Text; namespace UnityExplorer.Core.Search { - internal enum SearchContext + public enum SearchContext { UnityObject, GameObject, diff --git a/src/Core/Search/SearchProvider.cs b/src/Core/Search/SearchProvider.cs index bd198f9..122cf32 100644 --- a/src/Core/Search/SearchProvider.cs +++ b/src/Core/Search/SearchProvider.cs @@ -4,15 +4,16 @@ using System.Linq; using System.Reflection; using System.Text; using UnityEngine; +using UnityEngine.SceneManagement; using UnityExplorer.Core.Runtime; namespace UnityExplorer.Core.Search { public static class SearchProvider { - internal static object[] StaticClassSearch(string input) + internal static List StaticClassSearch(string input) { - var list = new List(); + var list = new List(); var nameFilter = ""; if (!string.IsNullOrEmpty(input)) @@ -29,7 +30,7 @@ namespace UnityExplorer.Core.Search } } - return list.ToArray(); + return list; } internal static string[] s_instanceNames = new string[] @@ -46,7 +47,7 @@ namespace UnityExplorer.Core.Search "k__BackingField", }; - internal static object[] SingletonSearch(string input) + internal static List SingletonSearch(string input) { var instances = new List(); @@ -72,12 +73,31 @@ namespace UnityExplorer.Core.Search } } - return instances.ToArray(); + return instances; } - internal static object[] UnityObjectSearch(string input, string customTypeInput, SearchContext context, - ChildFilter childFilter, SceneFilter sceneFilter, string sceneName = null) + 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 == SceneHandler.AssetScene; + case SceneFilter.ActivelyLoaded: + return scene != SceneHandler.DontDestroyScene && scene != SceneHandler.AssetScene; + default: + return false; + } + } + + internal static List UnityObjectSearch(string input, string customTypeInput, SearchContext context, + ChildFilter childFilter, SceneFilter sceneFilter) + { + var results = new List(); + Type searchType = null; switch (context) { @@ -91,13 +111,15 @@ namespace UnityExplorer.Core.Search if (string.IsNullOrEmpty(customTypeInput)) { ExplorerCore.LogWarning("Custom Type input must not be empty!"); - return null; + return results; } 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 a type by the name '{customTypeInput}'!"); break; @@ -106,11 +128,11 @@ namespace UnityExplorer.Core.Search searchType = typeof(UnityEngine.Object); break; } + if (searchType == null) - return null; + return results; var allObjects = RuntimeProvider.Instance.FindObjectsOfTypeAll(searchType); - var results = new List(); // perform filter comparers @@ -121,20 +143,11 @@ namespace UnityExplorer.Core.Search bool canGetGameObject = (sceneFilter != SceneFilter.Any || childFilter != ChildFilter.Any) && (context == SearchContext.GameObject || typeof(Component).IsAssignableFrom(searchType)); - string sceneFilterString = null; if (!canGetGameObject) { if (context != SearchContext.UnityObject && (sceneFilter != SceneFilter.Any || childFilter != ChildFilter.Any)) ExplorerCore.LogWarning($"Type '{searchType}' cannot have Scene or Child filters applied to it"); } - else - { - if (sceneFilter == SceneFilter.DontDestroyOnLoad) - sceneFilterString = "DontDestroyOnLoad"; - else if (sceneFilter == SceneFilter.Explicit) - //sceneFilterString = SearchPage.Instance.m_sceneDropdown.options[SearchPage.Instance.m_sceneDropdown.value].text; - sceneFilterString = sceneName; - } foreach (var obj in allObjects) { @@ -157,12 +170,9 @@ namespace UnityExplorer.Core.Search switch (context) { case SearchContext.GameObject: - if (go.scene.name != sceneFilterString) - continue; - break; case SearchContext.Custom: case SearchContext.Component: - if (go.scene.name != sceneFilterString) + if (!Filter(go.scene, sceneFilter)) continue; break; } @@ -184,7 +194,7 @@ namespace UnityExplorer.Core.Search results.Add(obj); } - return results.ToArray(); + return results; } } } diff --git a/src/UI/Models/UIModel.cs b/src/UI/Models/UIModel.cs index 4cc4816..1046d08 100644 --- a/src/UI/Models/UIModel.cs +++ b/src/UI/Models/UIModel.cs @@ -26,6 +26,13 @@ namespace UnityExplorer.UI.Models public abstract void ConstructUI(GameObject parent); + public virtual void Toggle() => SetActive(!Enabled); + + public virtual void SetActive(bool active) + { + UIRoot?.SetActive(active); + } + public virtual void Destroy() { if (UIRoot) diff --git a/src/UI/Models/UIPanel.cs b/src/UI/Models/UIPanel.cs index d2a2dd6..1d7e628 100644 --- a/src/UI/Models/UIPanel.cs +++ b/src/UI/Models/UIPanel.cs @@ -13,17 +13,19 @@ namespace UnityExplorer.UI.Models { public abstract class UIPanel : UIBehaviourModel { + // STATIC + public static event Action OnPanelsReordered; public static void UpdateFocus() { if (InputManager.GetMouseButtonDown(0) || InputManager.GetMouseButtonDown(1)) { - int count = UIManager.CanvasRoot.transform.childCount; + int count = UIManager.PanelHolder.transform.childCount; var mousePos = InputManager.MousePosition; for (int i = count - 1; i >= 0; i--) { - var transform = UIManager.CanvasRoot.transform.GetChild(i); + var transform = UIManager.PanelHolder.transform.GetChild(i); if (transformToPanelDict.TryGetValue(transform.GetInstanceID(), out UIPanel panel)) { var pos = panel.mainPanelRect.InverseTransformPoint(mousePos); @@ -44,16 +46,18 @@ namespace UnityExplorer.UI.Models private static readonly List instances = new List(); private static readonly Dictionary transformToPanelDict = new Dictionary(); + // INSTANCE + public UIPanel() { instances.Add(this); } - public override void Destroy() - { - instances.Remove(this); - base.Destroy(); - } + public abstract UIManager.Panels PanelType { get; } + + public abstract string Name { get; } + + public virtual bool ShouldSaveActiveState => true; public override GameObject UIRoot => uiRoot; protected GameObject uiRoot; @@ -61,7 +65,23 @@ namespace UnityExplorer.UI.Models public GameObject content; public PanelDragger dragger; - public abstract string Name { get; } + public abstract void ConstructPanelContent(); + + public virtual void OnFinishResize(RectTransform panel) + { + SaveToConfigManager(); + } + + public virtual void OnFinishDrag(RectTransform panel) + { + SaveToConfigManager(); + } + + public override void Destroy() + { + instances.Remove(this); + base.Destroy(); + } public override void ConstructUI(GameObject parent) { @@ -79,11 +99,28 @@ namespace UnityExplorer.UI.Models SetDefaultPosAndAnchors(); // Title bar + var titleGroup = UIFactory.CreateHorizontalGroup(content, "TitleBar", false, true, true, true, 2, + new Vector4(2, 2, 2, 2), new Color(0.09f, 0.09f, 0.09f)); + UIFactory.SetLayoutElement(titleGroup, minHeight: 25, flexibleHeight: 0); - var titleBar = UIFactory.CreateLabel(content, "TitleBar", Name, TextAnchor.MiddleLeft); - UIFactory.SetLayoutElement(titleBar.gameObject, minHeight: 25, flexibleHeight: 0); + // Title text - dragger = new PanelDragger(titleBar.GetComponent(), mainPanelRect); + var titleTxt = UIFactory.CreateLabel(titleGroup, "TitleBar", Name, TextAnchor.MiddleLeft); + UIFactory.SetLayoutElement(titleTxt.gameObject, minHeight: 25, flexibleHeight: 0, flexibleWidth: 9999); + + // close button + + var closeBtn = UIFactory.CreateButton(titleGroup, "CloseButton", "X", () => + { + UIManager.SetPanelActive(this.PanelType, false); + }); + UIFactory.SetLayoutElement(closeBtn.gameObject, minHeight: 25, minWidth: 25, flexibleWidth: 0); + RuntimeProvider.Instance.SetColorBlock(closeBtn, new Color(0.63f, 0.32f, 0.31f), + new Color(0.81f, 0.25f, 0.2f), new Color(0.6f, 0.18f, 0.16f)); + + // Panel dragger + + dragger = new PanelDragger(titleTxt.GetComponent(), mainPanelRect); dragger.OnFinishResize += OnFinishResize; dragger.OnFinishDrag += OnFinishDrag; @@ -97,8 +134,9 @@ namespace UnityExplorer.UI.Models LoadSaveData(); dragger.OnEndResize(); } - catch + catch (Exception ex) { + ExplorerCore.Log($"Exception loading panel save data: {ex}"); SetDefaultPosAndAnchors(); } @@ -108,18 +146,8 @@ namespace UnityExplorer.UI.Models SaveToConfigManager(); }; } - - public abstract void ConstructPanelContent(); - - public virtual void OnFinishResize(RectTransform panel) - { - SaveToConfigManager(); - } - - public virtual void OnFinishDrag(RectTransform panel) - { - SaveToConfigManager(); - } + + // SAVE DATA public abstract void SaveToConfigManager(); @@ -127,11 +155,11 @@ namespace UnityExplorer.UI.Models public abstract void LoadSaveData(); - public string ToSaveData() + public virtual string ToSaveData() { try { - return $"{Enabled}" + + return $"{(ShouldSaveActiveState ? Enabled : false)}" + $"|{mainPanelRect.RectAnchorsToString()}" + $"|{mainPanelRect.RectPositionToString()}"; } @@ -141,7 +169,7 @@ namespace UnityExplorer.UI.Models } } - public void ApplySaveData(string data) + public virtual void ApplySaveData(string data) { if (string.IsNullOrEmpty(data)) return; @@ -150,11 +178,15 @@ namespace UnityExplorer.UI.Models try { - uiRoot.SetActive(bool.Parse(split[0])); mainPanelRect.SetAnchorsFromString(split[1]); mainPanelRect.SetPositionFromString(split[2]); + UIManager.SetPanelActive(this.PanelType, bool.Parse(split[0])); + } + catch + { + ExplorerCore.LogWarning("Invalid or corrupt panel save data! Restoring to default."); + SetDefaultPosAndAnchors(); } - catch { } } } @@ -164,9 +196,6 @@ namespace UnityExplorer.UI.Models // Window Anchors helpers - //private const string DEFAULT_WINDOW_ANCHORS = "0.25,0.10,0.78,0.95"; - //private const string DEFAULT_WINDOW_POSITION = "0,0"; - internal static CultureInfo _enCulture = new CultureInfo("en-US"); internal static string RectAnchorsToString(this RectTransform rect) diff --git a/src/UI/Panels/InspectorTest.cs b/src/UI/Panels/InspectorTest.cs index 6f7bfd5..1a27e44 100644 --- a/src/UI/Panels/InspectorTest.cs +++ b/src/UI/Panels/InspectorTest.cs @@ -16,6 +16,8 @@ namespace UnityExplorer.UI.Panels public class InspectorTest : UIPanel { public override string Name => "Inspector"; + public override UIManager.Panels PanelType => UIManager.Panels.Inspector; + public override bool ShouldSaveActiveState => false; //public SimpleListSource ComponentList; @@ -81,6 +83,8 @@ namespace UnityExplorer.UI.Panels ExplorerCore.Log("Done"); //previousRectHeight = mainPanelRect.rect.height; + + UIManager.SetPanelActive(PanelType, false); } internal GameObject contentHolder; diff --git a/src/UI/Panels/ObjectExplorer.cs b/src/UI/Panels/ObjectExplorer.cs new file mode 100644 index 0000000..dfb849b --- /dev/null +++ b/src/UI/Panels/ObjectExplorer.cs @@ -0,0 +1,139 @@ +using System; +using System.Collections; +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.Core.Config; +using UnityExplorer.UI.Models; +using UnityExplorer.UI.Utility; +using UnityExplorer.UI.Widgets; + +namespace UnityExplorer.UI.Panels +{ + public class ObjectExplorer : UIPanel + { + public override string Name => "Object Explorer"; + public override UIManager.Panels PanelType => UIManager.Panels.ObjectExplorer; + + public SceneExplorer SceneExplorer; + public ObjectSearch ObjectSearch; + + public int SelectedTab = -1; + private readonly List tabPages = new List(); + private readonly List