Skip to content

Commit 5511628

Browse files
committed
Refine method selector popup and type matcher popup API
1 parent b699145 commit 5511628

File tree

6 files changed

+94
-62
lines changed

6 files changed

+94
-62
lines changed

Editor/UInspectorPlus/ComponentMethodDrawer.cs

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using UnityEngine;
22
using UnityEditor;
33
using UnityEditor.AnimatedValues;
4+
using UnityEditor.Experimental.GraphView;
45
using System;
56
using System.Collections.Generic;
67
using System.Linq;
@@ -14,15 +15,14 @@ internal class ComponentMethodDrawer: IReflectorDrawer, IDisposable {
1415
private AnimBool showMethodOptions;
1516
private AnimBool showMethodSelector;
1617
private AnimBool showResultSelector;
17-
private string[] methodNames;
18+
private readonly List<SearchTreeEntry> methodNames = new List<SearchTreeEntry>();
1819
private int selectedMethodIndex;
1920
private MemberInfo selectedMember, resolvedMember;
2021
private ParameterInfo[] parameterInfo;
2122
private MethodPropertyDrawer[] parameters;
2223
private TypeResolverGUI typeResolver;
2324
private MethodPropertyDrawer result;
2425
private Exception thrownException;
25-
private string filter;
2626
private readonly Type ctorType, targetType;
2727
private bool titleFolded = true, paramsFolded = true, resultFolded = true,
2828
drawHeader = true, privateFields = true, obsolete = true;
@@ -95,15 +95,6 @@ public ComponentMethodDrawer(Type type)
9595
InitComponentMethods();
9696
}
9797

98-
public string Filter {
99-
get { return filter; }
100-
set {
101-
if (filter == value) return;
102-
filter = value;
103-
InitComponentMethods(false);
104-
}
105-
}
106-
10798
public void Call() {
10899
var member = resolvedMember ?? selectedMember;
109100
if (member == null || parameters == null)
@@ -214,17 +205,21 @@ public bool UpdateValue() {
214205
return false;
215206
}
216207

208+
public void ShowSearchPopup() => SearchWindowProvider.OpenSearchWindow(methodNames, i => {
209+
selectedMethodIndex = (int)i;
210+
InitMethodParams();
211+
showMethodOptions.target = true;
212+
RequireRedraw();
213+
});
214+
217215
public void Dispose() {
218216
if (parameters != null)
219217
foreach (var parameter in parameters)
220218
parameter.Dispose();
221219
result?.Dispose();
222220
}
223221

224-
private bool FilterMemberInfo(MemberInfo m) =>
225-
(obsolete || !Attribute.IsDefined(m, typeof(ObsoleteAttribute))) &&
226-
(string.IsNullOrEmpty(filter) ||
227-
m.Name.IndexOf(filter, StringComparison.CurrentCultureIgnoreCase) >= 0);
222+
private bool FilterMemberInfo(MemberInfo m) => obsolete || !Attribute.IsDefined(m, typeof(ObsoleteAttribute));
228223

229224
private void AddComponentMethod(Type type) {
230225
BindingFlags flag = BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy;
@@ -289,23 +284,41 @@ where FilterMemberInfo(m)
289284

290285
private void InitComponentMethods(bool resetIndex = true) {
291286
methods.Clear();
287+
methodNames.Clear();
292288
switch (mode) {
293289
case MethodMode.Constructor:
294290
AddComponentMethod(ctorType);
295-
methodNames = methods.Select((m, i) => GetMethodNameFormatted(m, i)).ToArray();
291+
methodNames.Add(new SearchTreeGroupEntry(new GUIContent("Constructors")));
292+
methodNames.AddRange(methods.Select((m, i) => new SearchTreeEntry(new GUIContent(GetMethodNameFormatted(m))) { userData = i, level = 1 } ));
296293
break;
297294
default:
298295
AddComponentMethod(component, targetType);
299296
break;
300297
}
298+
methodNames.Add(new SearchTreeGroupEntry(new GUIContent("Methods")));
301299
if (drawHeader) {
302300
var gameObject = component as GameObject;
303301
if (gameObject != null)
304302
foreach (var c in gameObject.GetComponents(typeof(Component)))
305303
AddComponentMethod(c);
306-
methodNames = methods.Select((m, i) => $"{m.target.GetType().Name} ({m.target.ObjIdOrHashCode()})/{GetMethodNameFormatted(m, i)}").ToArray();
304+
var temp = new Dictionary<object, List<SearchTreeEntry>>();
305+
for (int i = 0; i < methods.Count; i++) {
306+
ComponentMethod m = methods[i];
307+
temp.GetValueOrDefault(m.target).Add(
308+
new SearchTreeEntry(new GUIContent(GetMethodNameFormatted(m))) {
309+
userData = i,
310+
level = 2,
311+
}
312+
);
313+
}
314+
foreach (var kv in temp) {
315+
methodNames.Add(new SearchTreeGroupEntry(
316+
new GUIContent($"{kv.Key.GetType().Name} ({kv.Key.ObjIdOrHashCode()})"), 1
317+
));
318+
methodNames.AddRange(kv.Value);
319+
}
307320
} else {
308-
methodNames = methods.Select((m, i) => GetMethodNameFormatted(m, i)).ToArray();
321+
methodNames.AddRange(methods.Select((m, i) => new SearchTreeEntry(new GUIContent(GetMethodNameFormatted(m))) { userData = i, level = 1 } ));
309322
}
310323
if (!resetIndex && selectedMember != null) {
311324
selectedMethodIndex = methods.FindIndex(m => m.member == selectedMember);
@@ -320,7 +333,7 @@ private void InitComponentMethods(bool resetIndex = true) {
320333
thrownException = null;
321334
}
322335

323-
private string GetMethodNameFormatted(ComponentMethod m, int i) {
336+
private string GetMethodNameFormatted(ComponentMethod m) {
324337
string name, formatStr;
325338
ParameterInfo[] parameters;
326339
switch (m.mode) {
@@ -392,7 +405,9 @@ private void CreateDrawers() {
392405
private void DrawComponent() {
393406
if (OnClose != null)
394407
EditorGUILayout.BeginHorizontal();
395-
selectedMethodIndex = EditorGUILayout.Popup(mode.ToString(), selectedMethodIndex, methodNames);
408+
EditorGUILayout.PrefixLabel(mode.ToString());
409+
if (GUILayout.Button(selectedMethodIndex < 0 ? GUIContent.none : methodNames[selectedMethodIndex].content, EditorStyles.popup))
410+
ShowSearchPopup();
396411
if (OnClose != null) {
397412
if (GUILayout.Button(EditorGUIUtility.IconContent("Toolbar Minus"), EditorStyles.miniLabel, GUILayout.ExpandWidth(false)))
398413
OnClose();

Editor/UInspectorPlus/Helpers.cs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.Reflection;
88
using System.Text;
99
using UnityObject = UnityEngine.Object;
10+
using UnityEditor.Experimental.GraphView;
1011

1112
namespace JLChnToZ.EditorExtensions.UInspectorPlus {
1213
internal enum PropertyType {
@@ -558,4 +559,24 @@ public static void PrintExceptionsWithInner(Exception ex) {
558559
} while (ex != null);
559560
}
560561
}
561-
}
562+
563+
internal class SearchWindowProvider: ScriptableObject, ISearchWindowProvider {
564+
List<SearchTreeEntry> entries;
565+
Action<object> OnSelected;
566+
567+
public static bool OpenSearchWindow(List<SearchTreeEntry> entries, Action<object> onSelected, float width = 0, float height = 0) {
568+
var provider = CreateInstance<SearchWindowProvider>();
569+
provider.OnSelected = onSelected;
570+
provider.entries = entries;
571+
return SearchWindow.Open(new SearchWindowContext(GUIUtility.GUIToScreenPoint(Event.current.mousePosition), width, height), provider);
572+
}
573+
574+
public List<SearchTreeEntry> CreateSearchTree(SearchWindowContext context) => entries;
575+
576+
public bool OnSelectEntry(SearchTreeEntry entry, SearchWindowContext context) {
577+
OnSelected?.Invoke(entry.userData);
578+
DestroyImmediate(this);
579+
return true;
580+
}
581+
}
582+
}

Editor/UInspectorPlus/InspectorDrawer.cs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,7 @@ public InspectorDrawer(object target, Type targetType, bool shown, bool showProp
142142
Debug.LogException(ex);
143143
}
144144
}
145-
if (allowMethods = showMethods)
146-
AddMethodMenu();
145+
allowMethods = showMethods;
147146
foreach (var d in drawer)
148147
d.OnRequireRedraw += RequireRedraw;
149148
if (!target.IsInvalid())
@@ -189,7 +188,6 @@ protected virtual void Draw(bool readOnly) {
189188
EditorGUILayout.BeginHorizontal();
190189
EditorGUILayout.LabelField(GUIContent.none, GUILayout.Width(EditorGUIUtility.singleLineHeight));
191190
EditorGUILayout.BeginVertical();
192-
methodDrawer.Filter = searchText;
193191
methodDrawer.Draw();
194192
EditorGUILayout.EndVertical();
195193
EditorGUILayout.EndHorizontal();
@@ -207,8 +205,15 @@ protected virtual void Draw(bool readOnly) {
207205
if (allowMethods) {
208206
GUILayout.BeginHorizontal();
209207
GUILayout.FlexibleSpace();
210-
if (GUILayout.Button(EditorGUIUtility.IconContent("Toolbar Plus", "Add Method / Index Properties Watcher"), EditorStyles.miniLabel, GUILayout.ExpandWidth(false)))
211-
AddMethodMenu();
208+
if (GUILayout.Button(EditorGUIUtility.IconContent("Toolbar Plus", "Add Method / Index Properties Watcher"), EditorStyles.miniLabel, GUILayout.ExpandWidth(false))) {
209+
ComponentMethodDrawer newDrawer = null;
210+
newDrawer = new ComponentMethodDrawer(target, targetType) {
211+
AllowPrivateFields = allowPrivate,
212+
OnClose = () => removingDrawers.Add(newDrawer)
213+
};
214+
drawer.Add(newDrawer);
215+
newDrawer.ShowSearchPopup();
216+
}
212217
GUILayout.EndHorizontal();
213218
}
214219
}

Editor/UInspectorPlus/InspectorPlus.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,14 @@ private void OnEnable() {
3333
titleContent = new GUIContent(titles[searchMode], EditorGUIUtility.FindTexture("UnityEditor.InspectorWindow"));
3434
Initialize();
3535
OnFocus();
36-
typeMatcher = CreateInstance<TypeMatcher>();
36+
typeMatcher = new TypeMatcher();
3737
typeMatcher.OnRequestRedraw += Repaint;
3838
typeMatcher.OnSelected += TypeSelected;
3939
}
4040

4141
private void OnDisable() => typeMatcher.OnRequestRedraw -= Repaint;
4242

43-
private void OnDestroy() => DestroyImmediate(typeMatcher);
43+
private void OnDestroy() => typeMatcher?.Dispose();
4444

4545
private static void TypeSelected(Type type) => InspectorChildWindow.OpenStatic(type, true, true, true, true, false, null);
4646

Editor/UInspectorPlus/MethodPropertyDrawer.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -686,10 +686,11 @@ private void DrawDirectField(bool readOnly, Rect? rect) {
686686
rect2 = Helper.ScaleRect(rect2, 0, 0, 1, 1, 0, 0, -36);
687687
} else
688688
buttonRect = Rect.zero;
689+
NamespacedType.Touch();
689690
if (GUI.Button(rect2, value is Type t ? $"T: {t.FullName}" : "<Null>", EditorStyles.textField) && !readOnly) {
690-
var typeMatcher = ScriptableObject.CreateInstance<TypeMatcher>();
691+
var typeMatcher = new TypeMatcher();
691692
typeMatcher.OnSelected += type => rawValue = type;
692-
SearchWindow.Open(new SearchWindowContext(GUIUtility.GUIToScreenPoint(Event.current.mousePosition)), typeMatcher);
693+
typeMatcher.ShowPopup();
693694
}
694695
EditorGUI.EndDisabledGroup();
695696
if (rect.HasValue)

Editor/UInspectorPlus/TypeMatcher.cs

Lines changed: 22 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ static NamespacedType() {
2929
new Thread(Init) { IsBackground = true }.Start();
3030
}
3131

32+
public static void Touch() {} // Ensure the class has instructed to initialize (Actual procedure is in static constructor)
33+
3234
private NamespacedType(string name) {
3335
namespaceName = name;
3436
subNamespaces = new Dictionary<string, NamespacedType>();
@@ -90,7 +92,7 @@ private static void OnAppDomainUnload(object sender, EventArgs e) {
9092
}
9193
}
9294

93-
internal class TypeMatcher: ScriptableObject, ISearchWindowProvider {
95+
internal class TypeMatcher: IDisposable {
9496
public Thread bgWorker;
9597
public event Action OnRequestRedraw;
9698
public event Action<Type> OnSelected;
@@ -117,6 +119,8 @@ public string SearchText {
117119

118120
public float Width => maxWidth;
119121

122+
static TypeMatcher() => NamespacedType.Touch();
123+
120124
public void Draw() {
121125
if (searchTypeResult == null || searchTypeResult.Length == 0) return;
122126
GUIContent temp = new GUIContent();
@@ -171,7 +175,7 @@ private void RequestRedraw() {
171175
if (OnRequestRedraw != null) OnRequestRedraw.Invoke();
172176
}
173177

174-
private void OnDestroy() {
178+
public void Dispose() {
175179
try {
176180
if (bgWorker != null && bgWorker.IsAlive)
177181
bgWorker.Abort();
@@ -181,21 +185,13 @@ private void OnDestroy() {
181185
}
182186
}
183187

184-
public List<SearchTreeEntry> CreateSearchTree(SearchWindowContext context) {
188+
public bool ShowPopup() {
185189
var searchTreeEntries = new List<SearchTreeEntry> {
186190
new SearchTreeGroupEntry(new GUIContent("Types")),
187191
};
188192
var typeIconContent = EditorGUIUtility.IconContent("Assembly Icon");
189-
if (NamespacedType.root.Types.Count > 0) {
190-
searchTreeEntries.Add(new SearchTreeGroupEntry(new GUIContent(NamespacedType.root.namespaceName), 1));
191-
foreach (var type in NamespacedType.root.Types)
192-
searchTreeEntries.Add(new SearchTreeEntry(new GUIContent(type.Name)) {
193-
level = 2,
194-
userData = type,
195-
});
196-
}
197193
var stack = new Stack<(Queue<NamespacedType>, ICollection<Type>)>();
198-
stack.Push((new Queue<NamespacedType>(NamespacedType.root.SubNamespaces), null));
194+
stack.Push((new Queue<NamespacedType>(NamespacedType.root.SubNamespaces), NamespacedType.root.Types));
199195
while (stack.Count > 0) {
200196
var (pendingChildren, types) = stack.Peek();
201197
if (pendingChildren.Count > 0) {
@@ -204,27 +200,19 @@ public List<SearchTreeEntry> CreateSearchTree(SearchWindowContext context) {
204200
stack.Push((new Queue<NamespacedType>(child.SubNamespaces), child.Types));
205201
continue;
206202
}
207-
if (types != null) {
208-
foreach (var type in types) {
209-
if (!Helper.IsTypeRseolvable(genericType, type)) continue;
210-
var objContent = type.IsSubclassOf(typeof(UnityEngine.Object)) ? EditorGUIUtility.ObjectContent(null, type) : typeIconContent;
211-
var name = type.Name;
212-
searchTreeEntries.Add(new SearchTreeEntry(new GUIContent(objContent) { text = name.Substring(name.LastIndexOf('.') + 1) }) {
213-
level = stack.Count,
214-
userData = type,
215-
});
216-
}
203+
foreach (var type in types) {
204+
if (!Helper.IsTypeRseolvable(genericType, type)) continue;
205+
var objContent = type.IsSubclassOf(typeof(UnityEngine.Object)) ? EditorGUIUtility.ObjectContent(null, type) : typeIconContent;
206+
searchTreeEntries.Add(new SearchTreeEntry(new GUIContent(objContent) { text = type.Name }) {
207+
level = stack.Count,
208+
userData = type,
209+
});
217210
}
218211
stack.Pop();
219212
}
220-
return searchTreeEntries;
221-
}
222-
223-
public bool OnSelectEntry(SearchTreeEntry entry, SearchWindowContext context) {
224-
OnSelected?.Invoke(entry.userData as Type);
225-
return true;
213+
return SearchWindowProvider.OpenSearchWindow(searchTreeEntries, o => OnSelected?.Invoke(o as Type));
226214
}
227-
}
215+
}
228216

229217
internal class TypeResolverGUI {
230218
public readonly Type srcType;
@@ -250,6 +238,7 @@ public bool IsReady {
250238
public MethodInfo ResolvedMethod => IsReady ? srcMethod?.MakeGenericMethod(Resolve()) : null;
251239

252240
public TypeResolverGUI(Type type) {
241+
NamespacedType.Touch();
253242
srcType = type;
254243
constraints = type.GetGenericArguments();
255244
subGUI = new TypeResolverGUI[constraints.Length];
@@ -269,13 +258,14 @@ public void Draw() {
269258
rect = EditorGUI.PrefixLabel(rect, new GUIContent(constraints[i].Name));
270259
if (GUI.Button(rect, resolvedTypes[i] != null ? $"T: {resolvedTypes[i].FullName}" : "", EditorStyles.textField)) {
271260
int index = i;
272-
var typeMatcher = ScriptableObject.CreateInstance<TypeMatcher>();
273-
typeMatcher.genericType = constraints[i];
261+
var typeMatcher = new TypeMatcher {
262+
genericType = constraints[i],
263+
};
274264
typeMatcher.OnSelected += type => {
275265
resolvedTypes[index] = type;
276266
subGUI[index] = null;
277267
};
278-
SearchWindow.Open(new SearchWindowContext(GUIUtility.GUIToScreenPoint(Event.current.mousePosition)), typeMatcher);
268+
typeMatcher.ShowPopup();
279269
}
280270
if (resolvedTypes[i] != null && resolvedTypes[i].ContainsGenericParameters) {
281271
if (subGUI[i] == null) subGUI[i] = new TypeResolverGUI(resolvedTypes[i]);

0 commit comments

Comments
 (0)