Skip to content

Instantly share code, notes, and snippets.

@TylerTemp
Last active January 15, 2024 09:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save TylerTemp/d8c5b3d4e770bbae1714f626d1ab876f to your computer and use it in GitHub Desktop.
Save TylerTemp/d8c5b3d4e770bbae1714f626d1ab876f to your computer and use it in GitHub Desktop.
ExtNaughtyAttributes
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using DG.DOTweenEditor;
using DG.Tweening;
using Groobo;
using Groobo.Util;
using Groobo.Util.ExtInspector;
using NaughtyAttributes;
using NaughtyAttributes.Editor;
using UnityEditor;
using UnityEngine;
// 这个脚本基本就是复制的 NaughtyInspector;原本的扩展性比较低,所以干脆重来修改
namespace Util.ExtInspector.Editor
{
[CanEditMultipleObjects]
[CustomEditor(typeof(MonoBehaviour), true)]
public class ExtBaseInspector : UnityEditor.Editor
{
protected struct FieldWithInfo
{
public enum RenderType
{
None,
SerializedField,
NonSerializedField,
Method,
// DOTweenMethod,
NativeProperty,
GroupAttribute,
}
public enum GroupedType
{
// None,
BoxGroup,
Foldout,
DOTween,
}
public int inherentDepth;
public int order;
public RenderType renderType;
public GroupedType groupedType;
public string groupName;
// public MemberTypes memberType;
public FieldInfo fieldInfo;
public MethodInfo methodInfo;
public PropertyInfo propertyInfo;
public List<FieldInfo> fieldInfos;
public List<MethodInfo> methodInfos;
}
private readonly Dictionary<string, SavedBool> _foldouts = new Dictionary<string, SavedBool>();
private readonly List<FieldWithInfo> _fieldWithInfos = new List<FieldWithInfo>();
#region DoTween
private const string DoTweenMethodsGroupName = "__DOTWEEN_INSPECTOR_PREVIEW_EXT_GROUP_KEY__";
// private IEnumerable<MethodInfo> _doTweenPreview;
private class DOTweenStatus
{
public Sequence sequence;
public bool isPlaying;
public DOTweenPreviewAttribute.StopAction stopAction;
}
private readonly Dictionary<int, DOTweenStatus> _funcToDOTweenStatus = new Dictionary<int, DOTweenStatus>();
private bool _doTweenPreviewPlaying;
#endregion
private MonoScript _monoScript;
#region Disposable Renders
protected abstract class Renderer
{
protected readonly FieldWithInfo fieldWithInfo;
protected readonly SerializedObject serializedObject;
protected Renderer(UnityEditor.Editor editor, FieldWithInfo fieldWithInfo)
{
this.fieldWithInfo = fieldWithInfo;
serializedObject = editor.serializedObject;
}
public abstract void Render();
public virtual void AfterRender()
{
}
}
protected class SerializedFieldRenderer: Renderer
{
public SerializedFieldRenderer(UnityEditor.Editor editor, FieldWithInfo fieldWithInfo) : base(editor, fieldWithInfo)
{
}
public override void Render()
{
// FieldWithInfo fieldWithInfo = this.fiend
// EditorGUI.BeginProperty(rect, label, property);
// Debug.Log(fieldWithInfo.fieldInfo.Name);
SerializedProperty property = serializedObject.FindProperty(fieldWithInfo.fieldInfo.Name);
ValidatorAttribute[] validatorAttributes = PropertyUtility.GetAttributes<ValidatorAttribute>(property);
foreach (ValidatorAttribute validatorAttribute in validatorAttributes)
{
validatorAttribute.GetValidator().ValidateProperty(property);
}
// Check if enabled and draw
EditorGUI.BeginChangeCheck();
bool enabled = PropertyUtility.IsEnabled(property);
using (new EditorGUI.DisabledScope(disabled: !enabled))
{
// propertyFieldFunction.Invoke(rect, property, PropertyUtility.GetLabel(property), true);
PropertyAttribute[] attrs = fieldWithInfo.fieldInfo
.GetCustomAttributes(typeof (PropertyAttribute), false)
.Cast<PropertyAttribute>()
.OrderBy(each => each.order)
.ToArray();
PropertyDec[] fieldDecorations = attrs
.Select(each => each as PropertyDec)
.Where(each => each != null)
.ToArray();
EditorGUILayout.PropertyField(property);
foreach (PropertyDec propDec in fieldDecorations)
{
Rect lastRec = GUILayoutUtility.GetLastRect();
Rect curRect = new Rect
{
x = lastRec.xMin,
y = lastRec.yMax,
width = lastRec.width,
height = EditorGUIUtility.singleLineHeight,
};
PropertyDecDrawer drawer = (PropertyDecDrawer)Activator.CreateInstance(Type.GetType(propDec.DrawerClassName)!, propDec);
Rect rect = new Rect(curRect)
{
height = drawer.ExtGetPropertyHeight(property),
};
EditorGUILayout.GetControlRect(false, rect.height);
drawer.ExtOnGUI(rect, property);
}
}
// Call OnValueChanged callbacks
if (EditorGUI.EndChangeCheck())
{
PropertyUtility.CallOnValueChangedCallbacks(property);
}
}
}
protected class BoxGroupRenderer : Renderer
{
private readonly ExtBaseInspector _editor;
public BoxGroupRenderer(ExtBaseInspector editor, FieldWithInfo fieldWithInfo) : base(editor, fieldWithInfo)
{
_editor = editor;
}
public override void Render()
{
Debug.Assert(fieldWithInfo.fieldInfos.Count >= 1);
(string name, SerializedProperty prop)[] visibleProperties = fieldWithInfo.fieldInfos
.Select(fieldInfo => (name: fieldInfo.Name, prop: serializedObject.FindProperty(fieldInfo.Name)))
// .Where(each => PropertyUtility.IsVisible(each.prop))
.Where(each => _editor.IsVisible(each.prop))
.ToArray();
if (visibleProperties.Length == 0)
{
return;
}
NaughtyEditorGUI.BeginBoxGroup_Layout(fieldWithInfo.groupName);
foreach ((string _, SerializedProperty prop) in visibleProperties)
{
NaughtyEditorGUI.PropertyField_Layout(prop, includeChildren: true);
}
NaughtyEditorGUI.EndBoxGroup_Layout();
}
}
protected class FoldoutRenderer : Renderer
{
// private readonly Dictionary<string, SavedBool> _foldouts;
// private readonly Func<Dictionary<string, SavedBool>> _getFoldoutsBool;
private Renderer _rendererImplementation;
private readonly ExtBaseInspector _extBaseInspector;
// public FoldoutRenderer(Func<Dictionary<string, SavedBool>> getFoldoutsBool)
// {
// _getFoldoutsBool = getFoldoutsBool;
// }
public FoldoutRenderer(ExtBaseInspector editor, FieldWithInfo fieldWithInfo) : base(editor, fieldWithInfo)
{
_extBaseInspector = editor;
}
public override void Render()
{
Debug.Assert(fieldWithInfo.fieldInfos.Count >= 1);
(string name, SerializedProperty prop)[] visibleProperties = fieldWithInfo.fieldInfos
.Select(fieldInfo => (name: fieldInfo.Name, prop: serializedObject.FindProperty(fieldInfo.Name)))
// .Where(each => PropertyUtility.IsVisible(each.prop))
.Where(each => _extBaseInspector.IsVisible(each.prop))
.ToArray();
if (visibleProperties.Length == 0)
{
return;
}
string groupKey = visibleProperties[0].name;
// Dictionary<string, SavedBool> foldouts = _extBaseInspector.foldouts;
if (!_extBaseInspector._foldouts.ContainsKey(groupKey))
{
_extBaseInspector._foldouts[groupKey] = new SavedBool($"{serializedObject.targetObject.GetInstanceID()}.{groupKey}", false);
}
_extBaseInspector._foldouts[groupKey].Value = EditorGUILayout.Foldout(_extBaseInspector._foldouts[groupKey].Value, groupKey, true);
// ReSharper disable once InvertIf
if (_extBaseInspector._foldouts[groupKey].Value)
{
foreach ((string _, SerializedProperty prop) in visibleProperties)
{
NaughtyEditorGUI.PropertyField_Layout(prop, true);
}
}
}
}
protected class NonSerializedFieldRenderer : Renderer
{
public NonSerializedFieldRenderer(UnityEditor.Editor editor, FieldWithInfo fieldWithInfo) : base(editor, fieldWithInfo)
{
}
public override void Render() => NaughtyEditorGUI.NonSerializedField_Layout(serializedObject.targetObject,
fieldWithInfo.fieldInfo);
}
protected class MethodRenderer : Renderer
{
public MethodRenderer(UnityEditor.Editor editor, FieldWithInfo fieldWithInfo) : base(editor, fieldWithInfo)
{
}
public override void Render() => NaughtyEditorGUI.Button(serializedObject.targetObject, fieldWithInfo.methodInfo);
}
protected class NativeProperty : Renderer
{
public NativeProperty(UnityEditor.Editor editor, FieldWithInfo fieldWithInfo) : base(editor, fieldWithInfo)
{
}
public override void Render() => NaughtyEditorGUI.NativeProperty_Layout(serializedObject.targetObject, fieldWithInfo.propertyInfo);
}
protected class DOTweenRenderer : Renderer
{
private readonly ExtBaseInspector _extBaseInspector;
public DOTweenRenderer(ExtBaseInspector editor, FieldWithInfo fieldWithInfo) : base(editor, fieldWithInfo)
{
_extBaseInspector = editor;
}
public override void Render()
{
_extBaseInspector.DrawDoTweenPreviews(fieldWithInfo.methodInfos);
}
}
#endregion
public virtual void OnEnable()
{
if (serializedObject.targetObject)
{
try
{
_monoScript = MonoScript.FromMonoBehaviour((MonoBehaviour) serializedObject.targetObject);
}
catch (Exception)
{
_monoScript = MonoScript.FromScriptableObject((ScriptableObject) serializedObject.targetObject);
}
}
List<Type> types = GetSelfAndBaseTypes(target);
// List<FieldWithInfo> fieldWithInfos = new List<FieldWithInfo>();
string[] serializableFields = GetSerializedProperties().ToArray();
foreach ((Type systemType, int inherentDepth) in types.WithIndex(1))
{
// Debug.Log($"{inherentDepth}/{systemType}");
FieldInfo[] allFields = systemType
.GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic |
BindingFlags.Public | BindingFlags.DeclaredOnly);
// Debug.Log(allFields.FirstOrDefault(each => each.Name == "m_Script"));
// foreach (FieldInfo fieldInfo in allFields)
// {
// Debug.Log($"[type={systemType}]");
// Debug.Log($"Name : {fieldInfo.Name}");
// Debug.Log($"Declaring Type : {fieldInfo.DeclaringType}");
// Debug.Log($"IsPublic : {fieldInfo.IsPublic}");
// Debug.Log($"MemberType : {fieldInfo.MemberType}");
// Debug.Log($"FieldType : {fieldInfo.FieldType}");
// Debug.Log($"IsFamily : {fieldInfo.IsFamily}");
// Debug.Log($"serializable : {serializableFields.Contains(fieldInfo.Name)}");
// Debug.Log($"IsLiteral : {fieldInfo.IsLiteral}");
// Debug.Log($"IsStatic : {fieldInfo.IsStatic}");
// Debug.Log($"IsInitOnly : {fieldInfo.IsInitOnly}");
// }
#region SerializedField
IEnumerable<FieldInfo> serializableFieldInfos =
allFields.Where(fieldInfo =>
{
if (serializableFields.Contains(fieldInfo.Name))
{
return true;
}
// Name : <GetHitPoint>k__BackingField
if (fieldInfo.Name.StartsWith("<") && fieldInfo.Name.EndsWith(">k__BackingField"))
{
return serializedObject.FindProperty(fieldInfo.Name) != null;
}
// return !fieldInfo.IsLiteral // const
// && !fieldInfo.IsStatic // static
// && !fieldInfo.IsInitOnly;
return false;
}
// readonly
);
foreach (FieldInfo fieldInfo in serializableFieldInfos)
{
// Debug.Log($"Name : {fieldInfo.Name}");
// Debug.Log($"Declaring Type : {fieldInfo.DeclaringType}");
// Debug.Log($"IsPublic : {fieldInfo.IsPublic}");
// Debug.Log($"MemberType : {fieldInfo.MemberType}");
// Debug.Log($"FieldType : {fieldInfo.FieldType}");
// Debug.Log($"IsFamily : {fieldInfo.IsFamily}");
OrderedAttribute orderProp = fieldInfo.GetCustomAttribute<OrderedAttribute>();
int order = orderProp?.Order ?? -4;
BoxGroupAttribute boxGroupAttribute = fieldInfo.GetCustomAttribute<BoxGroupAttribute>();
FoldoutAttribute foldoutAttribute = fieldInfo.GetCustomAttribute<FoldoutAttribute>();
#region BoxGrouped
if (boxGroupAttribute != null)
{
string groupName = boxGroupAttribute.Name;
FieldWithInfo existsInfo = _fieldWithInfos.FirstOrDefault(each => each.groupName == groupName);
if (existsInfo.renderType == FieldWithInfo.RenderType.None)
{
// Debug.Log($"new group {groupName}: {fieldInfo.Name}");
_fieldWithInfos.Add(new FieldWithInfo
{
renderType = FieldWithInfo.RenderType.GroupAttribute,
groupedType = FieldWithInfo.GroupedType.BoxGroup,
groupName = groupName,
inherentDepth = inherentDepth,
order = order,
fieldInfos = new List<FieldInfo>{fieldInfo},
});
}
else
{
// Debug.Log($"add group {groupName}: {fieldInfo.Name}");
Debug.Assert(existsInfo.renderType == FieldWithInfo.RenderType.GroupAttribute);
existsInfo.fieldInfos.Add(fieldInfo);
}
continue;
}
#endregion
#region Foldout
if (foldoutAttribute != null)
{
string groupName = foldoutAttribute.Name;
FieldWithInfo existsInfo = _fieldWithInfos.FirstOrDefault(each => each.groupName == groupName);
if (existsInfo.renderType == FieldWithInfo.RenderType.None)
{
_fieldWithInfos.Add(new FieldWithInfo
{
renderType = FieldWithInfo.RenderType.GroupAttribute,
groupedType = FieldWithInfo.GroupedType.Foldout,
groupName = groupName,
inherentDepth = inherentDepth,
order = order,
fieldInfos = new List<FieldInfo>{fieldInfo},
});
}
else
{
Debug.Assert(existsInfo.renderType == FieldWithInfo.RenderType.GroupAttribute);
existsInfo.fieldInfos.Add(fieldInfo);
}
continue;
}
#endregion
_fieldWithInfos.Add(new FieldWithInfo
{
// memberType = fieldInfo.MemberType,
renderType = FieldWithInfo.RenderType.SerializedField,
fieldInfo = fieldInfo,
inherentDepth = inherentDepth,
order = order,
// serializable = true,
});
}
#endregion
#region nonSerFieldInfo
IEnumerable<FieldInfo> nonSerFieldInfos = allFields
.Where(f => f.GetCustomAttributes(typeof(ShowNonSerializedFieldAttribute), true).Length > 0);
foreach (FieldInfo nonSerFieldInfo in nonSerFieldInfos)
{
OrderedAttribute orderProp = nonSerFieldInfo.GetCustomAttribute<OrderedAttribute>();
int order = orderProp?.Order ?? -3;
_fieldWithInfos.Add(new FieldWithInfo
{
renderType = FieldWithInfo.RenderType.NonSerializedField,
// memberType = nonSerFieldInfo.MemberType,
fieldInfo = nonSerFieldInfo,
inherentDepth = inherentDepth,
order = order,
// serializable = false,
});
}
#endregion
#region Method
MethodInfo[] methodInfos = systemType
.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic |
BindingFlags.Public | BindingFlags.DeclaredOnly);
IEnumerable<MethodInfo> buttonMethodInfos = methodInfos.Where(m =>
m.GetCustomAttributes(typeof(ButtonAttribute), true).Length > 0);
foreach (MethodInfo methodInfo in buttonMethodInfos)
{
OrderedAttribute orderProp =
methodInfo.GetCustomAttribute<OrderedAttribute>();
int order = orderProp?.Order ?? -2;
_fieldWithInfos.Add(new FieldWithInfo
{
// memberType = MemberTypes.Method,
renderType = FieldWithInfo.RenderType.Method,
methodInfo = methodInfo,
inherentDepth = inherentDepth,
order = order,
});
}
IEnumerable<MethodInfo> doTweenMethodInfos = methodInfos.Where(m =>
m.GetCustomAttributes(typeof(DOTweenPreviewAttribute), true).Length > 0);
foreach (MethodInfo methodInfo in doTweenMethodInfos)
{
OrderedAttribute orderProp =
methodInfo.GetCustomAttribute<OrderedAttribute>();
int order = orderProp?.Order ?? -2;
FieldWithInfo existsInfo = _fieldWithInfos.FirstOrDefault(each => each.groupName == DoTweenMethodsGroupName);
if (existsInfo.renderType == FieldWithInfo.RenderType.None)
{
// Debug.Log($"new group {groupName}: {methodInfo.Name}");
_fieldWithInfos.Add(new FieldWithInfo
{
renderType = FieldWithInfo.RenderType.Method,
groupedType = FieldWithInfo.GroupedType.DOTween,
groupName = DoTweenMethodsGroupName,
inherentDepth = inherentDepth,
order = order,
methodInfos = new List<MethodInfo>{methodInfo},
});
}
else
{
// Debug.Log($"add group {groupName}: {fieldInfo.Name}");
Debug.Assert(existsInfo is { renderType: FieldWithInfo.RenderType.Method, groupedType: FieldWithInfo.GroupedType.DOTween });
existsInfo.methodInfos.Add(methodInfo);
}
}
#endregion
#region NativeProperty
IEnumerable<PropertyInfo> propertyInfos = systemType
.GetProperties(BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly)
.Where(p => p.GetCustomAttributes(typeof(ShowNativePropertyAttribute), true).Length > 0);
foreach (PropertyInfo propertyInfo in propertyInfos)
{
OrderedAttribute orderProp =
propertyInfo.GetCustomAttribute<OrderedAttribute>();
int order = orderProp?.Order ?? -1;
_fieldWithInfos.Add(new FieldWithInfo
{
// memberType = MemberTypes.Property,
renderType = FieldWithInfo.RenderType.NativeProperty,
propertyInfo = propertyInfo,
inherentDepth = inherentDepth,
order = order,
});
}
#endregion
}
_fieldWithInfos = _fieldWithInfos
.WithIndex()
.OrderBy(each => each.value.inherentDepth)
.ThenBy(each => each.value.order)
.ThenBy(each => each.index)
.Select(each => each.value)
.ToArray();
// foreach (FieldWithInfo fieldWithInfo in _fieldWithInfos)
// {
// Debug.Log($"{fieldWithInfo.inherentDepth}/{fieldWithInfo.order}/{fieldWithInfo.fieldInfo?.Name}");
// }
}
private IEnumerable<string> GetSerializedProperties()
{
// outSerializedProperties.Clear();
using SerializedProperty iterator = serializedObject.GetIterator();
// ReSharper disable once InvertIf
if (iterator.NextVisible(true))
{
do
{
// outSerializedProperties.Add(serializedObject.FindProperty(iterator.name));
yield return iterator.name;
}
while (iterator.NextVisible(false));
}
}
private bool IsVisible(ExtShowHideConditionBase showIfAttribute)
{
if (showIfAttribute == null)
{
return true;
}
string checkProp = showIfAttribute.propOrMethodName;
bool inverted = showIfAttribute.inverted;
// Debug.Log($"IsVisible {checkProp}, {inverted}");
List<Type> types = GetSelfAndBaseTypes(target);
// List<FieldWithInfo> fieldWithInfos = new List<FieldWithInfo>();
foreach (Type systemType in types)
{
FieldInfo matchedField = systemType
.GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic |
BindingFlags.Public | BindingFlags.DeclaredOnly)
.FirstOrDefault(each => each.Name == checkProp);
if (matchedField != null)
{
// Debug.Log($"matchedField={matchedField}, target={target}");
bool result;
try
{
object targetResult = matchedField.GetValue(target);
// Debug.Log($"targetResult={targetResult}; equal null: {targetResult == null} {targetResult.Equals((object) null)}");
result = !targetResult.Equals(null);
}
catch (NullReferenceException)
{
result = false;
}
bool returnResult = inverted ? !result : result;
// Debug.Log($"return result = {returnResult}/direct={result}");
return returnResult;
}
#region Method
MethodInfo matchedMethod = systemType
.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic |
BindingFlags.Public | BindingFlags.DeclaredOnly)
.FirstOrDefault(each => each.Name == checkProp);
if (matchedMethod != null)
{
ParameterInfo[] methodParams = matchedMethod.GetParameters();
Debug.Assert(methodParams.All(p => p.IsOptional));
Debug.Assert(matchedMethod.ReturnType == typeof(bool));
bool result = (bool)matchedMethod.Invoke(target, methodParams.Select(p => p.DefaultValue).ToArray());
return inverted ? !result : result;
}
#endregion
#region NativeProperty
PropertyInfo matchedProperty = systemType
.GetProperties(BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly)
.FirstOrDefault(each => each.Name == checkProp);;
if (matchedProperty != null)
{
bool result = (bool)matchedProperty.GetValue(null);
return inverted ? !result : result;
}
#endregion
}
throw new ArgumentException($"{checkProp} not found in target {target}");
}
private bool IsVisible(SerializedProperty serializedProp) =>
IsVisible(PropertyUtility.GetAttribute<ExtShowHideConditionBase>(serializedProp));
public virtual void OnDisable()
{
ReorderableListPropertyDrawer.Instance.ClearCache();
// DOTweenEditorPreview.Stop();
// _doTweenPreviewPlaying = false;
DoTweenPreviewStop();
}
public override void OnInspectorGUI()
{
if (target == null)
{
Debug.LogError("The target object is null. Check for missing scripts.");
return;
}
// UnityEngine.Object scriptObject = GetScriptObject();
if(_monoScript)
{
EditorGUI.BeginDisabledGroup(true);
EditorGUILayout.ObjectField("Script", _monoScript, typeof(MonoScript), false);
EditorGUILayout.Space();
EditorGUI.EndDisabledGroup();
}
serializedObject.Update();
foreach (FieldWithInfo fieldWithInfo in _fieldWithInfos)
{
Renderer renderer = MakeRenderer(fieldWithInfo);
if(renderer != null)
{
renderer.Render();
renderer.AfterRender();
}
}
serializedObject.ApplyModifiedProperties();
// base.OnInspectorGUI();
}
private static List<Type> GetSelfAndBaseTypes(object target)
{
List<Type> types = new List<Type>()
{
target.GetType(),
};
while (types.Last().BaseType != null)
{
types.Add(types.Last().BaseType);
}
types.Reverse();
return types;
}
#region DOTween
private void DrawDoTweenPreviews(IReadOnlyList<MethodInfo> methodInfos)
{
// if ( )
// {
// return;
// }
Debug.Assert(methodInfos.Count > 0);
Rect labelTitleRect = EditorGUILayout.GetControlRect(false);
const string title = "DOTween Preview";
const float titleBtnWidth = 30f;
// float titleWidth = EditorStyles.label.CalcSize(new GUIContent(title)).x + 20f;
Rect titleRect = new Rect(labelTitleRect)
{
width = labelTitleRect.width - titleBtnWidth * 2,
};
EditorGUI.LabelField(titleRect, title, new GUIStyle("label")
{
fontStyle = FontStyle.Bold,
});
Rect titleBtn1Rect = new Rect(labelTitleRect)
{
x = titleRect.width,
width = titleBtnWidth,
};
Rect titleBtn2Rect = new Rect(labelTitleRect)
{
x = titleRect.width + titleBtnWidth,
width = titleBtnWidth,
};
EditorGUI.BeginDisabledGroup(_doTweenPreviewPlaying);
if (GUI.Button(titleBtn1Rect, "▶"))
{
DoTweenPreviewPlay();
}
EditorGUI.EndDisabledGroup();
EditorGUI.BeginDisabledGroup(!_doTweenPreviewPlaying);
if (GUI.Button(titleBtn2Rect, "■"))
{
DoTweenPreviewStop();
}
EditorGUI.EndDisabledGroup();
foreach ((MethodInfo methodInfo, bool isLast) in methodInfos.Select((each, index) => (each, index == methodInfos.Count - 1)))
{
Rect lineRect = EditorGUILayout.GetControlRect(false);
int methodHash = methodInfo.GetHashCode();
if (!_funcToDOTweenStatus.TryGetValue(methodHash, out DOTweenStatus status))
{
status = new DOTweenStatus();
}
DOTweenPreviewAttribute previewAttribute = (DOTweenPreviewAttribute)methodInfo.GetCustomAttributes(typeof(DOTweenPreviewAttribute), true)[0];
status.stopAction = previewAttribute.onManualStop;
string previewText = string.IsNullOrEmpty(previewAttribute.text) ? ObjectNames.NicifyVariableName(methodInfo.Name) : previewAttribute.text;
GUI.Label(lineRect, new GUIContent($"{(isLast ? "└" : "├")}{previewText}"));
float totalWidth = lineRect.width;
const float btnWidth = 30f;
Rect playBtnRect = new Rect(lineRect)
{
x = totalWidth - btnWidth * 3,
width = btnWidth,
};
Rect stopBtnRect = new Rect(lineRect)
{
x = totalWidth - btnWidth * 2,
width = btnWidth,
};
Rect restartBtnRect = new Rect(lineRect)
{
x = totalWidth - btnWidth,
width = btnWidth,
};
EditorGUI.BeginChangeCheck();
status.isPlaying = GUI.Toggle(playBtnRect, status.isPlaying, status.isPlaying? "‖ ‖": "▶", "Button");
if (EditorGUI.EndChangeCheck())
{
if (status.isPlaying)
{
DoTweenPreviewPlay();
if (status.sequence == null)
{
object[] defaultParams = methodInfo.GetParameters().Select(p => p.DefaultValue).ToArray();
Sequence methodResult = (Sequence)methodInfo.Invoke(target, defaultParams);
status.sequence = methodResult;
DOTweenEditorPreview.PrepareTweenForPreview(methodResult);
}
else
{
status.sequence.Play();
}
}
else
{
status.sequence.Pause();
}
}
EditorGUI.BeginDisabledGroup(!status.isPlaying);
if (GUI.Button(stopBtnRect, "■"))
{
StopDOTweenStatus(status);
status = new DOTweenStatus();
}
EditorGUI.EndDisabledGroup();
if (GUI.Button(restartBtnRect, "↻"))
{
DoTweenPreviewPlay();
if (status.sequence == null)
{
object[] defaultParams = methodInfo.GetParameters().Select(p => p.DefaultValue).ToArray();
Sequence methodResult = (Sequence)methodInfo.Invoke(target, defaultParams);
status.sequence = methodResult;
DOTweenEditorPreview.PrepareTweenForPreview(methodResult);
}
else
{
status.sequence.Restart();
}
status.isPlaying = true;
}
_funcToDOTweenStatus[methodHash] = status;
}
}
private void DoTweenPreviewPlay()
{
if (_doTweenPreviewPlaying)
{
return;
}
_doTweenPreviewPlaying = true;
DOTweenEditorPreview.Start();
}
private void DoTweenPreviewStop()
{
if (!_doTweenPreviewPlaying)
{
return;
}
_doTweenPreviewPlaying = false;
foreach (DOTweenStatus status in _funcToDOTweenStatus.Values)
{
StopDOTweenStatus(status);
}
_funcToDOTweenStatus.Clear();
DOTweenEditorPreview.Stop();
}
private static void StopDOTweenStatus(DOTweenStatus status)
{
if (status.sequence == null)
{
return;
}
switch (status.stopAction)
{
case DOTweenPreviewAttribute.StopAction.None:
status.sequence.Pause();
break;
case DOTweenPreviewAttribute.StopAction.Complete:
status.sequence.Complete();
break;
case DOTweenPreviewAttribute.StopAction.Rewind:
status.sequence.Rewind();
break;
default:
throw new ArgumentOutOfRangeException();
}
}
#endregion
#region 对外接口
protected virtual Renderer MakeRenderer(FieldWithInfo fieldWithInfo)
{
// Debug.Log($"field {fieldWithInfo.fieldInfo?.Name}/{fieldWithInfo.fieldInfo?.GetCustomAttribute<ExtShowHideConditionBase>()}");
switch (fieldWithInfo.renderType, fieldWithInfo.groupedType)
{
case (FieldWithInfo.RenderType.SerializedField, _):
{
// Debug.Log($"field {fieldWithInfo.fieldInfo.Name}/{fieldWithInfo.fieldInfo.GetCustomAttribute<ExtShowHideConditionBase>()}");
return IsVisible(fieldWithInfo.fieldInfo.GetCustomAttribute<ExtShowHideConditionBase>())
? new SerializedFieldRenderer(this, fieldWithInfo)
: null;
}
case (FieldWithInfo.RenderType.GroupAttribute, FieldWithInfo.GroupedType.BoxGroup):
{
return new BoxGroupRenderer(this, fieldWithInfo);
}
case (FieldWithInfo.RenderType.GroupAttribute, FieldWithInfo.GroupedType.Foldout):
{
return new FoldoutRenderer(this, fieldWithInfo);
}
case (FieldWithInfo.RenderType.NonSerializedField, _):
return IsVisible(fieldWithInfo.fieldInfo.GetCustomAttribute<ExtShowHideConditionBase>())
? new NonSerializedFieldRenderer(this, fieldWithInfo)
: null;
case (FieldWithInfo.RenderType.Method, FieldWithInfo.GroupedType.DOTween):
return IsVisible(fieldWithInfo.methodInfos[0].GetCustomAttribute<ExtShowHideConditionBase>())
? new DOTweenRenderer(this, fieldWithInfo)
: null;
case (FieldWithInfo.RenderType.Method, _):
return IsVisible(fieldWithInfo.methodInfo.GetCustomAttribute<ExtShowHideConditionBase>())
? new MethodRenderer(this, fieldWithInfo)
: null;
case (FieldWithInfo.RenderType.NativeProperty, _):
return IsVisible(fieldWithInfo.propertyInfo.GetCustomAttribute<ExtShowHideConditionBase>())
? new NativeProperty(this, fieldWithInfo)
: null;
default:
throw new ArgumentOutOfRangeException();
}
}
#endregion
}
}
using System;
using System.Runtime.CompilerServices;
namespace Groobo.Util.ExtInspector
{
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Property)]
public class OrderedAttribute: Attribute
{
public int Order { get; }
public OrderedAttribute([CallerLineNumber] int order = 0)
{
Order = order;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment