InteractiveEnum, InteractiveColor

This commit is contained in:
Sinai 2021-05-08 06:16:43 +10:00
parent d8f532d913
commit d34aeb81b3
5 changed files with 464 additions and 29 deletions

View File

@ -270,11 +270,8 @@ namespace UnityExplorer
try
{
var x = new Il2CppSystem.Int32 { m_value = 5 };
x.BoxIl2CppObject().Unbox<int>();
if (toType.IsEnum)
return Enum.ToObject(toType, Il2CppSystem.Enum.ToUInt64(cppObj));
return Enum.Parse(toType, cppObj.ToString());
var name = toType.AssemblyQualifiedName;
@ -316,12 +313,10 @@ namespace UnityExplorer
return null;
if (type.IsEnum)
return Il2CppSystem.Enum.ToObject(Il2CppType.From(type), (ulong)value);
return Il2CppSystem.Enum.Parse(Il2CppType.From(type), value.ToString());
if (type.IsPrimitive && AllTypes.TryGetValue($"Il2Cpp{type.FullName}", out Type cppType))
{
return BoxIl2CppObject(MakeIl2CppPrimitive(cppType, value), cppType);
}
return BoxIl2CppObject(value, type);
}

View File

@ -0,0 +1,200 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.CacheObject;
namespace UnityExplorer.UI.IValues
{
public class InteractiveColor : InteractiveValue
{
public bool IsValueColor32;
public Color EditedColor;
private Image m_colorImage;
private readonly InputFieldRef[] m_inputs = new InputFieldRef[4];
private readonly Slider[] m_sliders = new Slider[4];
private ButtonRef m_applyButton;
private static readonly string[] fieldNames = new[] { "R", "G", "B", "A" };
public override void OnBorrowed(CacheObjectBase owner)
{
base.OnBorrowed(owner);
m_applyButton.Button.gameObject.SetActive(owner.CanWrite);
foreach (var slider in m_sliders)
slider.interactable = owner.CanWrite;
foreach (var input in m_inputs)
input.InputField.readOnly = !owner.CanWrite;
}
public override void SetValue(object value)
{
OnOwnerSetValue(value);
}
public void SetValueToOwner()
{
if (IsValueColor32)
CurrentOwner.SetUserValue((Color32)EditedColor);
else
CurrentOwner.SetUserValue(EditedColor);
}
private void OnOwnerSetValue(object value)
{
if (value is Color32 c32)
{
IsValueColor32 = true;
EditedColor = c32;
m_inputs[0].Text = c32.r.ToString();
m_inputs[1].Text = c32.g.ToString();
m_inputs[2].Text = c32.b.ToString();
m_inputs[3].Text = c32.a.ToString();
foreach (var slider in m_sliders)
slider.maxValue = 255;
}
else
{
IsValueColor32 = false;
EditedColor = (Color)value;
m_inputs[0].Text = EditedColor.r.ToString();
m_inputs[1].Text = EditedColor.g.ToString();
m_inputs[2].Text = EditedColor.b.ToString();
m_inputs[3].Text = EditedColor.a.ToString();
foreach (var slider in m_sliders)
slider.maxValue = 1;
}
if (m_colorImage)
m_colorImage.color = EditedColor;
}
private void SetColorField(float val, int fieldIndex)
{
switch (fieldIndex)
{
case 0: EditedColor.r = val; break;
case 1: EditedColor.g = val; break;
case 2: EditedColor.b = val; break;
case 3: EditedColor.a = val; break;
}
if (m_colorImage)
m_colorImage.color = EditedColor;
}
private void OnInputChanged(string val, int fieldIndex)
{
try
{
float f;
if (IsValueColor32)
{
byte value = byte.Parse(val);
m_sliders[fieldIndex].value = value;
f = (float)((decimal)value / 255);
}
else
{
f = float.Parse(val);
m_sliders[fieldIndex].value = f;
}
SetColorField(f, fieldIndex);
}
catch (ArgumentException) { } // ignore bad user input
catch (FormatException) { }
catch (OverflowException) { }
catch (Exception ex)
{
ExplorerCore.LogWarning("InteractiveColor OnInput: " + ex.ToString());
}
}
private void OnSliderValueChanged(float val, int fieldIndex)
{
try
{
if (IsValueColor32)
{
m_inputs[fieldIndex].Text = ((byte)val).ToString();
val /= 255f;
}
else
{
m_inputs[fieldIndex].Text = val.ToString();
}
SetColorField(val, fieldIndex);
}
catch (Exception ex)
{
ExplorerCore.LogWarning("InteractiveColor OnSlider: " + ex.ToString());
}
}
// UI Construction
public override GameObject CreateContent(GameObject parent)
{
UIRoot = UIFactory.CreateVerticalGroup(parent, "InteractiveColor", false, false, true, true, 3, new Vector4(4, 4, 4, 4),
new Color(0.06f, 0.06f, 0.06f));
// hori group
var horiGroup = UIFactory.CreateHorizontalGroup(UIRoot, "ColorEditor", false, false, true, true, 5,
default, new Color(1, 1, 1, 0), TextAnchor.MiddleLeft);
// apply button
m_applyButton = UIFactory.CreateButton(horiGroup, "ApplyButton", "Apply", new Color(0.2f, 0.26f, 0.2f));
UIFactory.SetLayoutElement(m_applyButton.Button.gameObject, minHeight: 25, minWidth: 90);
m_applyButton.OnClick += SetValueToOwner;
// sliders / inputs
var grid = UIFactory.CreateGridGroup(horiGroup, "Grid", new Vector2(140, 25), new Vector2(2, 2), new Color(1, 1, 1, 0));
UIFactory.SetLayoutElement(grid, minWidth: 580, minHeight: 25, flexibleWidth: 0);
for (int i = 0; i < 4; i++)
AddEditorRow(i, grid);
// image of color
var imgObj = UIFactory.CreateUIObject("ColorImageHelper", horiGroup);
UIFactory.SetLayoutElement(imgObj, minHeight: 25, minWidth: 50, flexibleWidth: 50);
m_colorImage = imgObj.AddComponent<Image>();
return UIRoot;
}
internal void AddEditorRow(int index, GameObject groupObj)
{
var row = UIFactory.CreateHorizontalGroup(groupObj, "EditorRow_" + fieldNames[index],
false, true, true, true, 5, default, new Color(1, 1, 1, 0));
var label = UIFactory.CreateLabel(row, "RowLabel", $"{fieldNames[index]}:", TextAnchor.MiddleRight, Color.cyan);
UIFactory.SetLayoutElement(label.gameObject, minWidth: 17, flexibleWidth: 0, minHeight: 25);
var input = UIFactory.CreateInputField(row, "Input", "...");
UIFactory.SetLayoutElement(input.UIRoot, minWidth: 40, minHeight: 25, flexibleHeight: 0);
m_inputs[index] = input;
input.OnValueChanged += (string val) => { OnInputChanged(val, index); };
var sliderObj = UIFactory.CreateSlider(row, "Slider", out Slider slider);
m_sliders[index] = slider;
UIFactory.SetLayoutElement(sliderObj, minHeight: 25, minWidth: 70, flexibleWidth: 999, flexibleHeight: 0);
slider.minValue = 0;
slider.maxValue = 1;
slider.onValueChanged.AddListener((float val) => { OnSliderValueChanged(val, index); });
}
}
}

View File

@ -0,0 +1,256 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.CacheObject;
namespace UnityExplorer.UI.IValues
{
public class InteractiveEnum : InteractiveValue
{
public bool IsFlags;
public Type EnumType;
private Type lastType;
public OrderedDictionary CurrentValues;
public CachedEnumValue ValueAtIdx(int idx) => (CachedEnumValue)CurrentValues[idx];
public CachedEnumValue ValueAtKey(object key) => (CachedEnumValue)CurrentValues[key];
private Dropdown enumDropdown;
private GameObject toggleHolder;
private readonly List<Toggle> flagToggles = new List<Toggle>();
private readonly List<Text> flagTexts = new List<Text>();
// Setting value from owner
public override void SetValue(object value)
{
EnumType = value.GetType();
if (lastType != EnumType)
{
CurrentValues = GetEnumValues(EnumType, out IsFlags);
if (IsFlags)
SetupTogglesForEnumType();
else
SetupDropdownForEnumType();
lastType = EnumType;
}
// setup ui for changes
if (IsFlags)
SetTogglesForValue(value);
else
SetDropdownForValue(value);
}
// Setting value to owner
private void OnApplyClicked()
{
if (IsFlags)
SetValueFromFlags();
else
SetValueFromDropdown();
}
private void SetValueFromDropdown()
{
try
{
CurrentOwner.SetUserValue(ValueAtIdx(enumDropdown.value).ActualValue);
}
catch (Exception ex)
{
ExplorerCore.LogWarning("Exception setting from dropdown: " + ex);
}
}
private void SetValueFromFlags()
{
try
{
List<string> values = new List<string>();
for (int i = 0; i < CurrentValues.Count; i++)
{
if (flagToggles[i].isOn)
values.Add(ValueAtIdx(i).Name);
}
CurrentOwner.SetUserValue(Enum.Parse(EnumType, string.Join(", ", values)));
}
catch (Exception ex)
{
ExplorerCore.LogWarning("Exception setting from flag toggles: " + ex);
}
}
// setting UI state for value
private void SetDropdownForValue(object value)
{
if (CurrentValues.Contains(value))
{
var cached = ValueAtKey(value);
enumDropdown.value = cached.EnumIndex;
enumDropdown.RefreshShownValue();
}
else
ExplorerCore.LogWarning("CurrentValues does not contain key '" + value?.ToString() ?? "<null>" + "'");
}
private void SetTogglesForValue(object value)
{
try
{
var split = value.ToString().Split(',');
var set = new HashSet<string>();
foreach (var s in split)
set.Add(s.Trim());
for (int i = 0; i < CurrentValues.Count; i++)
flagToggles[i].isOn = set.Contains(ValueAtIdx(i).Name);
}
catch (Exception ex)
{
ExplorerCore.LogWarning("Exception setting flag toggles: " + ex);
}
}
// Setting up the UI for the enum type when it changes or is first set
private void SetupDropdownForEnumType()
{
toggleHolder.SetActive(false);
enumDropdown.gameObject.SetActive(true);
// create dropdown entries
enumDropdown.options.Clear();
foreach (CachedEnumValue entry in CurrentValues.Values)
enumDropdown.options.Add(new Dropdown.OptionData(entry.Name));
enumDropdown.value = 0;
enumDropdown.RefreshShownValue();
}
private void SetupTogglesForEnumType()
{
toggleHolder.SetActive(true);
enumDropdown.gameObject.SetActive(false);
// create / set / hide toggles
for (int i = 0; i < CurrentValues.Count || i < flagToggles.Count; i++)
{
if (i >= CurrentValues.Count)
{
if (i >= flagToggles.Count)
break;
flagToggles[i].gameObject.SetActive(false);
continue;
}
if (i >= flagToggles.Count)
AddToggleRow();
flagToggles[i].isOn = false;
flagTexts[i].text = ValueAtIdx(i).Name;
}
}
private void AddToggleRow()
{
var row = UIFactory.CreateUIObject("ToggleRow", toggleHolder);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(row, false, false, true, true, 2);
UIFactory.SetLayoutElement(row, minHeight: 25, flexibleWidth: 9999);
var toggleObj = UIFactory.CreateToggle(row, "ToggleObj", out Toggle toggle, out Text toggleText);
UIFactory.SetLayoutElement(toggleObj, minHeight: 25, flexibleWidth: 9999);
flagToggles.Add(toggle);
flagTexts.Add(toggleText);
}
// UI Construction
public override GameObject CreateContent(GameObject parent)
{
UIRoot = UIFactory.CreateVerticalGroup(parent, "InteractiveEnum", false, false, true, true, 3, new Vector4(4, 4, 4, 4),
new Color(0.06f, 0.06f, 0.06f));
UIFactory.SetLayoutElement(UIRoot, minHeight: 25, flexibleHeight: 9999, flexibleWidth: 9999);
var hori = UIFactory.CreateUIObject("Hori", UIRoot);
UIFactory.SetLayoutElement(hori, minHeight: 25, flexibleWidth: 9999);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(hori, false, false, true, true, 2);
var applyButton = UIFactory.CreateButton(hori, "ApplyButton", "Apply", new Color(0.2f, 0.27f, 0.2f));
UIFactory.SetLayoutElement(applyButton.Button.gameObject, minHeight: 25, minWidth: 100);
applyButton.OnClick += OnApplyClicked;
var dropdownObj = UIFactory.CreateDropdown(hori, out enumDropdown, "not set", 14, null);
UIFactory.SetLayoutElement(dropdownObj, minHeight: 25, flexibleWidth: 600);
toggleHolder = UIFactory.CreateUIObject("ToggleHolder", UIRoot);
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(toggleHolder, false, false, true, true, 4);
UIFactory.SetLayoutElement(toggleHolder, minHeight: 25, flexibleWidth: 9999, flexibleHeight: 9999);
return UIRoot;
}
#region Enum cache
public struct CachedEnumValue
{
public CachedEnumValue(object value, int index, string name)
{
EnumIndex = index;
Name = name;
ActualValue = value;
}
public readonly object ActualValue;
public int EnumIndex;
public readonly string Name;
}
internal static readonly Dictionary<string, OrderedDictionary> enumCache = new Dictionary<string, OrderedDictionary>();
internal static OrderedDictionary GetEnumValues(Type enumType, out bool isFlags)
{
isFlags = enumType.GetCustomAttributes(typeof(FlagsAttribute), true) is object[] fa && fa.Any();
if (!enumCache.ContainsKey(enumType.AssemblyQualifiedName))
{
var dict = new OrderedDictionary();
var addedNames = new HashSet<string>();
int i = 0;
foreach (var value in Enum.GetValues(enumType))
{
var name = value.ToString();
if (addedNames.Contains(name))
continue;
addedNames.Add(name);
dict.Add(value, new CachedEnumValue(value, i, name));
i++;
}
enumCache.Add(enumType.AssemblyQualifiedName, dict);
}
return enumCache[enumType.AssemblyQualifiedName];
}
#endregion
}
}

View File

@ -60,7 +60,7 @@ namespace UnityExplorer.UI.IValues
private void OnApplyClicked()
{
CurrentOwner.SetValueFromIValue(EditedValue);
CurrentOwner.SetUserValue(EditedValue);
}
private void OnInputChanged(string input)

View File

@ -17,17 +17,17 @@ namespace UnityExplorer.UI.IValues
{
case ValueState.String:
return typeof(InteractiveString);
//case ValueState.Enum:
// return typeof(InteractiveEnum);
case ValueState.Enum:
return typeof(InteractiveEnum);
case ValueState.Collection:
return typeof(InteractiveList);
case ValueState.Dictionary:
return typeof(InteractiveDictionary);
//case ValueState.ValueStruct:
// return typeof(InteractiveValueStruct);
//case ValueState.Color:
// return typeof(InteractiveColor);
default: return typeof(InteractiveValue);
case ValueState.Color:
return typeof(InteractiveColor);
default: return null;
}
}
@ -63,21 +63,5 @@ namespace UnityExplorer.UI.IValues
public virtual void SetLayout() { }
public abstract GameObject CreateContent(GameObject parent);
//
//public virtual GameObject CreateContent(GameObject parent)
//{
// UIRoot = UIFactory.CreateUIObject(this.GetType().Name, parent);
// UIRoot.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
// UIFactory.SetLayoutGroup<VerticalLayoutGroup>(UIRoot, true, true, true, true, 3, childAlignment: TextAnchor.MiddleLeft);
//
// UIFactory.CreateLabel(UIRoot, "Label", "this is an ivalue", TextAnchor.MiddleLeft);
// UIFactory.CreateInputField(UIRoot, "InputFIeld", "...", out var input);
// UIFactory.SetLayoutElement(input.gameObject, minHeight: 25, flexibleHeight: 500);
// input.lineType = InputField.LineType.MultiLineNewline;
// input.gameObject.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
//
// return UIRoot;
//}
}
}