Compare commits

..

2 Commits
1.6.5 ... 1.6.6

Author SHA1 Message Date
6ea435deee 1.6.6
* Added better support for Properties with index parameters, can now support multiple parameters and non-int parameters.
* Parameters are now formatted in a more expected fashion (in the `(Type arg0, Type arg1)` format).
* Got rid of all the ugly yellow text.
* Cleaned up some minor GUI display / layout issues.
* Refactored some of CacheMethod into CacheObjectBase
2020-09-09 19:15:47 +10:00
94f749342d Update .gitignore 2020-09-09 00:53:31 +10:00
17 changed files with 346 additions and 225 deletions

1
.gitignore vendored
View File

@ -9,6 +9,7 @@
*.user *.user
*.userosscache *.userosscache
*.sln.docstates *.sln.docstates
*/mcs-unity*
# User-specific files (MonoDevelop/Xamarin Studio) # User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs *.userprefs

View File

@ -97,12 +97,11 @@ CppExplorer can force the mouse to be visible and unlocked when the menu is open
If you'd like to build this yourself, everything you need (other than MelonLoader) is included with this repository, there is no need for recursive cloning etc. If you'd like to build this yourself, everything you need (other than MelonLoader) is included with this repository, there is no need for recursive cloning etc.
1. Install MelonLoader for your game. 1. Install MelonLoader for your game.
2. Download this repository and open the `CppExplorer.sln` file in the `src` folder. 2. Open the `src\CppExplorer.csproj` file in a text editor.
3. In Visual Studio, right-click the CppExplorer C# Project in the solution browser and click "Unload Project". 3. Scroll down until you see the `<ItemGroup>` containing the References.
4. Right-click the "CppExplorer (unloaded)" and select "Edit Project File". 4. Fix all of the paths in the `..\Steam\` directory for your game (use the full path if you need to).
5. Scroll down until you see the `<ItemGroup>` containing the References. 5. Open the `src\CppExplorer.sln` project and build it.
6. Fix all of the paths in the `..\Steam\` directory for your game (use the full path if you need to). 6. The dll is built to the `Release\` folder in the root of the repository.
7. Reload the project and everything should be working, you can now build and run it.
## Credits ## Credits
@ -110,4 +109,4 @@ Written by Sinai.
Thanks to: Thanks to:
* [ManlyMarco](https://github.com/ManlyMarco) for their [Runtime Unity Editor](https://github.com/ManlyMarco/RuntimeUnityEditor), which I used for the REPL Console and the "Find instances" snippet, and the UI style. * [ManlyMarco](https://github.com/ManlyMarco) for their [Runtime Unity Editor](https://github.com/ManlyMarco/RuntimeUnityEditor), which I used for the REPL Console and the "Find instances" snippet, and the UI style.
* [denikson](https://github.com/denikson) for [mcs-unity](https://github.com/denikson/mcs-unity). I commented out the `SkipVisibilityExt` constructor in `mcs.dll` since it was causing an exception with the Hook it attempted. * [denikson](https://github.com/denikson) for [mcs-unity](https://github.com/denikson/mcs-unity). I commented out the `SkipVisibilityExt` constructor since it was causing an exception with the Hook it attempted.

BIN
img.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 449 KiB

After

Width:  |  Height:  |  Size: 524 KiB

View File

@ -19,8 +19,12 @@ namespace Explorer
public MemberInfo MemInfo { get; set; } public MemberInfo MemInfo { get; set; }
public Type DeclaringType { get; set; } public Type DeclaringType { get; set; }
public object DeclaringInstance { get; set; } public object DeclaringInstance { get; set; }
public int PropertyIndex { get; private set; }
private string m_propertyIndexInput = "0"; public bool HasParameters => m_arguments != null && m_arguments.Length > 0;
public bool m_evaluated = false;
public bool m_isEvaluating;
public ParameterInfo[] m_arguments = new ParameterInfo[0];
public string[] m_argumentInput = new string[0];
public string ReflectionException { get; set; } public string ReflectionException { get; set; }
@ -43,7 +47,6 @@ namespace Explorer
// ===== Abstract/Virtual Methods ===== // // ===== Abstract/Virtual Methods ===== //
public virtual void Init() { } public virtual void Init() { }
public abstract void DrawValue(Rect window, float width); public abstract void DrawValue(Rect window, float width);
// ===== Static Methods ===== // // ===== Static Methods ===== //
@ -114,12 +117,21 @@ namespace Explorer
{ {
CacheObjectBase holder; CacheObjectBase holder;
var pi = memberInfo as PropertyInfo;
var mi = memberInfo as MethodInfo;
// if PropertyInfo, check if can process args
if (pi != null && !CanProcessArgs(pi.GetIndexParameters()))
{
return null;
}
// This is pretty ugly, could probably make a cleaner implementation. // This is pretty ugly, could probably make a cleaner implementation.
// However, the only cleaner ways I can think of are slower and probably not worth it. // However, the only cleaner ways I can think of are slower and probably not worth it.
// Note: the order is somewhat important. // Note: the order is somewhat important.
if (memberInfo is MethodInfo mi) if (mi != null)
{ {
if (CacheMethod.CanEvaluate(mi)) if (CacheMethod.CanEvaluate(mi))
{ {
@ -181,7 +193,21 @@ namespace Explorer
holder.MemInfo = memberInfo; holder.MemInfo = memberInfo;
holder.DeclaringType = memberInfo.DeclaringType; holder.DeclaringType = memberInfo.DeclaringType;
holder.DeclaringInstance = declaringInstance; holder.DeclaringInstance = declaringInstance;
}
if (pi != null)
{
holder.m_arguments = pi.GetIndexParameters();
}
else if (mi != null)
{
holder.m_arguments = mi.GetParameters();
}
holder.m_argumentInput = new string[holder.m_arguments.Length];
if (!holder.HasParameters)
{
holder.UpdateValue(); holder.UpdateValue();
} }
@ -190,11 +216,57 @@ namespace Explorer
return holder; return holder;
} }
public static bool CanProcessArgs(ParameterInfo[] parameters)
{
foreach (var param in parameters)
{
if (!param.ParameterType.IsPrimitive && param.ParameterType != typeof(string))
{
return false;
}
}
return true;
}
// ======== Instance Methods ========= // ======== Instance Methods =========
public object[] ParseArguments()
{
var parsedArgs = new List<object>();
for (int i = 0; i < m_arguments.Length; i++)
{
var input = m_argumentInput[i];
var type = m_arguments[i].ParameterType;
if (type == typeof(string))
{
parsedArgs.Add(input);
}
else
{
try
{
parsedArgs.Add(type.GetMethod("Parse", new Type[] { typeof(string) }).Invoke(null, new object[] { input }));
}
catch
{
//MelonLogger.Log($"Unable to parse '{input}' to type '{type.Name}'");
// try add a null arg i guess
parsedArgs.Add(null);
//break;
}
}
}
return parsedArgs.ToArray();
}
public virtual void UpdateValue() public virtual void UpdateValue()
{ {
if (MemInfo == null || !string.IsNullOrEmpty(ReflectionException)) if (MemInfo == null)
{ {
return; return;
} }
@ -209,13 +281,11 @@ namespace Explorer
else if (MemInfo.MemberType == MemberTypes.Property) else if (MemInfo.MemberType == MemberTypes.Property)
{ {
var pi = MemInfo as PropertyInfo; var pi = MemInfo as PropertyInfo;
bool isStatic = pi.GetAccessors()[0].IsStatic; var target = pi.GetAccessors()[0].IsStatic ? null : DeclaringInstance;
var target = isStatic ? null : DeclaringInstance;
if (pi.GetIndexParameters().Length > 0) if (HasParameters)
{ {
var indexes = new object[] { PropertyIndex }; Value = pi.GetValue(target, ParseArguments());
Value = pi.GetValue(target, indexes);
} }
else else
{ {
@ -224,6 +294,8 @@ namespace Explorer
} }
ReflectionException = null; ReflectionException = null;
m_evaluated = true;
m_isEvaluating = false;
} }
catch (Exception e) catch (Exception e)
{ {
@ -244,10 +316,9 @@ namespace Explorer
{ {
var pi = MemInfo as PropertyInfo; var pi = MemInfo as PropertyInfo;
if (pi.GetIndexParameters().Length > 0) if (HasParameters)
{ {
var indexes = new object[] { PropertyIndex }; pi.SetValue(pi.GetAccessors()[0].IsStatic ? null : DeclaringInstance, Value, ParseArguments());
pi.SetValue(pi.GetAccessors()[0].IsStatic ? null : DeclaringInstance, Value, indexes);
} }
else else
{ {
@ -264,6 +335,7 @@ namespace Explorer
// ========= Instance Gui Draw ========== // ========= Instance Gui Draw ==========
public const float MAX_LABEL_WIDTH = 400f; public const float MAX_LABEL_WIDTH = 400f;
public const string EVALUATE_LABEL = "<color=lime>Evaluate</color>";
public static void ClampLabelWidth(Rect window, ref float labelWidth) public static void ClampLabelWidth(Rect window, ref float labelWidth)
{ {
@ -282,19 +354,86 @@ namespace Explorer
if (MemInfo != null) if (MemInfo != null)
{ {
var name = RichTextName; GUILayout.Label(RichTextName, new GUILayoutOption[] { GUILayout.Width(labelWidth) });
if (MemInfo is PropertyInfo pi && pi.GetIndexParameters().Length > 0)
{
name += $"[{PropertyIndex}]";
}
GUILayout.Label(name, new GUILayoutOption[] { GUILayout.Width(labelWidth) });
} }
else else
{ {
GUILayout.Space(labelWidth); GUILayout.Space(labelWidth);
} }
var cm = this as CacheMethod;
if (HasParameters)
{
GUILayout.BeginVertical(null);
if (m_isEvaluating)
{
for (int i = 0; i < m_arguments.Length; i++)
{
var name = m_arguments[i].Name;
var input = m_argumentInput[i];
var type = m_arguments[i].ParameterType.Name;
GUILayout.BeginHorizontal(null);
GUILayout.Label(i.ToString(), new GUILayoutOption[] { GUILayout.Width(30) });
m_argumentInput[i] = GUILayout.TextField(input, new GUILayoutOption[] { GUILayout.Width(150) });
GUILayout.Label("<color=#2df7b2>" + type + "</color> <color=cyan>" + name + "</color>", null);
GUILayout.EndHorizontal();
}
GUILayout.BeginHorizontal(null);
if (cm != null)
{
if (GUILayout.Button(EVALUATE_LABEL, new GUILayoutOption[] { GUILayout.Width(70) }))
{
cm.Evaluate();
}
}
else
{
if (GUILayout.Button(EVALUATE_LABEL, new GUILayoutOption[] { GUILayout.Width(70) }))
{
UpdateValue();
}
}
if (GUILayout.Button("Cancel", new GUILayoutOption[] { GUILayout.Width(70) }))
{
m_isEvaluating = false;
}
GUILayout.EndHorizontal();
}
else
{
if (GUILayout.Button($"Evaluate ({m_arguments.Length} params)", new GUILayoutOption[] { GUILayout.Width(150) }))
{
m_isEvaluating = true;
}
}
GUILayout.EndVertical();
// new line and space
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null);
GUILayout.Space(labelWidth);
}
else if (cm != null)
{
//GUILayout.BeginHorizontal(null);
if (GUILayout.Button(EVALUATE_LABEL, new GUILayoutOption[] { GUILayout.Width(70) }))
{
cm.Evaluate();
}
// new line and space
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null);
GUILayout.Space(labelWidth);
}
if (!string.IsNullOrEmpty(ReflectionException)) if (!string.IsNullOrEmpty(ReflectionException))
{ {
GUILayout.Label("<color=red>Reflection failed!</color> (" + ReflectionException + ")", null); GUILayout.Label("<color=red>Reflection failed!</color> (" + ReflectionException + ")", null);
@ -305,30 +444,6 @@ namespace Explorer
} }
else else
{ {
if (MemInfo is PropertyInfo pi && pi.GetIndexParameters().Length > 0)
{
GUILayout.Label("index:", new GUILayoutOption[] { GUILayout.Width(50) });
m_propertyIndexInput = GUILayout.TextField(m_propertyIndexInput, new GUILayoutOption[] { GUILayout.Width(100) });
if (GUILayout.Button("Set", new GUILayoutOption[] { GUILayout.Width(60) }))
{
if (int.TryParse(m_propertyIndexInput, out int i))
{
PropertyIndex = i;
UpdateValue();
}
else
{
MelonLogger.Log($"Could not parse '{m_propertyIndexInput}' to an int!");
}
}
// new line and space
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null);
GUILayout.Space(labelWidth);
}
DrawValue(window, window.width - labelWidth - 90); DrawValue(window, window.width - labelWidth - 90);
} }
} }
@ -348,15 +463,15 @@ namespace Explorer
m_richTextName = $"<color=#2df7b2>{MemInfo.DeclaringType.Name}</color>.<color={memberColor}>{MemInfo.Name}</color>"; m_richTextName = $"<color=#2df7b2>{MemInfo.DeclaringType.Name}</color>.<color={memberColor}>{MemInfo.Name}</color>";
if (MemInfo is MethodInfo mi) if (m_arguments.Length > 0)
{ {
m_richTextName += "("; m_richTextName += "(";
var _params = ""; var _params = "";
foreach (var param in mi.GetParameters()) foreach (var param in m_arguments)
{ {
if (_params != "") _params += ", "; if (_params != "") _params += ", ";
_params += $"<color=#a6e9e9>{param.Name}</color>"; _params += $"<color=#2df7b2>{param.ParameterType.Name}</color> <color=#a6e9e9>{param.Name}</color>";
} }
m_richTextName += _params; m_richTextName += _params;
m_richTextName += ")"; m_richTextName += ")";

View File

@ -15,7 +15,7 @@ namespace Explorer
{ {
public bool IsExpanded { get; set; } public bool IsExpanded { get; set; }
public float WhiteSpace { get; set; } = 215f; public float WhiteSpace { get; set; } = 215f;
public float ButtonWidthOffset { get; set; } = 290f; public float ButtonWidthOffset { get; set; } = 350f;
public PageHelper Pages = new PageHelper(); public PageHelper Pages = new PageHelper();
@ -152,7 +152,6 @@ namespace Explorer
foreach (var key in IDict.Keys) foreach (var key in IDict.Keys)
{ {
var cache = GetCacheObject(key, TypeOfKeys); var cache = GetCacheObject(key, TypeOfKeys);
cache.UpdateValue();
keys.Add(cache); keys.Add(cache);
} }
@ -160,7 +159,6 @@ namespace Explorer
foreach (var val in IDict.Values) foreach (var val in IDict.Values)
{ {
var cache = GetCacheObject(val, TypeOfValues); var cache = GetCacheObject(val, TypeOfValues);
cache.UpdateValue();
values.Add(cache); values.Add(cache);
} }
@ -170,6 +168,11 @@ namespace Explorer
private bool EnsureDictionaryIsSupported() private bool EnsureDictionaryIsSupported()
{ {
if (typeof(IDictionary).IsAssignableFrom(ValueType))
{
return true;
}
try try
{ {
return Check(TypeOfKeys) && Check(TypeOfValues); return Check(TypeOfKeys) && Check(TypeOfValues);
@ -200,6 +203,12 @@ namespace Explorer
return; return;
} }
float whitespace = WhiteSpace;
if (whitespace > 0)
{
ClampLabelWidth(window, ref whitespace);
}
int count = m_cachedKeys.Length; int count = m_cachedKeys.Length;
if (!IsExpanded) if (!IsExpanded)
@ -217,9 +226,11 @@ namespace Explorer
} }
} }
var negativeWhitespace = window.width - (whitespace + 100f);
GUI.skin.button.alignment = TextAnchor.MiddleLeft; GUI.skin.button.alignment = TextAnchor.MiddleLeft;
string btnLabel = $"<color=yellow>[{count}] Dictionary<{TypeOfKeys.FullName}, {TypeOfValues.FullName}></color>"; string btnLabel = $"<color=#2df7b2>[{count}] Dictionary<{TypeOfKeys.FullName}, {TypeOfValues.FullName}></color>";
if (GUILayout.Button(btnLabel, new GUILayoutOption[] { GUILayout.MaxWidth(window.width - ButtonWidthOffset) })) if (GUILayout.Button(btnLabel, new GUILayoutOption[] { GUILayout.Width(negativeWhitespace) }))
{ {
WindowManager.InspectObject(Value, out bool _); WindowManager.InspectObject(Value, out bool _);
} }
@ -229,12 +240,6 @@ namespace Explorer
if (IsExpanded) if (IsExpanded)
{ {
float whitespace = WhiteSpace;
if (whitespace > 0)
{
ClampLabelWidth(window, ref whitespace);
}
Pages.ItemCount = count; Pages.ItemCount = count;
if (count > Pages.ItemsPerPage) if (count > Pages.ItemsPerPage)

View File

@ -219,7 +219,6 @@ namespace Explorer
if (GetCacheObject(obj, t) is CacheObjectBase cached) if (GetCacheObject(obj, t) is CacheObjectBase cached)
{ {
cached.UpdateValue();
list.Add(cached); list.Add(cached);
} }
else else
@ -246,6 +245,12 @@ namespace Explorer
return; return;
} }
float whitespace = WhiteSpace;
if (whitespace > 0)
{
ClampLabelWidth(window, ref whitespace);
}
int count = m_cachedEntries.Length; int count = m_cachedEntries.Length;
if (!IsExpanded) if (!IsExpanded)
@ -263,9 +268,11 @@ namespace Explorer
} }
} }
var negativeWhitespace = window.width - (whitespace + 100f);
GUI.skin.button.alignment = TextAnchor.MiddleLeft; GUI.skin.button.alignment = TextAnchor.MiddleLeft;
string btnLabel = "<color=yellow>[" + count + "] " + EntryType.FullName + "</color>"; string btnLabel = $"[{count}] <color=#2df7b2>{EntryType.FullName}</color>";
if (GUILayout.Button(btnLabel, new GUILayoutOption[] { GUILayout.MaxWidth(window.width - ButtonWidthOffset) })) if (GUILayout.Button(btnLabel, new GUILayoutOption[] { GUILayout.MaxWidth(negativeWhitespace) }))
{ {
WindowManager.InspectObject(Value, out bool _); WindowManager.InspectObject(Value, out bool _);
} }
@ -275,12 +282,6 @@ namespace Explorer
if (IsExpanded) if (IsExpanded)
{ {
float whitespace = WhiteSpace;
if (whitespace > 0)
{
ClampLabelWidth(window, ref whitespace);
}
Pages.ItemCount = count; Pages.ItemCount = count;
if (count > Pages.ItemsPerPage) if (count > Pages.ItemsPerPage)

View File

@ -11,15 +11,8 @@ namespace Explorer
{ {
public class CacheMethod : CacheObjectBase public class CacheMethod : CacheObjectBase
{ {
private bool m_evaluated = false;
private CacheObjectBase m_cachedReturnValue; private CacheObjectBase m_cachedReturnValue;
private bool m_isEvaluating;
private ParameterInfo[] m_arguments;
private string[] m_argumentInput;
public bool HasParameters => m_arguments != null && m_arguments.Length > 0;
public static bool CanEvaluate(MethodInfo mi) public static bool CanEvaluate(MethodInfo mi)
{ {
// TODO generic args // TODO generic args
@ -29,25 +22,7 @@ namespace Explorer
} }
// primitive and string args supported // primitive and string args supported
foreach (var param in mi.GetParameters()) return CanProcessArgs(mi.GetParameters());
{
if (!param.ParameterType.IsPrimitive && param.ParameterType != typeof(string))
{
return false;
}
}
return true;
}
public override void Init()
{
base.Init();
var mi = MemInfo as MethodInfo;
m_arguments = mi.GetParameters();
m_argumentInput = new string[m_arguments.Length];
} }
public override void UpdateValue() public override void UpdateValue()
@ -55,10 +30,11 @@ namespace Explorer
//base.UpdateValue(); //base.UpdateValue();
} }
private void Evaluate() public void Evaluate()
{ {
var mi = MemInfo as MethodInfo; m_isEvaluating = false;
var mi = MemInfo as MethodInfo;
object ret = null; object ret = null;
if (!HasParameters) if (!HasParameters)
@ -68,38 +44,7 @@ namespace Explorer
} }
else else
{ {
var parsedArgs = new List<object>(); var parsedArgs = ParseArguments();
for (int i = 0; i < m_arguments.Length; i++)
{
var input = m_argumentInput[i];
var type = m_arguments[i].ParameterType;
if (type == typeof(string))
{
parsedArgs.Add(input);
}
else
{
try
{
if (type.GetMethod("Parse", new Type[] { typeof(string) }).Invoke(null, new object[] { input }) is object parsed)
{
parsedArgs.Add(parsed);
}
else
{
// try add a null arg i guess
parsedArgs.Add(null);
}
}
catch
{
MelonLogger.Log($"Unable to parse '{input}' to type '{type.Name}'");
break;
}
}
}
try try
{ {
@ -115,13 +60,6 @@ namespace Explorer
if (ret != null) if (ret != null)
{ {
m_cachedReturnValue = GetCacheObject(ret); m_cachedReturnValue = GetCacheObject(ret);
if (m_cachedReturnValue is IExpandHeight expander)
{
expander.WhiteSpace = 0f;
expander.ButtonWidthOffset += 70f;
}
m_cachedReturnValue.UpdateValue(); m_cachedReturnValue.UpdateValue();
} }
else else
@ -134,58 +72,6 @@ namespace Explorer
public override void DrawValue(Rect window, float width) public override void DrawValue(Rect window, float width)
{ {
GUILayout.BeginVertical(null);
string evaluateLabel = "<color=lime>Evaluate</color>";
if (HasParameters)
{
if (m_isEvaluating)
{
for (int i = 0; i < m_arguments.Length; i++)
{
var name = m_arguments[i].Name;
var input = m_argumentInput[i];
var type = m_arguments[i].ParameterType.Name;
GUILayout.BeginHorizontal(null);
m_argumentInput[i] = GUILayout.TextField(input, new GUILayoutOption[] { GUILayout.Width(150) });
GUILayout.Label(i + ": <color=cyan>" + name + "</color> <color=yellow>(" + type + ")</color>", null);
GUILayout.EndHorizontal();
}
GUILayout.BeginHorizontal(null);
if (GUILayout.Button(evaluateLabel, new GUILayoutOption[] { GUILayout.Width(70) }))
{
Evaluate();
m_isEvaluating = false;
}
if (GUILayout.Button("Cancel", new GUILayoutOption[] { GUILayout.Width(70) }))
{
m_isEvaluating = false;
}
}
else
{
GUILayout.BeginHorizontal(null);
if (GUILayout.Button($"Evaluate ({m_arguments.Length} params)", new GUILayoutOption[] { GUILayout.Width(150) }))
{
m_isEvaluating = true;
}
}
}
else
{
GUILayout.BeginHorizontal(null);
if (GUILayout.Button(evaluateLabel, new GUILayoutOption[] { GUILayout.Width(70) }))
{
Evaluate();
}
}
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null);
if (m_evaluated) if (m_evaluated)
{ {
if (m_cachedReturnValue != null) if (m_cachedReturnValue != null)
@ -194,16 +80,13 @@ namespace Explorer
} }
else else
{ {
GUILayout.Label($"null (<color=yellow>{ValueTypeName}</color>)", null); GUILayout.Label($"null (<color=#2df7b2>{ValueTypeName}</color>)", null);
} }
} }
else else
{ {
GUILayout.Label($"<color=grey><i>Not yet evaluated</i></color> (<color=yellow>{ValueTypeName}</color>)", null); GUILayout.Label($"<color=grey><i>Not yet evaluated</i></color> (<color=#2df7b2>{ValueTypeName}</color>)", null);
} }
GUILayout.EndHorizontal();
GUILayout.EndVertical();
} }
} }
} }

View File

@ -42,15 +42,20 @@ namespace Explorer
if (!label.Contains(ValueTypeName)) if (!label.Contains(ValueTypeName))
{ {
label += $" ({ValueTypeName})"; label += $" (<color=#2df7b2>{ValueTypeName}</color>)";
} }
else
{
label = label.Replace(ValueTypeName, $"<color=#2df7b2>{ValueTypeName}</color>");
}
if (Value is UnityEngine.Object unityObj && !label.Contains(unityObj.name)) if (Value is UnityEngine.Object unityObj && !label.Contains(unityObj.name))
{ {
label = unityObj.name + " | " + label; label = unityObj.name + " | " + label;
} }
GUI.skin.button.alignment = TextAnchor.MiddleLeft; GUI.skin.button.alignment = TextAnchor.MiddleLeft;
if (GUILayout.Button("<color=yellow>" + label + "</color>", new GUILayoutOption[] { GUILayout.Width(width - 15) })) if (GUILayout.Button(label, new GUILayoutOption[] { GUILayout.Width(width - 15) }))
{ {
WindowManager.InspectObject(Value, out bool _); WindowManager.InspectObject(Value, out bool _);
} }

View File

@ -7,15 +7,17 @@ using UnityEngine;
namespace Explorer namespace Explorer
{ {
public class CacheColor : CacheObjectBase public class CacheColor : CacheObjectBase, IExpandHeight
{ {
private bool IsExpanded;
private string r = "0"; private string r = "0";
private string g = "0"; private string g = "0";
private string b = "0"; private string b = "0";
private string a = "0"; private string a = "0";
public bool IsExpanded { get; set; }
public float WhiteSpace { get; set; } = 215f;
public float ButtonWidthOffset { get; set; } = 290f;
public override void UpdateValue() public override void UpdateValue()
{ {
base.UpdateValue(); base.UpdateValue();
@ -50,13 +52,18 @@ namespace Explorer
var c = (Color)Value; var c = (Color)Value;
GUI.color = c; GUI.color = c;
GUILayout.Label($"<color=yellow>Color:</color> {c.ToString()}", null); GUILayout.Label($"<color=#2df7b2>Color:</color> {c.ToString()}", null);
GUI.color = Color.white; GUI.color = Color.white;
if (CanWrite && IsExpanded) if (CanWrite && IsExpanded)
{ {
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
var whitespace = window.width - width - 90;
float whitespace = WhiteSpace;
if (whitespace > 0)
{
ClampLabelWidth(window, ref whitespace);
}
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(null);
GUILayout.Space(whitespace); GUILayout.Space(whitespace);

View File

@ -51,7 +51,7 @@ namespace Explorer
} }
} }
GUILayout.Label(Value.ToString() + "<color=yellow><i> (" + ValueType + ")</i></color>", null); GUILayout.Label(Value.ToString() + "<color=#2df7b2><i> (" + ValueType + ")</i></color>", null);
} }
public void SetEnum(ref object value, int change) public void SetEnum(ref object value, int change)

View File

@ -69,10 +69,10 @@ namespace Explorer
else else
{ {
// using ValueType.Name instead of ValueTypeName, because we only want the short name. // using ValueType.Name instead of ValueTypeName, because we only want the short name.
GUILayout.Label("<color=yellow><i>" + ValueType.Name + "</i></color>", new GUILayoutOption[] { GUILayout.Width(50) }); GUILayout.Label("<color=#2df7b2><i>" + ValueType.Name + "</i></color>", new GUILayoutOption[] { GUILayout.Width(50) });
int dynSize = 25 + (m_valueToString.Length * 15); int dynSize = 25 + (m_valueToString.Length * 15);
var maxwidth = window.width - 300f; var maxwidth = window.width - 310f;
if (CanWrite) maxwidth -= 60; if (CanWrite) maxwidth -= 60;
if (dynSize > maxwidth) if (dynSize > maxwidth)
@ -92,7 +92,7 @@ namespace Explorer
} }
} }
GUILayout.Space(5); GUILayout.Space(10);
} }
} }

View File

@ -7,14 +7,16 @@ using UnityEngine;
namespace Explorer namespace Explorer
{ {
public class CacheQuaternion : CacheObjectBase public class CacheQuaternion : CacheObjectBase, IExpandHeight
{ {
private bool IsExpanded;
private string x = "0"; private string x = "0";
private string y = "0"; private string y = "0";
private string z = "0"; private string z = "0";
public bool IsExpanded { get; set; }
public float WhiteSpace { get; set; } = 215f;
public float ButtonWidthOffset { get; set; } = 290f;
public override void UpdateValue() public override void UpdateValue()
{ {
base.UpdateValue(); base.UpdateValue();
@ -46,12 +48,17 @@ namespace Explorer
} }
} }
GUILayout.Label($"<color=yellow>Quaternion</color>: {((Quaternion)Value).eulerAngles.ToString()}", null); GUILayout.Label($"<color=#2df7b2>Quaternion</color>: {((Quaternion)Value).eulerAngles.ToString()}", null);
if (CanWrite && IsExpanded) if (CanWrite && IsExpanded)
{ {
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
var whitespace = window.width - width - 90;
float whitespace = WhiteSpace;
if (whitespace > 0)
{
ClampLabelWidth(window, ref whitespace);
}
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(null);
GUILayout.Space(whitespace); GUILayout.Space(whitespace);

View File

@ -7,15 +7,17 @@ using UnityEngine;
namespace Explorer namespace Explorer
{ {
public class CacheRect : CacheObjectBase public class CacheRect : CacheObjectBase, IExpandHeight
{ {
private bool IsExpanded;
private string x = "0"; private string x = "0";
private string y = "0"; private string y = "0";
private string w = "0"; private string w = "0";
private string h = "0"; private string h = "0";
public bool IsExpanded { get; set; }
public float WhiteSpace { get; set; } = 215f;
public float ButtonWidthOffset { get; set; } = 290f;
public override void UpdateValue() public override void UpdateValue()
{ {
base.UpdateValue(); base.UpdateValue();
@ -48,12 +50,17 @@ namespace Explorer
} }
} }
GUILayout.Label($"<color=yellow>Rect</color>: {((Rect)Value).ToString()}", null); GUILayout.Label($"<color=#2df7b2>Rect</color>: {((Rect)Value).ToString()}", null);
if (CanWrite && IsExpanded) if (CanWrite && IsExpanded)
{ {
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
var whitespace = window.width - width - 90;
float whitespace = WhiteSpace;
if (whitespace > 0)
{
ClampLabelWidth(window, ref whitespace);
}
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(null);
GUILayout.Space(whitespace); GUILayout.Space(whitespace);

View File

@ -8,10 +8,8 @@ using UnityEngine;
namespace Explorer namespace Explorer
{ {
public class CacheVector : CacheObjectBase public class CacheVector : CacheObjectBase, IExpandHeight
{ {
private bool IsExpanded;
public int VectorSize = 2; public int VectorSize = 2;
private string x = "0"; private string x = "0";
@ -21,6 +19,10 @@ namespace Explorer
private MethodInfo m_toStringMethod; private MethodInfo m_toStringMethod;
public bool IsExpanded { get; set; }
public float WhiteSpace { get; set; } = 215f;
public float ButtonWidthOffset { get; set; } = 290f;
public override void Init() public override void Init()
{ {
if (Value is Vector2) if (Value is Vector2)
@ -83,12 +85,16 @@ namespace Explorer
} }
} }
GUILayout.Label($"<color=yellow>Vector{VectorSize}</color>: {(string)m_toStringMethod.Invoke(Value, new object[0])}", null); GUILayout.Label($"<color=#2df7b2>Vector{VectorSize}</color>: {(string)m_toStringMethod.Invoke(Value, new object[0])}", null);
if (CanWrite && IsExpanded) if (CanWrite && IsExpanded)
{ {
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
var whitespace = window.width - width - 90; float whitespace = WhiteSpace;
if (whitespace > 0)
{
ClampLabelWidth(window, ref whitespace);
}
// always draw x and y // always draw x and y
GUILayout.BeginHorizontal(null); GUILayout.BeginHorizontal(null);

View File

@ -88,6 +88,7 @@
<Compile Include="CppExplorer.cs" /> <Compile Include="CppExplorer.cs" />
<Compile Include="Extensions\ReflectionExtensions.cs" /> <Compile Include="Extensions\ReflectionExtensions.cs" />
<Compile Include="Helpers\InputHelper.cs" /> <Compile Include="Helpers\InputHelper.cs" />
<Compile Include="Tests\TestClass.cs" />
<Compile Include="UnstripFixes\GUIUnstrip.cs" /> <Compile Include="UnstripFixes\GUIUnstrip.cs" />
<Compile Include="UnstripFixes\ScrollViewStateUnstrip.cs" /> <Compile Include="UnstripFixes\ScrollViewStateUnstrip.cs" />
<Compile Include="Extensions\UnityExtensions.cs" /> <Compile Include="Extensions\UnityExtensions.cs" />

74
src/Tests/TestClass.cs Normal file
View File

@ -0,0 +1,74 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MelonLoader;
using UnityEngine;
namespace Explorer.Tests
{
public class TestClass
{
public static TestClass Instance => m_instance ?? (m_instance = new TestClass());
private static TestClass m_instance;
public string this[int index]
{
get
{
return $"int indexer: {index}";
}
}
public string this[string stringIndex]
{
get
{
return $"string indexer: {stringIndex}";
}
}
public string this[int arg0, string arg1]
{
get
{
return $"arg0: {arg0}, arg1: {arg1}";
}
}
public static List<string> TestList = new List<string>
{
"1",
"2",
"3",
"etc..."
};
public static Dictionary<int, List<string>> NestedDictionary = new Dictionary<int, List<string>>
{
{
123,
new List<string>
{
"One",
"Two"
}
},
{
567,
new List<string>
{
"One",
"Two"
}
},
};
public static Color TestMethod(float r, float g, float b, float a)
{
return new Color(r, g, b, a);
}
}
}

View File

@ -162,10 +162,20 @@ namespace Explorer
name += " ("; name += " (";
foreach (var param in mi.GetParameters()) foreach (var param in mi.GetParameters())
{ {
name += param.ParameterType.Name + ", "; name += $"{param.ParameterType.Name} {param.Name}, ";
} }
name += ")"; name += ")";
} }
else if (member is PropertyInfo pi)
{
name += " (";
foreach (var param in pi.GetIndexParameters())
{
name += $"{param.ParameterType.Name} {param.Name}, ";
}
name += ")";
}
if (names.Contains(name)) if (names.Contains(name))
{ {
continue; continue;