Add SoftTypeReference<T> to scripting API for lazy-load type references (via typename)

This commit is contained in:
Wojtek Figat
2023-05-24 22:56:29 +02:00
parent 982639f215
commit 7efb3e3f3d
5 changed files with 298 additions and 14 deletions

View File

@@ -393,11 +393,8 @@ namespace FlaxEditor.CustomEditors.Editors
if (_element != null)
{
_element.CustomControl.ValueChanged += () => SetValue(_element.CustomControl.Value.Type);
if (_element.CustomControl.Type == ScriptType.Object)
{
_element.CustomControl.Type = Values.Type.Type != typeof(object) || Values[0] == null ? ScriptType.Object : TypeUtils.GetObjectType(Values[0]);
}
}
}
@@ -407,9 +404,7 @@ namespace FlaxEditor.CustomEditors.Editors
base.Refresh();
if (!HasDifferentValues)
{
_element.CustomControl.Value = new ScriptType(Values[0] as Type);
}
}
}
@@ -425,9 +420,7 @@ namespace FlaxEditor.CustomEditors.Editors
base.Initialize(layout);
if (_element != null)
{
_element.CustomControl.ValueChanged += () => SetValue(_element.CustomControl.Value);
}
}
/// <inheritdoc />
@@ -436,9 +429,32 @@ namespace FlaxEditor.CustomEditors.Editors
base.Refresh();
if (!HasDifferentValues)
{
_element.CustomControl.Value = (ScriptType)Values[0];
}
}
}
/// <summary>
/// Default implementation of the inspector used to edit reference to the <see cref="FlaxEngine.SoftTypeReference"/>. Used to pick classes.
/// </summary>
[CustomEditor(typeof(SoftTypeReference)), DefaultEditor]
public class SoftTypeReferenceEditor : TypeEditorBase
{
/// <inheritdoc />
public override void Initialize(LayoutElementsContainer layout)
{
base.Initialize(layout);
if (_element != null)
_element.CustomControl.ValueChanged += () => SetValue(new SoftTypeReference(_element.CustomControl.ValueTypeName));
}
/// <inheritdoc />
public override void Refresh()
{
base.Refresh();
if (!HasDifferentValues)
_element.CustomControl.ValueTypeName = ((SoftTypeReference)Values[0]).TypeName;
}
}

View File

@@ -0,0 +1,87 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
using System;
namespace FlaxEngine
{
/// <summary>
/// The soft reference to the scripting type contained in the scripting assembly.
/// </summary>
public struct SoftTypeReference : IComparable, IComparable<SoftTypeReference>
{
private string _typeName;
/// <summary>
/// Gets or sets the type full name (eg. FlaxEngine.Actor).
/// </summary>
public string TypeName
{
get => _typeName;
set => _typeName = value;
}
/// <summary>
/// Gets or sets the type (resolves soft reference).
/// </summary>
public Type Type
{
get => _typeName != null ? Type.GetType(_typeName) : null;
set => _typeName = value?.FullName;
}
/// <summary>
/// Initializes a new instance of the <see cref="SoftTypeReference"/>.
/// </summary>
/// <param name="typeName">The type name.</param>
public SoftTypeReference(string typeName)
{
_typeName = typeName;
}
/// <summary>
/// Gets the soft type reference from full name.
/// </summary>
/// <param name="s">The type name.</param>
/// <returns>The soft type reference.</returns>
public static implicit operator SoftTypeReference(string s)
{
return new SoftTypeReference { _typeName = s };
}
/// <summary>
/// Gets the soft type reference from runtime type.
/// </summary>
/// <param name="s">The type.</param>
/// <returns>The soft type reference.</returns>
public static implicit operator SoftTypeReference(Type s)
{
return new SoftTypeReference { _typeName = s?.FullName };
}
/// <inheritdoc />
public override string ToString()
{
return _typeName;
}
/// <inheritdoc />
public override int GetHashCode()
{
return _typeName?.GetHashCode() ?? 0;
}
/// <inheritdoc />
public int CompareTo(object obj)
{
if (obj is SoftTypeReference other)
return CompareTo(other);
return 0;
}
/// <inheritdoc />
public int CompareTo(SoftTypeReference other)
{
return string.Compare(_typeName, other._typeName, StringComparison.Ordinal);
}
}
}

View File

@@ -0,0 +1,151 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
#pragma once
#include "Scripting.h"
#include "ScriptingObject.h"
#include "Engine/Core/Log.h"
#include "Engine/Core/Types/String.h"
#include "Engine/Serialization/SerializationFwd.h"
/// <summary>
/// The soft reference to the scripting type contained in the scripting assembly.
/// </summary>
template<typename T = ScriptingObject>
API_STRUCT(InBuild) struct SoftTypeReference
{
protected:
StringAnsi _typeName;
public:
SoftTypeReference() = default;
SoftTypeReference(const SoftTypeReference& s)
: _typeName(s._typeName)
{
}
SoftTypeReference(SoftTypeReference&& s) noexcept
: _typeName(MoveTemp(s._typeName))
{
}
SoftTypeReference(const StringView& s)
: _typeName(s)
{
}
SoftTypeReference(const StringAnsiView& s)
: _typeName(s)
{
}
SoftTypeReference(const char* s)
: _typeName(s)
{
}
public:
FORCE_INLINE SoftTypeReference& operator=(SoftTypeReference&& s) noexcept
{
_typeName = MoveTemp(s._typeName);
return *this;
}
FORCE_INLINE SoftTypeReference& operator=(StringAnsi&& s) noexcept
{
_typeName = MoveTemp(s);
return *this;
}
FORCE_INLINE SoftTypeReference& operator=(const SoftTypeReference& s)
{
_typeName = s._typeName;
return *this;
}
FORCE_INLINE SoftTypeReference& operator=(const StringAnsiView& s)
{
_typeName = s;
return *this;
}
FORCE_INLINE bool operator==(const SoftTypeReference& other) const
{
return _typeName == other._typeName;
}
FORCE_INLINE bool operator!=(const SoftTypeReference& other) const
{
return _typeName != other._typeName;
}
FORCE_INLINE bool operator==(const StringAnsiView& other) const
{
return _typeName == other;
}
FORCE_INLINE bool operator!=(const StringAnsiView& other) const
{
return _typeName != other;
}
FORCE_INLINE operator bool() const
{
return _typeName.HasChars();
}
public:
// Gets the type full name (eg. FlaxEngine.Actor).
StringAnsiView GetTypeName() const
{
return StringAnsiView(_typeName);
}
// Gets the type (resolves soft reference).
ScriptingTypeHandle GetType() const
{
return Scripting::FindScriptingType(_typeName);
}
// Creates a new objects of that type (or of type T if failed to solve typename).
T* NewObject() const
{
const ScriptingTypeHandle type = Scripting::FindScriptingType(_typeName);
auto obj = ScriptingObject::NewObject<T>(type);
if (!obj)
{
if (_typeName.HasChars())
LOG(Error, "Unknown or invalid type {0}", String(_typeName));
obj = ScriptingObject::NewObject<T>();
}
return obj;
}
};
template<typename T>
uint32 GetHash(const SoftTypeReference<T>& key)
{
return GetHash(key.GetTypeName());
}
// @formatter:off
namespace Serialization
{
template<typename T>
bool ShouldSerialize(const SoftTypeReference<T>& v, const void* otherObj)
{
return !otherObj || v != *(SoftTypeReference<T>*)otherObj;
}
template<typename T>
void Serialize(ISerializable::SerializeStream& stream, const SoftTypeReference<T>& v, const void* otherObj)
{
stream.String(v.GetTypeName());
}
template<typename T>
void Deserialize(ISerializable::DeserializeStream& stream, SoftTypeReference<T>& v, ISerializeModifier* modifier)
{
v = stream.GetTextAnsi();
}
}
// @formatter:on

View File

@@ -7,7 +7,7 @@ using Newtonsoft.Json;
namespace FlaxEngine.Json
{
/// <summary>
/// Serialize references to the FlaxEngine.Object as Guid.
/// Serialize references to the <see cref="FlaxEngine.Object"/> as Guid.
/// </summary>
/// <seealso cref="Newtonsoft.Json.JsonConverter" />
internal class FlaxObjectConverter : JsonConverter
@@ -46,7 +46,7 @@ namespace FlaxEngine.Json
}
/// <summary>
/// Serialize SceneReference as Guid in internal format.
/// Serialize <see cref="SceneReference"/> as Guid in internal format.
/// </summary>
/// <seealso cref="Newtonsoft.Json.JsonConverter" />
internal class SceneReferenceConverter : JsonConverter
@@ -79,7 +79,7 @@ namespace FlaxEngine.Json
}
/// <summary>
/// Serialize SoftObjectReference as Guid in internal format.
/// Serialize <see cref="SoftObjectReference"/> as Guid in internal format.
/// </summary>
/// <seealso cref="Newtonsoft.Json.JsonConverter" />
internal class SoftObjectReferenceConverter : JsonConverter
@@ -111,7 +111,36 @@ namespace FlaxEngine.Json
}
/// <summary>
/// Serialize SoftObjectReference as Guid in internal format.
/// Serialize <see cref="SoftTypeReference"/> as typename string in internal format.
/// </summary>
/// <seealso cref="Newtonsoft.Json.JsonConverter" />
internal class SoftTypeReferenceConverter : JsonConverter
{
/// <inheritdoc />
public override void WriteJson(JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer)
{
writer.WriteValue(((SoftTypeReference)value).TypeName);
}
/// <inheritdoc />
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer)
{
var result = new SoftTypeReference();
if (reader.TokenType == JsonToken.String)
result.TypeName = (string)reader.Value;
return result;
}
/// <inheritdoc />
public override bool CanConvert(Type objectType)
{
return objectType == typeof(SoftTypeReference);
}
}
/// <summary>
/// Serialize <see cref="Margin"/> as Guid in internal format.
/// </summary>
/// <seealso cref="Newtonsoft.Json.JsonConverter" />
internal class MarginConverter : JsonConverter
@@ -237,7 +266,7 @@ namespace FlaxEngine.Json
}
/// <summary>
/// Serialize LocalizedString as inlined text is not using localization (Id member is empty).
/// Serialize <see cref="LocalizedString"/> as inlined text is not using localization (Id member is empty).
/// </summary>
/// <seealso cref="Newtonsoft.Json.JsonConverter" />
internal class LocalizedStringConverter : JsonConverter

View File

@@ -123,6 +123,7 @@ namespace FlaxEngine.Json
settings.Converters.Add(ObjectConverter);
settings.Converters.Add(new SceneReferenceConverter());
settings.Converters.Add(new SoftObjectReferenceConverter());
settings.Converters.Add(new SoftTypeReferenceConverter());
settings.Converters.Add(new MarginConverter());
settings.Converters.Add(new VersionConverter());
settings.Converters.Add(new LocalizedStringConverter());