You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
CrowdControl/Assets/3rd/Plugins/UltEvents/Inspector/DrawerState.cs

376 lines
15 KiB
C#

3 months ago
// UltEvents // Copyright 2020 Kybernetik //
#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEditor;
namespace UltEvents.Editor
{
/// <summary>[Editor-Only]
/// Manages the GUI state used when drawing events.
/// </summary>
internal sealed class DrawerState
{
/************************************************************************************************************************/
/// <summary>The currently active state.</summary>
public static readonly DrawerState Current = new DrawerState();
/************************************************************************************************************************/
/// <summary>The <see cref="SerializedProperty"/> for the event currently being drawn.</summary>
public SerializedProperty EventProperty { get; private set; }
/// <summary>The event currently being drawn.</summary>
public UltEventBase Event { get; private set; }
/************************************************************************************************************************/
/// <summary>The <see cref="SerializedProperty"/> for the call currently being drawn.</summary>
public SerializedProperty CallProperty { get; private set; }
/// <summary>The <see cref="SerializedProperty"/> for the target of the call currently being drawn.</summary>
public SerializedProperty TargetProperty { get; private set; }
/// <summary>The <see cref="SerializedProperty"/> for the method name of the call currently being drawn.</summary>
public SerializedProperty MethodNameProperty { get; private set; }
/// <summary>The <see cref="SerializedProperty"/> for the persistent arguments array of the call currently being drawn.</summary>
public SerializedProperty PersistentArgumentsProperty { get; private set; }
/// <summary>The index of the call currently being drawn.</summary>
public int callIndex = -1;
/// <summary>The call currently being drawn.</summary>
public PersistentCall call;
/// <summary>The parameters of the call currently being drawn.</summary>
public ParameterInfo[] callParameters;
/// <summary>The index of the parameter currently being drawn.</summary>
public int parameterIndex;
/************************************************************************************************************************/
/// <summary>If true, each call will be stored so that subsequent calls can link to their return value.</summary>
public bool CachePreviousCalls { get; private set; }
/// <summary>The calls of the current event that come before the current call currently being drawn.</summary>
private readonly List<PersistentCall> PreviousCalls = new List<PersistentCall>();
/// <summary>The methods targeted by the calls of the event currently being drawn.</summary>
private readonly List<MethodBase> PersistentMethodCache = new List<MethodBase>();
/************************************************************************************************************************/
/// <summary>The parameter currently being drawn.</summary>
public ParameterInfo CurrentParameter
{
get { return callParameters[parameterIndex]; }
}
/************************************************************************************************************************/
/// <summary>Caches the event from the specified property and returns true as long as it is not null.</summary>
public bool TryBeginEvent(SerializedProperty eventProperty)
{
Event = eventProperty.GetValue<UltEventBase>();
if (Event == null)
return false;
EventProperty = eventProperty;
return true;
}
/// <summary>Cancels out a call to <see cref="TryBeginEvent"/>.</summary>
public void EndEvent()
{
EventProperty = null;
Event = null;
}
/************************************************************************************************************************/
/// <summary>Starts caching calls so that subsequent calls can link to earlier return values.</summary>
public void BeginCache()
{
CacheLinkedArguments();
CachePreviousCalls = true;
}
/// <summary>Cancels out a call to <see cref="EndCache"/>.</summary>
public void EndCache()
{
CachePreviousCalls = false;
PreviousCalls.Clear();
}
/************************************************************************************************************************/
/// <summary>Caches the call from the specified property.</summary>
public void BeginCall(SerializedProperty callProperty)
{
CallProperty = callProperty;
TargetProperty = GetTargetProperty(callProperty);
MethodNameProperty = GetMethodNameProperty(callProperty);
PersistentArgumentsProperty = GetPersistentArgumentsProperty(callProperty);
call = GetCall(callProperty);
}
/// <summary>Cancels out a call to <see cref="BeginCall"/>.</summary>
public void EndCall()
{
if (CachePreviousCalls)
PreviousCalls.Add(call);
call = null;
}
/************************************************************************************************************************/
/// <summary>Returns the property encapsulating the <see cref="PersistentCall.Target"/>.</summary>
public static SerializedProperty GetTargetProperty(SerializedProperty callProperty)
{
return callProperty.FindPropertyRelative(Names.PersistentCall.Target);
}
/// <summary>Returns the property encapsulating the <see cref="PersistentCall.MethodName"/>.</summary>
public static SerializedProperty GetMethodNameProperty(SerializedProperty callProperty)
{
return callProperty.FindPropertyRelative(Names.PersistentCall.MethodName);
}
/// <summary>Returns the property encapsulating the <see cref="PersistentCall.PersistentArguments"/>.</summary>
public static SerializedProperty GetPersistentArgumentsProperty(SerializedProperty callProperty)
{
return callProperty.FindPropertyRelative(Names.PersistentCall.PersistentArguments);
}
/// <summary>Returns the call encapsulated by the specified property.</summary>
public static PersistentCall GetCall(SerializedProperty callProperty)
{
return callProperty.GetValue<PersistentCall>();
}
/************************************************************************************************************************/
#region Linked Argument Cache
/************************************************************************************************************************/
/// <summary>Stores all the persistent methods in the current event.</summary>
public void CacheLinkedArguments()
{
PersistentMethodCache.Clear();
if (Event == null || Event._PersistentCalls == null)
return;
for (int i = 0; i < Event._PersistentCalls.Count; i++)
{
var call = Event._PersistentCalls[i];
PersistentMethodCache.Add(call != null ? call.GetMethodSafe() : null);
}
}
/************************************************************************************************************************/
/// <summary>Ensures that any linked parameters remain linked to the correct target index.</summary>
public void UpdateLinkedArguments()
{
if (Event == null ||
PersistentMethodCache.Count == 0)
return;
for (int i = 0; i < Event._PersistentCalls.Count; i++)
{
var call = Event._PersistentCalls[i];
if (call == null)
continue;
for (int j = 0; j < call._PersistentArguments.Length; j++)
{
var argument = call._PersistentArguments[j];
if (argument == null || argument._Type != PersistentArgumentType.ReturnValue)
continue;
var linkedMethod = PersistentMethodCache[argument.ReturnedValueIndex];
if (argument.ReturnedValueIndex < Event._PersistentCalls.Count)
{
var linkedCall = Event._PersistentCalls[argument.ReturnedValueIndex];
if (linkedMethod == (linkedCall != null ? linkedCall.GetMethodSafe() : null))
continue;
}
var index = IndexOfMethod(linkedMethod);
if (index >= 0)
argument.ReturnedValueIndex = index;
}
}
PersistentMethodCache.Clear();
}
/************************************************************************************************************************/
/// <summary>Returns the index of the persistent call that targets the specified `method` or -1 if there is none.</summary>
public int IndexOfMethod(MethodBase method)
{
for (int i = 0; i < Event._PersistentCalls.Count; i++)
{
var call = Event._PersistentCalls[i];
if ((call != null ? call.GetMethodSafe() : null) == method)
{
return i;
}
}
return -1;
}
/************************************************************************************************************************/
/// <summary>Returns the method cached from the persistent call at the specified `index`.</summary>
public MethodBase GetLinkedMethod(int index)
{
if (index >= 0 && index < PersistentMethodCache.Count)
return PersistentMethodCache[index];
else
return null;
}
/************************************************************************************************************************/
#endregion
/************************************************************************************************************************/
#region Previous Call Cache
/************************************************************************************************************************/
/// <summary>Tries to get the details of the a parameter or return value of the specified `type`.</summary>
public bool TryGetLinkable(Type type, out int linkIndex, out PersistentArgumentType linkType)
{
if (Event != null)
{
// Parameters.
var parameterTypes = Event.ParameterTypes;
for (int i = 0; i < parameterTypes.Length; i++)
{
if (type.IsAssignableFrom(parameterTypes[i]))
{
linkIndex = i;
linkType = PersistentArgumentType.Parameter;
return true;
}
}
// Return Values.
for (int i = 0; i < PreviousCalls.Count; i++)
{
var method = PreviousCalls[i].GetMethodSafe();
if (method == null)
continue;
if (type.IsAssignableFrom(method.GetReturnType()))
{
linkIndex = i;
linkType = PersistentArgumentType.ReturnValue;
return true;
}
}
}
linkIndex = -1;
linkType = PersistentArgumentType.None;
return false;
}
/************************************************************************************************************************/
/// <summary>Tries to get the details of the a parameter or return value of the current parameter type.</summary>
public bool TryGetLinkable(out int linkIndex, out PersistentArgumentType linkType)
{
if (callParameters != null)
{
return TryGetLinkable(CurrentParameter.ParameterType, out linkIndex, out linkType);
}
else
{
linkIndex = -1;
linkType = PersistentArgumentType.None;
return false;
}
}
/************************************************************************************************************************/
/// <summary>The number of persistent calls that came earlier in the current event.</summary>
public int PreviousCallCount
{
get { return PreviousCalls.Count; }
}
/************************************************************************************************************************/
/// <summary>Returns the persistent call at the specified index in the current event.</summary>
public PersistentCall GetPreviousCall(int index)
{
if (index >= 0 && index < PreviousCalls.Count)
return PreviousCalls[index];
else
return null;
}
/************************************************************************************************************************/
#endregion
/************************************************************************************************************************/
/// <summary>Copies the contents of the `other` state to overwrite this one.</summary>
public void CopyFrom(DrawerState other)
{
EventProperty = other.EventProperty;
Event = other.Event;
CallProperty = other.CallProperty;
TargetProperty = other.TargetProperty;
MethodNameProperty = other.MethodNameProperty;
PersistentArgumentsProperty = other.PersistentArgumentsProperty;
callIndex = other.callIndex;
call = other.call;
callParameters = other.callParameters;
parameterIndex = other.parameterIndex;
PreviousCalls.Clear();
PreviousCalls.AddRange(other.PreviousCalls);
}
/************************************************************************************************************************/
/// <summary>Clears all the details of this state.</summary>
public void Clear()
{
EventProperty = null;
Event = null;
CallProperty = null;
TargetProperty = null;
MethodNameProperty = null;
PersistentArgumentsProperty = null;
callIndex = -1;
call = null;
callParameters = null;
parameterIndex = 0;
PreviousCalls.Clear();
}
/************************************************************************************************************************/
}
}
#endif