Tanoda
Inspector.InspectorTab.cs
Go to the documentation of this file.
1using System;
2using System.Collections;
3using System.Collections.Generic;
4using System.ComponentModel;
5using System.Linq;
6using System.Reflection;
7using System.Runtime.CompilerServices;
9using UnityEngine;
10using Component = UnityEngine.Component;
11
13{
14 public sealed partial class Inspector
15 {
16 private class InspectorTab
17 {
18 private readonly List<ICacheEntry> _fieldCache = new List<ICacheEntry>();
19 private readonly Stack<InspectorStackEntryBase> _inspectorStack = new Stack<InspectorStackEntryBase>();
20
21 public InspectorStackEntryBase CurrentStackItem
22 {
23 get
24 {
25 return InspectorStack.Count > 0 ? InspectorStack.Peek() : null;
26 }
27 }
28
29 public IList<ICacheEntry> FieldCache
30 {
31 get
32 {
33 return _fieldCache;
34 }
35 }
36
37 public Stack<InspectorStackEntryBase> InspectorStack
38 {
39 get
40 {
41 return _inspectorStack;
42 }
43 }
44 public Vector2 InspectorStackScrollPos { get; set; }
45
46 public void Clear()
47 {
48 InspectorStack.Clear();
49 CacheAllMembers(null);
50 }
51
52 public void Pop()
53 {
54 InspectorStack.Pop();
55 LoadStackEntry(InspectorStack.Peek());
56 }
57
58 public void Push(InspectorStackEntryBase stackEntry)
59 {
60 InspectorStack.Push(stackEntry);
61 LoadStackEntry(stackEntry);
62 }
63
64 private static IEnumerable<ICacheEntry> MethodsToCacheEntries(object instance, Type instanceType,
65 MethodInfo[] methodsToCheck)
66 {
67 var cacheItems = methodsToCheck
68 .Where(x => !x.IsConstructor && !x.IsSpecialName && x.GetParameters().Length == 0)
69 .Where(f => !f.IsDefined(typeof(CompilerGeneratedAttribute), false))
70 .Where(x => x.Name != "MemberwiseClone" && x.Name != "obj_address") // Instant game crash
71 .Select(m =>
72 {
73 if (m.ContainsGenericParameters)
74 try
75 {
76 return m.MakeGenericMethod(instanceType);
77 }
78 catch (Exception)
79 {
80 return null;
81 }
82
83 return m;
84 }).Where(x => x != null)
85 .Select(m => new MethodCacheEntry(instance, m)).Cast<ICacheEntry>();
86 return cacheItems;
87 }
88
89 private void CacheAllMembers(InstanceStackEntry entry)
90 {
91 _fieldCache.Clear();
92
93 var objectToOpen = entry != null ? entry.Instance : null;
94 if (objectToOpen == null) return;
95
96 var type = objectToOpen.GetType();
97
98 try
99 {
100 var cmp = objectToOpen as Component;
101 var castedObj = objectToOpen as GameObject;
102 var list = objectToOpen as IList;
103 var enumerable = objectToOpen as IEnumerable;
104 if (cmp != null)
105 {
106 _fieldCache.Add(new CallbackCacheEntry("Open in Scene Object Browser",
107 "Navigate to GameObject this Component is attached to",
108 () => Inspector._treeListShowCallback(cmp.transform)));
109 }
110 else if (castedObj != null)
111 {
112 _fieldCache.Add(new CallbackCacheEntry("Open in Scene Object Browser",
113 "Navigate to this object in the Scene Object Browser",
114 () => Inspector._treeListShowCallback(castedObj.transform)));
115 _fieldCache.Add(new ReadonlyCacheEntry("Child objects",
116 castedObj.transform.Cast<Transform>().ToArray()));
117 _fieldCache.Add(new ReadonlyCacheEntry("Components", castedObj.GetComponents<Component>()));
118 }
119
120 // If we somehow enter a string, this allows user to see what the string actually says
121 if (type == typeof(string))
122 {
123 _fieldCache.Add(new ReadonlyCacheEntry("this", objectToOpen));
124 }
125 else if (objectToOpen is Transform)
126 {
127 // Prevent the list overloads from listing subcomponents
128 }
129 else if (list != null)
130 {
131 for (var i = 0; i < list.Count; i++)
132 _fieldCache.Add(new ListCacheEntry(list, i));
133 }
134 else if (enumerable != null)
135 {
136 _fieldCache.AddRange(enumerable.Cast<object>()
137 .Select((x, y) => x is ICacheEntry ? x : new ReadonlyListCacheEntry(x, y))
138 .Cast<ICacheEntry>());
139 }
140
141 // No need if it's not a value type, only used to propagate changes back so it's redundant with classes
142 var parent = entry.Parent != null ? entry.Parent.Type().IsValueType == true ? entry.Parent : null : null;
143
144 // Instance members
145 _fieldCache.AddRange(type
146 .GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance |
147 BindingFlags.FlattenHierarchy)
148 .Where(f => !f.IsDefined(typeof(CompilerGeneratedAttribute), false))
149 .Select(f => new FieldCacheEntry(objectToOpen, f, parent)).Cast<ICacheEntry>());
150 _fieldCache.AddRange(type
151 .GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance |
152 BindingFlags.FlattenHierarchy)
153 .Where(f => !f.IsDefined(typeof(CompilerGeneratedAttribute), false))
154 .Select(p => new PropertyCacheEntry(objectToOpen, p, parent)).Cast<ICacheEntry>());
155 try
156 {
157 _fieldCache.AddRange(MethodsToCacheEntries(objectToOpen, type,
158 type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance |
159 BindingFlags.FlattenHierarchy)));
160 }
161 catch (Exception e)
162 {
163 // ignored
164 }
165
166 CacheStaticMembersHelper(type);
167 }
168 catch (Exception ex)
169 {
170 RuntimeUnityEditorCore.Logger.Log(LogLevel.Warning, "[Inspector] CacheFields crash: " + ex);
171 }
172 }
173
174 private void CacheStaticMembers(StaticStackEntry entry)
175 {
176 _fieldCache.Clear();
177
178 if (entry == null) return;
179 if (entry.StaticType == null) return;
180
181 try
182 {
183 CacheStaticMembersHelper(entry.StaticType);
184 }
185 catch (Exception ex)
186 {
187 RuntimeUnityEditorCore.Logger.Log(LogLevel.Warning, "[Inspector] CacheFields crash: " + ex);
188 }
189 }
190
191 private void CacheStaticMembersHelper(Type type)
192 {
193 _fieldCache.AddRange(type
194 .GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static |
195 BindingFlags.FlattenHierarchy)
196 .Where(f => !f.IsDefined(typeof(CompilerGeneratedAttribute), false))
197 .Select(f => new FieldCacheEntry(null, f)).Cast<ICacheEntry>());
198 _fieldCache.AddRange(type
199 .GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static |
200 BindingFlags.FlattenHierarchy)
201 .Where(f => !f.IsDefined(typeof(CompilerGeneratedAttribute), false))
202 .Select(p => new PropertyCacheEntry(null, p)).Cast<ICacheEntry>());
203 try
204 {
205 _fieldCache.AddRange(MethodsToCacheEntries(null, type,
206 type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static |
207 BindingFlags.FlattenHierarchy)));
208 }
209 catch (Exception e)
210 {
211 // ignored
212 }
213 }
214
215 private void LoadStackEntry(InspectorStackEntryBase stackEntry)
216 {
217 var instanceStackEntry = stackEntry as InstanceStackEntry;
218 if (instanceStackEntry != null)
219 {
220 CacheAllMembers(instanceStackEntry);
221 return;
222 }
223 var staticStackEntry = stackEntry as StaticStackEntry;
224 if (staticStackEntry != null)
225 {
226 CacheStaticMembers(staticStackEntry);
227 return;
228 }
229 throw new InvalidEnumArgumentException(
230 "Invalid stack entry type: " + stackEntry.GetType().FullName);
231 }
232
233 public void PopUntil(InspectorStackEntryBase item)
234 {
235 if (CurrentStackItem == item) return;
236 while (CurrentStackItem != null && CurrentStackItem != item) InspectorStack.Pop();
237 LoadStackEntry(CurrentStackItem);
238 }
239 }
240 }
241}
UnityEngine.Component Component
RuntimeUnityEditor.Core.LogLevel LogLevel
Definition: RUEInvoker.cs:5
object Instance
ICacheEntry Parent
Type StaticType
Inspector(Action< Transform > treeListShowCallback)
Definition: Inspector.cs:58
void Push(InspectorStackEntryBase stackEntry, bool newTab)
Definition: Inspector.cs:122
Definition: ICacheEntry.cs:6
Type Type()