mirror of
https://github.com/GrahamKracker/UnityExplorer.git
synced 2025-01-09 18:48:46 +08:00
InteractiveEnum, InteractiveColor
This commit is contained in:
parent
d8f532d913
commit
d34aeb81b3
@ -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);
|
||||
}
|
||||
|
200
src/UI/IValues/InteractiveColor.cs
Normal file
200
src/UI/IValues/InteractiveColor.cs
Normal 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); });
|
||||
}
|
||||
}
|
||||
}
|
256
src/UI/IValues/InteractiveEnum.cs
Normal file
256
src/UI/IValues/InteractiveEnum.cs
Normal 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
|
||||
}
|
||||
}
|
@ -60,7 +60,7 @@ namespace UnityExplorer.UI.IValues
|
||||
|
||||
private void OnApplyClicked()
|
||||
{
|
||||
CurrentOwner.SetValueFromIValue(EditedValue);
|
||||
CurrentOwner.SetUserValue(EditedValue);
|
||||
}
|
||||
|
||||
private void OnInputChanged(string input)
|
||||
|
@ -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;
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user