1.4.5 (pre-release)

* Windows now display the gameobject name or the object type in the header
* Added "Set DontDestroyOnLoad" button to Gameobject controls.
* Added dynamic input field size and more intelligent auto-wrap for primitive values
* Resize Drag will now disable itself on Exceptions, and log the error (affects VRChat)
* Various misc UI improvements
* Various small fixes
This commit is contained in:
sinaioutlander 2020-08-28 00:45:34 +10:00
parent c47974115b
commit 6e644b4f50
10 changed files with 232 additions and 156 deletions

View File

@ -103,13 +103,20 @@ namespace Explorer
}
GUI.skin.button.alignment = TextAnchor.MiddleCenter;
GUILayout.Space(5);
if (IsExpanded)
{
float whitespace = 215;
ClampLabelWidth(window, ref whitespace);
if (count > ArrayLimit)
{
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null);
GUILayout.Space(190);
GUILayout.Space(whitespace);
int maxOffset = (int)Mathf.Ceil((float)(count / (decimal)ArrayLimit)) - 1;
GUILayout.Label($"Page {ArrayOffset + 1}/{maxOffset + 1}", new GUILayoutOption[] { GUILayout.Width(80) });
// prev/next page buttons
@ -128,11 +135,17 @@ namespace Explorer
{
ArrayLimit = i;
}
GUILayout.Space(5);
}
int offset = ArrayOffset * ArrayLimit;
if (offset >= count) offset = 0;
if (offset >= count)
{
offset = 0;
ArrayOffset = 0;
}
for (int i = offset; i < offset + ArrayLimit && i < count; i++)
{
@ -141,24 +154,27 @@ namespace Explorer
//collapsing the BeginHorizontal called from ReflectionWindow.WindowFunction or previous array entry
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null);
GUILayout.Space(190);
GUILayout.Space(whitespace);
if (entry.Value == null)
{
GUILayout.Label("<i><color=grey>null</color></i>", null);
GUILayout.Label(i + "<i><color=grey> (null)</color></i>", null);
}
else
{
GUILayout.Label(i.ToString(), new GUILayoutOption[] { GUILayout.Width(30) });
entry.DrawValue(window, window.width - 250);
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
GUILayout.Label($"[{i}]", new GUILayoutOption[] { GUILayout.Width(30) });
entry.DrawValue(window, window.width - (whitespace + 85));
}
}
GUI.skin.label.alignment = TextAnchor.UpperLeft;
}
}
/// <summary>
/// Called when the user presses the "Update" button, or if AutoUpdate is on.
/// Called only when the user presses the "Update" button, or if AutoUpdate is on.
/// </summary>
public override void UpdateValue()
{

View File

@ -88,26 +88,26 @@ namespace Explorer
/// <param name="obj">The current value (can be null if memberInfo is not null)</param>
/// <param name="memberInfo">The MemberInfo (can be null if obj is not null)</param>
/// <param name="declaringInstance">If MemberInfo is not null, the declaring class instance. Can be null if static.</param>
/// <param name="type">The type of the object or MemberInfo value.</param>
/// <param name="valueType">The type of the object or MemberInfo value.</param>
/// <returns></returns>
public static CacheObject GetCacheObject(object obj, MemberInfo memberInfo, object declaringInstance, Type type)
public static CacheObject GetCacheObject(object obj, MemberInfo memberInfo, object declaringInstance, Type valueType)
{
CacheObject holder;
if ((obj is Il2CppSystem.Object || typeof(Il2CppSystem.Object).IsAssignableFrom(type))
&& (type.FullName.Contains("UnityEngine.GameObject") || type.FullName.Contains("UnityEngine.Transform")))
if ((obj is Il2CppSystem.Object || typeof(Il2CppSystem.Object).IsAssignableFrom(valueType))
&& (valueType.FullName.Contains("UnityEngine.GameObject") || valueType.FullName.Contains("UnityEngine.Transform")))
{
holder = new CacheGameObject();
}
else if (type.IsPrimitive || type == typeof(string))
else if (valueType.IsPrimitive || valueType == typeof(string))
{
holder = new CachePrimitive();
}
else if (type.IsEnum)
else if (valueType.IsEnum)
{
holder = new CacheEnum();
}
else if (typeof(System.Collections.IEnumerable).IsAssignableFrom(type) || ReflectionHelpers.IsList(type))
else if (typeof(System.Collections.IEnumerable).IsAssignableFrom(valueType) || ReflectionHelpers.IsList(valueType))
{
holder = new CacheList();
}
@ -117,31 +117,37 @@ namespace Explorer
}
holder.Value = obj;
holder.ValueType = type.FullName;
holder.ValueType = valueType.FullName;
if (memberInfo != null)
{
holder.MemberInfo = memberInfo;
holder.DeclaringType = memberInfo.DeclaringType;
holder.DeclaringInstance = declaringInstance;
holder.UpdateValue();
}
holder.UpdateValue();
holder.Init();
return holder;
}
private const float MAX_WIDTH = 400f;
public const float MAX_LABEL_WIDTH = 400f;
public static void ClampLabelWidth(Rect window, ref float labelWidth)
{
float min = window.width * 0.37f;
if (min > MAX_LABEL_WIDTH) min = MAX_LABEL_WIDTH;
labelWidth = Mathf.Clamp(labelWidth, min, MAX_LABEL_WIDTH);
}
public void Draw(Rect window, float labelWidth = 215f)
{
if (labelWidth > 0)
{
float min = (window.width * 0.37f);
if (min > MAX_WIDTH) min = MAX_WIDTH;
labelWidth = Mathf.Clamp(labelWidth, min, MAX_WIDTH);
ClampLabelWidth(window, ref labelWidth);
}
if (MemberInfo != null)

View File

@ -111,14 +111,20 @@ namespace Explorer
{
GUILayout.Label("<color=yellow><i>" + PrimitiveType + "</i></color>", new GUILayoutOption[] { GUILayout.Width(50) });
var _width = window.width - 200;
if (m_valueToString.Length > 37)
//var content = new GUIContent(m_valueToString);
//var contentSize = GUI.skin.textField.CalcSize(content);
int dynSize = 25 + (m_valueToString.Length * 15);
var maxwidth = window.width - 300f;
if (CanWrite) maxwidth -= 60;
if (dynSize > maxwidth)
{
m_valueToString = GUILayout.TextArea(m_valueToString, new GUILayoutOption[] { GUILayout.MaxWidth(_width) });
m_valueToString = GUILayout.TextArea(m_valueToString, new GUILayoutOption[] { GUILayout.MaxWidth(maxwidth) });
}
else
{
m_valueToString = GUILayout.TextField(m_valueToString, new GUILayoutOption[] { GUILayout.MaxWidth(_width) });
m_valueToString = GUILayout.TextField(m_valueToString, new GUILayoutOption[] { GUILayout.MaxWidth(dynSize) });
}
if (CanWrite)
@ -128,6 +134,8 @@ namespace Explorer
SetValue(m_valueToString);
}
}
GUILayout.Space(5);
}
}

View File

@ -133,6 +133,7 @@
<Compile Include="Helpers\UnityHelpers.cs" />
<Compile Include="MainMenu\InspectUnderMouse.cs" />
<Compile Include="CachedObjects\CacheObject.cs" />
<Compile Include="Windows\ResizeDrag.cs" />
<Compile Include="Windows\UIWindow.cs" />
<Compile Include="MainMenu\Pages\ConsolePage.cs" />
<Compile Include="MainMenu\Pages\Console\REPL.cs" />

View File

@ -81,7 +81,7 @@ namespace Explorer
page.DrawWindow();
GUILayout.EndScrollView();
MainRect = WindowManager.ResizeWindow(MainRect, MainWindowID);
MainRect = ResizeDrag.ResizeWindow(MainRect, MainWindowID);
GUILayout.EndArea();
}

View File

@ -6,12 +6,13 @@ using MelonLoader;
using UnhollowerRuntimeLib;
using UnityEngine;
using UnityEngine.SceneManagement;
using ComponentList = Il2CppSystem.Collections.Generic.List<UnityEngine.Component>;
namespace Explorer
{
public class GameObjectWindow : UIWindow
{
public override string Name { get => "GameObject Inspector"; set => Name = value; }
public override string Name { get => $"GameObject Inspector ({m_object.name})"; }
public GameObject m_object;
@ -21,18 +22,18 @@ namespace Explorer
private Vector2 m_transformScroll = Vector2.zero;
private Transform[] m_children;
private ComponentList m_components;
private Vector2 m_compScroll = Vector2.zero;
//private Component[] m_components;
private float m_translateAmount = 0.3f;
private float m_rotateAmount = 50f;
private float m_scaleAmount = 0.1f;
private List<Component> m_cachedDestroyList = new List<Component>();
private readonly List<Component> m_cachedDestroyList = new List<Component>();
//private string m_addComponentInput = "";
private string m_setParentInput = "";
private string m_setParentInput = "Enter a GameObject name or path";
public bool GetObjectAsGameObject()
{
@ -84,11 +85,26 @@ namespace Explorer
public override void Update()
{
if (!m_object && !GetObjectAsGameObject())
try
{
MelonLogger.Log("Object is null! Destroying window...");
DestroyWindow();
if (!m_object && !GetObjectAsGameObject())
{
throw new Exception("Object is null!");
}
m_components = new Il2CppSystem.Collections.Generic.List<Component>();
m_object.GetComponentsInternal(ReflectionHelpers.ComponentType, false, false, true, false, m_components);
}
catch (Exception e)
{
DestroyOnException(e);
}
}
private void DestroyOnException(Exception e)
{
MelonLogger.Log($"{e.GetType()}, {e.Message}");
DestroyWindow();
}
private void InspectGameObject(Transform obj)
@ -125,67 +141,74 @@ namespace Explorer
public override void WindowFunction(int windowID)
{
Header();
GUILayout.BeginArea(new Rect(5, 25, m_rect.width - 10, m_rect.height - 35), GUI.skin.box);
scroll = GUILayout.BeginScrollView(scroll, GUI.skin.scrollView);
GUILayout.BeginHorizontal(null);
GUILayout.Label("Scene: <color=cyan>" + (m_scene == "" ? "n/a" : m_scene) + "</color>", null);
if (m_scene == UnityHelpers.ActiveSceneName)
try
{
if (GUILayout.Button("<color=#00FF00>< View in Scene Explorer</color>", new GUILayoutOption[] { GUILayout.Width(230) }))
{
ScenePage.Instance.SetTransformTarget(m_object.transform);
MainMenu.SetCurrentPage(0);
}
}
GUILayout.EndHorizontal();
Header();
GUILayout.BeginHorizontal(null);
GUILayout.Label("Path:", new GUILayoutOption[] { GUILayout.Width(50) });
string pathlabel = m_object.transform.GetGameObjectPath();
if (m_object.transform.parent != null)
GUILayout.BeginArea(new Rect(5, 25, m_rect.width - 10, m_rect.height - 35), GUI.skin.box);
scroll = GUILayout.BeginScrollView(scroll, GUI.skin.scrollView);
GUILayout.BeginHorizontal(null);
GUILayout.Label("Scene: <color=cyan>" + (m_scene == "" ? "n/a" : m_scene) + "</color>", null);
if (m_scene == UnityHelpers.ActiveSceneName)
{
if (GUILayout.Button("<color=#00FF00>< View in Scene Explorer</color>", new GUILayoutOption[] { GUILayout.Width(230) }))
{
ScenePage.Instance.SetTransformTarget(m_object.transform);
MainMenu.SetCurrentPage(0);
}
}
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null);
GUILayout.Label("Path:", new GUILayoutOption[] { GUILayout.Width(50) });
string pathlabel = m_object.transform.GetGameObjectPath();
if (m_object.transform.parent != null)
{
if (GUILayout.Button("<-", new GUILayoutOption[] { GUILayout.Width(35) }))
{
InspectGameObject(m_object.transform.parent);
}
}
GUILayout.TextArea(pathlabel, null);
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null);
GUILayout.Label("Name:", new GUILayoutOption[] { GUILayout.Width(50) });
GUILayout.TextArea(m_name, null);
GUILayout.EndHorizontal();
// --- Horizontal Columns section ---
GUILayout.BeginHorizontal(null);
GUILayout.BeginVertical(new GUILayoutOption[] { GUILayout.Width(m_rect.width / 2 - 17) });
TransformList();
GUILayout.EndVertical();
GUILayout.BeginVertical(new GUILayoutOption[] { GUILayout.Width(m_rect.width / 2 - 17) });
ComponentList();
GUILayout.EndVertical();
GUILayout.EndHorizontal(); // end horiz columns
GameObjectControls();
GUILayout.EndScrollView();
m_rect = ResizeDrag.ResizeWindow(m_rect, windowID);
GUILayout.EndArea();
}
catch (Exception e)
{
if (GUILayout.Button("<-", new GUILayoutOption[] { GUILayout.Width(35) }))
{
InspectGameObject(m_object.transform.parent);
}
DestroyOnException(e);
}
GUILayout.TextArea(pathlabel, null);
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null);
GUILayout.Label("Name:", new GUILayoutOption[] { GUILayout.Width(50) });
GUILayout.TextArea(m_name, null);
GUILayout.EndHorizontal();
// --- Horizontal Columns section ---
GUILayout.BeginHorizontal(null);
GUILayout.BeginVertical(new GUILayoutOption[] { GUILayout.Width(m_rect.width / 2 - 17) });
TransformList();
GUILayout.EndVertical();
GUILayout.BeginVertical(new GUILayoutOption[] { GUILayout.Width(m_rect.width / 2 - 17) });
ComponentList();
GUILayout.EndVertical();
GUILayout.EndHorizontal(); // end horiz columns
GameObjectControls();
GUILayout.EndScrollView();
m_rect = WindowManager.ResizeWindow(m_rect, windowID);
GUILayout.EndArea();
}
private void TransformList()
{
GUILayout.BeginVertical(GUI.skin.box, new GUILayoutOption[] { GUILayout.Height(250) });
GUILayout.BeginVertical(GUI.skin.box, null); // new GUILayoutOption[] { GUILayout.Height(250) });
m_transformScroll = GUILayout.BeginScrollView(m_transformScroll, GUI.skin.scrollView);
GUILayout.Label("<b>Children:</b>", null);
@ -222,7 +245,7 @@ namespace Explorer
private void ComponentList()
{
GUILayout.BeginVertical(GUI.skin.box, new GUILayoutOption[] { GUILayout.Height(250) });
GUILayout.BeginVertical(GUI.skin.box, null); // new GUILayoutOption[] { GUILayout.Height(250) });
m_compScroll = GUILayout.BeginScrollView(m_compScroll, GUI.skin.scrollView);
GUILayout.Label("<b><size=15>Components</size></b>", null);
@ -232,11 +255,10 @@ namespace Explorer
m_cachedDestroyList.Clear();
}
var m_components = new Il2CppSystem.Collections.Generic.List<Component>();
m_object.GetComponentsInternal(Il2CppType.Of<Component>(), false, false, true, false, m_components);
foreach (var component in m_components)
{
if (!component) continue;
var ilType = component.GetIl2CppType();
if (ilType == ReflectionHelpers.TransformType)
{
@ -248,6 +270,10 @@ namespace Explorer
{
BehaviourEnabledBtn(component.TryCast<Behaviour>());
}
else
{
GUILayout.Space(26);
}
if (GUILayout.Button("<color=cyan>" + ilType.Name + "</color>", new GUILayoutOption[] { GUILayout.Width(m_rect.width / 2 - 90) }))
{
ReflectObject(component);
@ -259,7 +285,6 @@ namespace Explorer
GUILayout.EndHorizontal();
}
GUI.skin.button.alignment = TextAnchor.MiddleCenter;
if (m_cachedDestroyList.Count > 0)
{
@ -300,7 +325,7 @@ namespace Explorer
private void GameObjectControls()
{
GUILayout.BeginVertical(GUI.skin.box, new GUILayoutOption[] { GUILayout.Width(530) });
GUILayout.BeginVertical(GUI.skin.box, new GUILayoutOption[] { GUILayout.Width(520) });
GUILayout.Label("<b><size=15>GameObject Controls</size></b>", null);
GUILayout.BeginHorizontal(null);
@ -309,17 +334,18 @@ namespace Explorer
new GUILayoutOption[] { GUILayout.Width(80) });
if (m_object.activeSelf != m_active) { m_object.SetActive(m_active); }
UIHelpers.InstantiateButton(m_object, 100);
UIHelpers.InstantiateButton(m_object, 100);
if (GUILayout.Button("Set DontDestroyOnLoad", new GUILayoutOption[] { GUILayout.Width(170) }))
{
GameObject.DontDestroyOnLoad(m_object);
m_object.hideFlags |= HideFlags.DontUnloadUnusedAsset;
}
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null);
if (GUILayout.Button("Remove from parent", new GUILayoutOption[] { GUILayout.Width(160) }))
{
m_object.transform.parent = null;
}
m_setParentInput = GUILayout.TextField(m_setParentInput, new GUILayoutOption[] { GUILayout.Width(m_rect.width - 280) });
m_setParentInput = GUILayout.TextField(m_setParentInput, null);
if (GUILayout.Button("Set Parent", new GUILayoutOption[] { GUILayout.Width(80) }))
{
if (GameObject.Find(m_setParentInput) is GameObject newparent)
@ -331,6 +357,11 @@ namespace Explorer
MelonLogger.LogWarning($"Could not find gameobject '{m_setParentInput}'");
}
}
if (GUILayout.Button("Detach from parent", new GUILayoutOption[] { GUILayout.Width(160) }))
{
m_object.transform.parent = null;
}
GUILayout.EndHorizontal();
GUILayout.BeginVertical(GUI.skin.box, null);
@ -342,7 +373,7 @@ namespace Explorer
GUILayout.EndVertical();
if (GUILayout.Button("<color=red><b>Destroy</b></color>", null))
if (GUILayout.Button("<color=red><b>Destroy</b></color>", new GUILayoutOption[] { GUILayout.Width(120) }))
{
GameObject.Destroy(m_object);
DestroyWindow();

View File

@ -12,7 +12,7 @@ namespace Explorer
{
public class ReflectionWindow : UIWindow
{
public override string Name { get => "Object Reflection"; set => Name = value; }
public override string Name { get => $"Reflection Inspector ({ObjectType.Name})"; }
public Type ObjectType;
@ -272,7 +272,7 @@ namespace Explorer
GUILayout.EndScrollView();
m_rect = WindowManager.ResizeWindow(m_rect, windowID);
m_rect = ResizeDrag.ResizeWindow(m_rect, windowID);
GUILayout.EndArea();
}

65
src/Windows/ResizeDrag.cs Normal file
View File

@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using MelonLoader;
namespace Explorer
{
public class ResizeDrag
{
private static bool RESIZE_FAILED = false;
private static readonly GUIContent gcDrag = new GUIContent("<-- Drag to resize -->");
private static bool isResizing = false;
private static Rect m_currentResize;
private static int m_currentWindow;
public static Rect ResizeWindow(Rect _rect, int ID)
{
if (RESIZE_FAILED) return _rect;
try
{
GUILayout.BeginHorizontal(GUI.skin.box, null);
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
GUILayout.Button(gcDrag, GUI.skin.label, new GUILayoutOption[] { GUILayout.Height(15) });
var r = GUILayoutUtility.GetLastRect();
Vector2 mouse = GUIUtility.ScreenToGUIPoint(new Vector2(Input.mousePosition.x, Screen.height - Input.mousePosition.y));
if (r.Contains(mouse) && Input.GetMouseButtonDown(0))
{
isResizing = true;
m_currentWindow = ID;
m_currentResize = new Rect(mouse.x, mouse.y, _rect.width, _rect.height);
}
else if (!Input.GetMouseButton(0))
{
isResizing = false;
}
if (isResizing && ID == m_currentWindow)
{
_rect.width = Mathf.Max(100, m_currentResize.width + (mouse.x - m_currentResize.x));
_rect.height = Mathf.Max(100, m_currentResize.height + (mouse.y - m_currentResize.y));
_rect.xMax = Mathf.Min(Screen.width, _rect.xMax); // modifying xMax affects width, not x
_rect.yMax = Mathf.Min(Screen.height, _rect.yMax); // modifying yMax affects height, not y
}
GUILayout.EndHorizontal();
}
catch (Exception e)
{
RESIZE_FAILED = true;
MelonLogger.Log("Exception on GuiResize: " + e.GetType() + ", " + e.Message);
}
return _rect;
}
}
}

View File

@ -14,7 +14,7 @@ namespace Explorer
{
public abstract class UIWindow
{
public abstract string Name { get; set; }
public abstract string Name { get; }
public object Target;

View File

@ -110,8 +110,7 @@ namespace Explorer
foreach (var window in Windows)
{
bool equals;
equals = ReferenceEquals(obj, window.Target);
bool equals = ReferenceEquals(obj, window.Target);
if (!equals && uObj != null && window.Target is UnityEngine.Object uTarget)
{
@ -152,55 +151,5 @@ namespace Explorer
return new_window;
}
// ============= Resize Window Helper ============
// static readonly GUIContent gcDrag = new GUIContent("<->", "drag to resize");
private static readonly GUIContent gcDrag = new GUIContent("<->");
private static bool isResizing = false;
private static Rect m_currentResize;
private static int m_currentWindow;
public static Rect ResizeWindow(Rect _rect, int ID)
{
try
{
GUILayout.BeginHorizontal(null);
GUILayout.Space(_rect.width - 35);
GUILayout.Button(gcDrag, GUI.skin.label, new GUILayoutOption[] { GUILayout.Width(25), GUILayout.Height(25) });
var r = GUILayoutUtility.GetLastRect();
Vector2 mouse = GUIUtility.ScreenToGUIPoint(new Vector2(Input.mousePosition.x, Screen.height - Input.mousePosition.y));
if (r.Contains(mouse) && Input.GetMouseButtonDown(0))
{
isResizing = true;
m_currentWindow = ID;
m_currentResize = new Rect(mouse.x, mouse.y, _rect.width, _rect.height);
}
else if (!Input.GetMouseButton(0))
{
isResizing = false;
}
if (isResizing && ID == m_currentWindow)
{
_rect.width = Mathf.Max(100, m_currentResize.width + (mouse.x - m_currentResize.x));
_rect.height = Mathf.Max(100, m_currentResize.height + (mouse.y - m_currentResize.y));
_rect.xMax = Mathf.Min(Screen.width, _rect.xMax); // modifying xMax affects width, not x
_rect.yMax = Mathf.Min(Screen.height, _rect.yMax); // modifying yMax affects height, not y
}
GUILayout.EndHorizontal();
}
catch //(Exception e)
{
//MelonLogger.Log("Exception on GuiResize: " + e.GetType() + ", " + e.Message);
}
return _rect;
}
}
}