Add support for using interfaces in Visual Scripting

This commit is contained in:
Wojtek Figat
2021-10-04 12:23:34 +02:00
parent ecf926a537
commit e92c22585a
8 changed files with 111 additions and 6 deletions

View File

@@ -338,6 +338,9 @@ namespace FlaxEditor.Content
/// <inheritdoc />
public bool IsClass => true;
/// <inheritdoc />
public bool IsInterface => false;
/// <inheritdoc />
public bool IsArray => false;
@@ -371,6 +374,12 @@ namespace FlaxEditor.Content
return Object.New(TypeName);
}
/// <inheritdoc />
public bool ImplementInterface(ScriptType c)
{
return BaseType.ImplementInterface(c);
}
/// <inheritdoc />
public bool HasAttribute(Type attributeType, bool inherit)
{

View File

@@ -79,6 +79,18 @@ namespace FlaxEditor.Content
return ScriptType.Null;
}
/// <inheritdoc />
public void GetTypes(List<ScriptType> result, Func<ScriptType, bool> checkFunc)
{
var visualScripts = VisualScriptItem.VisualScripts;
for (var i = 0; i < visualScripts.Count; i++)
{
var t = visualScripts[i].ScriptType;
if (checkFunc(t))
result.Add(t);
}
}
/// <inheritdoc />
public void GetDerivedTypes(ScriptType baseType, List<ScriptType> result, Func<ScriptType, bool> checkFunc)
{

View File

@@ -76,9 +76,14 @@ namespace FlaxEditor.Modules.SourceCodeEditing
/// </summary>
protected virtual void Search()
{
// Special case for attributes
if (_type.Type != null && new ScriptType(typeof(Attribute)).IsAssignableFrom(_type))
if (_type == ScriptType.Null)
{
// Special case for all types
TypeUtils.GetTypes(_list, _checkFunc, _checkAssembly);
}
else if (_type.Type != null && new ScriptType(typeof(Attribute)).IsAssignableFrom(_type))
{
// Special case for attributes
TypeUtils.GetTypesWithAttributeDefined(_type.Type, _list, _checkFunc, _checkAssembly);
}
else

View File

@@ -157,7 +157,7 @@ namespace FlaxEditor.Modules.SourceCodeEditing
/// <summary>
/// The all types collection from all assemblies (excluding C# system libraries). Includes only primitive and basic types from std lib.
/// </summary>
public readonly CachedTypesCollection All = new CachedAllTypesCollection(8096, new ScriptType(typeof(object)), type => true, HasAssemblyValidAnyTypes);
public readonly CachedTypesCollection All = new CachedAllTypesCollection(8096, ScriptType.Null, type => true, HasAssemblyValidAnyTypes);
/// <summary>
/// The all valid types collection for the Visual Script property types (includes basic types like int/float, structures, object references).

View File

@@ -775,6 +775,11 @@ namespace FlaxEditor.Scripting
/// </summary>
public bool IsClass => _managed != null ? _managed.IsClass : _custom != null && _custom.IsClass;
/// <summary>
/// Gets a value indicating whether the type is an interface.
/// </summary>
public bool IsInterface => _managed != null ? _managed.IsInterface : _custom != null && _custom.IsInterface;
/// <summary>
/// Gets a value indicating whether the type is an array.
/// </summary>
@@ -995,6 +1000,20 @@ namespace FlaxEditor.Scripting
return false;
}
/// <summary>
/// Determines whether the current type implements the specified interface type. Checks this type, its base classes and implemented interfaces base interfaces too.
/// </summary>
/// <param name="c">The type of the interface to check.</param>
/// <returns>True if this type implements the given interface, otherwise false.</returns>
public bool ImplementInterface(ScriptType c)
{
if (c._managed != null && _managed != null)
return c._managed.IsAssignableFrom(_managed);
if (_custom != null)
return _custom.ImplementInterface(c);
return false;
}
/// <summary>
/// Determines whether the specified object is an instance of the current type.
/// </summary>
@@ -1014,6 +1033,8 @@ namespace FlaxEditor.Scripting
/// <c>false</c> if none of these conditions are true, or if <paramref name="c" /> is <see cref="Null"/>.</returns>
public bool IsAssignableFrom(ScriptType c)
{
if (IsInterface)
return c.ImplementInterface(this);
while (c != Null)
{
if (c == this)
@@ -1377,6 +1398,11 @@ namespace FlaxEditor.Scripting
/// </summary>
bool IsClass { get; }
/// <summary>
/// Gets a value indicating whether the type is an interface.
/// </summary>
bool IsInterface { get; }
/// <summary>
/// Gets a value indicating whether the type is an array.
/// </summary>
@@ -1428,6 +1454,13 @@ namespace FlaxEditor.Scripting
/// <returns>The created instance of the object.</returns>
object CreateInstance();
/// <summary>
/// Determines whether the current type implements the specified interface type. Checks this type, its base classes and implemented interfaces base interfaces too.
/// </summary>
/// <param name="c">The type of the interface to check.</param>
/// <returns>True if this type implements the given interface, otherwise false.</returns>
bool ImplementInterface(ScriptType c);
/// <summary>
/// Determines whether the specified attribute was defined for this type.
/// </summary>
@@ -1613,6 +1646,13 @@ namespace FlaxEditor.Scripting
/// <returns>The type or null if failed.</returns>
ScriptType GetType(string typeName);
/// <summary>
/// Gets all the types within all the loaded assemblies.
/// </summary>
/// <param name="result">The result collection. Elements will be added to it. Clear it before usage.</param>
/// <param name="checkFunc">Additional callback used to check if the given type is valid. Returns true if add type, otherwise false.</param>
void GetTypes(List<ScriptType> result, Func<ScriptType, bool> checkFunc);
/// <summary>
/// Gets all the derived types from the given base type (excluding that type) within all the loaded assemblies.
/// </summary>

View File

@@ -151,6 +151,44 @@ namespace FlaxEditor.Scripting
customTypesInfo.GetDerivedTypes(baseType, result, checkFunc);
}
/// <summary>
/// Gets all the types within the given assembly.
/// </summary>
/// <param name="assembly">The target assembly to check its types.</param>
/// <param name="result">The result collection. Elements will be added to it. Clear it before usage.</param>
/// <param name="checkFunc">Additional callback used to check if the given type is valid. Returns true if add type, otherwise false.</param>
public static void GetTypes(Assembly assembly, List<ScriptType> result, Func<ScriptType, bool> checkFunc)
{
var types = assembly.GetTypes();
for (int i = 0; i < types.Length; i++)
{
var t = new ScriptType(types[i]);
if (checkFunc(t))
result.Add(t);
}
}
/// <summary>
/// Gets all the types from all the loaded assemblies.
/// </summary>
/// <param name="result">The result collection. Elements will be added to it. Clear it before usage.</param>
/// <param name="checkFunc">Additional callback used to check if the given type is valid. Returns true if add type, otherwise false.</param>
/// <param name="checkAssembly">Additional callback used to check if the given assembly is valid. Returns true if search for types in the given assembly, otherwise false.</param>
public static void GetTypes(List<ScriptType> result, Func<ScriptType, bool> checkFunc, Func<Assembly, bool> checkAssembly)
{
// C#/C++ types
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
for (int i = 0; i < assemblies.Length; i++)
{
if (checkAssembly(assemblies[i]))
GetTypes(assemblies[i], result, checkFunc);
}
// Custom types
foreach (var customTypesInfo in CustomTypes)
customTypesInfo.GetTypes(result, checkFunc);
}
/// <summary>
/// Gets all the types that have the given attribute defined within the given assembly.
/// </summary>

View File

@@ -178,7 +178,7 @@ namespace FlaxEditor.Surface
color = Colors.Enum;
else if (type.IsValueType)
color = Colors.Structures;
else if (new ScriptType(typeof(FlaxEngine.Object)).IsAssignableFrom(type))
else if (new ScriptType(typeof(FlaxEngine.Object)).IsAssignableFrom(type) || type.IsInterface)
color = Colors.Object;
else if (hint == ConnectionsHint.Vector)
color = Colors.Vector;

View File

@@ -1060,13 +1060,14 @@ bool ManagedBinaryModule::InvokeMethod(void* method, const Variant& instance, Sp
// Get instance object
void* mInstance = nullptr;
const bool withInterfaces = !mMethod->IsStatic() && mMethod->GetParentClass()->IsInterface();
if (!mMethod->IsStatic())
{
// Box instance into C# object
MonoObject* instanceObject = MUtils::BoxVariant(instance);
// Validate instance
if (!instanceObject || !mono_class_is_subclass_of(mono_object_get_class(instanceObject), mMethod->GetParentClass()->GetNative(), false))
if (!instanceObject || !mono_class_is_subclass_of(mono_object_get_class(instanceObject), mMethod->GetParentClass()->GetNative(), withInterfaces))
{
if (!instanceObject)
LOG(Error, "Failed to call method '{0}.{1}' (args count: {2}) without object instance", String(mMethod->GetParentClass()->GetFullName()), String(mMethod->GetName()), parametersCount);
@@ -1101,7 +1102,7 @@ bool ManagedBinaryModule::InvokeMethod(void* method, const Variant& instance, Sp
// Invoke the method
MonoObject* exception = nullptr;
MonoObject* resultObject = mMethod->Invoke(mInstance, params, &exception);
MonoObject* resultObject = withInterfaces ? mMethod->InvokeVirtual((MonoObject*)mInstance, params, &exception) : mMethod->Invoke(mInstance, params, &exception);
if (exception)
{
MException ex(exception);