Made ParseUtility helper to simplify and improve parsing of various input types

This commit is contained in:
Sinai 2021-05-09 01:25:26 +10:00
parent c828d9b642
commit c04a864b74
12 changed files with 512 additions and 106 deletions

View File

@ -92,17 +92,23 @@ namespace UnityExplorer
public static string ReflectionExToString(this Exception e, bool innerMost = true)
{
if (innerMost)
{
while (e.InnerException != null)
{
if (e.InnerException is System.Runtime.CompilerServices.RuntimeWrappedException)
break;
e = e.InnerException;
}
}
e.GetInnerMostException();
return $"{e.GetType()}: {e.Message}";
}
public static Exception GetInnerMostException(this Exception e)
{
while (e.InnerException != null)
{
#if CPP
if (e.InnerException is System.Runtime.CompilerServices.RuntimeWrappedException)
break;
#endif
e = e.InnerException;
}
return e;
}
}
}

View File

@ -275,7 +275,7 @@ namespace UnityExplorer
var name = toType.AssemblyQualifiedName;
if (!unboxMethods.ContainsKey(toType.AssemblyQualifiedName))
if (!unboxMethods.ContainsKey(name))
{
unboxMethods.Add(name, typeof(Il2CppObjectBase)
.GetMethod("Unbox")

View File

@ -182,7 +182,6 @@ namespace UnityExplorer
#endregion
#region Type and Generic Parameter implementation cache
// cache for GetImplementationsOf
@ -302,7 +301,6 @@ namespace UnityExplorer
#endregion
#region Internal MemberInfo Cache
internal static Dictionary<Type, Dictionary<string, FieldInfo>> fieldInfos = new Dictionary<Type, Dictionary<string, FieldInfo>>();

View File

@ -8,14 +8,12 @@ namespace UnityExplorer
{
public static class MiscUtility
{
private static CultureInfo _enCulture = new CultureInfo("en-US");
/// <summary>
/// Check if a string contains another string, case-insensitive.
/// </summary>
public static bool ContainsIgnoreCase(this string _this, string s)
{
return _enCulture.CompareInfo.IndexOf(_this, s, CompareOptions.IgnoreCase) >= 0;
return ParseUtility.en_US.CompareInfo.IndexOf(_this, s, CompareOptions.IgnoreCase) >= 0;
}
/// <summary>

View File

@ -0,0 +1,390 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace UnityExplorer
{
public static class ParseUtility
{
public static CultureInfo en_US = new CultureInfo("en-US");
private static readonly HashSet<Type> nonPrimitiveTypes = new HashSet<Type>
{
typeof(string),
typeof(decimal),
typeof(DateTime),
};
public static bool CanParse(Type type)
{
if (string.IsNullOrEmpty(type.FullName))
return false;
return type.IsPrimitive || nonPrimitiveTypes.Contains(type) || customTypes.ContainsKey(type.FullName);
}
public static bool TryParse(string input, Type type, out object obj, out Exception parseException)
{
obj = null;
parseException = null;
if (type == null)
return false;
if (type == typeof(string))
{
obj = input;
return true;
}
try
{
if (customTypes.ContainsKey(type.FullName))
{
obj = customTypes[type.FullName].Invoke(input);
}
else
{
obj = ReflectionUtility.GetMethodInfo(type, "Parse", ArgumentUtility.ParseArgs)
.Invoke(null, new object[] { input });
}
return true;
}
catch (Exception ex)
{
ex = ex.GetInnerMostException();
parseException = ex;
}
return false;
}
public static string ToStringForInput(object obj, Type type)
{
if (type == null || obj == null)
return null;
if (type == typeof(string))
return obj as string;
try
{
if (customTypes.ContainsKey(type.FullName))
{
return customTypesToString[type.FullName].Invoke(obj);
}
else
{
if (obj is IntPtr ptr)
return ptr.ToString();
else if (obj is UIntPtr uPtr)
return uPtr.ToString();
else
return ReflectionUtility.GetMethodInfo(type, "ToString", new Type[] { typeof(IFormatProvider) })
.Invoke(obj, new object[] { en_US })
as string;
}
}
catch (Exception ex)
{
ExplorerCore.LogWarning($"Exception formatting object for input: {ex}");
return null;
}
}
private static readonly Dictionary<string, string> typeInputExamples = new Dictionary<string, string>();
public static string GetExampleInput(Type type)
{
if (!typeInputExamples.ContainsKey(type.AssemblyQualifiedName))
{
try
{
var instance = Activator.CreateInstance(type);
typeInputExamples.Add(type.AssemblyQualifiedName, ToStringForInput(instance, type));
}
catch (Exception ex)
{
ExplorerCore.LogWarning("Exception generating default instance for example input for '" + type.FullName + "'");
ExplorerCore.Log(ex);
return "";
}
}
return typeInputExamples[type.AssemblyQualifiedName];
}
#region Custom parse methods
internal delegate object ParseMethod(string input);
private static readonly Dictionary<string, ParseMethod> customTypes = new Dictionary<string, ParseMethod>
{
{ typeof(Vector2).FullName, TryParseVector2 },
{ typeof(Vector3).FullName, TryParseVector3 },
{ typeof(Vector4).FullName, TryParseVector4 },
{ typeof(Quaternion).FullName, TryParseQuaternion },
{ typeof(Rect).FullName, TryParseRect },
{ typeof(Color).FullName, TryParseColor },
{ typeof(Color32).FullName, TryParseColor32 },
{ typeof(LayerMask).FullName, TryParseLayerMask },
};
internal delegate string ToStringMethod(object obj);
private static readonly Dictionary<string, ToStringMethod> customTypesToString = new Dictionary<string, ToStringMethod>
{
{ typeof(Vector2).FullName, Vector2ToString },
{ typeof(Vector3).FullName, Vector3ToString },
{ typeof(Vector4).FullName, Vector4ToString },
{ typeof(Quaternion).FullName, QuaternionToString },
{ typeof(Rect).FullName, RectToString },
{ typeof(Color).FullName, ColorToString },
{ typeof(Color32).FullName, Color32ToString },
{ typeof(LayerMask).FullName, LayerMaskToString },
};
// Vector2
public static object TryParseVector2(string input)
{
Vector2 vector = default;
var split = input.Split(',');
vector.x = float.Parse(split[0].Trim(), en_US);
vector.y = float.Parse(split[1].Trim(), en_US);
return vector;
}
public static string Vector2ToString(object obj)
{
if (!(obj is Vector2 vector))
return null;
return string.Format(en_US, "{0}, {1}", new object[]
{
vector.x,
vector.y
});
}
// Vector3
public static object TryParseVector3(string input)
{
Vector3 vector = default;
var split = input.Split(',');
vector.x = float.Parse(split[0].Trim(), en_US);
vector.y = float.Parse(split[1].Trim(), en_US);
vector.z = float.Parse(split[2].Trim(), en_US);
return vector;
}
public static string Vector3ToString(object obj)
{
if (!(obj is Vector3 vector))
return null;
return string.Format(en_US, "{0}, {1}, {2}", new object[]
{
vector.x,
vector.y,
vector.z
});
}
// Vector4
public static object TryParseVector4(string input)
{
Vector4 vector = default;
var split = input.Split(',');
vector.x = float.Parse(split[0].Trim(), en_US);
vector.y = float.Parse(split[1].Trim(), en_US);
vector.z = float.Parse(split[2].Trim(), en_US);
vector.w = float.Parse(split[3].Trim(), en_US);
return vector;
}
public static string Vector4ToString(object obj)
{
if (!(obj is Vector4 vector))
return null;
return string.Format(en_US, "{0}, {1}, {2}, {3}", new object[]
{
vector.x,
vector.y,
vector.z,
vector.w
});
}
// Quaternion
public static object TryParseQuaternion(string input)
{
Vector3 vector = default;
var split = input.Split(',');
if (split.Length == 4)
{
Quaternion quat = default;
quat.x = float.Parse(split[0].Trim(), en_US);
quat.y = float.Parse(split[1].Trim(), en_US);
quat.z = float.Parse(split[2].Trim(), en_US);
quat.w = float.Parse(split[3].Trim(), en_US);
return quat;
}
else
{
vector.x = float.Parse(split[0].Trim(), en_US);
vector.y = float.Parse(split[1].Trim(), en_US);
vector.z = float.Parse(split[2].Trim(), en_US);
return Quaternion.Euler(vector);
}
}
public static string QuaternionToString(object obj)
{
if (!(obj is Quaternion quaternion))
return null;
Vector3 vector = Quaternion.ToEulerAngles(quaternion);
return string.Format(en_US, "{0}, {1}, {2}", new object[]
{
vector.x,
vector.y,
vector.z,
});
}
// Rect
public static object TryParseRect(string input)
{
Rect rect = default;
var split = input.Split(',');
rect.x = float.Parse(split[0].Trim(), en_US);
rect.y = float.Parse(split[1].Trim(), en_US);
rect.width = float.Parse(split[2].Trim(), en_US);
rect.height = float.Parse(split[3].Trim(), en_US);
return rect;
}
public static string RectToString(object obj)
{
if (!(obj is Rect rect))
return null;
return string.Format(en_US, "{0}, {1}, {2}, {3}", new object[]
{
rect.x,
rect.y,
rect.width,
rect.height
});
}
// Color
public static object TryParseColor(string input)
{
Color color = default;
var split = input.Split(',');
color.r = float.Parse(split[0].Trim(), en_US);
color.g = float.Parse(split[1].Trim(), en_US);
color.b = float.Parse(split[2].Trim(), en_US);
if (split.Length > 3)
color.a = float.Parse(split[3].Trim(), en_US);
else
color.a = 1;
return color;
}
public static string ColorToString(object obj)
{
if (!(obj is Color color))
return null;
return string.Format(en_US, "{0}, {1}, {2}, {3}", new object[]
{
color.r,
color.g,
color.b,
color.a
});
}
// Color32
public static object TryParseColor32(string input)
{
Color32 color = default;
var split = input.Split(',');
color.r = byte.Parse(split[0].Trim(), en_US);
color.g = byte.Parse(split[1].Trim(), en_US);
color.b = byte.Parse(split[2].Trim(), en_US);
if (split.Length > 3)
color.a = byte.Parse(split[3].Trim(), en_US);
else
color.a = 255;
return color;
}
public static string Color32ToString(object obj)
{
if (!(obj is Color32 color))
return null;
return string.Format(en_US, "{0}, {1}, {2}, {3}", new object[]
{
color.r,
color.g,
color.b,
color.a
});
}
// Layermask (Int32)
public static object TryParseLayerMask(string input)
{
return (LayerMask)int.Parse(input);
}
public static string LayerMaskToString(object obj)
{
if (!(obj is LayerMask mask))
return null;
return mask.ToString();
}
#endregion
}
}

View File

@ -171,7 +171,7 @@ namespace UnityExplorer.UI.CacheObject
#region Cache Member Util
public static bool CanProcessArgs(ParameterInfo[] parameters)
public static bool CanParseArgs(ParameterInfo[] parameters)
{
foreach (var param in parameters)
{
@ -180,7 +180,7 @@ namespace UnityExplorer.UI.CacheObject
if (pType.IsByRef && pType.HasElementType)
pType = pType.GetElementType();
if (pType != null && (pType.IsPrimitive || pType == typeof(string)))
if (pType != null && ParseUtility.CanParse(pType))
continue;
else
return false;
@ -260,7 +260,7 @@ namespace UnityExplorer.UI.CacheObject
return;
var args = mi.GetParameters();
if (!CanProcessArgs(args))
if (!CanParseArgs(args))
return;
sig += AppendArgsToSig(args);
@ -277,7 +277,7 @@ namespace UnityExplorer.UI.CacheObject
var pi = member as PropertyInfo;
var args = pi.GetIndexParameters();
if (!CanProcessArgs(args))
if (!CanParseArgs(args))
return;
if (!pi.CanRead && pi.CanWrite)

View File

@ -39,6 +39,9 @@ namespace UnityExplorer.UI.CacheObject
public Type FallbackType { get; protected set; }
public bool LastValueWasNull { get; private set; }
public ValueState State = ValueState.NotEvaluated;
public Type LastValueType;
public InteractiveValue IValue { get; private set; }
public Type CurrentIValueType { get; private set; }
public bool SubContentShowWanted { get; private set; }
@ -58,12 +61,6 @@ namespace UnityExplorer.UI.CacheObject
this.ValueLabelText = GetValueLabel();
}
// internals
// private static readonly Dictionary<string, MethodInfo> numberParseMethods = new Dictionary<string, MethodInfo>();
public ValueState State = ValueState.NotEvaluated;
protected const string NOT_YET_EVAL = "<color=grey>Not yet evaluated</color>";
public virtual void ReleasePooledObjects()
@ -127,6 +124,7 @@ namespace UnityExplorer.UI.CacheObject
if (HadException)
{
LastValueWasNull = true;
LastValueType = FallbackType;
State = ValueState.Exception;
}
else if (Value.IsNullOrDestroyed())
@ -158,6 +156,10 @@ namespace UnityExplorer.UI.CacheObject
public ValueState GetStateForType(Type type)
{
if (LastValueType == type)
return State;
LastValueType = type;
if (type == typeof(bool))
return ValueState.Boolean;
else if (type.IsPrimitive || type == typeof(decimal))
@ -184,17 +186,24 @@ namespace UnityExplorer.UI.CacheObject
switch (State)
{
case ValueState.NotEvaluated:
return $"<i>{NOT_YET_EVAL} ({SignatureHighlighter.Parse(FallbackType, true)})</i>";
case ValueState.Exception:
return $"<i><color=red>{LastException.ReflectionExToString()}</color></i>";
// bool and number dont want the label for the value at all
case ValueState.Boolean:
case ValueState.Number:
return null;
case ValueState.NotEvaluated:
return $"<i>{NOT_YET_EVAL} ({SignatureHighlighter.Parse(FallbackType, true)})</i>";
case ValueState.Exception:
return $"<i><color=red>{LastException.ReflectionExToString()}</color></i>";
// and valuestruct also doesnt want it if we can parse it
case ValueState.ValueStruct:
if (ParseUtility.CanParse(LastValueType))
return null;
break;
// string wants it trimmed to max 200 chars
case ValueState.String:
if (!LastValueWasNull)
{
@ -204,7 +213,8 @@ namespace UnityExplorer.UI.CacheObject
return $"\"{s}\"";
}
break;
// try to prefix the count of the collection for lists and dicts
case ValueState.Collection:
if (!LastValueWasNull)
{
@ -256,7 +266,6 @@ namespace UnityExplorer.UI.CacheObject
switch (State)
{
case ValueState.Exception:
//case ValueState.NullValue:
SetValueState(cell, ValueStateArgs.Default);
break;
case ValueState.Boolean:
@ -274,10 +283,15 @@ namespace UnityExplorer.UI.CacheObject
case ValueState.Enum:
SetValueState(cell, new ValueStateArgs(true, subContentButtonActive: CanWrite));
break;
case ValueState.Color:
case ValueState.ValueStruct:
if (ParseUtility.CanParse(LastValueType))
SetValueState(cell, new ValueStateArgs(false, false, null, true, false, true, CanWrite, true, true));
else
SetValueState(cell, new ValueStateArgs(true, inspectActive: true, subContentButtonActive: true));
break;
case ValueState.Collection:
case ValueState.Dictionary:
case ValueState.ValueStruct:
case ValueState.Color:
SetValueState(cell, new ValueStateArgs(true, inspectActive: !LastValueWasNull, subContentButtonActive: !LastValueWasNull));
break;
case ValueState.Unsupported:
@ -303,7 +317,7 @@ namespace UnityExplorer.UI.CacheObject
// Type label (for primitives)
cell.TypeLabel.gameObject.SetActive(args.typeLabelActive);
if (args.typeLabelActive)
cell.TypeLabel.text = SignatureHighlighter.Parse(Value.GetActualType(), false);
cell.TypeLabel.text = SignatureHighlighter.Parse(LastValueType, false);
// toggle for bools
cell.Toggle.gameObject.SetActive(args.toggleActive);
@ -318,7 +332,7 @@ namespace UnityExplorer.UI.CacheObject
cell.InputField.UIRoot.SetActive(args.inputActive);
if (args.inputActive)
{
cell.InputField.Text = Value.ToString();
cell.InputField.Text = ParseUtility.ToStringForInput(Value, LastValueType);
cell.InputField.InputField.readOnly = !CanWrite;
}
@ -342,21 +356,15 @@ namespace UnityExplorer.UI.CacheObject
SetUserValue(this.CellView.Toggle.isOn);
else
{
try
if (ParseUtility.TryParse(CellView.InputField.Text, LastValueType, out object value, out Exception ex))
{
var type = Value.GetType();
var val = ReflectionUtility.GetMethodInfo(type, "Parse", ArgumentUtility.ParseArgs)
.Invoke(null, new object[] { CellView.InputField.Text });
SetUserValue(val);
SetUserValue(value);
}
catch (ArgumentException) { } // ignore bad user input
catch (FormatException) { }
catch (OverflowException) { }
catch (Exception ex)
else
{
ExplorerCore.LogWarning("CacheObjectBase OnCellApplyClicked (number): " + ex.ToString());
ExplorerCore.LogWarning("Unable to parse input!");
if (ex != null)
ExplorerCore.Log(ex.ReflectionExToString());
}
}

View File

@ -155,16 +155,18 @@ namespace UnityExplorer.UI.CacheObject.Views
InputField = UIFactory.CreateInputField(rightHoriGroup, "InputField", "...");
UIFactory.SetLayoutElement(InputField.UIRoot, minWidth: 150, flexibleWidth: 0, minHeight: 25, flexibleHeight: 0);
// Inspect and apply buttons
// Apply
InspectButton = UIFactory.CreateButton(rightHoriGroup, "InspectButton", "Inspect", new Color(0.15f, 0.15f, 0.15f));
UIFactory.SetLayoutElement(InspectButton.Button.gameObject, minWidth: 60, flexibleWidth: 0, minHeight: 25);
InspectButton.OnClick += InspectClicked;
ApplyButton = UIFactory.CreateButton(rightHoriGroup, "ApplyButton", "Apply", new Color(0.15f, 0.15f, 0.15f));
ApplyButton = UIFactory.CreateButton(rightHoriGroup, "ApplyButton", "Apply", new Color(0.15f, 0.19f, 0.15f));
UIFactory.SetLayoutElement(ApplyButton.Button.gameObject, minWidth: 70, minHeight: 25, flexibleWidth: 0, flexibleHeight: 0);
ApplyButton.OnClick += ApplyClicked;
// Inspect
InspectButton = UIFactory.CreateButton(rightHoriGroup, "InspectButton", "Inspect", new Color(0.15f, 0.15f, 0.15f));
UIFactory.SetLayoutElement(InspectButton.Button.gameObject, minWidth: 70, flexibleWidth: 0, minHeight: 25);
InspectButton.OnClick += InspectClicked;
// Main value label
ValueLabel = UIFactory.CreateLabel(rightHoriGroup, "ValueLabel", "Value goes here", TextAnchor.MiddleLeft);

View File

@ -33,7 +33,7 @@ namespace UnityExplorer.UI.CacheObject.Views
private readonly List<Text> genericArgLabels = new List<Text>();
private readonly List<TypeCompleter> genericAutocompleters = new List<TypeCompleter>();
private readonly List<InputFieldRef> inputFieldCache = new List<InputFieldRef>();
private readonly List<InputFieldRef> inputFields = new List<InputFieldRef>();
public void OnBorrowedFromPool(CacheMember owner)
{
@ -52,7 +52,7 @@ namespace UnityExplorer.UI.CacheObject.Views
public void OnReturnToPool()
{
foreach (var input in inputFieldCache)
foreach (var input in inputFields)
input.Text = "";
this.Owner = null;
@ -99,15 +99,11 @@ namespace UnityExplorer.UI.CacheObject.Views
continue;
}
try
if (!ParseUtility.TryParse(input, type, out outArgs[i], out Exception ex))
{
var parse = ReflectionUtility.GetMethodInfo(type, "Parse", ArgumentUtility.ParseArgs);
outArgs[i] = parse.Invoke(null, new object[] { input });
}
catch (Exception ex)
{
ExplorerCore.LogWarning($"Cannot parse argument '{arg.Name}' ({arg.ParameterType.Name}), {ex.GetType().Name}: {ex.Message}");
outArgs[i] = null;
ExplorerCore.LogWarning($"Cannot parse argument '{arg.Name}' ({arg.ParameterType.Name})" +
$"{(ex == null ? "" : $", {ex.GetType().Name}: {ex.Message}")}");
}
}
@ -199,6 +195,15 @@ namespace UnityExplorer.UI.CacheObject.Views
argRows[i].SetActive(true);
argLabels[i].text = $"{SignatureHighlighter.Parse(arg.ParameterType, false)} <color={SignatureHighlighter.LOCAL_ARG}>{arg.Name}</color>";
if (arg.ParameterType == typeof(string))
inputFields[i].PlaceholderText.text = "";
else
{
var elemType = arg.ParameterType;
if (elemType.IsByRef)
elemType = elemType.GetElementType();
inputFields[i].PlaceholderText.text = $"eg. {ParseUtility.GetExampleInput(elemType)}";
}
}
}
@ -228,7 +233,7 @@ namespace UnityExplorer.UI.CacheObject.Views
inputField.InputField.lineType = InputField.LineType.MultiLineNewline;
inputField.UIRoot.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
inputField.OnValueChanged += (string val) => { inputArray[index] = val; };
inputFieldCache.Add(inputField);
inputFields.Add(inputField);
if (autocomplete)
genericAutocompleters.Add(new TypeCompleter(null, inputField));

View File

@ -26,33 +26,31 @@ namespace UnityExplorer.UI.IValues
Fields = fields;
}
public void SetValue(object instance, string value, int fieldIndex)
public void SetValue(object instance, string input, int fieldIndex)
{
try
{
var field = Fields[fieldIndex];
var field = Fields[fieldIndex];
object val;
if (field.FieldType == typeof(string))
val = value;
else
val = ReflectionUtility.GetMethodInfo(field.FieldType, "Parse", ArgumentUtility.ParseArgs)
.Invoke(null, new object[] { value });
field.SetValue(instance, val);
}
catch (FormatException) { ExplorerCore.LogWarning($"Invalid argument '{value}'!"); }
catch (ArgumentException) { ExplorerCore.LogWarning($"Invalid argument '{value}'!"); }
catch (OverflowException) { ExplorerCore.LogWarning($"Invalid argument '{value}'!"); }
catch (Exception ex)
object val;
if (field.FieldType == typeof(string))
val = input;
else
{
ExplorerCore.Log("Excepting setting value '" + value + "'! " + ex);
if (!ParseUtility.TryParse(input, field.FieldType, out val, out Exception ex))
{
ExplorerCore.LogWarning("Unable to parse input!");
if (ex != null) ExplorerCore.Log(ex.ReflectionExToString());
return;
}
}
field.SetValue(instance, val);
}
public string GetValue(object instance, int fieldIndex)
{
return Fields[fieldIndex].GetValue(instance)?.ToString() ?? "";
var field = Fields[fieldIndex];
var value = field.GetValue(instance);
return ParseUtility.ToStringForInput(value, field.FieldType);
}
}
@ -69,19 +67,21 @@ namespace UnityExplorer.UI.IValues
if (typeSupportCache.TryGetValue(type.AssemblyQualifiedName, out var info))
return info.IsSupported;
var supported = true;
var supported = false;
var fields = type.GetFields(INSTANCE_FLAGS);
if (fields.Any(it => !it.FieldType.IsPrimitive && it.FieldType != typeof(string)))
if (fields.Length > 0)
{
supported = false;
info = new StructInfo(supported, null);
}
else
{
supported = true;
info = new StructInfo(supported, fields);
if (fields.Any(it => !ParseUtility.CanParse(it.FieldType)))
{
supported = false;
info = new StructInfo(supported, null);
}
else
{
supported = true;
info = new StructInfo(supported, fields);
}
}
typeSupportCache.Add(type.AssemblyQualifiedName, info);
@ -182,12 +182,12 @@ namespace UnityExplorer.UI.IValues
fieldRows.Add(row);
var label = UIFactory.CreateLabel(row, "Label", "notset", TextAnchor.MiddleRight);
UIFactory.SetLayoutElement(label.gameObject, minHeight: 25, minWidth: 150, flexibleWidth: 0);
UIFactory.SetLayoutElement(label.gameObject, minHeight: 25, minWidth: 175, flexibleWidth: 0);
label.horizontalOverflow = HorizontalWrapMode.Wrap;
labels.Add(label);
var input = UIFactory.CreateInputField(row, "InputField", "...");
UIFactory.SetLayoutElement(input.UIRoot, minHeight: 25, minWidth: 100);
UIFactory.SetLayoutElement(input.UIRoot, minHeight: 25, minWidth: 200);
var fitter = input.UIRoot.AddComponent<ContentSizeFitter>();
fitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
fitter.horizontalFit = ContentSizeFitter.FitMode.PreferredSize;
@ -204,7 +204,7 @@ namespace UnityExplorer.UI.IValues
UIFactory.SetLayoutElement(UIRoot, minHeight: 25, flexibleWidth: 9999);
applyButton = UIFactory.CreateButton(UIRoot, "ApplyButton", "Apply", new Color(0.2f, 0.27f, 0.2f));
UIFactory.SetLayoutElement(applyButton.Button.gameObject, minHeight: 25, minWidth: 100);
UIFactory.SetLayoutElement(applyButton.Button.gameObject, minHeight: 25, minWidth: 175);
applyButton.OnClick += OnApplyClicked;
return UIRoot;

View File

@ -295,14 +295,12 @@ namespace UnityExplorer.UI.Panels
{
// Window Anchors helpers
internal static CultureInfo _enCulture = new CultureInfo("en-US");
internal static string RectAnchorsToString(this RectTransform rect)
{
if (!rect)
throw new ArgumentNullException("rect");
return string.Format(_enCulture, "{0},{1},{2},{3}", new object[]
return string.Format(ParseUtility.en_US, "{0},{1},{2},{3}", new object[]
{
rect.anchorMin.x,
rect.anchorMin.y,
@ -322,10 +320,10 @@ namespace UnityExplorer.UI.Panels
throw new Exception($"stringAnchors split is unexpected length: {split.Length}");
Vector4 anchors;
anchors.x = float.Parse(split[0], _enCulture);
anchors.y = float.Parse(split[1], _enCulture);
anchors.z = float.Parse(split[2], _enCulture);
anchors.w = float.Parse(split[3], _enCulture);
anchors.x = float.Parse(split[0], ParseUtility.en_US);
anchors.y = float.Parse(split[1], ParseUtility.en_US);
anchors.z = float.Parse(split[2], ParseUtility.en_US);
anchors.w = float.Parse(split[3], ParseUtility.en_US);
panel.anchorMin = new Vector2(anchors.x, anchors.y);
panel.anchorMax = new Vector2(anchors.z, anchors.w);
@ -336,7 +334,7 @@ namespace UnityExplorer.UI.Panels
if (!rect)
throw new ArgumentNullException("rect");
return string.Format(_enCulture, "{0},{1}", new object[]
return string.Format(ParseUtility.en_US, "{0},{1}", new object[]
{
rect.localPosition.x, rect.localPosition.y
});
@ -350,8 +348,8 @@ namespace UnityExplorer.UI.Panels
throw new Exception($"stringPosition split is unexpected length: {split.Length}");
Vector3 vector = rect.localPosition;
vector.x = float.Parse(split[0], _enCulture);
vector.y = float.Parse(split[1], _enCulture);
vector.x = float.Parse(split[0], ParseUtility.en_US);
vector.y = float.Parse(split[1], ParseUtility.en_US);
rect.localPosition = vector;
}
}

View File

@ -226,6 +226,7 @@
<Compile Include="Core\Reflection\Il2CppReflection.cs" />
<Compile Include="Core\Utility\ArgumentUtility.cs" />
<Compile Include="Core\Utility\MiscUtility.cs" />
<Compile Include="Core\Utility\ParseUtility.cs" />
<Compile Include="UI\Inspectors\TODO_InspectUnderMouse.cs" />
<Compile Include="UI\CSConsole\CSConsoleManager.cs" />
<Compile Include="UI\CacheObject\CacheField.cs" />