Skip to content
This repository was archived by the owner on May 9, 2023. It is now read-only.

Commit 7920c54

Browse files
committed
added InteractiveUnityStruct, thats the end of the interactive values for now
1 parent fd50996 commit 7920c54

File tree

4 files changed

+342
-7
lines changed

4 files changed

+342
-7
lines changed
Lines changed: 332 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,332 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using UnityEngine;
6+
using UnityEngine.UI;
7+
using UnityExplorer.Helpers;
8+
using UnityExplorer.UI;
9+
10+
namespace UnityExplorer.Inspectors.Reflection
11+
{
12+
#region IStructInfo helper
13+
14+
public interface IStructInfo
15+
{
16+
string[] FieldNames { get; }
17+
object SetValue(ref object value, int fieldIndex, float val);
18+
void RefreshUI(InputField[] inputs, object value);
19+
}
20+
21+
public class StructInfo<T> : IStructInfo where T : struct
22+
{
23+
public string[] FieldNames { get; set; }
24+
25+
public delegate void SetMethod(ref T value, int fieldIndex, float val);
26+
public SetMethod SetValueMethod;
27+
28+
public delegate void UpdateMethod(InputField[] inputs, object value);
29+
public UpdateMethod UpdateUIMethod;
30+
31+
public object SetValue(ref object value, int fieldIndex, float val)
32+
{
33+
var box = (T)value;
34+
SetValueMethod.Invoke(ref box, fieldIndex, val);
35+
return box;
36+
}
37+
38+
public void RefreshUI(InputField[] inputs, object value)
39+
{
40+
UpdateUIMethod.Invoke(inputs, value);
41+
}
42+
}
43+
44+
// This part is a bit ugly, but everything else is generalized above.
45+
// I could generalize it more with reflection, but it would be different for
46+
// mono/il2cpp and also slower.
47+
public static class StructInfoFactory
48+
{
49+
public static IStructInfo Create(Type type)
50+
{
51+
if (type == typeof(Vector2))
52+
{
53+
return new StructInfo<Vector2>()
54+
{
55+
FieldNames = new[] { "x", "y", },
56+
SetValueMethod = (ref Vector2 vec, int fieldIndex, float val) =>
57+
{
58+
switch (fieldIndex)
59+
{
60+
case 0: vec.x = val; break;
61+
case 1: vec.y = val; break;
62+
}
63+
},
64+
UpdateUIMethod = (InputField[] inputs, object value) =>
65+
{
66+
Vector2 vec = (Vector2)value;
67+
inputs[0].text = vec.x.ToString();
68+
inputs[1].text = vec.y.ToString();
69+
}
70+
};
71+
}
72+
else if (type == typeof(Vector3))
73+
{
74+
return new StructInfo<Vector3>()
75+
{
76+
FieldNames = new[] { "x", "y", "z" },
77+
SetValueMethod = (ref Vector3 vec, int fieldIndex, float val) =>
78+
{
79+
switch (fieldIndex)
80+
{
81+
case 0: vec.x = val; break;
82+
case 1: vec.y = val; break;
83+
case 2: vec.z = val; break;
84+
}
85+
},
86+
UpdateUIMethod = (InputField[] inputs, object value) =>
87+
{
88+
Vector3 vec = (Vector3)value;
89+
inputs[0].text = vec.x.ToString();
90+
inputs[1].text = vec.y.ToString();
91+
inputs[2].text = vec.z.ToString();
92+
}
93+
};
94+
}
95+
else if (type == typeof(Vector4))
96+
{
97+
return new StructInfo<Vector4>()
98+
{
99+
FieldNames = new[] { "x", "y", "z", "w" },
100+
SetValueMethod = (ref Vector4 vec, int fieldIndex, float val) =>
101+
{
102+
switch (fieldIndex)
103+
{
104+
case 0: vec.x = val; break;
105+
case 1: vec.y = val; break;
106+
case 2: vec.z = val; break;
107+
case 3: vec.w = val; break;
108+
}
109+
},
110+
UpdateUIMethod = (InputField[] inputs, object value) =>
111+
{
112+
Vector4 vec = (Vector4)value;
113+
inputs[0].text = vec.x.ToString();
114+
inputs[1].text = vec.y.ToString();
115+
inputs[2].text = vec.z.ToString();
116+
inputs[3].text = vec.w.ToString();
117+
}
118+
};
119+
}
120+
else if (type == typeof(Rect))
121+
{
122+
return new StructInfo<Rect>()
123+
{
124+
FieldNames = new[] { "x", "y", "width", "height" },
125+
SetValueMethod = (ref Rect vec, int fieldIndex, float val) =>
126+
{
127+
switch (fieldIndex)
128+
{
129+
case 0: vec.x = val; break;
130+
case 1: vec.y = val; break;
131+
case 2: vec.width = val; break;
132+
case 3: vec.height = val; break;
133+
}
134+
},
135+
UpdateUIMethod = (InputField[] inputs, object value) =>
136+
{
137+
Rect vec = (Rect)value;
138+
inputs[0].text = vec.x.ToString();
139+
inputs[1].text = vec.y.ToString();
140+
inputs[2].text = vec.width.ToString();
141+
inputs[3].text = vec.height.ToString();
142+
}
143+
};
144+
}
145+
else if (type == typeof(Color))
146+
{
147+
return new StructInfo<Color>()
148+
{
149+
FieldNames = new[] { "r", "g", "b", "a" },
150+
SetValueMethod = (ref Color vec, int fieldIndex, float val) =>
151+
{
152+
switch (fieldIndex)
153+
{
154+
case 0: vec.r = val; break;
155+
case 1: vec.g = val; break;
156+
case 2: vec.b = val; break;
157+
case 3: vec.a = val; break;
158+
}
159+
},
160+
UpdateUIMethod = (InputField[] inputs, object value) =>
161+
{
162+
Color vec = (Color)value;
163+
inputs[0].text = vec.r.ToString();
164+
inputs[1].text = vec.g.ToString();
165+
inputs[2].text = vec.b.ToString();
166+
inputs[3].text = vec.a.ToString();
167+
}
168+
};
169+
}
170+
else
171+
throw new NotImplementedException();
172+
}
173+
}
174+
175+
#endregion
176+
177+
public class InteractiveUnityStruct : InteractiveValue
178+
{
179+
public static bool SupportsType(Type type) => s_supportedTypes.Contains(type);
180+
private static readonly HashSet<Type> s_supportedTypes = new HashSet<Type>
181+
{
182+
typeof(Vector2),
183+
typeof(Vector3),
184+
typeof(Vector4),
185+
typeof(Rect),
186+
typeof(Color) // todo might make a special editor for colors
187+
};
188+
189+
//~~~~~~~~~ Instance ~~~~~~~~~~
190+
191+
public InteractiveUnityStruct(object value, Type valueType) : base(value, valueType) { }
192+
193+
public override bool HasSubContent => true;
194+
public override bool SubContentWanted => true;
195+
public override bool WantInspectBtn => true;
196+
197+
public IStructInfo StructInfo;
198+
199+
public override void RefreshUIForValue()
200+
{
201+
base.RefreshUIForValue();
202+
203+
if (m_subContentConstructed)
204+
StructInfo.RefreshUI(m_inputs, this.Value);
205+
}
206+
207+
internal override void OnToggleSubcontent(bool toggle)
208+
{
209+
InitializeStructInfo();
210+
211+
base.OnToggleSubcontent(toggle);
212+
213+
StructInfo.RefreshUI(m_inputs, this.Value);
214+
}
215+
216+
#region STRUCT INFO HANDLERS
217+
218+
internal Type m_lastStructType;
219+
220+
internal void InitializeStructInfo()
221+
{
222+
var type = Value?.GetType() ?? FallbackType;
223+
224+
if (StructInfo != null && type == m_lastStructType)
225+
return;
226+
227+
if (StructInfo != null)
228+
{
229+
// changing types, destroy subcontent
230+
for (int i = 0; i < m_subContentParent.transform.childCount; i++)
231+
{
232+
var child = m_subContentParent.transform.GetChild(i);
233+
GameObject.Destroy(child.gameObject);
234+
}
235+
}
236+
237+
m_lastStructType = type;
238+
239+
StructInfo = StructInfoFactory.Create(type);
240+
}
241+
242+
#endregion
243+
244+
#region UI CONSTRUCTION
245+
246+
internal InputField[] m_inputs;
247+
248+
public override void ConstructUI(GameObject parent, GameObject subGroup)
249+
{
250+
base.ConstructUI(parent, subGroup);
251+
}
252+
253+
public override void ConstructSubcontent()
254+
{
255+
base.ConstructSubcontent();
256+
257+
if (StructInfo == null)
258+
{
259+
ExplorerCore.LogWarning("Setting up subcontent but structinfo is null");
260+
return;
261+
}
262+
263+
var editorContainer = UIFactory.CreateVerticalGroup(m_subContentParent, new Color(0.08f, 0.08f, 0.08f));
264+
var editorGroup = editorContainer.GetComponent<VerticalLayoutGroup>();
265+
editorGroup.childForceExpandWidth = false;
266+
editorGroup.padding.top = 4;
267+
editorGroup.padding.right = 4;
268+
editorGroup.padding.left = 4;
269+
editorGroup.padding.bottom = 4;
270+
editorGroup.spacing = 2;
271+
272+
m_inputs = new InputField[StructInfo.FieldNames.Length];
273+
274+
for (int i = 0; i < StructInfo.FieldNames.Length; i++)
275+
{
276+
AddEditorRow(i, editorContainer);
277+
}
278+
279+
if (Owner.CanWrite)
280+
{
281+
var applyBtnObj = UIFactory.CreateButton(editorContainer, new Color(0.2f, 0.2f, 0.2f));
282+
var applyLayout = applyBtnObj.AddComponent<LayoutElement>();
283+
applyLayout.minWidth = 175;
284+
applyLayout.minHeight = 25;
285+
applyLayout.flexibleWidth = 0;
286+
var m_applyBtn = applyBtnObj.GetComponent<Button>();
287+
m_applyBtn.onClick.AddListener(OnSetValue);
288+
289+
void OnSetValue()
290+
{
291+
Owner.SetValue();
292+
RefreshUIForValue();
293+
}
294+
295+
var applyText = applyBtnObj.GetComponentInChildren<Text>();
296+
applyText.text = "Apply";
297+
}
298+
}
299+
300+
internal void AddEditorRow(int index, GameObject groupObj)
301+
{
302+
var rowObj = UIFactory.CreateHorizontalGroup(groupObj, new Color(1, 1, 1, 0));
303+
var rowGroup = rowObj.GetComponent<HorizontalLayoutGroup>();
304+
rowGroup.childForceExpandHeight = true;
305+
rowGroup.childForceExpandWidth = false;
306+
rowGroup.spacing = 5;
307+
308+
var label = UIFactory.CreateLabel(rowObj, TextAnchor.MiddleRight);
309+
var labelLayout = label.AddComponent<LayoutElement>();
310+
labelLayout.minWidth = 50;
311+
labelLayout.flexibleWidth = 0;
312+
labelLayout.minHeight = 25;
313+
var labelText = label.GetComponent<Text>();
314+
labelText.text = $"{StructInfo.FieldNames[index]}:";
315+
labelText.color = Color.cyan;
316+
317+
var inputFieldObj = UIFactory.CreateInputField(rowObj, 14, 3, 1);
318+
var inputField = inputFieldObj.GetComponent<InputField>();
319+
inputField.characterValidation = InputField.CharacterValidation.Decimal;
320+
var inputLayout = inputFieldObj.AddComponent<LayoutElement>();
321+
inputLayout.flexibleWidth = 0;
322+
inputLayout.minWidth = 120;
323+
inputLayout.minHeight = 25;
324+
325+
m_inputs[index] = inputField;
326+
327+
inputField.onValueChanged.AddListener((string val) => { Value = StructInfo.SetValue(ref this.Value, index, float.Parse(val)); });
328+
}
329+
330+
#endregion
331+
}
332+
}

src/Inspectors/Reflection/InteractiveValue/InteractiveValue.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ public static Type GetIValueForType(Type type)
2828
else
2929
return typeof(InteractiveEnum);
3030
}
31+
else if (InteractiveUnityStruct.SupportsType(type))
32+
return typeof(InteractiveUnityStruct);
3133
else if (typeof(Transform).IsAssignableFrom(type))
3234
return typeof(InteractiveValue);
3335
else if (ReflectionHelpers.IsDictionary(type))
@@ -56,7 +58,7 @@ public InteractiveValue(object value, Type valueType)
5658

5759
public CacheObjectBase Owner;
5860

59-
public object Value { get; set; }
61+
public object Value;
6062
public readonly Type FallbackType;
6163

6264
public virtual bool HasSubContent => false;

src/Tests/Tests.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ public static void StaticMethod() { }
2424

2525
public class TestClass
2626
{
27+
public Vector2 AATestVector2 = new Vector2(1, 2);
28+
public Vector3 AATestVector3 = new Vector3(1, 2, 3);
29+
public Vector4 AATestVector4 = new Vector4(1, 2, 3, 4);
30+
public Rect AATestRect = new Rect(1, 2, 3, 4);
31+
public Color AATestColor = new Color(0.1f, 0.2f, 0.3f, 0.4f);
32+
2733
public bool ATestBoolMethod() => false;
2834

2935
public bool this[int index]
@@ -139,12 +145,6 @@ public TestClass()
139145
GameObject.DontDestroyOnLoad(TestTexture);
140146
GameObject.DontDestroyOnLoad(TestSprite);
141147

142-
testRefArray = new Il2CppReferenceArray<Il2CppSystem.Object>(5);
143-
for (int i = 0; i < 5; i++)
144-
{
145-
testRefArray[i] = "hi " + i;
146-
}
147-
148148
//// test loading a tex from file
149149
//var dataToLoad = System.IO.File.ReadAllBytes(@"Mods\UnityExplorer\Tex_Nemundis_Nebula.png");
150150
//ExplorerCore.Log($"Tex load success: {TestTexture.LoadImage(dataToLoad, false)}");

src/UnityExplorer.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,7 @@
331331
<Compile Include="Inspectors\Reflection\InteractiveValue\InteractiveFlags.cs" />
332332
<Compile Include="Inspectors\Reflection\InteractiveValue\InteractiveNumber.cs" />
333333
<Compile Include="Inspectors\Reflection\InteractiveValue\InteractiveString.cs" />
334+
<Compile Include="Inspectors\Reflection\InteractiveValue\InteractiveUnityStruct.cs" />
334335
<Compile Include="UI\ForceUnlockCursor.cs" />
335336
<Compile Include="Input\IHandleInput.cs" />
336337
<Compile Include="Tests\Tests.cs" />

0 commit comments

Comments
 (0)