Add support for array of Visual Script objects

This commit is contained in:
Wojtek Figat
2022-05-01 20:42:05 +02:00
parent c5bfdc66a4
commit 74b23d0e00
20 changed files with 221 additions and 62 deletions

View File

@@ -375,9 +375,9 @@ namespace FlaxEditor.Content
}
/// <inheritdoc />
public bool ImplementInterface(ScriptType c)
public bool HasInterface(ScriptType c)
{
return BaseType.ImplementInterface(c);
return BaseType.HasInterface(c);
}
/// <inheritdoc />

View File

@@ -548,7 +548,7 @@ namespace FlaxEditor.CustomEditors
text = text.Remove(idx, endIdx - idx);
}
}
else if (new ScriptType(typeof(FlaxEngine.Object)).IsAssignableFrom(Values.Type))
else if (ScriptType.FlaxObject.IsAssignableFrom(Values.Type))
{
// Object reference
text = JsonSerializer.GetStringID(Values[0] as FlaxEngine.Object);
@@ -598,7 +598,7 @@ namespace FlaxEditor.CustomEditors
return false;
}
}
else if (new ScriptType(typeof(FlaxEngine.Object)).IsAssignableFrom(Values.Type))
else if (ScriptType.FlaxObject.IsAssignableFrom(Values.Type))
{
// Object reference
if (text.Length != 32)

View File

@@ -155,7 +155,7 @@ namespace FlaxEditor.CustomEditors
}
if (targetType.IsGenericType)
{
if (DictionaryEditor.CanEditType(targetTypeType))
if (targetTypeType.GetGenericTypeDefinition() == typeof(Dictionary<,>))
{
return new DictionaryEditor();
}

View File

@@ -21,7 +21,7 @@ namespace FlaxEditor.CustomEditors.Editors
{
var arrayType = Values.Type;
var elementType = arrayType.GetElementType();
return Array.CreateInstance(elementType, size);
return TypeUtils.CreateArrayInstance(elementType, size);
}
/// <inheritdoc />
@@ -35,7 +35,7 @@ namespace FlaxEditor.CustomEditors.Editors
// Allocate new array
var arrayType = Values.Type;
var elementType = arrayType.GetElementType();
var newValues = Array.CreateInstance(elementType, newSize);
var newValues = TypeUtils.CreateArrayInstance(elementType, newSize);
var sharedCount = Mathf.Min(oldSize, newSize);
if (array != null && sharedCount > 0)
@@ -52,7 +52,7 @@ namespace FlaxEditor.CustomEditors.Editors
else
{
// Initialize new entries with default values
var defaultValue = TypeUtils.GetDefaultValue(new ScriptType(elementType));
var defaultValue = TypeUtils.GetDefaultValue(elementType);
for (int i = oldSize; i < newSize; i++)
newValues.SetValue(defaultValue, i);
}
@@ -60,7 +60,7 @@ namespace FlaxEditor.CustomEditors.Editors
else if (newSize > 0)
{
// Initialize new entries with default values
var defaultValue = TypeUtils.GetDefaultValue(new ScriptType(elementType));
var defaultValue = TypeUtils.GetDefaultValue(elementType);
for (int i = 0; i < newSize; i++)
newValues.SetValue(defaultValue, i);
}
@@ -79,7 +79,7 @@ namespace FlaxEditor.CustomEditors.Editors
var size = array.Length;
var arrayType = Values.Type;
var elementType = arrayType.GetElementType();
var cloned = Array.CreateInstance(elementType, size);
var cloned = TypeUtils.CreateArrayInstance(elementType, size);
Array.Copy(array, 0, cloned, 0, size);

View File

@@ -101,7 +101,7 @@ namespace FlaxEditor.CustomEditors.Editors
get
{
var type = Values.Type;
return new ScriptType(type.IsGenericType ? type.GetGenericArguments()[0] : type.GetElementType());
return type.IsGenericType ? new ScriptType(type.GetGenericArguments()[0]) : type.GetElementType();
}
}

View File

@@ -142,22 +142,6 @@ namespace FlaxEditor.CustomEditors.Editors
private bool _canEditKeys;
private bool _keyEdited;
/// <summary>
/// Determines whether this editor[can edit the specified dictionary type.
/// </summary>
/// <param name="type">Type of the dictionary.</param>
/// <returns>True if can edit, otherwise false.</returns>
public static bool CanEditType(Type type)
{
// Ensure it's a generic dictionary type
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<,>))
{
return true;
}
return false;
}
/// <summary>
/// Gets the length of the collection.
/// </summary>

View File

@@ -0,0 +1,148 @@
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
using System;
using System.Reflection;
using FlaxEditor.Content;
using FlaxEngine;
namespace FlaxEditor.Scripting
{
/// <summary>
/// The implementation of the <see cref="IScriptType"/> for array of custom script type.
/// </summary>
[HideInEditor]
internal sealed class ScriptTypeArray : IScriptType
{
/// <summary>
/// The array element type.
/// </summary>
public readonly ScriptType ElementType;
/// <summary>
/// Initializes a new instance of the <see cref="ScriptTypeArray"/> class.
/// </summary>
/// <param name="elementType">Type of the array element.</param>
public ScriptTypeArray(ScriptType elementType)
{
ElementType = elementType;
}
/// <inheritdoc />
public string Name => ElementType + "[]";
/// <inheritdoc />
public string Namespace => ElementType.Namespace;
/// <inheritdoc />
public string TypeName
{
get
{
var result = ElementType + "[]";
var nameSpace = ElementType.Namespace;
if (nameSpace.Length != 0)
result = nameSpace + '.';
return result;
}
}
/// <inheritdoc />
public bool IsPublic => ElementType.IsPublic;
/// <inheritdoc />
public bool IsAbstract => false;
/// <inheritdoc />
public bool IsSealed => true;
/// <inheritdoc />
public bool IsEnum => false;
/// <inheritdoc />
public bool IsClass => true;
/// <inheritdoc />
public bool IsInterface => false;
/// <inheritdoc />
public bool IsArray => true;
/// <inheritdoc />
public bool IsValueType => false;
/// <inheritdoc />
public bool IsGenericType => false;
/// <inheritdoc />
public bool IsReference => false;
/// <inheritdoc />
public bool IsPointer => false;
/// <inheritdoc />
public bool IsStatic => false;
/// <inheritdoc />
public bool CanCreateInstance => false;
/// <inheritdoc />
public ScriptType BaseType => new ScriptType(typeof(Array));
/// <inheritdoc />
public ContentItem ContentItem => null;
/// <inheritdoc />
public object CreateInstance()
{
return null;
}
/// <inheritdoc />
public bool HasInterface(ScriptType c)
{
return false;
}
/// <inheritdoc />
public bool HasAttribute(Type attributeType, bool inherit)
{
return false;
}
/// <inheritdoc />
public object[] GetAttributes(bool inherit)
{
return Utils.GetEmptyArray<object>();
}
/// <inheritdoc />
public ScriptMemberInfo[] GetMembers(string name, MemberTypes type, BindingFlags bindingAttr)
{
return Utils.GetEmptyArray<ScriptMemberInfo>();
}
/// <inheritdoc />
public ScriptMemberInfo[] GetMembers(BindingFlags bindingAttr)
{
return Utils.GetEmptyArray<ScriptMemberInfo>();
}
/// <inheritdoc />
public ScriptMemberInfo[] GetFields(BindingFlags bindingAttr)
{
return Utils.GetEmptyArray<ScriptMemberInfo>();
}
/// <inheritdoc />
public ScriptMemberInfo[] GetProperties(BindingFlags bindingAttr)
{
return Utils.GetEmptyArray<ScriptMemberInfo>();
}
/// <inheritdoc />
public ScriptMemberInfo[] GetMethods(BindingFlags bindingAttr)
{
return Utils.GetEmptyArray<ScriptMemberInfo>();
}
}
}

View File

@@ -113,7 +113,7 @@ namespace FlaxEditor.Scripting
/// </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);
bool HasInterface(ScriptType c);
/// <summary>
/// Determines whether the specified attribute was defined for this type.

View File

@@ -703,6 +703,11 @@ namespace FlaxEditor.Scripting
/// </summary>
public static readonly ScriptType Object = new ScriptType(typeof(object));
/// <summary>
/// A <see cref="ScriptType" /> that is FlaxEngine.Object.
/// </summary>
public static readonly ScriptType FlaxObject = new ScriptType(typeof(FlaxEngine.Object));
/// <summary>
/// Gets the type of the script as <see cref="System.Type"/>.
/// </summary>
@@ -827,7 +832,7 @@ namespace FlaxEditor.Scripting
{
if (_managed != null)
return _managed.GetConstructor(Type.EmptyTypes) != null;
return _custom.CanCreateInstance;
return _custom?.CanCreateInstance ?? false;
}
}
@@ -1003,12 +1008,12 @@ namespace FlaxEditor.Scripting
/// </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)
public bool HasInterface(ScriptType c)
{
if (c._managed != null && _managed != null)
return c._managed.IsAssignableFrom(_managed);
if (_custom != null)
return _custom.ImplementInterface(c);
return _custom.HasInterface(c);
return false;
}
@@ -1032,7 +1037,7 @@ namespace FlaxEditor.Scripting
public bool IsAssignableFrom(ScriptType c)
{
if (IsInterface)
return c.ImplementInterface(this);
return c.HasInterface(this);
while (c != Null)
{
if (c == this)
@@ -1075,11 +1080,13 @@ namespace FlaxEditor.Scripting
/// When overridden in a derived class, returns the type of the object encompassed or referred to by the current array, pointer or reference type.
/// </summary>
/// <returns>The type of the object encompassed or referred to by the current array, pointer, or reference type, or <see langword="null" /> if the current type is not an array or a pointer, or is not passed by reference, or represents a generic type or a type parameter in the definition of a generic type or generic method.</returns>
public Type GetElementType()
public ScriptType GetElementType()
{
if (_managed != null)
return _managed.GetElementType();
throw new NotImplementedException("TODO: Script.Type.GetElementType for custom types");
return new ScriptType(_managed.GetElementType());
if (_custom is ScriptTypeArray array)
return array.ElementType;
return Null;
}
/// <summary>
@@ -1112,7 +1119,9 @@ namespace FlaxEditor.Scripting
{
if (_managed != null)
return new ScriptType(_managed.MakeArrayType());
throw new NotImplementedException("TODO: Script.Type.MakeArrayType for custom types");
if (_custom != null)
return new ScriptType(new ScriptTypeArray(this));
return Null;
}
/// <summary>

View File

@@ -335,9 +335,7 @@ namespace FlaxEditor.Scripting
{
var type = assembly.GetType(typeName);
if (type != null)
{
return new ScriptType(type);
}
}
}
@@ -346,8 +344,18 @@ namespace FlaxEditor.Scripting
{
var type = customTypesInfo.GetType(typeName);
if (type)
{
return type;
}
if (typeName.EndsWith("[]"))
{
if (typeName[0] == '.')
typeName = typeName.Substring(1);
typeName = typeName.Substring(0, typeName.Length - 2);
foreach (var customTypesInfo in CustomTypes)
{
var type = customTypesInfo.GetType(typeName);
if (type)
return type.MakeArrayType();
}
}
@@ -361,10 +369,10 @@ namespace FlaxEditor.Scripting
/// <returns>The created object or null if failed.</returns>
public static object CreateInstance(string typeName)
{
var type = GetType(typeName);
if (type)
object obj = null;
ScriptType type = GetType(typeName);
if (type && type.CanCreateInstance)
{
object obj = null;
try
{
return obj = type.CreateInstance();
@@ -373,11 +381,19 @@ namespace FlaxEditor.Scripting
{
Debug.LogException(ex);
}
return obj;
}
return obj;
}
return null;
/// <summary>
/// Creates a one-dimensional <see cref="T:System.Array" /> of the specified type and length.
/// </summary>
/// <param name="elementType">The type of the array to create.</param>
/// <param name="size">The length of the array to create.</param>
/// <returns>The created object or null if failed.</returns>
public static Array CreateArrayInstance(ScriptType elementType, int size)
{
return Array.CreateInstance(GetType(elementType), size);
}
/// <summary>
@@ -402,6 +418,8 @@ namespace FlaxEditor.Scripting
/// <returns>The managed type.</returns>
public static Type GetType(ScriptType type)
{
if (type == ScriptType.Null)
return null;
while (type.Type == null)
type = type.BaseType;
return type.Type;

View File

@@ -16,7 +16,7 @@ namespace FlaxEditor.Surface.Archetypes
{
if (type == ScriptType.Null)
return box.DefaultType;
return box.DefaultType != null ? new ScriptType(type.GetElementType()) : type;
return box.DefaultType != null ? type.GetElementType() : type;
}
internal static ScriptType GetDictionaryItemType(Box box, ScriptType type)

View File

@@ -181,9 +181,9 @@ namespace FlaxEditor.Surface.Archetypes
private NodeElementArchetype[] GetElementArchetypes(SurfaceParameter selected)
{
if (selected != null && selected.Type.Type != null)
if (selected != null && selected.Type != ScriptType.Null)
{
if (Prototypes != null && Prototypes.TryGetValue(selected.Type.Type, out var elements))
if (selected.Type.Type != null && Prototypes != null && Prototypes.TryGetValue(selected.Type.Type, out var elements))
{
// Special case for Normal Maps
if (selected.Type.Type == typeof(Texture) && UseNormalMaps && selected.Value != null)

View File

@@ -796,7 +796,7 @@ namespace FlaxEditor.Surface.Archetypes
{
_picker = new TypePickerControl
{
Type = new ScriptType(typeof(FlaxEngine.Object)),
Type = ScriptType.FlaxObject,
Bounds = new Rectangle(FlaxEditor.Surface.Constants.NodeMarginX + 20, FlaxEditor.Surface.Constants.NodeMarginY + FlaxEditor.Surface.Constants.NodeHeaderSize, 160, 16),
Parent = this,
};
@@ -833,7 +833,7 @@ namespace FlaxEditor.Surface.Archetypes
{
var type = TypeUtils.GetType((string)Values[0]);
var box = (OutputBox)GetBox(0);
box.CurrentType = type ? type : new ScriptType(typeof(FlaxEngine.Object));
box.CurrentType = type ? type : ScriptType.FlaxObject;
}
/// <inheritdoc />
@@ -905,7 +905,7 @@ namespace FlaxEditor.Surface.Archetypes
{
_picker = new TypePickerControl
{
Type = new ScriptType(typeof(FlaxEngine.Object)),
Type = ScriptType.FlaxObject,
Bounds = new Rectangle(FlaxEditor.Surface.Constants.NodeMarginX + 20, FlaxEditor.Surface.Constants.NodeMarginY + FlaxEditor.Surface.Constants.NodeHeaderSize, 160, 16),
Parent = this,
};

View File

@@ -1078,7 +1078,7 @@ namespace FlaxEditor.Surface.Elements
object obj;
var type = CurrentType;
if (new ScriptType(typeof(FlaxEngine.Object)).IsAssignableFrom(type))
if (ScriptType.FlaxObject.IsAssignableFrom(type))
{
// Object reference
if (text.Length != 32)

View File

@@ -157,7 +157,7 @@ namespace FlaxEditor.Surface
GetConnectionColor(type, hint, out color);
}
else if (type.IsArray)
GetConnectionColor(new ScriptType(type.GetElementType()), hint, out color);
GetConnectionColor(type.GetElementType(), hint, out color);
else if (type.Type == typeof(void))
color = Colors.Impulse;
else if (type.Type == typeof(bool))
@@ -180,7 +180,7 @@ namespace FlaxEditor.Surface
color = Colors.Enum;
else if (type.IsValueType)
color = Colors.Structures;
else if (new ScriptType(typeof(FlaxEngine.Object)).IsAssignableFrom(type) || type.IsInterface)
else if (ScriptType.FlaxObject.IsAssignableFrom(type) || type.IsInterface)
color = Colors.Object;
else if (hint == ConnectionsHint.Vector)
color = Colors.Vector;

View File

@@ -76,7 +76,7 @@ namespace FlaxEditor.Surface
// Implicit casting is supported for object reference to test whenever it is valid
var toType = to.Type;
if (_supportsImplicitCastFromObjectToBoolean && toType == typeof(bool) && new ScriptType(typeof(FlaxEngine.Object)).IsAssignableFrom(from))
if (_supportsImplicitCastFromObjectToBoolean && toType == typeof(bool) && ScriptType.FlaxObject.IsAssignableFrom(from))
{
return true;
}

View File

@@ -41,7 +41,7 @@ namespace FlaxEditor.Utilities
// Special case for collections
if (Index != null)
result = new ScriptType(result.GetElementType());
result = result.GetElementType();
return result;
}

View File

@@ -64,11 +64,11 @@ namespace FlaxEditor.Utilities
&& memberValue != null
&& !refStack.Contains(memberValue))
{
if (memberType.IsArray && !typeof(FlaxEngine.Object).IsAssignableFrom(memberType.GetElementType()))
if (memberType.IsArray && !ScriptType.FlaxObject.IsAssignableFrom(memberType.GetElementType()))
{
// Array
var array = (Array)memberValue;
var elementType = new ScriptType(memberType.GetElementType());
var elementType = memberType.GetElementType();
var length = array.Length;
refStack.Push(memberValue);
for (int i = 0; i < length; i++)
@@ -78,7 +78,7 @@ namespace FlaxEditor.Utilities
}
refStack.Pop();
}
else if (typeof(IList).IsAssignableFrom(memberType.Type) && !typeof(FlaxEngine.Object).IsAssignableFrom(memberType.GetElementType()))
else if (typeof(IList).IsAssignableFrom(memberType.Type) && !ScriptType.FlaxObject.IsAssignableFrom(memberType.GetElementType()))
{
// List
var list = (IList)memberValue;
@@ -105,7 +105,7 @@ namespace FlaxEditor.Utilities
GetEntries(new MemberInfoPath.Entry(member.Member, key), membersPath, result, values, refStack, valueType, value);
}
}
else if (memberType.IsClass && !new ScriptType(typeof(FlaxEngine.Object)).IsAssignableFrom(memberType))
else if (memberType.IsClass && !ScriptType.FlaxObject.IsAssignableFrom(memberType))
{
// Object
refStack.Push(memberValue);

View File

@@ -282,7 +282,7 @@ namespace FlaxEditor.Utilities
case VariantType.Pointer: return new ScriptType(typeof(IntPtr));
case VariantType.String: return new ScriptType(typeof(string));
case VariantType.Typename: return new ScriptType(typeof(Type));
case VariantType.Object: return new ScriptType(typeof(FlaxEngine.Object));
case VariantType.Object: return ScriptType.FlaxObject;
case VariantType.Asset: return new ScriptType(typeof(Asset));
case VariantType.Vector2: return new ScriptType(typeof(Vector2));
case VariantType.Vector3: return new ScriptType(typeof(Vector3));

View File

@@ -203,7 +203,7 @@ namespace FlaxEditor.Windows.Assets
{
if (isArray)
{
singleValueType = new ScriptType(param.Type.GetElementType());
singleValueType = param.Type.GetElementType();
arrayType = param.Type;
dictionaryType = ScriptType.MakeDictionaryType(new ScriptType(typeof(int)), singleValueType);
b = cmType.ContextMenu.AddButton(window.Surface.GetTypeName(singleValueType) + "[]...", () => OnChangeType(item => window.SetParamType(index, ((ScriptType)item.Tag).MakeArrayType())));