Merge remote-tracking branch 'origin/master' into gi
# Conflicts: # Source/Editor/Windows/Assets/VisualScriptWindow.cs
This commit is contained in:
@@ -107,7 +107,7 @@ float4 PS_Forward(PixelInput input) : SV_Target0
|
||||
Texture2D sceneColorTexture = MATERIAL_REFLECTIONS_SSR_COLOR;
|
||||
float2 screenUV = materialInput.SvPosition.xy * ScreenSize.zw;
|
||||
float stepSize = ScreenSize.z; // 1 / screenWidth
|
||||
float maxSamples = 32;
|
||||
float maxSamples = 48;
|
||||
float worldAntiSelfOcclusionBias = 0.1f;
|
||||
float brdfBias = 0.82f;
|
||||
float drawDistance = 5000.0f;
|
||||
|
||||
@@ -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 />
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -57,8 +57,9 @@ void OnBinaryModuleLoaded(BinaryModule* module);
|
||||
|
||||
MonoReflectionType* CustomEditorsUtil::GetCustomEditor(MonoReflectionType* refType)
|
||||
{
|
||||
if (!refType)
|
||||
return nullptr;
|
||||
MonoType* type = mono_reflection_type_get_type(refType);
|
||||
|
||||
Entry result;
|
||||
if (Cache.TryGet(type, result))
|
||||
{
|
||||
@@ -68,7 +69,6 @@ MonoReflectionType* CustomEditorsUtil::GetCustomEditor(MonoReflectionType* refTy
|
||||
return MUtils::GetType(editor->GetNative());
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -59,6 +59,8 @@ namespace FlaxEditor.CustomEditors
|
||||
|
||||
internal static CustomEditor CreateEditor(ScriptType targetType, bool canUseRefPicker = true)
|
||||
{
|
||||
if (targetType == ScriptType.Null)
|
||||
return new GenericEditor();
|
||||
if (targetType.IsArray)
|
||||
{
|
||||
return new ArrayEditor();
|
||||
@@ -107,7 +109,7 @@ namespace FlaxEditor.CustomEditors
|
||||
}
|
||||
if (targetType.IsGenericType)
|
||||
{
|
||||
if (DictionaryEditor.CanEditType(targetTypeType))
|
||||
if (targetTypeType.GetGenericTypeDefinition() == typeof(Dictionary<,>))
|
||||
{
|
||||
return new DictionaryEditor();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
148
Source/Editor/Scripting/ScriptType.Custom.cs
Normal file
148
Source/Editor/Scripting/ScriptType.Custom.cs
Normal 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>();
|
||||
}
|
||||
}
|
||||
}
|
||||
318
Source/Editor/Scripting/ScriptType.Interfaces.cs
Normal file
318
Source/Editor/Scripting/ScriptType.Interfaces.cs
Normal file
@@ -0,0 +1,318 @@
|
||||
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using FlaxEditor.Content;
|
||||
|
||||
namespace FlaxEditor.Scripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for custom scripting languages type object with metadata.
|
||||
/// </summary>
|
||||
public interface IScriptType
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a type name (eg. name of the class or enum without leading namespace).
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a full namespace of the type.
|
||||
/// </summary>
|
||||
string Namespace { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a full name of the type including leading namespace and any nested types names. Uniquely identifies the type and can be used to find it via <see cref="TypeUtils.GetType(string)"/>.
|
||||
/// </summary>
|
||||
string TypeName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the type is declared public.
|
||||
/// </summary>
|
||||
bool IsPublic { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the type is abstract and must be overridden.
|
||||
/// </summary>
|
||||
bool IsAbstract { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the type is declared sealed and cannot be overridden.
|
||||
/// </summary>
|
||||
bool IsSealed { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the type represents an enumeration.
|
||||
/// </summary>
|
||||
bool IsEnum { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the type is a class or a delegate; that is, not a value type or interface.
|
||||
/// </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>
|
||||
bool IsArray { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the type is a value type (basic type, enumeration or a structure).
|
||||
/// </summary>
|
||||
bool IsValueType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the type is generic type and is used as a template.
|
||||
/// </summary>
|
||||
bool IsGenericType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the type is a reference and value is represented by the valid pointer to the data.
|
||||
/// </summary>
|
||||
bool IsReference { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the type is a pointer and value is represented by the pointer to the data.
|
||||
/// </summary>
|
||||
bool IsPointer { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the type is static.
|
||||
/// </summary>
|
||||
bool IsStatic { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether can create default instance of this type via <see cref="CreateInstance"/> method.
|
||||
/// </summary>
|
||||
bool CanCreateInstance { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type from which the current type directly inherits.
|
||||
/// </summary>
|
||||
ScriptType BaseType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the editor content item that corresponds to this script type. Can be null.
|
||||
/// </summary>
|
||||
ContentItem ContentItem { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates the instance of the object of this type (or throws exception in case of error).
|
||||
/// </summary>
|
||||
/// <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 HasInterface(ScriptType c);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified attribute was defined for this type.
|
||||
/// </summary>
|
||||
/// <param name="attributeType">The attribute type.</param>
|
||||
/// <param name="inherit"><c>true</c> to search this member's inheritance chain to find the attributes; otherwise, <c>false</c>.</param>
|
||||
/// <returns><c>true</c> if the specified type has attribute; otherwise, <c>false</c>.</returns>
|
||||
bool HasAttribute(Type attributeType, bool inherit);
|
||||
|
||||
/// <summary>
|
||||
/// When overridden in a derived class, returns an array of all custom attributes applied to this member.
|
||||
/// </summary>
|
||||
/// <param name="inherit"><c>true</c> to search this member's inheritance chain to find the attributes; otherwise, <c>false</c>.</param>
|
||||
/// <returns>An array that contains all the custom attributes applied to this member, or an array with zero elements if no attributes are defined.</returns>
|
||||
object[] GetAttributes(bool inherit);
|
||||
|
||||
/// <summary>
|
||||
/// Searches for the specified members of the specified member type, using the specified binding constraints.
|
||||
/// </summary>
|
||||
/// <param name="name">The string containing the name of the members to get.</param>
|
||||
/// <param name="type">The value to search for.</param>
|
||||
/// <param name="bindingAttr">A bitmask comprised of one or more <see cref="T:System.Reflection.BindingFlags" /> that specify how the search is conducted.-or- Zero, to return an empty array.</param>
|
||||
/// <returns>An array of member objects representing the public members with the specified name, if found; otherwise, an empty array.</returns>
|
||||
ScriptMemberInfo[] GetMembers(string name, MemberTypes type, BindingFlags bindingAttr);
|
||||
|
||||
/// <summary>
|
||||
/// When overridden in a derived class, searches for the members defined for the current type using the specified binding constraints.
|
||||
/// </summary>
|
||||
/// <param name="bindingAttr">A bitmask comprised of one or more <see cref="T:System.Reflection.BindingFlags" /> that specify how the search is conducted.-or- Zero (<see cref="F:System.Reflection.BindingFlags.Default" />), to return an empty array.</param>
|
||||
/// <returns>An array of member objects representing all members defined for the current type that match the specified binding constraints.-or- An empty array of type member, if no members are defined for the current type, or if none of the defined members match the binding constraints.</returns>
|
||||
ScriptMemberInfo[] GetMembers(BindingFlags bindingAttr);
|
||||
|
||||
/// <summary>
|
||||
/// When overridden in a derived class, searches for the fields defined for the current type using the specified binding constraints.
|
||||
/// </summary>
|
||||
/// <param name="bindingAttr">A bitmask comprised of one or more <see cref="T:System.Reflection.BindingFlags" /> that specify how the search is conducted.-or- Zero (<see cref="F:System.Reflection.BindingFlags.Default" />), to return an empty array.</param>
|
||||
/// <returns>An array of member objects representing all fields defined for the current type that match the specified binding constraints.-or- An empty array of type member, if no fields are defined for the current type, or if none of the defined fields match the binding constraints.</returns>
|
||||
ScriptMemberInfo[] GetFields(BindingFlags bindingAttr);
|
||||
|
||||
/// <summary>
|
||||
/// When overridden in a derived class, searches for the properties defined for the current type using the specified binding constraints.
|
||||
/// </summary>
|
||||
/// <param name="bindingAttr">A bitmask comprised of one or more <see cref="T:System.Reflection.BindingFlags" /> that specify how the search is conducted.-or- Zero (<see cref="F:System.Reflection.BindingFlags.Default" />), to return an empty array.</param>
|
||||
/// <returns>An array of member objects representing all properties defined for the current type that match the specified binding constraints.-or- An empty array of type member, if no properties are defined for the current type, or if none of the defined properties match the binding constraints.</returns>
|
||||
ScriptMemberInfo[] GetProperties(BindingFlags bindingAttr);
|
||||
|
||||
/// <summary>
|
||||
/// When overridden in a derived class, searches for the methods defined for the current type using the specified binding constraints.
|
||||
/// </summary>
|
||||
/// <param name="bindingAttr">A bitmask comprised of one or more <see cref="T:System.Reflection.BindingFlags" /> that specify how the search is conducted.-or- Zero (<see cref="F:System.Reflection.BindingFlags.Default" />), to return an empty array.</param>
|
||||
/// <returns>An array of member objects representing all methods defined for the current type that match the specified binding constraints.-or- An empty array of type member, if no methods are defined for the current type, or if none of the defined methods match the binding constraints.</returns>
|
||||
ScriptMemberInfo[] GetMethods(BindingFlags bindingAttr);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interface for custom scripting languages type object with metadata.
|
||||
/// </summary>
|
||||
public interface IScriptMemberInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a member name (eg. name of the field or method without leading class name nor namespace prefixes).
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a metadata token for sorting so it may not be the actual token.
|
||||
/// </summary>
|
||||
int MetadataToken { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the type is declared public.
|
||||
/// </summary>
|
||||
bool IsPublic { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the type is declared in static scope.
|
||||
/// </summary>
|
||||
bool IsStatic { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this method is declared as virtual (can be overriden).
|
||||
/// </summary>
|
||||
bool IsVirtual { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this method is declared as abstract (has to be overriden).
|
||||
/// </summary>
|
||||
bool IsAbstract { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this method is declared as generic (needs to be inflated with type arguments).
|
||||
/// </summary>
|
||||
bool IsGeneric { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this member is a field.
|
||||
/// </summary>
|
||||
bool IsField { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this member is a property.
|
||||
/// </summary>
|
||||
bool IsProperty { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this member is a method.
|
||||
/// </summary>
|
||||
bool IsMethod { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this member is an event.
|
||||
/// </summary>
|
||||
bool IsEvent { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this member value can be gathered (via getter method or directly from the field).
|
||||
/// </summary>
|
||||
bool HasGet { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this member value can be set (via setter method or directly to the field).
|
||||
/// </summary>
|
||||
bool HasSet { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the method parameters count (valid for methods only).
|
||||
/// </summary>
|
||||
int ParametersCount { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type that declares this member.
|
||||
/// </summary>
|
||||
ScriptType DeclaringType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of the value (field type, property type or method return value).
|
||||
/// </summary>
|
||||
ScriptType ValueType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified attribute was defined for this member.
|
||||
/// </summary>
|
||||
/// <param name="attributeType">The attribute type.</param>
|
||||
/// <param name="inherit"><c>true</c> to search this member's inheritance chain to find the attributes; otherwise, <c>false</c>.</param>
|
||||
/// <returns><c>true</c> if the specified member has attribute; otherwise, <c>false</c>.</returns>
|
||||
bool HasAttribute(Type attributeType, bool inherit);
|
||||
|
||||
/// <summary>
|
||||
/// When overridden in a derived class, returns an array of all custom attributes applied to this member.
|
||||
/// </summary>
|
||||
/// <param name="inherit"><c>true</c> to search this member's inheritance chain to find the attributes; otherwise, <c>false</c>.</param>
|
||||
/// <returns>An array that contains all the custom attributes applied to this member, or an array with zero elements if no attributes are defined.</returns>
|
||||
object[] GetAttributes(bool inherit);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the method parameters metadata (or event delegate signature parameters).
|
||||
/// </summary>
|
||||
ScriptMemberInfo.Parameter[] GetParameters();
|
||||
|
||||
/// <summary>
|
||||
/// Returns the member value of a specified object.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object whose member value will be returned.</param>
|
||||
/// <returns>The member value of the specified object.</returns>
|
||||
object GetValue(object obj);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the member value of a specified object.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object whose member value will be modified.</param>
|
||||
/// <param name="value">The new member value.</param>
|
||||
void SetValue(object obj, object value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interface for custom scripting languages types information containers that can inject types metadata into Editor.
|
||||
/// </summary>
|
||||
public interface IScriptTypesContainer
|
||||
{
|
||||
/// <summary>
|
||||
/// Tries to get the object type from the given full typename. Searches in-build Flax Engine/Editor assemblies and game assemblies.
|
||||
/// </summary>
|
||||
/// <param name="typeName">The full name of the type.</param>
|
||||
/// <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>
|
||||
/// <param name="baseType">The base type.</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>
|
||||
void GetDerivedTypes(ScriptType baseType, List<ScriptType> result, Func<ScriptType, bool> checkFunc);
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -714,7 +719,7 @@ namespace FlaxEditor.Scripting
|
||||
public IScriptType IScriptType => _custom;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a type name (eg. name of the class or enum without leading namespace).
|
||||
/// Gets a type display name (eg. name of the class or enum without leading namespace).
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
@@ -724,19 +729,7 @@ namespace FlaxEditor.Scripting
|
||||
return _custom.Name;
|
||||
if (_managed == null)
|
||||
return string.Empty;
|
||||
if (_managed == typeof(float))
|
||||
return "Float";
|
||||
if (_managed == typeof(int))
|
||||
return "Int";
|
||||
if (_managed == typeof(uint))
|
||||
return "Uint";
|
||||
if (_managed == typeof(short))
|
||||
return "Int16";
|
||||
if (_managed == typeof(ushort))
|
||||
return "Uint16";
|
||||
if (_managed == typeof(bool))
|
||||
return "Bool";
|
||||
return _managed.Name;
|
||||
return _managed.GetTypeDisplayName();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -790,6 +783,11 @@ namespace FlaxEditor.Scripting
|
||||
/// </summary>
|
||||
public bool IsArray => _managed != null ? _managed.IsArray : _custom != null && _custom.IsArray;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the type is a dictionary.
|
||||
/// </summary>
|
||||
public bool IsDictionary => IsGenericType && GetGenericTypeDefinition() == typeof(Dictionary<,>);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the type is a value type (basic type, enumeration or a structure).
|
||||
/// </summary>
|
||||
@@ -834,7 +832,7 @@ namespace FlaxEditor.Scripting
|
||||
{
|
||||
if (_managed != null)
|
||||
return _managed.GetConstructor(Type.EmptyTypes) != null;
|
||||
return _custom.CanCreateInstance;
|
||||
return _custom?.CanCreateInstance ?? false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -981,7 +979,7 @@ namespace FlaxEditor.Scripting
|
||||
public override string ToString()
|
||||
{
|
||||
if (_managed != null)
|
||||
return _managed.FullName ?? string.Empty;
|
||||
return _managed.GetTypeDisplayName() ?? string.Empty;
|
||||
if (_custom != null)
|
||||
return _custom.TypeName;
|
||||
return "<null>";
|
||||
@@ -1010,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;
|
||||
}
|
||||
|
||||
@@ -1039,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)
|
||||
@@ -1082,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>
|
||||
@@ -1119,7 +1119,20 @@ 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));
|
||||
throw new ArgumentNullException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a type object that represents a dictionary of the key and value types.
|
||||
/// </summary>
|
||||
/// <returns>A type object representing a dictionary of the key and value types.</returns>
|
||||
public static ScriptType MakeDictionaryType(ScriptType keyType, ScriptType valueType)
|
||||
{
|
||||
if (keyType == Null || valueType == Null)
|
||||
throw new ArgumentNullException();
|
||||
return new ScriptType(typeof(Dictionary<,>).MakeGenericType(TypeUtils.GetType(keyType), TypeUtils.GetType(valueType)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1368,313 +1381,4 @@ namespace FlaxEditor.Scripting
|
||||
return ScriptMemberInfo.Null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interface for custom scripting languages type object with metadata.
|
||||
/// </summary>
|
||||
public interface IScriptType
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a type name (eg. name of the class or enum without leading namespace).
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a full namespace of the type.
|
||||
/// </summary>
|
||||
string Namespace { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a full name of the type including leading namespace and any nested types names. Uniquely identifies the type and can be used to find it via <see cref="TypeUtils.GetType(string)"/>.
|
||||
/// </summary>
|
||||
string TypeName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the type is declared public.
|
||||
/// </summary>
|
||||
bool IsPublic { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the type is abstract and must be overridden.
|
||||
/// </summary>
|
||||
bool IsAbstract { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the type is declared sealed and cannot be overridden.
|
||||
/// </summary>
|
||||
bool IsSealed { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the type represents an enumeration.
|
||||
/// </summary>
|
||||
bool IsEnum { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the type is a class or a delegate; that is, not a value type or interface.
|
||||
/// </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>
|
||||
bool IsArray { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the type is a value type (basic type, enumeration or a structure).
|
||||
/// </summary>
|
||||
bool IsValueType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the type is generic type and is used as a template.
|
||||
/// </summary>
|
||||
bool IsGenericType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the type is a reference and value is represented by the valid pointer to the data.
|
||||
/// </summary>
|
||||
bool IsReference { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the type is a pointer and value is represented by the pointer to the data.
|
||||
/// </summary>
|
||||
bool IsPointer { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the type is static.
|
||||
/// </summary>
|
||||
bool IsStatic { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether can create default instance of this type via <see cref="CreateInstance"/> method.
|
||||
/// </summary>
|
||||
bool CanCreateInstance { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type from which the current type directly inherits.
|
||||
/// </summary>
|
||||
ScriptType BaseType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the editor content item that corresponds to this script type. Can be null.
|
||||
/// </summary>
|
||||
ContentItem ContentItem { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates the instance of the object of this type (or throws exception in case of error).
|
||||
/// </summary>
|
||||
/// <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>
|
||||
/// <param name="attributeType">The attribute type.</param>
|
||||
/// <param name="inherit"><c>true</c> to search this member's inheritance chain to find the attributes; otherwise, <c>false</c>.</param>
|
||||
/// <returns><c>true</c> if the specified type has attribute; otherwise, <c>false</c>.</returns>
|
||||
bool HasAttribute(Type attributeType, bool inherit);
|
||||
|
||||
/// <summary>
|
||||
/// When overridden in a derived class, returns an array of all custom attributes applied to this member.
|
||||
/// </summary>
|
||||
/// <param name="inherit"><c>true</c> to search this member's inheritance chain to find the attributes; otherwise, <c>false</c>.</param>
|
||||
/// <returns>An array that contains all the custom attributes applied to this member, or an array with zero elements if no attributes are defined.</returns>
|
||||
object[] GetAttributes(bool inherit);
|
||||
|
||||
/// <summary>
|
||||
/// Searches for the specified members of the specified member type, using the specified binding constraints.
|
||||
/// </summary>
|
||||
/// <param name="name">The string containing the name of the members to get.</param>
|
||||
/// <param name="type">The value to search for.</param>
|
||||
/// <param name="bindingAttr">A bitmask comprised of one or more <see cref="T:System.Reflection.BindingFlags" /> that specify how the search is conducted.-or- Zero, to return an empty array.</param>
|
||||
/// <returns>An array of member objects representing the public members with the specified name, if found; otherwise, an empty array.</returns>
|
||||
ScriptMemberInfo[] GetMembers(string name, MemberTypes type, BindingFlags bindingAttr);
|
||||
|
||||
/// <summary>
|
||||
/// When overridden in a derived class, searches for the members defined for the current type using the specified binding constraints.
|
||||
/// </summary>
|
||||
/// <param name="bindingAttr">A bitmask comprised of one or more <see cref="T:System.Reflection.BindingFlags" /> that specify how the search is conducted.-or- Zero (<see cref="F:System.Reflection.BindingFlags.Default" />), to return an empty array.</param>
|
||||
/// <returns>An array of member objects representing all members defined for the current type that match the specified binding constraints.-or- An empty array of type member, if no members are defined for the current type, or if none of the defined members match the binding constraints.</returns>
|
||||
ScriptMemberInfo[] GetMembers(BindingFlags bindingAttr);
|
||||
|
||||
/// <summary>
|
||||
/// When overridden in a derived class, searches for the fields defined for the current type using the specified binding constraints.
|
||||
/// </summary>
|
||||
/// <param name="bindingAttr">A bitmask comprised of one or more <see cref="T:System.Reflection.BindingFlags" /> that specify how the search is conducted.-or- Zero (<see cref="F:System.Reflection.BindingFlags.Default" />), to return an empty array.</param>
|
||||
/// <returns>An array of member objects representing all fields defined for the current type that match the specified binding constraints.-or- An empty array of type member, if no fields are defined for the current type, or if none of the defined fields match the binding constraints.</returns>
|
||||
ScriptMemberInfo[] GetFields(BindingFlags bindingAttr);
|
||||
|
||||
/// <summary>
|
||||
/// When overridden in a derived class, searches for the properties defined for the current type using the specified binding constraints.
|
||||
/// </summary>
|
||||
/// <param name="bindingAttr">A bitmask comprised of one or more <see cref="T:System.Reflection.BindingFlags" /> that specify how the search is conducted.-or- Zero (<see cref="F:System.Reflection.BindingFlags.Default" />), to return an empty array.</param>
|
||||
/// <returns>An array of member objects representing all properties defined for the current type that match the specified binding constraints.-or- An empty array of type member, if no properties are defined for the current type, or if none of the defined properties match the binding constraints.</returns>
|
||||
ScriptMemberInfo[] GetProperties(BindingFlags bindingAttr);
|
||||
|
||||
/// <summary>
|
||||
/// When overridden in a derived class, searches for the methods defined for the current type using the specified binding constraints.
|
||||
/// </summary>
|
||||
/// <param name="bindingAttr">A bitmask comprised of one or more <see cref="T:System.Reflection.BindingFlags" /> that specify how the search is conducted.-or- Zero (<see cref="F:System.Reflection.BindingFlags.Default" />), to return an empty array.</param>
|
||||
/// <returns>An array of member objects representing all methods defined for the current type that match the specified binding constraints.-or- An empty array of type member, if no methods are defined for the current type, or if none of the defined methods match the binding constraints.</returns>
|
||||
ScriptMemberInfo[] GetMethods(BindingFlags bindingAttr);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interface for custom scripting languages type object with metadata.
|
||||
/// </summary>
|
||||
public interface IScriptMemberInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a member name (eg. name of the field or method without leading class name nor namespace prefixes).
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a metadata token for sorting so it may not be the actual token.
|
||||
/// </summary>
|
||||
int MetadataToken { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the type is declared public.
|
||||
/// </summary>
|
||||
bool IsPublic { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the type is declared in static scope.
|
||||
/// </summary>
|
||||
bool IsStatic { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this method is declared as virtual (can be overriden).
|
||||
/// </summary>
|
||||
bool IsVirtual { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this method is declared as abstract (has to be overriden).
|
||||
/// </summary>
|
||||
bool IsAbstract { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this method is declared as generic (needs to be inflated with type arguments).
|
||||
/// </summary>
|
||||
bool IsGeneric { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this member is a field.
|
||||
/// </summary>
|
||||
bool IsField { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this member is a property.
|
||||
/// </summary>
|
||||
bool IsProperty { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this member is a method.
|
||||
/// </summary>
|
||||
bool IsMethod { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this member is an event.
|
||||
/// </summary>
|
||||
bool IsEvent { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this member value can be gathered (via getter method or directly from the field).
|
||||
/// </summary>
|
||||
bool HasGet { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this member value can be set (via setter method or directly to the field).
|
||||
/// </summary>
|
||||
bool HasSet { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the method parameters count (valid for methods only).
|
||||
/// </summary>
|
||||
int ParametersCount { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type that declares this member.
|
||||
/// </summary>
|
||||
ScriptType DeclaringType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of the value (field type, property type or method return value).
|
||||
/// </summary>
|
||||
ScriptType ValueType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified attribute was defined for this member.
|
||||
/// </summary>
|
||||
/// <param name="attributeType">The attribute type.</param>
|
||||
/// <param name="inherit"><c>true</c> to search this member's inheritance chain to find the attributes; otherwise, <c>false</c>.</param>
|
||||
/// <returns><c>true</c> if the specified member has attribute; otherwise, <c>false</c>.</returns>
|
||||
bool HasAttribute(Type attributeType, bool inherit);
|
||||
|
||||
/// <summary>
|
||||
/// When overridden in a derived class, returns an array of all custom attributes applied to this member.
|
||||
/// </summary>
|
||||
/// <param name="inherit"><c>true</c> to search this member's inheritance chain to find the attributes; otherwise, <c>false</c>.</param>
|
||||
/// <returns>An array that contains all the custom attributes applied to this member, or an array with zero elements if no attributes are defined.</returns>
|
||||
object[] GetAttributes(bool inherit);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the method parameters metadata (or event delegate signature parameters).
|
||||
/// </summary>
|
||||
ScriptMemberInfo.Parameter[] GetParameters();
|
||||
|
||||
/// <summary>
|
||||
/// Returns the member value of a specified object.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object whose member value will be returned.</param>
|
||||
/// <returns>The member value of the specified object.</returns>
|
||||
object GetValue(object obj);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the member value of a specified object.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object whose member value will be modified.</param>
|
||||
/// <param name="value">The new member value.</param>
|
||||
void SetValue(object obj, object value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interface for custom scripting languages types information containers that can inject types metadata into Editor.
|
||||
/// </summary>
|
||||
public interface IScriptTypesContainer
|
||||
{
|
||||
/// <summary>
|
||||
/// Tries to get the object type from the given full typename. Searches in-build Flax Engine/Editor assemblies and game assemblies.
|
||||
/// </summary>
|
||||
/// <param name="typeName">The full name of the type.</param>
|
||||
/// <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>
|
||||
/// <param name="baseType">The base type.</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>
|
||||
void GetDerivedTypes(ScriptType baseType, List<ScriptType> result, Func<ScriptType, bool> checkFunc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Scripting
|
||||
@@ -33,6 +34,74 @@ namespace FlaxEditor.Scripting
|
||||
return o != null ? new ScriptType(o.GetType()) : ScriptType.Null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the typename full name.
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <returns>The full typename of the type.</returns>
|
||||
public static string GetTypeName(this Type type)
|
||||
{
|
||||
if (type.IsGenericType)
|
||||
{
|
||||
// For generic types (eg. Dictionary) FullName returns generic parameter types with fully qualified name so simplify it manually
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(type.Namespace);
|
||||
sb.Append('.');
|
||||
sb.Append(type.Name);
|
||||
sb.Append('[');
|
||||
var genericArgs = type.GetGenericArguments();
|
||||
for (var i = 0; i < genericArgs.Length; i++)
|
||||
{
|
||||
if (i != 0)
|
||||
sb.Append(',');
|
||||
sb.Append(genericArgs[i].GetTypeName());
|
||||
}
|
||||
sb.Append(']');
|
||||
return sb.ToString();
|
||||
}
|
||||
return type.FullName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the typename name for UI.
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <returns>The display of the type.</returns>
|
||||
public static string GetTypeDisplayName(this Type type)
|
||||
{
|
||||
// Special display for in-built basic types
|
||||
if (type == typeof(bool))
|
||||
return "Bool";
|
||||
if (type == typeof(float))
|
||||
return "Float";
|
||||
if (type == typeof(int))
|
||||
return "Int";
|
||||
if (type == typeof(uint))
|
||||
return "Uint";
|
||||
|
||||
// For generic types (eg. Dictionary) Name returns generic parameter types with fully qualified name so simplify it manually
|
||||
if (type.IsGenericType)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
var name = type.Name;
|
||||
var idx = name.IndexOf('`');
|
||||
sb.Append(idx != -1 ? name.Substring(0, idx) : name);
|
||||
sb.Append('<');
|
||||
var genericArgs = type.GetGenericArguments();
|
||||
for (var i = 0; i < genericArgs.Length; i++)
|
||||
{
|
||||
if (i != 0)
|
||||
sb.Append(", ");
|
||||
sb.Append(genericArgs[i].GetTypeDisplayName());
|
||||
}
|
||||
sb.Append('>');
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
// Default name
|
||||
return type.Name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default value for the given type (can be value type or reference type).
|
||||
/// </summary>
|
||||
@@ -258,18 +327,10 @@ namespace FlaxEditor.Scripting
|
||||
return ScriptType.Null;
|
||||
|
||||
// C#/C++ types
|
||||
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
|
||||
for (int i = 0; i < assemblies.Length; i++)
|
||||
{
|
||||
var assembly = assemblies[i];
|
||||
if (assembly != null)
|
||||
{
|
||||
var type = assembly.GetType(typeName);
|
||||
if (type != null)
|
||||
{
|
||||
return new ScriptType(type);
|
||||
}
|
||||
}
|
||||
var type = Type.GetType(typeName);
|
||||
if (type != null)
|
||||
return new ScriptType(type);
|
||||
}
|
||||
|
||||
// Custom types
|
||||
@@ -277,11 +338,23 @@ namespace FlaxEditor.Scripting
|
||||
{
|
||||
var type = customTypesInfo.GetType(typeName);
|
||||
if (type)
|
||||
{
|
||||
return type;
|
||||
}
|
||||
if (typeName.EndsWith("[]"))
|
||||
{
|
||||
// Array of custom type
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
Editor.LogWarning($"Failed to find type '{typeName}'.");
|
||||
return ScriptType.Null;
|
||||
}
|
||||
|
||||
@@ -292,10 +365,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();
|
||||
@@ -304,11 +377,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>
|
||||
@@ -333,6 +414,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;
|
||||
|
||||
@@ -16,7 +16,16 @@ 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)
|
||||
{
|
||||
if (type == ScriptType.Null)
|
||||
return box.DefaultType;
|
||||
// BoxID = 1 is Key
|
||||
// BoxID = 2 is Value
|
||||
return box.DefaultType != null ? new ScriptType(type.GetGenericArguments()[box.ID == 1 ? 0 : 1]) : type;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -28,7 +37,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 1,
|
||||
Title = "Array Length",
|
||||
Description = "Gets the length of the arary (amount of the items).",
|
||||
Description = "Gets the length of the array (amount of the items).",
|
||||
AlternativeTitles = new[] { "Count" },
|
||||
Flags = NodeFlags.VisualScriptGraph | NodeFlags.AnimGraph,
|
||||
Size = new Vector2(150, 20),
|
||||
@@ -44,7 +53,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 2,
|
||||
Title = "Array Contains",
|
||||
Description = "Returns the true if arrayt contains a given item, otherwise false.",
|
||||
Description = "Returns the true if array contains a given item, otherwise false.",
|
||||
AlternativeTitles = new[] { "Contains" },
|
||||
Flags = NodeFlags.VisualScriptGraph | NodeFlags.AnimGraph,
|
||||
Size = new Vector2(150, 40),
|
||||
@@ -277,6 +286,138 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
}
|
||||
},
|
||||
// first 100 IDs reserved for arrays
|
||||
|
||||
new NodeArchetype
|
||||
{
|
||||
TypeID = 101,
|
||||
Title = "Dictionary Count",
|
||||
Description = "Gets the size of the dictionary (amount of the items).",
|
||||
AlternativeTitles = new[] { "size" },
|
||||
Flags = NodeFlags.VisualScriptGraph | NodeFlags.AnimGraph,
|
||||
Size = new Vector2(180, 20),
|
||||
ConnectionsHints = ConnectionsHint.Dictionary,
|
||||
IndependentBoxes = new int[] { 0 },
|
||||
Elements = new[]
|
||||
{
|
||||
NodeElementArchetype.Factory.Input(0, "Dictionary", true, null, 0),
|
||||
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(int), 1)
|
||||
}
|
||||
},
|
||||
new NodeArchetype
|
||||
{
|
||||
TypeID = 102,
|
||||
Title = "Dictionary Contains Key",
|
||||
Description = "Returns the true if dictionary contains a given key, otherwise false.",
|
||||
AlternativeTitles = new[] { "Contains" },
|
||||
Flags = NodeFlags.VisualScriptGraph | NodeFlags.AnimGraph,
|
||||
Size = new Vector2(240, 40),
|
||||
DefaultValues = new object[] { 0 },
|
||||
ConnectionsHints = ConnectionsHint.Dictionary,
|
||||
IndependentBoxes = new int[] { 0 },
|
||||
DependentBoxes = new int[] { 1 },
|
||||
DependentBoxFilter = GetDictionaryItemType,
|
||||
Elements = new[]
|
||||
{
|
||||
NodeElementArchetype.Factory.Input(0, "Dictionary", true, null, 0),
|
||||
NodeElementArchetype.Factory.Input(1, "Key", true, typeof(object), 1, 0),
|
||||
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(bool), 3)
|
||||
}
|
||||
},
|
||||
new NodeArchetype
|
||||
{
|
||||
TypeID = 103,
|
||||
Title = "Dictionary Contains Value",
|
||||
Description = "Returns the true if dictionary contains a given value, otherwise false.",
|
||||
AlternativeTitles = new[] { "Contains" },
|
||||
Flags = NodeFlags.VisualScriptGraph | NodeFlags.AnimGraph,
|
||||
Size = new Vector2(240, 40),
|
||||
DefaultValues = new object[] { 0 },
|
||||
ConnectionsHints = ConnectionsHint.Dictionary,
|
||||
IndependentBoxes = new int[] { 0 },
|
||||
DependentBoxes = new int[] { 2 },
|
||||
DependentBoxFilter = GetDictionaryItemType,
|
||||
Elements = new[]
|
||||
{
|
||||
NodeElementArchetype.Factory.Input(0, "Dictionary", true, null, 0),
|
||||
NodeElementArchetype.Factory.Input(1, "Value", true, typeof(object), 2, 0),
|
||||
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(bool), 3)
|
||||
}
|
||||
},
|
||||
new NodeArchetype
|
||||
{
|
||||
TypeID = 104,
|
||||
Title = "Dictionary Clear",
|
||||
Description = "Clears dictionary.",
|
||||
Flags = NodeFlags.VisualScriptGraph | NodeFlags.AnimGraph,
|
||||
Size = new Vector2(180, 20),
|
||||
ConnectionsHints = ConnectionsHint.Dictionary,
|
||||
IndependentBoxes = new int[] { 0 },
|
||||
DependentBoxes = new int[] { 1 },
|
||||
Elements = new[]
|
||||
{
|
||||
NodeElementArchetype.Factory.Input(0, "Dictionary", true, null, 0),
|
||||
NodeElementArchetype.Factory.Output(0, string.Empty, null, 1)
|
||||
}
|
||||
},
|
||||
new NodeArchetype
|
||||
{
|
||||
TypeID = 105,
|
||||
Title = "Dictionary Remove",
|
||||
Description = "Removes the given item from the dictionary (by key).",
|
||||
Flags = NodeFlags.VisualScriptGraph | NodeFlags.AnimGraph,
|
||||
Size = new Vector2(180, 40),
|
||||
DefaultValues = new object[] { 0 },
|
||||
ConnectionsHints = ConnectionsHint.Dictionary,
|
||||
IndependentBoxes = new int[] { 0 },
|
||||
DependentBoxes = new int[] { 1, 3 },
|
||||
DependentBoxFilter = GetDictionaryItemType,
|
||||
Elements = new[]
|
||||
{
|
||||
NodeElementArchetype.Factory.Input(0, "Dictionary", true, null, 0),
|
||||
NodeElementArchetype.Factory.Input(1, "Key", true, typeof(object), 1, 0),
|
||||
NodeElementArchetype.Factory.Output(0, string.Empty, null, 3)
|
||||
}
|
||||
},
|
||||
new NodeArchetype
|
||||
{
|
||||
TypeID = 106,
|
||||
Title = "Dictionary Set",
|
||||
Description = "Set the item in the dictionary (a pair of key and value). Adds or updates the pair.",
|
||||
Flags = NodeFlags.VisualScriptGraph | NodeFlags.AnimGraph,
|
||||
Size = new Vector2(180, 60),
|
||||
DefaultValues = new object[] { 0, 0 },
|
||||
ConnectionsHints = ConnectionsHint.Dictionary,
|
||||
IndependentBoxes = new int[] { 0 },
|
||||
DependentBoxes = new int[] { 1, 2, 3 },
|
||||
DependentBoxFilter = GetDictionaryItemType,
|
||||
Elements = new[]
|
||||
{
|
||||
NodeElementArchetype.Factory.Input(0, "Dictionary", true, null, 0),
|
||||
NodeElementArchetype.Factory.Input(1, "Key", true, typeof(object), 1, 0),
|
||||
NodeElementArchetype.Factory.Input(2, "Value", true, typeof(object), 2, 1),
|
||||
NodeElementArchetype.Factory.Output(0, string.Empty, null, 3)
|
||||
}
|
||||
},
|
||||
new NodeArchetype
|
||||
{
|
||||
TypeID = 107,
|
||||
Title = "Dictionary Get",
|
||||
Description = "Gets the item from the dictionary (a pair of key and value).",
|
||||
Flags = NodeFlags.VisualScriptGraph | NodeFlags.AnimGraph,
|
||||
Size = new Vector2(180, 40),
|
||||
DefaultValues = new object[] { 0 },
|
||||
ConnectionsHints = ConnectionsHint.Dictionary,
|
||||
IndependentBoxes = new int[] { 0 },
|
||||
DependentBoxes = new int[] { 1, 3 },
|
||||
DependentBoxFilter = GetDictionaryItemType,
|
||||
Elements = new[]
|
||||
{
|
||||
NodeElementArchetype.Factory.Input(0, "Dictionary", true, null, 0),
|
||||
NodeElementArchetype.Factory.Input(1, "Key", true, typeof(object), 1, 0),
|
||||
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(object), 3)
|
||||
}
|
||||
},
|
||||
// second 100 IDs reserved for dictionaries
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
Array.Copy(prev, next, Mathf.Min(prev.Length, next.Length));
|
||||
SetValue(0, next);
|
||||
}
|
||||
|
||||
|
||||
public override void OnSurfaceCanEditChanged(bool canEdit)
|
||||
{
|
||||
base.OnSurfaceCanEditChanged(canEdit);
|
||||
@@ -141,7 +141,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
_addButton.Enabled = canEdit;
|
||||
_removeButton.Enabled = canEdit;
|
||||
}
|
||||
|
||||
|
||||
public override void OnDestroy()
|
||||
{
|
||||
_output = null;
|
||||
@@ -179,7 +179,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
break;
|
||||
RemoveElement(box);
|
||||
}
|
||||
|
||||
|
||||
var canEdit = Surface.CanEdit;
|
||||
_typePicker.Enabled = canEdit;
|
||||
_addButton.Enabled = count < countMax && canEdit;
|
||||
@@ -212,6 +212,132 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
}
|
||||
}
|
||||
|
||||
private class DictionaryNode : SurfaceNode
|
||||
{
|
||||
private OutputBox _output;
|
||||
private TypePickerControl _keyTypePicker;
|
||||
private TypePickerControl _valueTypePicker;
|
||||
private bool _isUpdatingUI;
|
||||
|
||||
public DictionaryNode(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch)
|
||||
: base(id, context, nodeArch, groupArch)
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnValuesChanged()
|
||||
{
|
||||
UpdateUI();
|
||||
|
||||
base.OnValuesChanged();
|
||||
}
|
||||
|
||||
public override void OnLoaded()
|
||||
{
|
||||
base.OnLoaded();
|
||||
|
||||
_output = (OutputBox)Elements[0];
|
||||
_keyTypePicker = new TypePickerControl
|
||||
{
|
||||
Bounds = new Rectangle(FlaxEditor.Surface.Constants.NodeMarginX, FlaxEditor.Surface.Constants.NodeMarginY + FlaxEditor.Surface.Constants.NodeHeaderSize, 160, 16),
|
||||
Parent = this,
|
||||
};
|
||||
_keyTypePicker.ValueChanged += OnKeyTypeChanged;
|
||||
_valueTypePicker = new TypePickerControl
|
||||
{
|
||||
Bounds = new Rectangle(_keyTypePicker.X, _keyTypePicker.Y + FlaxEditor.Surface.Constants.LayoutOffsetY, _keyTypePicker.Width, _keyTypePicker.Height),
|
||||
Parent = this,
|
||||
};
|
||||
_valueTypePicker.ValueChanged += OnValueTypeChanged;
|
||||
|
||||
UpdateUI();
|
||||
}
|
||||
|
||||
private void OnKeyTypeChanged()
|
||||
{
|
||||
if (_isUpdatingUI)
|
||||
return;
|
||||
SetValue(0, _keyTypePicker.ValueTypeName);
|
||||
}
|
||||
|
||||
private void OnValueTypeChanged()
|
||||
{
|
||||
if (_isUpdatingUI)
|
||||
return;
|
||||
SetValue(1, _valueTypePicker.ValueTypeName);
|
||||
}
|
||||
|
||||
public override void OnSurfaceCanEditChanged(bool canEdit)
|
||||
{
|
||||
base.OnSurfaceCanEditChanged(canEdit);
|
||||
|
||||
_keyTypePicker.Enabled = canEdit;
|
||||
_valueTypePicker.Enabled = canEdit;
|
||||
}
|
||||
|
||||
public override void OnDestroy()
|
||||
{
|
||||
_output = null;
|
||||
_keyTypePicker = null;
|
||||
_valueTypePicker = null;
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
private void UpdateUI()
|
||||
{
|
||||
if (_isUpdatingUI)
|
||||
return;
|
||||
var keyTypeName = (string)Values[0];
|
||||
var valueTypeName = (string)Values[1];
|
||||
var keyType = TypeUtils.GetType(keyTypeName);
|
||||
var valueType = TypeUtils.GetType(valueTypeName);
|
||||
if (keyType == ScriptType.Null)
|
||||
{
|
||||
Editor.LogError("Missing type " + keyTypeName);
|
||||
keyType = ScriptType.Object;
|
||||
}
|
||||
if (valueType == ScriptType.Null)
|
||||
{
|
||||
Editor.LogError("Missing type " + valueTypeName);
|
||||
valueType = ScriptType.Object;
|
||||
}
|
||||
var dictionaryType = ScriptType.MakeDictionaryType(keyType, valueType);
|
||||
|
||||
_isUpdatingUI = true;
|
||||
_keyTypePicker.Value = keyType;
|
||||
_valueTypePicker.Value = valueType;
|
||||
_output.CurrentType = dictionaryType;
|
||||
_isUpdatingUI = false;
|
||||
|
||||
var canEdit = Surface.CanEdit;
|
||||
_keyTypePicker.Enabled = canEdit;
|
||||
_valueTypePicker.Enabled = canEdit;
|
||||
|
||||
Title = Surface.GetTypeName(dictionaryType);
|
||||
_keyTypePicker.Width = 160.0f;
|
||||
_valueTypePicker.Width = 160.0f;
|
||||
ResizeAuto();
|
||||
_keyTypePicker.Width = Width - 30;
|
||||
_valueTypePicker.Width = Width - 30;
|
||||
}
|
||||
|
||||
private object GetBoxValue(InputBox box)
|
||||
{
|
||||
var array = (Array)Values[0];
|
||||
return array.GetValue(box.ID - 1);
|
||||
}
|
||||
|
||||
private void SetBoxValue(InputBox box, object value)
|
||||
{
|
||||
if (_isDuringValuesEditing || !Surface.CanEdit)
|
||||
return;
|
||||
var array = (Array)Values[0];
|
||||
array = (Array)array.Clone();
|
||||
array.SetValue(value, box.ID - 1);
|
||||
SetValue(0, array);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The nodes for that group.
|
||||
/// </summary>
|
||||
@@ -236,12 +362,12 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
TryParseText = (string filterText, out object[] data) =>
|
||||
{
|
||||
data = null;
|
||||
if (filterText == "true")
|
||||
if (string.Equals(filterText, bool.TrueString, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
data = new object[] { true };
|
||||
return true;
|
||||
}
|
||||
if (filterText == "false")
|
||||
if (string.Equals(filterText, bool.FalseString, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
data = new object[] { false };
|
||||
return true;
|
||||
@@ -544,6 +670,17 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
DefaultValues = new object[] { new int[] { 0, 1, 2 } },
|
||||
Elements = new[] { NodeElementArchetype.Factory.Output(0, string.Empty, null, 0) }
|
||||
},
|
||||
new NodeArchetype
|
||||
{
|
||||
TypeID = 14,
|
||||
Title = "Dictionary",
|
||||
Create = (id, context, arch, groupArch) => new DictionaryNode(id, context, arch, groupArch),
|
||||
Description = "Creates an empty dictionary.",
|
||||
Flags = NodeFlags.VisualScriptGraph | NodeFlags.AnimGraph,
|
||||
Size = new Vector2(150, 40),
|
||||
DefaultValues = new object[] { typeof(int).FullName, typeof(string).FullName },
|
||||
Elements = new[] { NodeElementArchetype.Factory.Output(0, string.Empty, null, 0) }
|
||||
},
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -331,6 +331,29 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
NodeElementArchetype.Factory.Output(3, "Done", typeof(void), 6, true),
|
||||
}
|
||||
},
|
||||
new NodeArchetype
|
||||
{
|
||||
TypeID = 8,
|
||||
Title = "Dictionary For Each",
|
||||
AlternativeTitles = new[] { "foreach" },
|
||||
Description = "Iterates over the dictionary items.",
|
||||
Flags = NodeFlags.VisualScriptGraph,
|
||||
Size = new Vector2(180, 80),
|
||||
ConnectionsHints = ConnectionsHint.Dictionary,
|
||||
IndependentBoxes = new int[] { 4 },
|
||||
DependentBoxes = new int[] { 1, 2, },
|
||||
DependentBoxFilter = Collections.GetDictionaryItemType,
|
||||
Elements = new[]
|
||||
{
|
||||
NodeElementArchetype.Factory.Input(0, string.Empty, false, typeof(void), 0),
|
||||
NodeElementArchetype.Factory.Input(1, "Dictionary", true, null, 4),
|
||||
NodeElementArchetype.Factory.Input(2, "Break", false, typeof(void), 5),
|
||||
NodeElementArchetype.Factory.Output(0, "Loop", typeof(void), 3, true),
|
||||
NodeElementArchetype.Factory.Output(1, "Key", typeof(object), 1),
|
||||
NodeElementArchetype.Factory.Output(2, "Value", typeof(object), 2),
|
||||
NodeElementArchetype.Factory.Output(3, "Done", typeof(void), 6, true),
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -202,6 +202,8 @@ namespace FlaxEditor.Surface.Elements
|
||||
return "Scalar";
|
||||
if ((hint & ConnectionsHint.Array) == ConnectionsHint.Array)
|
||||
return "Array";
|
||||
if ((hint & ConnectionsHint.Dictionary) == ConnectionsHint.Dictionary)
|
||||
return "Dictionary";
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -215,7 +217,6 @@ namespace FlaxEditor.Surface.Elements
|
||||
// Check direct connection
|
||||
if (Surface.CanUseDirectCast(type, _currentType))
|
||||
{
|
||||
// Can
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -224,25 +225,15 @@ namespace FlaxEditor.Surface.Elements
|
||||
if (Archetype.ConnectionsType == ScriptType.Null && connectionsHints != ConnectionsHint.None)
|
||||
{
|
||||
if ((connectionsHints & ConnectionsHint.Anything) == ConnectionsHint.Anything)
|
||||
{
|
||||
// Can
|
||||
return true;
|
||||
}
|
||||
if ((connectionsHints & ConnectionsHint.Value) == ConnectionsHint.Value && type.Type != typeof(void))
|
||||
{
|
||||
// Can
|
||||
return true;
|
||||
}
|
||||
if ((connectionsHints & ConnectionsHint.Enum) == ConnectionsHint.Enum && type.IsEnum)
|
||||
{
|
||||
// Can
|
||||
return true;
|
||||
}
|
||||
if ((connectionsHints & ConnectionsHint.Array) == ConnectionsHint.Array && type.IsArray)
|
||||
{
|
||||
// Can
|
||||
return true;
|
||||
}
|
||||
if ((connectionsHints & ConnectionsHint.Dictionary) == ConnectionsHint.Dictionary && type.IsDictionary)
|
||||
return true;
|
||||
if ((connectionsHints & ConnectionsHint.Vector) == ConnectionsHint.Vector)
|
||||
{
|
||||
var t = type.Type;
|
||||
@@ -251,7 +242,6 @@ namespace FlaxEditor.Surface.Elements
|
||||
t == typeof(Vector4) ||
|
||||
t == typeof(Color))
|
||||
{
|
||||
// Can
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -270,7 +260,6 @@ namespace FlaxEditor.Surface.Elements
|
||||
t == typeof(float) ||
|
||||
t == typeof(double))
|
||||
{
|
||||
// Can
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -57,6 +57,11 @@ namespace FlaxEditor.Surface
|
||||
/// </summary>
|
||||
Array = 32,
|
||||
|
||||
/// <summary>
|
||||
/// Allow any dictionary types connections.
|
||||
/// </summary>
|
||||
Dictionary = 64,
|
||||
|
||||
/// <summary>
|
||||
/// Allow any scalar or vector numeric value types connections (bool, int, float, vector2, color..).
|
||||
/// </summary>
|
||||
@@ -65,7 +70,7 @@ namespace FlaxEditor.Surface
|
||||
/// <summary>
|
||||
/// All flags.
|
||||
/// </summary>
|
||||
All = Scalar | Vector | Enum | Anything | Value | Array,
|
||||
All = Scalar | Vector | Enum | Anything | Value | Array | Dictionary,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -415,8 +415,13 @@ namespace FlaxEditor.Surface
|
||||
|
||||
internal static bool IsValidVisualScriptType(ScriptType scriptType)
|
||||
{
|
||||
if (scriptType.IsGenericType || !scriptType.IsPublic || scriptType.HasAttribute(typeof(HideInEditorAttribute), true))
|
||||
if (!scriptType.IsPublic || scriptType.HasAttribute(typeof(HideInEditorAttribute), true))
|
||||
return false;
|
||||
if (scriptType.IsGenericType)
|
||||
{
|
||||
// Only Dictionary generic type is valid
|
||||
return scriptType.GetGenericTypeDefinition() == typeof(Dictionary<,>);
|
||||
}
|
||||
var managedType = TypeUtils.GetType(scriptType);
|
||||
return !TypeUtils.IsDelegate(managedType);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -387,17 +387,7 @@ namespace FlaxEditor.Surface
|
||||
/// <returns>The display name (for UI).</returns>
|
||||
public virtual string GetTypeName(ScriptType type)
|
||||
{
|
||||
if (type == ScriptType.Null)
|
||||
return null;
|
||||
if (type.Type == typeof(float))
|
||||
return "Float";
|
||||
if (type.Type == typeof(int))
|
||||
return "Int";
|
||||
if (type.Type == typeof(uint))
|
||||
return "Uint";
|
||||
if (type.Type == typeof(bool))
|
||||
return "Bool";
|
||||
return type.Name;
|
||||
return type == ScriptType.Null ? null : type.Name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -492,6 +492,19 @@ namespace FlaxEditor.Surface
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (box.HasSingleConnection)
|
||||
{
|
||||
var connectedBox = box.Connections[0];
|
||||
for (int i = 0; i < state.Locals.Length; i++)
|
||||
{
|
||||
ref var local = ref state.Locals[i];
|
||||
if (local.BoxId == connectedBox.ID && local.NodeId == connectedBox.ParentNode.ID)
|
||||
{
|
||||
text = $"{local.Value ?? string.Empty} ({local.ValueTypeName})";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluate the value using the Visual Scripting backend
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace FlaxEditor.Utilities
|
||||
|
||||
// Special case for collections
|
||||
if (Index != null)
|
||||
result = new ScriptType(result.GetElementType());
|
||||
result = result.GetElementType();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using FlaxEditor.Scripting;
|
||||
@@ -134,7 +135,7 @@ namespace FlaxEditor.Utilities
|
||||
variantType = VariantType.Blob;
|
||||
else if (type.IsArray)
|
||||
variantType = VariantType.Array;
|
||||
else if (type == typeof(Dictionary<object, object>))
|
||||
else if (type == typeof(Dictionary<object, object>) || (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<,>)))
|
||||
variantType = VariantType.Dictionary;
|
||||
else if (type.IsPointer || type.IsByRef)
|
||||
{
|
||||
@@ -206,10 +207,13 @@ namespace FlaxEditor.Utilities
|
||||
case VariantType.Enum:
|
||||
case VariantType.Structure:
|
||||
case VariantType.ManagedObject:
|
||||
case VariantType.Typename:
|
||||
stream.Write(int.MaxValue);
|
||||
stream.WriteStrAnsi(type.FullName, 77);
|
||||
break;
|
||||
case VariantType.Typename:
|
||||
stream.Write(int.MaxValue);
|
||||
stream.WriteStrAnsi(type.GetTypeName(), 77);
|
||||
break;
|
||||
case VariantType.Array:
|
||||
if (type != typeof(object[]))
|
||||
{
|
||||
@@ -219,6 +223,15 @@ namespace FlaxEditor.Utilities
|
||||
else
|
||||
stream.Write(0);
|
||||
break;
|
||||
case VariantType.Dictionary:
|
||||
if (type != typeof(Dictionary<object, object>))
|
||||
{
|
||||
stream.Write(int.MaxValue);
|
||||
stream.WriteStrAnsi(type.GetTypeName(), 77);
|
||||
}
|
||||
else
|
||||
stream.Write(0);
|
||||
break;
|
||||
default:
|
||||
stream.Write(0);
|
||||
break;
|
||||
@@ -269,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));
|
||||
@@ -455,7 +468,7 @@ namespace FlaxEditor.Utilities
|
||||
if (type == null)
|
||||
type = typeof(object[]);
|
||||
else if (!type.IsArray)
|
||||
throw new Exception("Invalid arry type for the Variant array " + typeName);
|
||||
throw new Exception("Invalid type for the Variant array " + typeName);
|
||||
var count = stream.ReadInt32();
|
||||
var result = Array.CreateInstance(type.GetElementType(), count);
|
||||
for (int i = 0; i < count; i++)
|
||||
@@ -464,8 +477,12 @@ namespace FlaxEditor.Utilities
|
||||
}
|
||||
case VariantType.Dictionary:
|
||||
{
|
||||
if (type == null)
|
||||
type = typeof(Dictionary<object, object>);
|
||||
else if (!type.IsGenericType || type.GetGenericTypeDefinition() != typeof(Dictionary<,>))
|
||||
throw new Exception("Invalid type for the Variant dictionary " + typeName);
|
||||
var count = stream.ReadInt32();
|
||||
var result = new Dictionary<object, object>();
|
||||
var result = (IDictionary)Activator.CreateInstance(type);
|
||||
for (int i = 0; i < count; i++)
|
||||
result.Add(stream.ReadVariant(), stream.ReadVariant());
|
||||
return result;
|
||||
@@ -500,7 +517,7 @@ namespace FlaxEditor.Utilities
|
||||
}
|
||||
return null;
|
||||
}
|
||||
default: throw new ArgumentOutOfRangeException($"Unknown Variant Type {variantType}." + (type != null ? $" Type: {type.FullName}" : string.Empty));
|
||||
default: throw new ArgumentOutOfRangeException($"Unknown Variant Type {variantType}." + (type != null ? $" Type: {type.GetTypeDisplayName()}" : string.Empty));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -550,6 +567,15 @@ namespace FlaxEditor.Utilities
|
||||
else
|
||||
stream.Write(0);
|
||||
break;
|
||||
case VariantType.Dictionary:
|
||||
if (type != typeof(Dictionary<object, object>))
|
||||
{
|
||||
stream.Write(int.MaxValue);
|
||||
stream.WriteStrAnsi(type.GetTypeName(), 77);
|
||||
}
|
||||
else
|
||||
stream.Write(0);
|
||||
break;
|
||||
default:
|
||||
stream.Write(0);
|
||||
break;
|
||||
@@ -660,16 +686,19 @@ namespace FlaxEditor.Utilities
|
||||
break;
|
||||
}
|
||||
case VariantType.Dictionary:
|
||||
stream.Write(((Dictionary<object, object>)value).Count);
|
||||
foreach (var e in (Dictionary<object, object>)value)
|
||||
{
|
||||
var dictionary = (IDictionary)value;
|
||||
stream.Write(dictionary.Count);
|
||||
foreach (var key in dictionary.Keys)
|
||||
{
|
||||
stream.WriteVariant(e.Key);
|
||||
stream.WriteVariant(e.Value);
|
||||
stream.WriteVariant(key);
|
||||
stream.WriteVariant(dictionary[key]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case VariantType.Typename:
|
||||
if (value is Type)
|
||||
stream.WriteStrAnsi(((Type)value).FullName, -14);
|
||||
stream.WriteStrAnsi(((Type)value).GetTypeName(), -14);
|
||||
else if (value is ScriptType)
|
||||
stream.WriteStrAnsi(((ScriptType)value).TypeName, -14);
|
||||
break;
|
||||
@@ -708,6 +737,10 @@ namespace FlaxEditor.Utilities
|
||||
if (value != typeof(object[]))
|
||||
withoutTypeName = false;
|
||||
break;
|
||||
case VariantType.Dictionary:
|
||||
if (value != typeof(Dictionary<object, object>))
|
||||
withoutTypeName = false;
|
||||
break;
|
||||
}
|
||||
if (withoutTypeName)
|
||||
{
|
||||
@@ -721,7 +754,7 @@ namespace FlaxEditor.Utilities
|
||||
stream.WriteValue((int)variantType);
|
||||
|
||||
stream.WritePropertyName("TypeName");
|
||||
stream.WriteValue(value.FullName);
|
||||
stream.WriteValue(value.GetTypeName());
|
||||
|
||||
stream.WriteEndObject();
|
||||
}
|
||||
@@ -1114,19 +1147,20 @@ namespace FlaxEditor.Utilities
|
||||
case VariantType.Dictionary:
|
||||
{
|
||||
stream.WriteStartArray();
|
||||
foreach (var e in (Dictionary<object, object>)value)
|
||||
var dictionary = (IDictionary)value;
|
||||
foreach (var key in dictionary.Keys)
|
||||
{
|
||||
stream.WritePropertyName("Key");
|
||||
stream.WriteVariant(e.Key);
|
||||
stream.WriteVariant(key);
|
||||
stream.WritePropertyName("Value");
|
||||
stream.WriteVariant(e.Value);
|
||||
stream.WriteVariant(dictionary[key]);
|
||||
}
|
||||
stream.WriteEndArray();
|
||||
break;
|
||||
}
|
||||
case VariantType.Typename:
|
||||
if (value is Type)
|
||||
stream.WriteValue(((Type)value).FullName);
|
||||
stream.WriteValue(((Type)value).GetTypeName());
|
||||
else if (value is ScriptType)
|
||||
stream.WriteValue(((ScriptType)value).TypeName);
|
||||
break;
|
||||
@@ -1137,7 +1171,7 @@ namespace FlaxEditor.Utilities
|
||||
stream.WriteRaw(json);
|
||||
break;
|
||||
}
|
||||
default: throw new ArgumentOutOfRangeException($"Unknown Variant Type {variantType} for type {type.FullName}.");
|
||||
default: throw new ArgumentOutOfRangeException($"Unknown Variant Type {variantType} for type {type.GetTypeDisplayName()}.");
|
||||
}
|
||||
// ReSharper restore PossibleNullReferenceException
|
||||
|
||||
|
||||
@@ -175,37 +175,56 @@ namespace FlaxEditor.Windows.Assets
|
||||
// Parameter type editing
|
||||
var cmType = menu.AddChildMenu("Type");
|
||||
{
|
||||
var b = cmType.ContextMenu.AddButton(window.Surface.GetTypeName(param.Type) + "...", () =>
|
||||
var isArray = param.Type.IsArray;
|
||||
var isDictionary = !isArray && param.Type.IsDictionary;
|
||||
ScriptType singleValueType, arrayType, dictionaryType;
|
||||
ContextMenuButton b;
|
||||
if (isDictionary)
|
||||
{
|
||||
// Show context menu with list of parameter types to use
|
||||
var cm = new ItemsListContextMenu(180);
|
||||
var newParameterTypes = window.NewParameterTypes;
|
||||
foreach (var newParameterType in newParameterTypes)
|
||||
{
|
||||
var item = new TypeSearchPopup.TypeItemView(newParameterType);
|
||||
if (newParameterType.Type != null)
|
||||
item.Name = window.VisjectSurface.GetTypeName(newParameterType);
|
||||
cm.AddItem(item);
|
||||
}
|
||||
cm.ItemClicked += (ItemsListContextMenu.Item item) => window.SetParamType(index, (ScriptType)item.Tag);
|
||||
cm.SortItems();
|
||||
cm.Show(window, window.PointFromScreen(Input.MouseScreenPosition));
|
||||
});
|
||||
b.Enabled = window._canEdit;
|
||||
b.TooltipText = "Opens the type picker window to change the parameter type.";
|
||||
cmType.ContextMenu.AddSeparator();
|
||||
var args = param.Type.GetGenericArguments();
|
||||
singleValueType = new ScriptType(args[0]);
|
||||
arrayType = singleValueType.MakeArrayType();
|
||||
dictionaryType = param.Type;
|
||||
var keyName = window.Surface.GetTypeName(new ScriptType(args[0]));
|
||||
var valueName = window.Surface.GetTypeName(new ScriptType(args[1]));
|
||||
|
||||
ScriptType singleValueType, arrayType;
|
||||
if (param.Type.IsArray)
|
||||
{
|
||||
singleValueType = new ScriptType(param.Type.GetElementType());
|
||||
arrayType = param.Type;
|
||||
b = cmType.ContextMenu.AddButton($"Dictionary<{keyName}, {valueName}>");
|
||||
b.Enabled = false;
|
||||
|
||||
b = cmType.ContextMenu.AddButton($"Edit key type: {keyName}...", () => OnChangeType(item => window.SetParamType(index, ScriptType.MakeDictionaryType((ScriptType)item.Tag, new ScriptType(args[1])))));
|
||||
b.Enabled = window._canEdit;
|
||||
b.TooltipText = "Opens the type picker window to change the parameter type.";
|
||||
|
||||
b = cmType.ContextMenu.AddButton($"Edit value type: {valueName}...", () => OnChangeType(item => window.SetParamType(index, ScriptType.MakeDictionaryType(new ScriptType(args[0]), (ScriptType)item.Tag))));
|
||||
b.Enabled = window._canEdit;
|
||||
b.TooltipText = "Opens the type picker window to change the parameter type.";
|
||||
}
|
||||
else
|
||||
{
|
||||
singleValueType = param.Type;
|
||||
arrayType = param.Type.MakeArrayType();
|
||||
if (param.Type == ScriptType.Null)
|
||||
{
|
||||
b = cmType.ContextMenu.AddButton(window.Surface.GetTypeName(param.Type) + "...", () => OnChangeType(item => window.SetParamType(index, (ScriptType)item.Tag)));
|
||||
return;
|
||||
}
|
||||
else if (isArray)
|
||||
{
|
||||
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())));
|
||||
}
|
||||
else
|
||||
{
|
||||
singleValueType = param.Type;
|
||||
arrayType = param.Type.MakeArrayType();
|
||||
dictionaryType = ScriptType.MakeDictionaryType(new ScriptType(typeof(int)), singleValueType);
|
||||
b = cmType.ContextMenu.AddButton(window.Surface.GetTypeName(param.Type) + "...", () => OnChangeType(item => window.SetParamType(index, (ScriptType)item.Tag)));
|
||||
}
|
||||
b.Enabled = window._canEdit;
|
||||
b.TooltipText = "Opens the type picker window to change the parameter type.";
|
||||
}
|
||||
cmType.ContextMenu.AddSeparator();
|
||||
|
||||
b = cmType.ContextMenu.AddButton("Value", () => window.SetParamType(index, singleValueType));
|
||||
b.Checked = param.Type == singleValueType;
|
||||
b.Enabled = window._canEdit;
|
||||
@@ -214,8 +233,30 @@ namespace FlaxEditor.Windows.Assets
|
||||
b.Checked = param.Type == arrayType;
|
||||
b.Enabled = window._canEdit;
|
||||
b.TooltipText = "Changes parameter type to an array.";
|
||||
b = cmType.ContextMenu.AddButton("Dictionary", () => window.SetParamType(index, dictionaryType));
|
||||
b.Checked = param.Type == dictionaryType;
|
||||
b.Enabled = window._canEdit;
|
||||
b.TooltipText = "Changes parameter type to a dictionary.";
|
||||
}
|
||||
}
|
||||
|
||||
private void OnChangeType(Action<ItemsListContextMenu.Item> itemClicked)
|
||||
{
|
||||
// Show context menu with list of parameter types to use
|
||||
var cm = new ItemsListContextMenu(180);
|
||||
var window = (VisualScriptWindow)Values[0];
|
||||
var newParameterTypes = window.NewParameterTypes;
|
||||
foreach (var newParameterType in newParameterTypes)
|
||||
{
|
||||
var item = new TypeSearchPopup.TypeItemView(newParameterType);
|
||||
if (newParameterType.Type != null)
|
||||
item.Name = window.VisjectSurface.GetTypeName(newParameterType);
|
||||
cm.AddItem(item);
|
||||
}
|
||||
cm.ItemClicked += itemClicked;
|
||||
cm.SortItems();
|
||||
cm.Show(window, window.PointFromScreen(Input.MouseScreenPosition));
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class PropertiesProxy
|
||||
@@ -366,7 +407,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
gridControl.Height = Button.DefaultHeight;
|
||||
gridControl.SlotsHorizontally = 2;
|
||||
gridControl.SlotsVertically = 1;
|
||||
|
||||
|
||||
var addOverride = grid.Button("Add Override");
|
||||
addOverride.Button.Clicked += OnOverrideMethodClicked;
|
||||
// TODO: Add sender arg to button clicked action?
|
||||
@@ -1198,6 +1239,22 @@ namespace FlaxEditor.Windows.Assets
|
||||
AfterType = type,
|
||||
AfterValue = TypeUtils.GetDefaultValue(type),
|
||||
};
|
||||
if (action.BeforeValue != null)
|
||||
{
|
||||
// Try to maintain existing value
|
||||
var beforeType = TypeUtils.GetObjectType(action.BeforeValue);
|
||||
if (type.IsArray && beforeType.IsArray && type.GetElementType().IsAssignableFrom(beforeType.GetElementType()))
|
||||
{
|
||||
var beforeArray = (Array)action.BeforeValue;
|
||||
var afterArray = TypeUtils.CreateArrayInstance(type.GetElementType(), beforeArray.Length);
|
||||
Array.Copy(beforeArray, afterArray, beforeArray.Length);
|
||||
action.AfterValue = afterArray;
|
||||
}
|
||||
else if (type.IsAssignableFrom(beforeType))
|
||||
{
|
||||
action.AfterValue = action.BeforeValue;
|
||||
}
|
||||
}
|
||||
_undo.AddAction(action);
|
||||
action.Do();
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ namespace FlaxEditor.Windows
|
||||
/// <summary>
|
||||
/// Proxy object for the Build tab.
|
||||
/// </summary>
|
||||
[HideInEditor]
|
||||
[CustomEditor(typeof(BuildTabProxy.Editor))]
|
||||
private class BuildTabProxy
|
||||
{
|
||||
@@ -65,6 +66,7 @@ namespace FlaxEditor.Windows
|
||||
PerPlatformOptions[PlatformType.Mac].Init("Output/Mac", "Mac");
|
||||
}
|
||||
|
||||
[HideInEditor]
|
||||
abstract class Platform
|
||||
{
|
||||
[HideInEditor]
|
||||
|
||||
@@ -222,7 +222,7 @@ void VisualScriptExecutor::ProcessGroupParameters(Box* box, Node* node, Value& v
|
||||
LOG(Error, "Failed to access Visual Script parameter for {0}.", stack.Stack->Instance->ToString());
|
||||
PrintStack(LogType::Error);
|
||||
}
|
||||
if (node->Boxes[2].HasConnection())
|
||||
if (box->ID == 0 && node->Boxes[2].HasConnection())
|
||||
eatBox(node, node->Boxes[2].FirstConnection());
|
||||
break;
|
||||
}
|
||||
@@ -1154,17 +1154,19 @@ void VisualScriptExecutor::ProcessGroupFlow(Box* boxBase, Node* node, Value& val
|
||||
arrayValue.NodeId = node->ID;
|
||||
arrayValue.BoxId = 1;
|
||||
arrayValue.Value = tryGetValue(node->GetBox(1), Value::Null);
|
||||
if (arrayValue.Value.Type.Type != VariantType::Array)
|
||||
if (arrayValue.Value.Type.Type == VariantType::Array)
|
||||
{
|
||||
const int32 count = arrayValue.Value.AsArray().Count();
|
||||
for (; iteratorValue.Value.AsInt < count; iteratorValue.Value.AsInt++)
|
||||
{
|
||||
boxBase = node->GetBox(3);
|
||||
if (boxBase->HasConnection())
|
||||
eatBox(node, boxBase->FirstConnection());
|
||||
}
|
||||
}
|
||||
else if (arrayValue.Value.Type.Type != VariantType::Null)
|
||||
{
|
||||
OnError(node, boxBase, String::Format(TEXT("Input value {0} is not an array."), arrayValue.Value));
|
||||
return;
|
||||
}
|
||||
const int32 count = arrayValue.Value.AsArray().Count();
|
||||
for (; iteratorValue.Value.AsInt < count; iteratorValue.Value.AsInt++)
|
||||
{
|
||||
boxBase = node->GetBox(3);
|
||||
if (boxBase->HasConnection())
|
||||
eatBox(node, boxBase->FirstConnection());
|
||||
}
|
||||
boxBase = node->GetBox(6);
|
||||
if (boxBase->HasConnection())
|
||||
@@ -1190,6 +1192,88 @@ void VisualScriptExecutor::ProcessGroupFlow(Box* boxBase, Node* node, Value& val
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Dictionary For Each
|
||||
case 8:
|
||||
{
|
||||
const auto scope = ThreadStacks.Get().Stack->Scope;
|
||||
int32 iteratorIndex = 0;
|
||||
for (; iteratorIndex < scope->ReturnedValues.Count(); iteratorIndex++)
|
||||
{
|
||||
const auto& e = scope->ReturnedValues[iteratorIndex];
|
||||
if (e.NodeId == node->ID && e.BoxId == 0)
|
||||
break;
|
||||
}
|
||||
int32 dictionaryIndex = 0;
|
||||
for (; iteratorIndex < scope->ReturnedValues.Count(); dictionaryIndex++)
|
||||
{
|
||||
const auto& e = scope->ReturnedValues[dictionaryIndex];
|
||||
if (e.NodeId == node->ID && e.BoxId == 1)
|
||||
break;
|
||||
}
|
||||
switch (boxBase->ID)
|
||||
{
|
||||
// Loop
|
||||
case 0:
|
||||
{
|
||||
if (iteratorIndex == scope->ReturnedValues.Count())
|
||||
{
|
||||
if (dictionaryIndex == scope->ReturnedValues.Count())
|
||||
dictionaryIndex++;
|
||||
scope->ReturnedValues.AddOne();
|
||||
}
|
||||
if (dictionaryIndex == scope->ReturnedValues.Count())
|
||||
scope->ReturnedValues.AddOne();
|
||||
auto& iteratorValue = scope->ReturnedValues[iteratorIndex];
|
||||
iteratorValue.NodeId = node->ID;
|
||||
iteratorValue.BoxId = 0;
|
||||
auto& dictionaryValue = scope->ReturnedValues[dictionaryIndex];
|
||||
dictionaryValue.NodeId = node->ID;
|
||||
dictionaryValue.BoxId = 1;
|
||||
dictionaryValue.Value = tryGetValue(node->GetBox(4), Value::Null);
|
||||
if (dictionaryValue.Value.Type.Type == VariantType::Dictionary)
|
||||
{
|
||||
auto& dictionary = *dictionaryValue.Value.AsDictionary;
|
||||
iteratorValue.Value = dictionary.Begin().Index();
|
||||
int32 end = dictionary.End().Index();
|
||||
while (iteratorValue.Value.AsInt < end)
|
||||
{
|
||||
boxBase = node->GetBox(3);
|
||||
if (boxBase->HasConnection())
|
||||
eatBox(node, boxBase->FirstConnection());
|
||||
Dictionary<Variant, Variant>::Iterator it(dictionary, iteratorValue.Value.AsInt);
|
||||
++it;
|
||||
iteratorValue.Value.AsInt = it.Index();
|
||||
}
|
||||
}
|
||||
else if (dictionaryValue.Value.Type.Type != VariantType::Null)
|
||||
{
|
||||
OnError(node, boxBase, String::Format(TEXT("Input value {0} is not a dictionary."), dictionaryValue.Value));
|
||||
return;
|
||||
}
|
||||
boxBase = node->GetBox(6);
|
||||
if (boxBase->HasConnection())
|
||||
eatBox(node, boxBase->FirstConnection());
|
||||
break;
|
||||
}
|
||||
// Key
|
||||
case 1:
|
||||
if (iteratorIndex != scope->ReturnedValues.Count() && dictionaryIndex != scope->ReturnedValues.Count())
|
||||
value = Dictionary<Variant, Variant>::Iterator(*scope->ReturnedValues[dictionaryIndex].Value.AsDictionary, scope->ReturnedValues[iteratorIndex].Value.AsInt)->Key;
|
||||
break;
|
||||
// Value
|
||||
case 2:
|
||||
if (iteratorIndex != scope->ReturnedValues.Count() && dictionaryIndex != scope->ReturnedValues.Count())
|
||||
value = Dictionary<Variant, Variant>::Iterator(*scope->ReturnedValues[dictionaryIndex].Value.AsDictionary, scope->ReturnedValues[iteratorIndex].Value.AsInt)->Value;
|
||||
break;
|
||||
// Break
|
||||
case 5:
|
||||
// Reset loop iterator
|
||||
if (iteratorIndex != scope->ReturnedValues.Count())
|
||||
scope->ReturnedValues[iteratorIndex].Value.AsInt = MAX_int32 - 1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -245,6 +245,7 @@ public:
|
||||
Dictionary& _collection;
|
||||
int32 _index;
|
||||
|
||||
public:
|
||||
Iterator(Dictionary& collection, const int32 index)
|
||||
: _collection(collection)
|
||||
, _index(index)
|
||||
@@ -257,8 +258,6 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Iterator(const Iterator& i)
|
||||
: _collection(i._collection)
|
||||
, _index(i._index)
|
||||
@@ -272,6 +271,10 @@ public:
|
||||
}
|
||||
|
||||
public:
|
||||
FORCE_INLINE int32 Index() const
|
||||
{
|
||||
return _index;
|
||||
}
|
||||
|
||||
FORCE_INLINE bool IsEnd() const
|
||||
{
|
||||
|
||||
@@ -1679,7 +1679,7 @@ namespace FlaxEngine
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator ==(Vector2 left, Vector2 right)
|
||||
{
|
||||
return Mathf.NearEqual(left.X, left.X) && Mathf.NearEqual(left.Y, right.Y);
|
||||
return Mathf.NearEqual(left.X, right.X) && Mathf.NearEqual(left.Y, right.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1691,7 +1691,7 @@ namespace FlaxEngine
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator !=(Vector2 left, Vector2 right)
|
||||
{
|
||||
return !Mathf.NearEqual(left.X, left.X) || !Mathf.NearEqual(left.Y, right.Y);
|
||||
return !Mathf.NearEqual(left.X, right.X) || !Mathf.NearEqual(left.Y, right.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1445,7 +1445,7 @@ namespace FlaxEngine
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator ==(Vector4 left, Vector4 right)
|
||||
{
|
||||
return left.Equals(ref right);
|
||||
return Mathf.NearEqual(left.X, right.X) && Mathf.NearEqual(left.Y, right.Y) && Mathf.NearEqual(left.Z, right.Z) && Mathf.NearEqual(left.W, right.W);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -66,7 +66,7 @@ namespace
|
||||
"FlaxEngine.Ray",// Ray
|
||||
"FlaxEngine.Matrix",// Matrix
|
||||
"System.Object[]",// Array
|
||||
"Dictionary<System.Object,System.Object>",// Dictionary
|
||||
"System.Collections.Generic.Dictionary`2[System.Object,System.Object]",// Dictionary
|
||||
"System.Object",// ManagedObject
|
||||
"System.Type",// Typename
|
||||
"FlaxEngine.Int2"// Int2
|
||||
@@ -767,6 +767,12 @@ Variant::Variant(const Array<Variant, HeapAllocation>& v)
|
||||
new(array)Array<Variant, HeapAllocation>(v);
|
||||
}
|
||||
|
||||
Variant::Variant(Dictionary<Variant, Variant>&& v)
|
||||
: Type(VariantType::Dictionary)
|
||||
{
|
||||
AsDictionary = New<Dictionary<Variant, Variant>>(MoveTemp(v));
|
||||
}
|
||||
|
||||
Variant::Variant(const Dictionary<Variant, Variant>& v)
|
||||
: Type(VariantType::Dictionary)
|
||||
{
|
||||
|
||||
@@ -228,6 +228,7 @@ public:
|
||||
explicit Variant(const Matrix& v);
|
||||
Variant(Array<Variant, HeapAllocation>&& v);
|
||||
Variant(const Array<Variant, HeapAllocation>& v);
|
||||
explicit Variant(Dictionary<Variant, Variant, HeapAllocation>&& v);
|
||||
explicit Variant(const Dictionary<Variant, Variant, HeapAllocation>& v);
|
||||
explicit Variant(const Span<byte>& v);
|
||||
explicit Variant(const CommonValue& v);
|
||||
|
||||
@@ -210,7 +210,7 @@ public:
|
||||
/// </summary>
|
||||
/// <param name="type">Type of the actor to search for. Includes any actors derived from the type.</param>
|
||||
/// <returns>The child actor or null.</returns>
|
||||
API_FUNCTION() Actor* GetChild(const MClass* type) const;
|
||||
API_FUNCTION() Actor* GetChild(API_PARAM(Attributes="TypeReference(typeof(Actor))") const MClass* type) const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the child actor of the given type.
|
||||
@@ -227,7 +227,7 @@ public:
|
||||
/// </summary>
|
||||
/// <param name="type">Type of the actor to search for. Includes any actors derived from the type.</param>
|
||||
/// <returns>The child actors.</returns>
|
||||
API_FUNCTION() Array<Actor*> GetChildren(const MClass* type) const;
|
||||
API_FUNCTION() Array<Actor*> GetChildren(API_PARAM(Attributes="TypeReference(typeof(Actor))") const MClass* type) const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the child actors of the given type.
|
||||
@@ -273,7 +273,7 @@ public:
|
||||
/// </summary>
|
||||
/// <param name="type">Type of the script to search for. Includes any scripts derived from the type.</param>
|
||||
/// <returns>The script or null.</returns>
|
||||
API_FUNCTION() Script* GetScript(const MClass* type) const;
|
||||
API_FUNCTION() Script* GetScript(API_PARAM(Attributes="TypeReference(typeof(Script))") const MClass* type) const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the script of the given type from this actor.
|
||||
@@ -290,7 +290,7 @@ public:
|
||||
/// </summary>
|
||||
/// <param name="type">Type of the script to search for. Includes any scripts derived from the type.</param>
|
||||
/// <returns>The scripts.</returns>
|
||||
API_FUNCTION() Array<Script*> GetScripts(const MClass* type) const;
|
||||
API_FUNCTION() Array<Script*> GetScripts(API_PARAM(Attributes="TypeReference(typeof(Script))") const MClass* type) const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the scripts of the given type from this actor.
|
||||
@@ -702,7 +702,7 @@ public:
|
||||
/// </summary>
|
||||
/// <param name="type">Type of the actor to search for. Includes any actors derived from the type.</param>
|
||||
/// <returns>Actor instance if found, null otherwise.</returns>
|
||||
API_FUNCTION() Actor* FindActor(const MClass* type) const;
|
||||
API_FUNCTION() Actor* FindActor(API_PARAM(Attributes="TypeReference(typeof(Actor))") const MClass* type) const;
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find the actor of the given type in this actor hierarchy (checks this actor and all children hierarchy).
|
||||
@@ -719,7 +719,7 @@ public:
|
||||
/// </summary>
|
||||
/// <param name="type">Type of the actor to search for. Includes any actors derived from the type.</param>
|
||||
/// <returns>Script instance if found, null otherwise.</returns>
|
||||
API_FUNCTION() Script* FindScript(const MClass* type) const;
|
||||
API_FUNCTION() Script* FindScript(API_PARAM(Attributes="TypeReference(typeof(Script))") const MClass* type) const;
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find the script of the given type in this actor hierarchy (checks this actor and all children hierarchy).
|
||||
|
||||
@@ -362,7 +362,7 @@ public:
|
||||
/// </summary>
|
||||
/// <param name="type">Type of the actor to search for. Includes any actors derived from the type.</param>
|
||||
/// <returns>Found actor or null.</returns>
|
||||
API_FUNCTION() static Actor* FindActor(const MClass* type);
|
||||
API_FUNCTION() static Actor* FindActor(API_PARAM(Attributes="TypeReference(typeof(Actor))") const MClass* type);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find the actor of the given type in all the loaded scenes.
|
||||
@@ -379,7 +379,7 @@ public:
|
||||
/// </summary>
|
||||
/// <param name="type">Type of the script to search for. Includes any scripts derived from the type.</param>
|
||||
/// <returns>Found script or null.</returns>
|
||||
API_FUNCTION() static Script* FindScript(const MClass* type);
|
||||
API_FUNCTION() static Script* FindScript(API_PARAM(Attributes="TypeReference(typeof(Script))") const MClass* type);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find the script of the given type in all the loaded scenes.
|
||||
@@ -396,14 +396,14 @@ public:
|
||||
/// </summary>
|
||||
/// <param name="type">Type of the actor to search for. Includes any actors derived from the type.</param>
|
||||
/// <returns>Found actors list.</returns>
|
||||
API_FUNCTION() static Array<Actor*> GetActors(const MClass* type);
|
||||
API_FUNCTION() static Array<Actor*> GetActors(API_PARAM(Attributes="TypeReference(typeof(Actor))") const MClass* type);
|
||||
|
||||
/// <summary>
|
||||
/// Finds all the scripts of the given type in all the loaded scenes.
|
||||
/// </summary>
|
||||
/// <param name="type">Type of the script to search for. Includes any scripts derived from the type.</param>
|
||||
/// <returns>Found scripts list.</returns>
|
||||
API_FUNCTION() static Array<Script*> GetScripts(const MClass* type);
|
||||
API_FUNCTION() static Array<Script*> GetScripts(API_PARAM(Attributes="TypeReference(typeof(Script))") const MClass* type);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find scene with given ID.
|
||||
|
||||
@@ -14,6 +14,9 @@
|
||||
#if USE_MONO
|
||||
#include <ThirdParty/mono-2.0/mono/metadata/appdomain.h>
|
||||
|
||||
/// <summary>
|
||||
/// Utility interop between C++ and C# for Dictionary collection.
|
||||
/// </summary>
|
||||
struct FLAXENGINE_API ManagedDictionary
|
||||
{
|
||||
MonoObject* Instance;
|
||||
@@ -74,17 +77,13 @@ struct FLAXENGINE_API ManagedDictionary
|
||||
return result;
|
||||
}
|
||||
|
||||
static ManagedDictionary New(MonoType* keyType, MonoType* valueType)
|
||||
static MonoReflectionType* GetClass(MonoType* keyType, MonoType* valueType)
|
||||
{
|
||||
ManagedDictionary result;
|
||||
|
||||
auto domain = mono_domain_get();
|
||||
auto scriptingClass = Scripting::GetStaticClass();
|
||||
CHECK_RETURN(scriptingClass, result);
|
||||
CHECK_RETURN(scriptingClass, nullptr);
|
||||
auto makeGenericMethod = scriptingClass->GetMethod("MakeGenericType", 2);
|
||||
CHECK_RETURN(makeGenericMethod, result);
|
||||
auto createMethod = StdTypesContainer::Instance()->ActivatorClass->GetMethod("CreateInstance", 2);
|
||||
CHECK_RETURN(createMethod, result);
|
||||
CHECK_RETURN(makeGenericMethod, nullptr);
|
||||
|
||||
auto genericType = MUtils::GetType(StdTypesContainer::Instance()->DictionaryClass->GetNative());
|
||||
auto genericArgs = mono_array_new(domain, mono_get_object_class(), 2);
|
||||
@@ -100,9 +99,25 @@ struct FLAXENGINE_API ManagedDictionary
|
||||
{
|
||||
MException ex(exception);
|
||||
ex.Log(LogType::Error, TEXT(""));
|
||||
return result;
|
||||
return nullptr;
|
||||
}
|
||||
return (MonoReflectionType*)dictionaryType;
|
||||
}
|
||||
|
||||
static ManagedDictionary New(MonoType* keyType, MonoType* valueType)
|
||||
{
|
||||
ManagedDictionary result;
|
||||
auto dictionaryType = GetClass(keyType, valueType);
|
||||
if (!dictionaryType)
|
||||
return result;
|
||||
|
||||
auto scriptingClass = Scripting::GetStaticClass();
|
||||
CHECK_RETURN(scriptingClass, result);
|
||||
auto createMethod = StdTypesContainer::Instance()->ActivatorClass->GetMethod("CreateInstance", 2);
|
||||
CHECK_RETURN(createMethod, result);
|
||||
|
||||
MObject* exception = nullptr;
|
||||
void* params[2];
|
||||
params[0] = dictionaryType;
|
||||
params[1] = nullptr;
|
||||
auto instance = createMethod->Invoke(nullptr, params, &exception);
|
||||
|
||||
@@ -19,11 +19,42 @@
|
||||
#include "Engine/Scripting/Scripting.h"
|
||||
#include "Engine/Scripting/ScriptingObject.h"
|
||||
#include "Engine/Scripting/StdTypesContainer.h"
|
||||
#include "Engine/Scripting/InternalCalls/ManagedDictionary.h"
|
||||
#include "Engine/Utilities/StringConverter.h"
|
||||
#include "Engine/Content/Asset.h"
|
||||
|
||||
#if USE_MONO
|
||||
|
||||
// Inlined mono private types to access MonoType internals
|
||||
|
||||
typedef struct _MonoGenericClass MonoGenericClass;
|
||||
typedef struct _MonoGenericContext MonoGenericContext;
|
||||
|
||||
struct _MonoGenericInst
|
||||
{
|
||||
unsigned int id;
|
||||
unsigned int type_argc : 22;
|
||||
unsigned int is_open : 1;
|
||||
MonoType* type_argv[MONO_ZERO_LEN_ARRAY];
|
||||
};
|
||||
|
||||
struct _MonoGenericContext
|
||||
{
|
||||
MonoGenericInst* class_inst;
|
||||
MonoGenericInst* method_inst;
|
||||
};
|
||||
|
||||
struct _MonoGenericClass
|
||||
{
|
||||
MonoClass* container_class;
|
||||
MonoGenericContext context;
|
||||
unsigned int is_dynamic : 1;
|
||||
unsigned int is_tb_open : 1;
|
||||
unsigned int need_sync : 1;
|
||||
MonoClass* cached_class;
|
||||
class MonoImageSet* owner;
|
||||
};
|
||||
|
||||
struct _MonoType
|
||||
{
|
||||
union
|
||||
@@ -43,6 +74,21 @@ struct _MonoType
|
||||
unsigned int pinned : 1;
|
||||
};
|
||||
|
||||
namespace
|
||||
{
|
||||
// typeName in format System.Collections.Generic.Dictionary`2[KeyType,ValueType]
|
||||
void GetDictionaryKeyValueTypes(const StringAnsiView& typeName, MonoClass*& keyClass, MonoClass*& valueClass)
|
||||
{
|
||||
const int32 keyStart = typeName.Find('[');
|
||||
const int32 keyEnd = typeName.Find(',');
|
||||
const int32 valueEnd = typeName.Find(']');
|
||||
const StringAnsiView keyTypename(*typeName + keyStart + 1, keyEnd - keyStart - 1);
|
||||
const StringAnsiView valueTypename(*typeName + keyEnd + 1, valueEnd - keyEnd - 1);
|
||||
keyClass = Scripting::FindClassNative(keyTypename);
|
||||
valueClass = Scripting::FindClassNative(valueTypename);
|
||||
}
|
||||
}
|
||||
|
||||
StringView MUtils::ToString(MonoString* str)
|
||||
{
|
||||
if (str == nullptr)
|
||||
@@ -439,6 +485,30 @@ Variant MUtils::UnboxVariant(MonoObject* value)
|
||||
}
|
||||
return v;
|
||||
}
|
||||
case MONO_TYPE_GENERICINST:
|
||||
{
|
||||
if (StringUtils::Compare(mono_class_get_name(klass), "Dictionary`2") == 0 && StringUtils::Compare(mono_class_get_namespace(klass), "System.Collections.Generic") == 0)
|
||||
{
|
||||
// Dictionary
|
||||
ManagedDictionary managed(value);
|
||||
MonoArray* managedKeys = managed.GetKeys();
|
||||
auto length = managedKeys ? (int32)mono_array_length(managedKeys) : 0;
|
||||
Dictionary<Variant, Variant> native;
|
||||
native.EnsureCapacity(length);
|
||||
for (int32 i = 0; i < length; i++)
|
||||
{
|
||||
MonoObject* keyManaged = mono_array_get(managedKeys, MonoObject*, i);
|
||||
MonoObject* valueManaged = managed.GetValue(keyManaged);
|
||||
native.Add(UnboxVariant(keyManaged), UnboxVariant(valueManaged));
|
||||
}
|
||||
Variant v(MoveTemp(native));
|
||||
StringAnsi typeName;
|
||||
GetClassFullname(klass, typeName);
|
||||
v.Type.SetTypeName(typeName);
|
||||
return v;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (mono_class_is_subclass_of(klass, Asset::GetStaticClass()->GetNative(), false) != 0)
|
||||
@@ -473,7 +543,6 @@ Variant MUtils::UnboxVariant(MonoObject* value)
|
||||
}
|
||||
return Variant(value);
|
||||
}
|
||||
// TODO: support any dictionary unboxing
|
||||
|
||||
return Variant(value);
|
||||
}
|
||||
@@ -642,7 +711,31 @@ MonoObject* MUtils::BoxVariant(const Variant& value)
|
||||
}
|
||||
return (MonoObject*)managed;
|
||||
}
|
||||
// TODO: VariantType::Dictionary
|
||||
case VariantType::Dictionary:
|
||||
{
|
||||
// Get dictionary key and value types
|
||||
MonoClass *keyClass, *valueClass;
|
||||
GetDictionaryKeyValueTypes(value.Type.GetTypeName(), keyClass, valueClass);
|
||||
if (!keyClass || !valueClass)
|
||||
{
|
||||
LOG(Error, "Invalid type to box {0}", value.Type);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Allocate managed dictionary
|
||||
ManagedDictionary managed = ManagedDictionary::New(mono_class_get_type(keyClass), mono_class_get_type(valueClass));
|
||||
if (!managed.Instance)
|
||||
return nullptr;
|
||||
|
||||
// Add native keys and values
|
||||
const auto& dictionary = *value.AsDictionary;
|
||||
for (const auto& e : dictionary)
|
||||
{
|
||||
managed.Add(BoxVariant(e.Key), BoxVariant(e.Value));
|
||||
}
|
||||
|
||||
return managed.Instance;
|
||||
}
|
||||
case VariantType::Structure:
|
||||
{
|
||||
if (value.AsBlob.Data == nullptr)
|
||||
@@ -684,7 +777,6 @@ void MUtils::GetClassFullname(MonoObject* obj, MString& fullname)
|
||||
{
|
||||
if (obj == nullptr)
|
||||
return;
|
||||
|
||||
MonoClass* monoClass = mono_object_get_class(obj);
|
||||
GetClassFullname(monoClass, fullname);
|
||||
}
|
||||
@@ -693,11 +785,13 @@ void MUtils::GetClassFullname(MonoClass* monoClass, MString& fullname)
|
||||
{
|
||||
static MString plusStr("+");
|
||||
static MString dotStr(".");
|
||||
MonoClass* nestingClass = mono_class_get_nesting_type(monoClass);
|
||||
MonoClass* lastClass = monoClass;
|
||||
|
||||
// Name
|
||||
fullname = mono_class_get_name(monoClass);
|
||||
|
||||
// Outer class for nested types
|
||||
MonoClass* nestingClass = mono_class_get_nesting_type(monoClass);
|
||||
MonoClass* lastClass = monoClass;
|
||||
while (nestingClass)
|
||||
{
|
||||
lastClass = nestingClass;
|
||||
@@ -705,9 +799,27 @@ void MUtils::GetClassFullname(MonoClass* monoClass, MString& fullname)
|
||||
nestingClass = mono_class_get_nesting_type(nestingClass);
|
||||
}
|
||||
|
||||
// Namespace
|
||||
const char* lastClassNamespace = mono_class_get_namespace(lastClass);
|
||||
if (lastClassNamespace && *lastClassNamespace)
|
||||
fullname = lastClassNamespace + dotStr + fullname;
|
||||
|
||||
// Generic instance arguments
|
||||
const MonoType* monoType = mono_class_get_type(monoClass);
|
||||
if (monoType && monoType->type == MONO_TYPE_GENERICINST)
|
||||
{
|
||||
fullname += '[';
|
||||
MString tmp;
|
||||
for (unsigned int i = 0; i < monoType->data.generic_class->context.class_inst->type_argc; i++)
|
||||
{
|
||||
if (i != 0)
|
||||
fullname += ',';
|
||||
MonoType* argType = monoType->data.generic_class->context.class_inst->type_argv[i];
|
||||
GetClassFullname(mono_type_get_class(argType), tmp);
|
||||
fullname += tmp;
|
||||
}
|
||||
fullname += ']';
|
||||
}
|
||||
}
|
||||
|
||||
void MUtils::GetClassFullname(MonoReflectionType* type, MString& fullname)
|
||||
@@ -715,7 +827,7 @@ void MUtils::GetClassFullname(MonoReflectionType* type, MString& fullname)
|
||||
if (!type)
|
||||
return;
|
||||
MonoType* monoType = mono_reflection_type_get_type(type);
|
||||
MonoClass* monoClass = mono_type_get_class(monoType);
|
||||
MonoClass* monoClass = mono_class_from_mono_type(monoType);
|
||||
GetClassFullname(monoClass, fullname);
|
||||
}
|
||||
|
||||
@@ -729,7 +841,7 @@ MonoClass* MUtils::GetClass(MonoReflectionType* type)
|
||||
if (!type)
|
||||
return nullptr;
|
||||
MonoType* monoType = mono_reflection_type_get_type(type);
|
||||
return mono_type_get_class(monoType);
|
||||
return mono_class_from_mono_type(monoType);
|
||||
}
|
||||
|
||||
MonoClass* MUtils::GetClass(const VariantType& value)
|
||||
@@ -805,6 +917,17 @@ MonoClass* MUtils::GetClass(const VariantType& value)
|
||||
return mono_array_class_get(mclass->GetNative(), 1);
|
||||
}
|
||||
return mono_array_class_get(mono_get_object_class(), 1);
|
||||
case VariantType::Dictionary:
|
||||
{
|
||||
MonoClass *keyClass, *valueClass;
|
||||
GetDictionaryKeyValueTypes(value.GetTypeName(), keyClass, valueClass);
|
||||
if (!keyClass || !valueClass)
|
||||
{
|
||||
LOG(Error, "Invalid type to box {0}", value.ToString());
|
||||
return nullptr;
|
||||
}
|
||||
return GetClass(ManagedDictionary::GetClass(mono_class_get_type(keyClass), mono_class_get_type(valueClass)));
|
||||
}
|
||||
case VariantType::ManagedObject:
|
||||
return mono_get_object_class();
|
||||
default: ;
|
||||
@@ -1052,6 +1175,15 @@ void* MUtils::VariantToManagedArgPtr(Variant& value, const MType& type, bool& fa
|
||||
object = nullptr;
|
||||
return object;
|
||||
}
|
||||
case MONO_TYPE_GENERICINST:
|
||||
{
|
||||
if (value.Type.Type == VariantType::Null)
|
||||
return nullptr;
|
||||
MonoObject* object = BoxVariant(value);
|
||||
if (object && !mono_class_is_subclass_of(mono_object_get_class(object), mono_class_from_mono_type(type.GetNative()), false))
|
||||
object = nullptr;
|
||||
return object;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include "Engine/Core/Collections/Dictionary.h"
|
||||
#include "Engine/Content/Asset.h"
|
||||
#include "Engine/Core/Cache.h"
|
||||
#include "Engine/Debug/DebugLog.h"
|
||||
#include "Engine/Debug/Exceptions/JsonParseException.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Scripting/ManagedSerialization.h"
|
||||
@@ -458,12 +457,11 @@ void ReadStream::ReadVariant(Variant* data)
|
||||
auto& dictionary = *data->AsDictionary;
|
||||
dictionary.Clear();
|
||||
dictionary.EnsureCapacity(count);
|
||||
Variant key, value;
|
||||
for (int32 i = 0; i < count; i++)
|
||||
{
|
||||
Variant key;
|
||||
ReadVariant(&key);
|
||||
ReadVariant(&value);
|
||||
dictionary.Add(key, value);
|
||||
ReadVariant(&dictionary[MoveTemp(key)]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -133,6 +133,18 @@ void VisjectExecutor::ProcessGroupConstants(Box* box, Node* node, Value& value)
|
||||
}
|
||||
}
|
||||
break;
|
||||
// Dictionary
|
||||
case 14:
|
||||
{
|
||||
value = Variant(Dictionary<Variant, Variant>());
|
||||
String typeName = TEXT("System.Collections.Generic.Dictionary`2[");
|
||||
typeName += (StringView)node->Values[0];
|
||||
typeName += ',';
|
||||
typeName += (StringView)node->Values[1];
|
||||
typeName += ']';
|
||||
value.Type.SetTypeName(typeName);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -1257,6 +1269,8 @@ void VisjectExecutor::ProcessGroupCollections(Box* box, Node* node, Value& value
|
||||
{
|
||||
// Array
|
||||
Variant v = tryGetValue(node->GetBox(0), Value::Null);
|
||||
if (v.Type.Type == VariantType::Null)
|
||||
v = Variant(Array<Variant>());
|
||||
ENSURE(v.Type.Type == VariantType::Array, String::Format(TEXT("Input value {0} is not an array."), v));
|
||||
auto& array = v.AsArray();
|
||||
Box* b;
|
||||
@@ -1359,4 +1373,65 @@ void VisjectExecutor::ProcessGroupCollections(Box* box, Node* node, Value& value
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (node->TypeID < 200)
|
||||
{
|
||||
// Dictionary
|
||||
Variant v = tryGetValue(node->GetBox(0), Value::Null);
|
||||
if (v.Type.Type == VariantType::Null)
|
||||
v = Variant(Dictionary<Variant, Variant>());
|
||||
ENSURE(v.Type.Type == VariantType::Dictionary, String::Format(TEXT("Input value {0} is not a dictionary."), v));
|
||||
auto& dictionary = *v.AsDictionary;
|
||||
switch (node->TypeID)
|
||||
{
|
||||
// Count
|
||||
case 101:
|
||||
value = dictionary.Count();
|
||||
break;
|
||||
// Contains Key
|
||||
case 102:
|
||||
{
|
||||
Variant inKey = tryGetValue(node->GetBox(1), 0, Value::Null);
|
||||
value = dictionary.ContainsKey(inKey);
|
||||
break;
|
||||
}
|
||||
// Contains Value
|
||||
case 103:
|
||||
{
|
||||
Variant inValue = tryGetValue(node->GetBox(2), 0, Value::Null);
|
||||
value = dictionary.ContainsValue(inValue);
|
||||
break;
|
||||
}
|
||||
// Clear
|
||||
case 104:
|
||||
dictionary.Clear();
|
||||
value = MoveTemp(v);
|
||||
break;
|
||||
// Remove
|
||||
case 105:
|
||||
{
|
||||
Variant inKey = tryGetValue(node->GetBox(1), 0, Value::Null);
|
||||
dictionary.Remove(inKey);
|
||||
value = MoveTemp(v);
|
||||
break;
|
||||
}
|
||||
// Set
|
||||
case 106:
|
||||
{
|
||||
Variant inKey = tryGetValue(node->GetBox(1), 0, Value::Null);
|
||||
Variant inValue = tryGetValue(node->GetBox(2), 1, Value::Null);
|
||||
dictionary[MoveTemp(inKey)] = MoveTemp(inValue);
|
||||
value = MoveTemp(v);
|
||||
break;
|
||||
}
|
||||
// Get
|
||||
case 107:
|
||||
{
|
||||
Variant key = tryGetValue(node->GetBox(1), 0, Value::Null);
|
||||
Variant* valuePtr = dictionary.TryGet(key);
|
||||
ENSURE(valuePtr, TEXT("Missing key to get."));
|
||||
value = MoveTemp(*valuePtr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user