Skip to content

Commit f7b6d99

Browse files
committed
Add restore flow and extension method support
1 parent 8390e75 commit f7b6d99

File tree

6 files changed

+133
-38
lines changed

6 files changed

+133
-38
lines changed

Editor/UInspectorPlus/ComponentMethodDrawer.cs

Lines changed: 74 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Collections.Generic;
77
using System.Linq;
88
using System.Reflection;
9+
using System.Runtime.CompilerServices;
910
using UnityObject = UnityEngine.Object;
1011

1112
namespace JLChnToZ.EditorExtensions.UInspectorPlus {
@@ -106,7 +107,7 @@ public void Call() {
106107
}
107108
try {
108109
thrownException = null;
109-
var requestData = Array.ConvertAll(parameters, d => d.Value);
110+
var requestData = Array.ConvertAll(parameters, d => d?.Value);
110111
object returnData;
111112
switch (mode) {
112113
case MethodMode.Constructor:
@@ -129,9 +130,13 @@ public void Call() {
129130
OnClose = OnClose,
130131
};
131132
break;
132-
case MethodMode.Method:
133133
default:
134-
returnData = (member as MethodInfo).Invoke(component, requestData);
134+
var thisType = component;
135+
if (mode == MethodMode.ExtensionMethod) {
136+
requestData[0] = component;
137+
thisType = null;
138+
}
139+
returnData = (member as MethodInfo).Invoke(thisType, requestData);
135140
result = (member as MethodInfo).ReturnType == typeof(void) ?
136141
null :
137142
new MethodPropertyDrawer(
@@ -143,6 +148,7 @@ public void Call() {
143148
break;
144149
}
145150
for (int i = 0, l = Math.Min(parameters.Length, requestData.Length); i < l; i++) {
151+
if (parameters[i] == null) continue;
146152
parameters[i].Value = requestData[i];
147153
if (parameters[i].ReferenceMode)
148154
Helper.AssignValue(parameters[i].RefFieldInfo, parameters[i].Component, requestData[i]);
@@ -167,7 +173,7 @@ public void Draw() {
167173
}
168174
if (mode != MethodMode.Indexer || result == null)
169175
EditorGUILayout.Space();
170-
if (mode == MethodMode.Constructor ||
176+
if (mode == MethodMode.Constructor || mode == MethodMode.ExtensionMethod ||
171177
(mode == MethodMode.Method && (component != null || !selectedMember.IsInstanceMember())) ||
172178
(mode == MethodMode.Indexer && component != null && result == null)) {
173179
if (GUI.changed) {
@@ -177,6 +183,7 @@ public void Draw() {
177183
showMethodSelector.target = true;
178184
} else
179185
showMethodSelector.target = false;
186+
EditorGUILayout.BeginVertical(GUI.skin.box);
180187
if (EditorGUILayout.BeginFadeGroup(showMethodSelector.faded))
181188
DrawComponent();
182189
EditorGUILayout.EndFadeGroup();
@@ -190,6 +197,7 @@ public void Draw() {
190197
}
191198
if (execButton)
192199
DrawExecButton();
200+
EditorGUILayout.EndVertical();
193201
}
194202
}
195203

@@ -215,7 +223,7 @@ public void ShowSearchPopup() => SearchWindowProvider.OpenSearchWindow(methodNam
215223
public void Dispose() {
216224
if (parameters != null)
217225
foreach (var parameter in parameters)
218-
parameter.Dispose();
226+
parameter?.Dispose();
219227
result?.Dispose();
220228
}
221229

@@ -242,6 +250,7 @@ private void AddComponentMethod(object target, Type type = null) {
242250
if (target == null) AddConstructors(type, flag | BindingFlags.Instance);
243251
AddIndexers(type, target, flag);
244252
AddMethods(type, target, flag);
253+
AddExtensionMethods(type, target);
245254
}
246255

247256
private void AddMethods(Type type, BindingFlags flag) => methods.AddRange(
@@ -282,6 +291,23 @@ where FilterMemberInfo(m)
282291
}
283292
);
284293

294+
private void AddExtensionMethods(Type type, object target) => methods.AddRange(
295+
from a in AppDomain.CurrentDomain.GetAssemblies()
296+
where a.IsDefined(typeof(ExtensionAttribute), false)
297+
from t in a.GetTypes()
298+
where t.IsSealed && !t.IsGenericType && !t.IsNested &&
299+
t.IsDefined(typeof(ExtensionAttribute), false)
300+
from m in t.GetMethods(BindingFlags.Static | BindingFlags.Public)
301+
where FilterMemberInfo(m) &&
302+
m.IsDefined(typeof(ExtensionAttribute), false) &&
303+
(m.GetParameters().FirstOrDefault()?.ParameterType.IsAssignableFrom(type)).GetValueOrDefault(false)
304+
select new ComponentMethod {
305+
member = m,
306+
target = target,
307+
mode = MethodMode.ExtensionMethod,
308+
}
309+
);
310+
285311
private void InitComponentMethods(bool resetIndex = true) {
286312
methods.Clear();
287313
methodNames.Clear();
@@ -340,19 +366,15 @@ private string GetMethodNameFormatted(ComponentMethod m) {
340366
case MethodMode.Indexer:
341367
name = null;
342368
break;
343-
case MethodMode.Constructor:
344-
case MethodMode.Method:
345369
default:
346-
name = m.member.GetMemberName().Replace('_', ' ');
370+
name = m.member.GetMemberName(isExtensionMethod: m.mode == MethodMode.ExtensionMethod).Replace('_', ' ');
347371
break;
348372
}
349373
switch (m.mode) {
350374
case MethodMode.Indexer:
351375
parameters = (m.member as PropertyInfo).GetIndexParameters();
352376
formatStr = "[{1}]";
353377
break;
354-
case MethodMode.Constructor:
355-
case MethodMode.Method:
356378
default:
357379
parameters = (m.member as MethodBase).GetParameters();
358380
formatStr = "{0} ({1})";
@@ -372,7 +394,6 @@ private void InitMethodParams() {
372394
parameterInfo = (selectedMember as PropertyInfo).GetIndexParameters();
373395
paramsFolded = parameterInfo.Length > 0;
374396
break;
375-
case MethodMode.Method:
376397
default:
377398
component = methods[selectedMethodIndex].target;
378399
break;
@@ -381,8 +402,44 @@ private void InitMethodParams() {
381402
resolvedMember = null;
382403
if (selectedMember is MethodBase methodBase) {
383404
parameterInfo = methodBase.GetParameters();
384-
if (methodBase is MethodInfo method && method.ContainsGenericParameters)
385-
typeResolver = new TypeResolverGUI(method);
405+
if (methodBase is MethodInfo method && method.ContainsGenericParameters) {
406+
if (mode == MethodMode.ExtensionMethod) {
407+
var parameters = method.GetParameters();
408+
var genericArgs = method.GetGenericArguments();
409+
if (parameters.Length > 0) {
410+
var firstParam = parameters[0].ParameterType;
411+
if (firstParam.IsGenericMethodParameter) {
412+
genericArgs[firstParam.GenericParameterPosition] = targetType;
413+
method = method.MakeGenericMethod(genericArgs);
414+
} else if (firstParam.ContainsGenericParameters) {
415+
var firstParamResolvedGenericArgs = firstParam.GetGenericArguments();
416+
var type = targetType;
417+
Type[] resolvedGenericArgs = null;
418+
var genericDefinitionType = firstParam.GetGenericTypeDefinition();
419+
while (type != null) {
420+
if (type.IsGenericType && type.GetGenericTypeDefinition() == genericDefinitionType) {
421+
resolvedGenericArgs = type.GetGenericArguments();
422+
break;
423+
}
424+
type = type.BaseType;
425+
}
426+
if (resolvedGenericArgs == null)
427+
foreach (var interfaceType in targetType.GetInterfaces())
428+
if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == genericDefinitionType) {
429+
resolvedGenericArgs = interfaceType.GetGenericArguments();
430+
break;
431+
}
432+
if (resolvedGenericArgs != null)
433+
for (int i = 0; i < genericArgs.Length; i++)
434+
if (firstParamResolvedGenericArgs[i].IsGenericParameter)
435+
genericArgs[i] = resolvedGenericArgs[firstParamResolvedGenericArgs[i].GenericParameterPosition];
436+
method = method.MakeGenericMethod(genericArgs);
437+
}
438+
}
439+
}
440+
if (method.ContainsGenericParameters)
441+
typeResolver = new TypeResolverGUI(method);
442+
}
386443
paramsFolded = parameterInfo.Length > 0;
387444
}
388445
CreateDrawers();
@@ -395,7 +452,7 @@ private void CreateDrawers() {
395452
foreach (var entry in parameters)
396453
entry?.Dispose();
397454
parameters = new MethodPropertyDrawer[parameterInfo.Length];
398-
for (int i = 0; i < parameterInfo.Length; i++) {
455+
for (int i = mode == MethodMode.ExtensionMethod ? 1 : 0; i < parameterInfo.Length; i++) {
399456
var info = parameterInfo[i];
400457
parameters[i] = new MethodPropertyDrawer(info, privateFields, obsolete);
401458
parameters[i].OnRequireRedraw += RequireRedraw;
@@ -405,7 +462,7 @@ private void CreateDrawers() {
405462
private void DrawComponent() {
406463
if (OnClose != null)
407464
EditorGUILayout.BeginHorizontal();
408-
EditorGUILayout.PrefixLabel(mode.ToString());
465+
EditorGUILayout.PrefixLabel(ObjectNames.NicifyVariableName(mode.ToString()));
409466
if (GUILayout.Button(selectedMethodIndex < 0 ? GUIContent.none : methodNames[selectedMethodIndex + 1].content, EditorStyles.popup))
410467
ShowSearchPopup();
411468
if (OnClose != null) {
@@ -434,7 +491,6 @@ private void DrawMethod() {
434491
case MethodMode.Indexer:
435492
paramsFolded = EditorGUILayout.Foldout(paramsFolded, $"Indexer ({parameters.Length})");
436493
break;
437-
case MethodMode.Method:
438494
default:
439495
paramsFolded = EditorGUILayout.Foldout(paramsFolded, $"{selectedMember.Name} ({parameters.Length})");
440496
break;
@@ -460,10 +516,10 @@ private void DrawMethod() {
460516
}
461517
if (mode != MethodMode.Indexer && component == null && selectedMember.IsInstanceMember())
462518
EditorGUILayout.HelpBox("Method requires an exists instance.", MessageType.Warning);
463-
if (parameterInfo.Length == 0)
519+
if (parameterInfo.Length == (mode == MethodMode.ExtensionMethod ? 1 : 0))
464520
EditorGUILayout.HelpBox("There is no parameters required for this method.", MessageType.Info);
465521
foreach (var drawer in parameters)
466-
drawer.Draw();
522+
drawer?.Draw();
467523
EditorGUILayout.EndVertical();
468524
EditorGUI.indentLevel--;
469525
}
@@ -502,7 +558,6 @@ private void DrawExecButton() {
502558
if (result != null) return;
503559
execute = GUILayout.Button("Create Property");
504560
break;
505-
case MethodMode.Method:
506561
default:
507562
execute = GUILayout.Button($"Execute {selectedMember.Name}");
508563
break;

Editor/UInspectorPlus/Helpers.cs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ internal enum PropertyType {
4040
internal enum MethodMode {
4141
Method,
4242
Constructor,
43-
Indexer
43+
Indexer,
44+
ExtensionMethod,
4445
}
4546

4647
internal struct ComponentMethod {
@@ -180,7 +181,7 @@ internal static bool IsInstanceMember(this MemberInfo member, bool defaultResult
180181
return defaultResult;
181182
}
182183

183-
internal static string GetMemberName(this MemberInfo member, bool simplifed = false, bool appendMemberName = true) {
184+
internal static string GetMemberName(this MemberInfo member, bool simplifed = false, bool appendMemberName = true, bool isExtensionMethod = false) {
184185
var ret = new StringBuilder();
185186
var props = new List<string>();
186187
if (member is FieldInfo field) {
@@ -193,6 +194,8 @@ internal static string GetMemberName(this MemberInfo member, bool simplifed = fa
193194
if (field.IsLiteral)
194195
props.Add(simplifed ? "C" : "Constant");
195196
} else if (member is MethodInfo method) {
197+
if (isExtensionMethod)
198+
props.Add(simplifed ? "E" : "Ext.");
196199
if (!method.IsPublic)
197200
props.Add(simplifed ? "P" : "Private");
198201
if (method.IsStatic)
@@ -222,6 +225,8 @@ internal static string GetMemberName(this MemberInfo member, bool simplifed = fa
222225
ret.JoinStringList(props, simplifed ? "" : ", ");
223226
if (props.Count > 0)
224227
ret.Append(") ");
228+
if (isExtensionMethod)
229+
ret.Append(member.DeclaringType.Name).Append('.');
225230
if (appendMemberName)
226231
ret.Append(member.Name);
227232
return ret.ToString();
@@ -519,6 +524,28 @@ internal static bool IsTypeRseolvable(Type genericType, Type targetType) {
519524
// Special checker to deal with "null" UnityEngine.Object (Internally null, but still exists in Mono heap)
520525
internal static bool IsInvalid(this object obj) => obj is UnityObject uObj ? !uObj : obj == null;
521526

527+
internal static bool TryRecoverInvalid<T>(ref T obj, string guid = null) where T : class {
528+
// If an object is an invalid UnityObject, try to recover it
529+
if (obj is UnityObject uObj && !uObj) {
530+
var originalType = obj.GetType();
531+
uObj = EditorUtility.InstanceIDToObject(uObj.GetInstanceID());
532+
if (!uObj && !string.IsNullOrEmpty(guid)) {
533+
var assetPath = AssetDatabase.GUIDToAssetPath(guid);
534+
if (!string.IsNullOrEmpty(assetPath)) {
535+
var asset = AssetDatabase.LoadAssetAtPath(assetPath, originalType);
536+
if (asset) uObj = asset;
537+
}
538+
}
539+
if (uObj && uObj.GetType() == originalType) {
540+
obj = (T)(object)uObj;
541+
return false;
542+
}
543+
obj = null; // Recover failed, set to null
544+
return true;
545+
}
546+
return obj == null;
547+
}
548+
522549
internal static bool IsInternalType(this Type type) => !(
523550
type.IsSubclassOf(typeof(MonoBehaviour)) ||
524551
type.IsSubclassOf(typeof(StateMachineBehaviour))

Editor/UInspectorPlus/InspectorChildWindow.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ internal class InspectorChildWindow: EditorWindow {
1616
private bool showObsolete;
1717
private bool showMethods;
1818
private Rect menuButtonRect;
19+
private object target;
20+
private string targetGuid;
21+
private Type targetType;
1922

2023
public static void Open(object target, bool showProps, bool showPrivate, bool showObsolete, bool showMethods, bool updateProps, MethodPropertyDrawer parent) =>
2124
CreateInstance<InspectorChildWindow>().InternalOpen(target, target.GetType(), showProps, showPrivate, showObsolete, showMethods, updateProps, parent);
@@ -30,13 +33,16 @@ private void InternalOpen(object target, Type targetType, bool showProps, bool s
3033
} else {
3134
drawer = InspectorDrawer.GetDrawer(target, targetType, true, showProps, showPrivate, showObsolete, showMethods);
3235
drawer.OnRequireRedraw += Repaint;
36+
if (target is UnityObject unityObject) AssetDatabase.TryGetGUIDAndLocalFileIdentifier(unityObject, out targetGuid, out long _);
3337
}
3438
this.showProps = showProps;
3539
this.showPrivate = showPrivate;
3640
this.showObsolete = showObsolete;
3741
this.showMethods = showMethods;
3842
this.parent = parent;
3943
this.updateProps = updateProps;
44+
this.target = target;
45+
this.targetType = targetType;
4046
ShowUtility();
4147
UpdateValues();
4248
isReadOnly = parent != null && parent.IsReadOnly && parent.requiredType != null && parent.requiredType.IsValueType;
@@ -59,6 +65,11 @@ private void OnGUI() {
5965
}
6066
return;
6167
}
68+
if (drawer == null && target != null) {
69+
InternalOpen(target, targetType, showProps, showPrivate, showObsolete, showMethods, updateProps, parent);
70+
EditorGUILayout.HelpBox("No drawer found.", MessageType.Error);
71+
return;
72+
}
6273
GUILayout.BeginHorizontal(EditorStyles.toolbar);
6374
drawer.searchText = Helper.ToolbarSearchField(drawer.searchText ?? string.Empty);
6475
GUILayout.FlexibleSpace();
@@ -136,8 +147,8 @@ private void OnInspectorUpdate() {
136147

137148
private void RefreshDrawer() {
138149
if (drawer == null) return;
150+
if (Helper.TryRecoverInvalid(ref drawer.target, targetGuid)) return;
139151
var target = drawer.target;
140-
if (target.IsInvalid()) return;
141152
drawer.Dispose();
142153
drawer = InspectorDrawer.GetDrawer(target, target.GetType(), true, showProps, showPrivate, showObsolete, showMethods);
143154
drawer.OnRequireRedraw += Repaint;

0 commit comments

Comments
 (0)