2using System.Collections.Generic;
4using JetBrains.Annotations;
14 private const int InspectorRecordHeight = 25;
15 private readonly GUILayoutOption[] _inspectorTypeWidth = { GUILayout.Width(170), GUILayout.MaxWidth(170) };
16 private readonly GUILayoutOption[] _inspectorNameWidth = { GUILayout.Width(240), GUILayout.MaxWidth(240) };
17 private readonly GUILayoutOption _inspectorRecordHeight = GUILayout.Height(InspectorRecordHeight);
18 private readonly GUILayoutOption _dnSpyButtonOptions = GUILayout.Width(19);
19 private readonly
int _windowId;
21 private readonly List<InspectorTab> _tabs =
new List<InspectorTab>();
22 private InspectorTab _currentTab;
23 private InspectorTab GetCurrentTab()
25 return _currentTab ?? (_currentTab = _tabs.FirstOrDefault());
28 private Vector2 _tabScrollPos = Vector2.zero;
30 private GUIStyle _alignedButtonStyle;
31 private Rect _inspectorWindowRect;
33 private object _currentlyEditingTag;
34 private string _currentlyEditingText;
35 private bool _userHasHitReturn;
37 public bool Show {
get;
set; }
39 private bool _focusSearchBox;
40 private const string SearchBoxName =
"InspectorFilterBox";
41 private string _searchString =
"";
52 _searchString = value ??
"";
56 private static Action<Transform> _treeListShowCallback;
58 public Inspector(Action<Transform> treeListShowCallback)
60 _treeListShowCallback = treeListShowCallback;
61 _windowId = GetHashCode();
64 private void DrawEditableValue(
ICacheEntry field,
object value, params GUILayoutOption[] layoutParams)
66 var isBeingEdited = _currentlyEditingTag == field;
67 var text = isBeingEdited ? _currentlyEditingText : ToStringConverter.GetEditValue(field, value);
68 var result = GUILayout.TextField(text, layoutParams);
70 if (!Equals(text, result) || isBeingEdited)
71 if (_userHasHitReturn)
73 _currentlyEditingTag =
null;
74 _userHasHitReturn =
false;
77 ToStringConverter.SetEditValue(field, value, result);
86 _currentlyEditingText = result;
87 _currentlyEditingTag = field;
91 private void DrawVariableNameEnterButton(
ICacheEntry field)
93 if (_alignedButtonStyle ==
null)
95 _alignedButtonStyle =
new GUIStyle(GUI.skin.button)
97 alignment = TextAnchor.MiddleLeft,
102 if (GUILayout.Button(field.
Name(), _alignedButtonStyle, _inspectorNameWidth))
108 Push(entry, IsContextClick());
113 [Obsolete(
"Use push and Show instead")]
116 [Obsolete(
"Use push instead")]
119 Push(stackEntry,
true);
124 _focusSearchBox =
true;
127 var tab = GetCurrentTab();
128 if (tab ==
null || newTab)
130 tab =
new InspectorTab();
134 tab.Push(stackEntry);
139 private void RemoveTab(InspectorTab tab)
142 if (_currentTab == tab)
148 var tmp = GetCurrentTab();
149 if (tmp ==
null)
return null;
151 return se !=
null ? se.
Instance :
null;
154 private void InspectorWindow(
int id)
158 GUILayout.BeginVertical();
160 GUILayout.BeginHorizontal();
162 GUILayout.BeginHorizontal(GUI.skin.box, GUILayout.ExpandWidth(
true));
164 GUILayout.Label(
"Filter:", GUILayout.ExpandWidth(
false));
166 GUI.SetNextControlName(SearchBoxName);
172 GUI.FocusControl(SearchBoxName);
173 _focusSearchBox =
false;
176 GUILayout.Label(
"Find:", GUILayout.ExpandWidth(
false));
177 foreach (var obj
in new[]
179 new KeyValuePair<object, string>(
180 EditorUtilities.GetInstanceClassScanner().OrderBy(x => x.Name()),
"Instances"),
181 new KeyValuePair<object, string>(EditorUtilities.GetComponentScanner().OrderBy(x => x.Name()),
183 new KeyValuePair<object, string>(
184 EditorUtilities.GetMonoBehaviourScanner().OrderBy(x => x.Name()),
"MonoBehaviours"),
185 new KeyValuePair<object, string>(EditorUtilities.GetTransformScanner().OrderBy(x => x.Name()),
190 if (obj.Key ==
null)
continue;
191 if (GUILayout.Button(obj.Value, GUILayout.ExpandWidth(
false)))
195 GUILayout.EndHorizontal();
199 GUILayout.BeginHorizontal(GUI.skin.box, GUILayout.Width(160));
201 if (GUILayout.Button(
"Help"))
202 Push(InspectorHelpObject.Create(),
true);
203 if (GUILayout.Button(
"Close"))
206 GUILayout.EndHorizontal();
208 GUILayout.EndHorizontal();
210 var currentTab = GetCurrentTab();
211 var defaultGuiColor = GUI.color;
212 if (_tabs.Count >= 2)
214 _tabScrollPos = GUILayout.BeginScrollView(_tabScrollPos,
false,
false,
215 GUI.skin.horizontalScrollbar, GUIStyle.none, GUIStyle.none);
217 GUILayout.BeginHorizontal(GUILayout.ExpandWidth(
false), GUILayout.ExpandHeight(
false));
218 for (var index = 0; index < _tabs.Count; index++)
220 var tab = _tabs[index];
222 if (currentTab == tab)
223 GUI.color =
Color.cyan;
225 if (GUILayout.Button(
226 string.Format(
"Tab {0}: {1}", index + 1,
227 LimitStringLengthForPreview(tab !=
null ? tab.CurrentStackItem !=
null ? tab.CurrentStackItem.Name :
"" :
"", 18)), GUILayout.ExpandWidth(
false)))
229 if (IsContextClick())
234 GUI.color = defaultGuiColor;
238 GUI.color = defaultGuiColor;
241 GUILayout.FlexibleSpace();
242 GUI.color =
new Color(1, 1, 1, 0.6f);
243 if (GUILayout.Button(
"Close all"))
248 GUI.color = defaultGuiColor;
250 GUILayout.EndHorizontal();
252 GUILayout.EndScrollView();
255 if (currentTab !=
null)
257 currentTab.InspectorStackScrollPos = GUILayout.BeginScrollView(currentTab.InspectorStackScrollPos,
false,
false,
258 GUI.skin.horizontalScrollbar, GUIStyle.none, GUIStyle.none);
260 GUILayout.BeginHorizontal(GUI.skin.box, GUILayout.ExpandWidth(
false), GUILayout.ExpandHeight(
false));
261 var stackEntries = currentTab.InspectorStack.Reverse().ToArray();
262 for (var i = 0; i < stackEntries.Length; i++)
264 var item = stackEntries[i];
266 if (i + 1 == stackEntries.Length)
267 GUI.color =
Color.cyan;
269 if (GUILayout.Button(LimitStringLengthForPreview(item.Name, 90), GUILayout.ExpandWidth(
false)))
271 currentTab.PopUntil(item);
272 GUI.color = defaultGuiColor;
276 if (i + 1 < stackEntries.Length)
277 GUILayout.Label(
">", GUILayout.ExpandWidth(
false));
279 GUI.color = defaultGuiColor;
281 GUILayout.EndHorizontal();
283 GUILayout.EndScrollView();
285 GUILayout.BeginVertical(GUI.skin.box);
287 GUILayout.BeginHorizontal();
290 GUILayout.Label(
"Value/return type", GUI.skin.box, _inspectorTypeWidth);
292 GUILayout.Label(
"Member name", GUI.skin.box, _inspectorNameWidth);
294 GUILayout.Label(
"Value", GUI.skin.box, GUILayout.ExpandWidth(
true));
296 GUILayout.EndHorizontal();
298 DrawContentScrollView(currentTab);
300 GUILayout.EndVertical();
304 GUILayout.Label(
"Nothing to show. Click on objects in the scene browser to open them in a new tab.");
305 GUILayout.Label(
"Tip: You can right click on a member inside inspector to open in a new tab, and on a tab to close it.");
306 GUILayout.FlexibleSpace();
309 GUILayout.EndVertical();
313 RuntimeUnityEditorCore.Logger.Log(
LogLevel.Error,
"[Inspector] GUI crash: " + ex);
320 private static string LimitStringLengthForPreview(
string name,
int maxLetters)
322 if (name ==
null) name =
"NULL";
323 if (name.Length >= maxLetters) name = name.Substring(0, maxLetters - 2) +
"...";
327 private static bool IsContextClick()
329 return Event.current.button >= 1;
332 private void DrawContentScrollView(InspectorTab tab)
334 if (tab ==
null || tab.InspectorStack.Count == 0)
336 GUILayout.FlexibleSpace();
340 var currentItem = tab.CurrentStackItem;
341 currentItem.ScrollPosition = GUILayout.BeginScrollView(currentItem.ScrollPosition);
343 GUILayout.BeginVertical();
345 var visibleFields =
string.IsNullOrEmpty(
SearchString) ?
347 tab.FieldCache.Where(x => x.Name().Contains(
SearchString, StringComparison.OrdinalIgnoreCase) || x.TypeName().Contains(
SearchString, StringComparison.OrdinalIgnoreCase)).ToList();
349 var firstIndex = (int)(currentItem.ScrollPosition.y / InspectorRecordHeight);
351 GUILayout.Space(firstIndex * InspectorRecordHeight);
353 var currentVisibleCount = (int)(_inspectorWindowRect.height / InspectorRecordHeight) - 4;
354 for (var index = firstIndex; index < Mathf.Min(visibleFields.Count, firstIndex + currentVisibleCount); index++)
356 var entry = visibleFields[index];
359 DrawSingleContentEntry(entry);
361 catch (ArgumentException)
368 GUILayout.Space(Mathf.FloorToInt(Mathf.Max(_inspectorWindowRect.height / 2, (visibleFields.Count - firstIndex - currentVisibleCount) * InspectorRecordHeight)));
370 GUILayout.FlexibleSpace();
377 GUILayout.EndVertical();
379 GUILayout.EndScrollView();
382 private void DrawSingleContentEntry(
ICacheEntry entry)
384 GUILayout.BeginHorizontal(_inspectorRecordHeight);
386 GUILayout.Label(entry.
TypeName(), _inspectorTypeWidth);
391 DrawVariableNameEnterButton(entry);
393 GUILayout.TextArea(entry.
Name(), GUI.skin.label, _inspectorNameWidth);
395 if (entry.
CanSetValue() && ToStringConverter.CanEditValue(entry, value))
396 DrawEditableValue(entry, value, GUILayout.ExpandWidth(
true));
398 GUILayout.TextArea(ToStringConverter.ObjectToString(value), GUI.skin.label, GUILayout.ExpandWidth(
true));
400 GUILayout.EndHorizontal();
407 if (Event.current.isKey && (Event.current.keyCode == KeyCode.Return || Event.current.keyCode == KeyCode.KeypadEnter)) _userHasHitReturn =
true;
410 foreach (var tab
in _tabs.ToList())
412 while (tab.InspectorStack.Count > 0 && !tab.InspectorStack.Peek().EntryIsValid())
415 string.Format(
"[Inspector] Removed invalid/removed stack object: \"{0}\"",
416 tab.InspectorStack.Peek().Name));
420 if (tab.InspectorStack.Count == 0) RemoveTab(tab);
423 _inspectorWindowRect = GUILayout.Window(_windowId, _inspectorWindowRect, InspectorWindow,
"Inspector");
424 InterfaceMaker.EatInputInRect(_inspectorWindowRect);
429 _inspectorWindowRect = windowRect;
RuntimeUnityEditor.Core.LogLevel LogLevel
object GetInspectedObject()
Inspector(Action< Transform > treeListShowCallback)
void UpdateWindowSize(Rect windowRect)
void Push(InspectorStackEntryBase stackEntry, bool newTab)
void InspectorPush(InspectorStackEntryBase stackEntry)
void Log(LogLevel logLogLevel, object content)
object EnterValue()
Get object that is entered when variable name is clicked in inspector