summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp7
-rw-r--r--modules/gltf/gltf_document.cpp4
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props3
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs9
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotMemberData.cs6
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs374
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs8
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs14
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs12
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs6
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs8
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs16
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs28
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs10
-rw-r--r--modules/mono/editor/bindings_generator.cpp61
-rw-r--r--modules/mono/editor/bindings_generator.h9
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs4
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs59
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs275
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs517
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.generic.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs52
-rw-r--r--modules/text_server_adv/text_server_adv.cpp274
-rw-r--r--modules/text_server_adv/text_server_adv.h142
-rw-r--r--modules/text_server_fb/text_server_fb.cpp243
-rw-r--r--modules/text_server_fb/text_server_fb.h141
26 files changed, 1276 insertions, 1008 deletions
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index dff6e41dca..663d72038d 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -196,8 +196,11 @@ Error GDScriptAnalyzer::check_class_member_name_conflict(const GDScriptParser::C
while (current_data_type && current_data_type->kind == GDScriptParser::DataType::Kind::CLASS) {
GDScriptParser::ClassNode *current_class_node = current_data_type->class_type;
if (has_member_name_conflict_in_script_class(p_member_name, current_class_node, p_member_node)) {
- push_error(vformat(R"(The member "%s" already exists in parent class %s.)", p_member_name, current_class_node->identifier->name),
- p_member_node);
+ String parent_class_name = current_class_node->fqcn;
+ if (current_class_node->identifier != nullptr) {
+ parent_class_name = current_class_node->identifier->name;
+ }
+ push_error(vformat(R"(The member "%s" already exists in parent class %s.)", p_member_name, parent_class_name), p_member_node);
return ERR_PARSE_ERROR;
}
current_data_type = &current_class_node->base_type;
diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp
index 735e35ac1e..a3685daf0e 100644
--- a/modules/gltf/gltf_document.cpp
+++ b/modules/gltf/gltf_document.cpp
@@ -796,7 +796,7 @@ Error GLTFDocument::_parse_buffers(Ref<GLTFState> state, const String &p_base_pa
ERR_FAIL_COND_V(p_base_path.is_empty(), ERR_INVALID_PARAMETER);
uri = uri.uri_decode();
uri = p_base_path.path_join(uri).replace("\\", "/"); // Fix for Windows.
- buffer_data = FileAccess::get_file_as_array(uri);
+ buffer_data = FileAccess::get_file_as_bytes(uri);
ERR_FAIL_COND_V_MSG(buffer.size() == 0, ERR_PARSE_ERROR, "glTF: Couldn't load binary file as an array: " + uri);
}
@@ -3139,7 +3139,7 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> state, const String &p_base_pat
// Fallback to loading as byte array.
// This enables us to support the spec's requirement that we honor mimetype
// regardless of file URI.
- data = FileAccess::get_file_as_array(uri);
+ data = FileAccess::get_file_as_bytes(uri);
if (data.size() == 0) {
WARN_PRINT(vformat("glTF: Image index '%d' couldn't be loaded as a buffer of MIME type '%s' from URI: %s. Skipping it.", i, mimetype, uri));
state->images.push_back(Ref<Texture2D>()); // Placeholder to keep count.
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props
index 0459257106..0d0889c491 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props
@@ -12,8 +12,7 @@
<Configurations>Debug;ExportDebug;ExportRelease</Configurations>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
- <GodotProjectDir Condition=" '$(SolutionDir)' != '' ">$(SolutionDir)</GodotProjectDir>
- <GodotProjectDir Condition=" '$(SolutionDir)' == '' ">$(MSBuildProjectDirectory)</GodotProjectDir>
+ <GodotProjectDir Condition=" '$(GodotProjectDir)' == '' ">$(MSBuildProjectDirectory)</GodotProjectDir>
<GodotProjectDir>$([MSBuild]::EnsureTrailingSlash('$(GodotProjectDir)'))</GodotProjectDir>
<!-- Custom output paths for Godot projects. In brief, 'bin\' and 'obj\' are moved to '$(GodotProjectDir)\.godot\mono\temp\'. -->
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs
index 7008fb638f..d67e57edc2 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs
@@ -268,8 +268,9 @@ namespace Godot.SourceGenerators
if (parameters.Length > paramTypes.Length)
return null; // Ignore incompatible method
- return new GodotMethodData(method, paramTypes, parameters
- .Select(p => p.Type).ToImmutableArray(), retType, retSymbol);
+ return new GodotMethodData(method, paramTypes,
+ parameters.Select(p => p.Type).ToImmutableArray(),
+ retType != null ? (retType.Value, retSymbol) : null);
}
public static IEnumerable<GodotMethodData> WhereHasGodotCompatibleSignature(
@@ -330,10 +331,10 @@ namespace Godot.SourceGenerators
public static string Path(this Location location)
=> location.SourceTree?.GetLineSpan(location.SourceSpan).Path
- ?? location.GetLineSpan().Path;
+ ?? location.GetLineSpan().Path;
public static int StartLine(this Location location)
=> location.SourceTree?.GetLineSpan(location.SourceSpan).StartLinePosition.Line
- ?? location.GetLineSpan().StartLinePosition.Line;
+ ?? location.GetLineSpan().StartLinePosition.Line;
}
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotMemberData.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotMemberData.cs
index abd8079922..0760ea11bb 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotMemberData.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotMemberData.cs
@@ -6,20 +6,18 @@ namespace Godot.SourceGenerators
public readonly struct GodotMethodData
{
public GodotMethodData(IMethodSymbol method, ImmutableArray<MarshalType> paramTypes,
- ImmutableArray<ITypeSymbol> paramTypeSymbols, MarshalType? retType, ITypeSymbol? retSymbol)
+ ImmutableArray<ITypeSymbol> paramTypeSymbols, (MarshalType MarshalType, ITypeSymbol TypeSymbol)? retType)
{
Method = method;
ParamTypes = paramTypes;
ParamTypeSymbols = paramTypeSymbols;
RetType = retType;
- RetSymbol = retSymbol;
}
public IMethodSymbol Method { get; }
public ImmutableArray<MarshalType> ParamTypes { get; }
public ImmutableArray<ITypeSymbol> ParamTypeSymbols { get; }
- public MarshalType? RetType { get; }
- public ITypeSymbol? RetSymbol { get; }
+ public (MarshalType MarshalType, ITypeSymbol TypeSymbol)? RetType { get; }
}
public readonly struct GodotSignalDelegateData
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs
index 4fdd40f638..5b3f677f87 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs
@@ -304,240 +304,33 @@ namespace Godot.SourceGenerators
{
return marshalType switch
{
- MarshalType.Boolean =>
- source.Append(VariantUtils, ".ConvertToBool(", inputExpr, ")"),
- MarshalType.Char =>
- source.Append("(char)", VariantUtils, ".ConvertToUInt16(", inputExpr, ")"),
- MarshalType.SByte =>
- source.Append(VariantUtils, ".ConvertToInt8(", inputExpr, ")"),
- MarshalType.Int16 =>
- source.Append(VariantUtils, ".ConvertToInt16(", inputExpr, ")"),
- MarshalType.Int32 =>
- source.Append(VariantUtils, ".ConvertToInt32(", inputExpr, ")"),
- MarshalType.Int64 =>
- source.Append(VariantUtils, ".ConvertToInt64(", inputExpr, ")"),
- MarshalType.Byte =>
- source.Append(VariantUtils, ".ConvertToUInt8(", inputExpr, ")"),
- MarshalType.UInt16 =>
- source.Append(VariantUtils, ".ConvertToUInt16(", inputExpr, ")"),
- MarshalType.UInt32 =>
- source.Append(VariantUtils, ".ConvertToUInt32(", inputExpr, ")"),
- MarshalType.UInt64 =>
- source.Append(VariantUtils, ".ConvertToUInt64(", inputExpr, ")"),
- MarshalType.Single =>
- source.Append(VariantUtils, ".ConvertToFloat32(", inputExpr, ")"),
- MarshalType.Double =>
- source.Append(VariantUtils, ".ConvertToFloat64(", inputExpr, ")"),
- MarshalType.String =>
- source.Append(VariantUtils, ".ConvertToStringObject(", inputExpr, ")"),
- MarshalType.Vector2 =>
- source.Append(VariantUtils, ".ConvertToVector2(", inputExpr, ")"),
- MarshalType.Vector2i =>
- source.Append(VariantUtils, ".ConvertToVector2i(", inputExpr, ")"),
- MarshalType.Rect2 =>
- source.Append(VariantUtils, ".ConvertToRect2(", inputExpr, ")"),
- MarshalType.Rect2i =>
- source.Append(VariantUtils, ".ConvertToRect2i(", inputExpr, ")"),
- MarshalType.Transform2D =>
- source.Append(VariantUtils, ".ConvertToTransform2D(", inputExpr, ")"),
- MarshalType.Vector3 =>
- source.Append(VariantUtils, ".ConvertToVector3(", inputExpr, ")"),
- MarshalType.Vector3i =>
- source.Append(VariantUtils, ".ConvertToVector3i(", inputExpr, ")"),
- MarshalType.Basis =>
- source.Append(VariantUtils, ".ConvertToBasis(", inputExpr, ")"),
- MarshalType.Quaternion =>
- source.Append(VariantUtils, ".ConvertToQuaternion(", inputExpr, ")"),
- MarshalType.Transform3D =>
- source.Append(VariantUtils, ".ConvertToTransform3D(", inputExpr, ")"),
- MarshalType.Vector4 =>
- source.Append(VariantUtils, ".ConvertToVector4(", inputExpr, ")"),
- MarshalType.Vector4i =>
- source.Append(VariantUtils, ".ConvertToVector4i(", inputExpr, ")"),
- MarshalType.Projection =>
- source.Append(VariantUtils, ".ConvertToProjection(", inputExpr, ")"),
- MarshalType.AABB =>
- source.Append(VariantUtils, ".ConvertToAABB(", inputExpr, ")"),
- MarshalType.Color =>
- source.Append(VariantUtils, ".ConvertToColor(", inputExpr, ")"),
- MarshalType.Plane =>
- source.Append(VariantUtils, ".ConvertToPlane(", inputExpr, ")"),
- MarshalType.Callable =>
- source.Append(VariantUtils, ".ConvertToCallableManaged(", inputExpr, ")"),
- MarshalType.SignalInfo =>
- source.Append(VariantUtils, ".ConvertToSignalInfo(", inputExpr, ")"),
- MarshalType.Enum =>
- source.Append("(", typeSymbol.FullQualifiedNameIncludeGlobal(),
- ")", VariantUtils, ".ConvertToInt32(", inputExpr, ")"),
- MarshalType.ByteArray =>
- source.Append(VariantUtils, ".ConvertAsPackedByteArrayToSystemArray(", inputExpr, ")"),
- MarshalType.Int32Array =>
- source.Append(VariantUtils, ".ConvertAsPackedInt32ArrayToSystemArray(", inputExpr, ")"),
- MarshalType.Int64Array =>
- source.Append(VariantUtils, ".ConvertAsPackedInt64ArrayToSystemArray(", inputExpr, ")"),
- MarshalType.Float32Array =>
- source.Append(VariantUtils, ".ConvertAsPackedFloat32ArrayToSystemArray(", inputExpr, ")"),
- MarshalType.Float64Array =>
- source.Append(VariantUtils, ".ConvertAsPackedFloat64ArrayToSystemArray(", inputExpr, ")"),
- MarshalType.StringArray =>
- source.Append(VariantUtils, ".ConvertAsPackedStringArrayToSystemArray(", inputExpr, ")"),
- MarshalType.Vector2Array =>
- source.Append(VariantUtils, ".ConvertAsPackedVector2ArrayToSystemArray(", inputExpr, ")"),
- MarshalType.Vector3Array =>
- source.Append(VariantUtils, ".ConvertAsPackedVector3ArrayToSystemArray(", inputExpr, ")"),
- MarshalType.ColorArray =>
- source.Append(VariantUtils, ".ConvertAsPackedColorArrayToSystemArray(", inputExpr, ")"),
- MarshalType.GodotObjectOrDerivedArray =>
- source.Append(VariantUtils, ".ConvertToSystemArrayOfGodotObject<",
- ((IArrayTypeSymbol)typeSymbol).ElementType.FullQualifiedNameIncludeGlobal(), ">(", inputExpr, ")"),
- MarshalType.SystemArrayOfStringName =>
- source.Append(VariantUtils, ".ConvertToSystemArrayOfStringName(", inputExpr, ")"),
- MarshalType.SystemArrayOfNodePath =>
- source.Append(VariantUtils, ".ConvertToSystemArrayOfNodePath(", inputExpr, ")"),
- MarshalType.SystemArrayOfRID =>
- source.Append(VariantUtils, ".ConvertToSystemArrayOfRID(", inputExpr, ")"),
- MarshalType.Variant =>
- source.Append("global::Godot.Variant.CreateCopyingBorrowed(", inputExpr, ")"),
- MarshalType.GodotObjectOrDerived =>
- source.Append("(", typeSymbol.FullQualifiedNameIncludeGlobal(),
- ")", VariantUtils, ".ConvertToGodotObject(", inputExpr, ")"),
- MarshalType.StringName =>
- source.Append(VariantUtils, ".ConvertToStringNameObject(", inputExpr, ")"),
- MarshalType.NodePath =>
- source.Append(VariantUtils, ".ConvertToNodePathObject(", inputExpr, ")"),
- MarshalType.RID =>
- source.Append(VariantUtils, ".ConvertToRID(", inputExpr, ")"),
- MarshalType.GodotDictionary =>
- source.Append(VariantUtils, ".ConvertToDictionaryObject(", inputExpr, ")"),
- MarshalType.GodotArray =>
- source.Append(VariantUtils, ".ConvertToArrayObject(", inputExpr, ")"),
+ // For generic Godot collections, VariantUtils.ConvertTo<T> is slower, so we need this special case
MarshalType.GodotGenericDictionary =>
source.Append(VariantUtils, ".ConvertToDictionaryObject<",
((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedNameIncludeGlobal(), ", ",
- ((INamedTypeSymbol)typeSymbol).TypeArguments[1].FullQualifiedNameIncludeGlobal(), ">(", inputExpr, ")"),
+ ((INamedTypeSymbol)typeSymbol).TypeArguments[1].FullQualifiedNameIncludeGlobal(), ">(",
+ inputExpr, ")"),
MarshalType.GodotGenericArray =>
source.Append(VariantUtils, ".ConvertToArrayObject<",
- ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedNameIncludeGlobal(), ">(", inputExpr, ")"),
- _ => throw new ArgumentOutOfRangeException(nameof(marshalType), marshalType,
- "Received unexpected marshal type")
+ ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedNameIncludeGlobal(), ">(",
+ inputExpr, ")"),
+ _ => source.Append(VariantUtils, ".ConvertTo<",
+ typeSymbol.FullQualifiedNameIncludeGlobal(), ">(", inputExpr, ")"),
};
}
- public static StringBuilder AppendManagedToNativeVariantExpr(
- this StringBuilder source, string inputExpr, MarshalType marshalType)
+ public static StringBuilder AppendManagedToNativeVariantExpr(this StringBuilder source,
+ string inputExpr, ITypeSymbol typeSymbol, MarshalType marshalType)
{
return marshalType switch
{
- MarshalType.Boolean =>
- source.Append(VariantUtils, ".CreateFromBool(", inputExpr, ")"),
- MarshalType.Char =>
- source.Append(VariantUtils, ".CreateFromInt((ushort)", inputExpr, ")"),
- MarshalType.SByte =>
- source.Append(VariantUtils, ".CreateFromInt(", inputExpr, ")"),
- MarshalType.Int16 =>
- source.Append(VariantUtils, ".CreateFromInt(", inputExpr, ")"),
- MarshalType.Int32 =>
- source.Append(VariantUtils, ".CreateFromInt(", inputExpr, ")"),
- MarshalType.Int64 =>
- source.Append(VariantUtils, ".CreateFromInt(", inputExpr, ")"),
- MarshalType.Byte =>
- source.Append(VariantUtils, ".CreateFromInt(", inputExpr, ")"),
- MarshalType.UInt16 =>
- source.Append(VariantUtils, ".CreateFromInt(", inputExpr, ")"),
- MarshalType.UInt32 =>
- source.Append(VariantUtils, ".CreateFromInt(", inputExpr, ")"),
- MarshalType.UInt64 =>
- source.Append(VariantUtils, ".CreateFromInt(", inputExpr, ")"),
- MarshalType.Single =>
- source.Append(VariantUtils, ".CreateFromFloat(", inputExpr, ")"),
- MarshalType.Double =>
- source.Append(VariantUtils, ".CreateFromFloat(", inputExpr, ")"),
- MarshalType.String =>
- source.Append(VariantUtils, ".CreateFromString(", inputExpr, ")"),
- MarshalType.Vector2 =>
- source.Append(VariantUtils, ".CreateFromVector2(", inputExpr, ")"),
- MarshalType.Vector2i =>
- source.Append(VariantUtils, ".CreateFromVector2i(", inputExpr, ")"),
- MarshalType.Rect2 =>
- source.Append(VariantUtils, ".CreateFromRect2(", inputExpr, ")"),
- MarshalType.Rect2i =>
- source.Append(VariantUtils, ".CreateFromRect2i(", inputExpr, ")"),
- MarshalType.Transform2D =>
- source.Append(VariantUtils, ".CreateFromTransform2D(", inputExpr, ")"),
- MarshalType.Vector3 =>
- source.Append(VariantUtils, ".CreateFromVector3(", inputExpr, ")"),
- MarshalType.Vector3i =>
- source.Append(VariantUtils, ".CreateFromVector3i(", inputExpr, ")"),
- MarshalType.Basis =>
- source.Append(VariantUtils, ".CreateFromBasis(", inputExpr, ")"),
- MarshalType.Quaternion =>
- source.Append(VariantUtils, ".CreateFromQuaternion(", inputExpr, ")"),
- MarshalType.Transform3D =>
- source.Append(VariantUtils, ".CreateFromTransform3D(", inputExpr, ")"),
- MarshalType.Vector4 =>
- source.Append(VariantUtils, ".CreateFromVector4(", inputExpr, ")"),
- MarshalType.Vector4i =>
- source.Append(VariantUtils, ".CreateFromVector4i(", inputExpr, ")"),
- MarshalType.Projection =>
- source.Append(VariantUtils, ".CreateFromProjection(", inputExpr, ")"),
- MarshalType.AABB =>
- source.Append(VariantUtils, ".CreateFromAABB(", inputExpr, ")"),
- MarshalType.Color =>
- source.Append(VariantUtils, ".CreateFromColor(", inputExpr, ")"),
- MarshalType.Plane =>
- source.Append(VariantUtils, ".CreateFromPlane(", inputExpr, ")"),
- MarshalType.Callable =>
- source.Append(VariantUtils, ".CreateFromCallable(", inputExpr, ")"),
- MarshalType.SignalInfo =>
- source.Append(VariantUtils, ".CreateFromSignalInfo(", inputExpr, ")"),
- MarshalType.Enum =>
- source.Append(VariantUtils, ".CreateFromInt((int)", inputExpr, ")"),
- MarshalType.ByteArray =>
- source.Append(VariantUtils, ".CreateFromPackedByteArray(", inputExpr, ")"),
- MarshalType.Int32Array =>
- source.Append(VariantUtils, ".CreateFromPackedInt32Array(", inputExpr, ")"),
- MarshalType.Int64Array =>
- source.Append(VariantUtils, ".CreateFromPackedInt64Array(", inputExpr, ")"),
- MarshalType.Float32Array =>
- source.Append(VariantUtils, ".CreateFromPackedFloat32Array(", inputExpr, ")"),
- MarshalType.Float64Array =>
- source.Append(VariantUtils, ".CreateFromPackedFloat64Array(", inputExpr, ")"),
- MarshalType.StringArray =>
- source.Append(VariantUtils, ".CreateFromPackedStringArray(", inputExpr, ")"),
- MarshalType.Vector2Array =>
- source.Append(VariantUtils, ".CreateFromPackedVector2Array(", inputExpr, ")"),
- MarshalType.Vector3Array =>
- source.Append(VariantUtils, ".CreateFromPackedVector3Array(", inputExpr, ")"),
- MarshalType.ColorArray =>
- source.Append(VariantUtils, ".CreateFromPackedColorArray(", inputExpr, ")"),
- MarshalType.GodotObjectOrDerivedArray =>
- source.Append(VariantUtils, ".CreateFromSystemArrayOfGodotObject(", inputExpr, ")"),
- MarshalType.SystemArrayOfStringName =>
- source.Append(VariantUtils, ".CreateFromSystemArrayOfStringName(", inputExpr, ")"),
- MarshalType.SystemArrayOfNodePath =>
- source.Append(VariantUtils, ".CreateFromSystemArrayOfNodePath(", inputExpr, ")"),
- MarshalType.SystemArrayOfRID =>
- source.Append(VariantUtils, ".CreateFromSystemArrayOfRID(", inputExpr, ")"),
- MarshalType.Variant =>
- source.Append(inputExpr, ".CopyNativeVariant()"),
- MarshalType.GodotObjectOrDerived =>
- source.Append(VariantUtils, ".CreateFromGodotObject(", inputExpr, ")"),
- MarshalType.StringName =>
- source.Append(VariantUtils, ".CreateFromStringName(", inputExpr, ")"),
- MarshalType.NodePath =>
- source.Append(VariantUtils, ".CreateFromNodePath(", inputExpr, ")"),
- MarshalType.RID =>
- source.Append(VariantUtils, ".CreateFromRID(", inputExpr, ")"),
- MarshalType.GodotDictionary =>
- source.Append(VariantUtils, ".CreateFromDictionary(", inputExpr, ")"),
- MarshalType.GodotArray =>
- source.Append(VariantUtils, ".CreateFromArray(", inputExpr, ")"),
+ // For generic Godot collections, VariantUtils.CreateFrom<T> is slower, so we need this special case
MarshalType.GodotGenericDictionary =>
source.Append(VariantUtils, ".CreateFromDictionary(", inputExpr, ")"),
MarshalType.GodotGenericArray =>
source.Append(VariantUtils, ".CreateFromArray(", inputExpr, ")"),
- _ => throw new ArgumentOutOfRangeException(nameof(marshalType), marshalType,
- "Received unexpected marshal type")
+ _ => source.Append(VariantUtils, ".CreateFrom<",
+ typeSymbol.FullQualifiedNameIncludeGlobal(), ">(", inputExpr, ")"),
};
}
@@ -546,137 +339,30 @@ namespace Godot.SourceGenerators
{
return marshalType switch
{
- MarshalType.Boolean => source.Append(inputExpr, ".AsBool()"),
- MarshalType.Char => source.Append(inputExpr, ".AsChar()"),
- MarshalType.SByte => source.Append(inputExpr, ".AsSByte()"),
- MarshalType.Int16 => source.Append(inputExpr, ".AsInt16()"),
- MarshalType.Int32 => source.Append(inputExpr, ".AsInt32()"),
- MarshalType.Int64 => source.Append(inputExpr, ".AsInt64()"),
- MarshalType.Byte => source.Append(inputExpr, ".AsByte()"),
- MarshalType.UInt16 => source.Append(inputExpr, ".AsUInt16()"),
- MarshalType.UInt32 => source.Append(inputExpr, ".AsUInt32()"),
- MarshalType.UInt64 => source.Append(inputExpr, ".AsUInt64()"),
- MarshalType.Single => source.Append(inputExpr, ".AsSingle()"),
- MarshalType.Double => source.Append(inputExpr, ".AsDouble()"),
- MarshalType.String => source.Append(inputExpr, ".AsString()"),
- MarshalType.Vector2 => source.Append(inputExpr, ".AsVector2()"),
- MarshalType.Vector2i => source.Append(inputExpr, ".AsVector2i()"),
- MarshalType.Rect2 => source.Append(inputExpr, ".AsRect2()"),
- MarshalType.Rect2i => source.Append(inputExpr, ".AsRect2i()"),
- MarshalType.Transform2D => source.Append(inputExpr, ".AsTransform2D()"),
- MarshalType.Vector3 => source.Append(inputExpr, ".AsVector3()"),
- MarshalType.Vector3i => source.Append(inputExpr, ".AsVector3i()"),
- MarshalType.Basis => source.Append(inputExpr, ".AsBasis()"),
- MarshalType.Quaternion => source.Append(inputExpr, ".AsQuaternion()"),
- MarshalType.Transform3D => source.Append(inputExpr, ".AsTransform3D()"),
- MarshalType.Vector4 => source.Append(inputExpr, ".AsVector4()"),
- MarshalType.Vector4i => source.Append(inputExpr, ".AsVector4i()"),
- MarshalType.Projection => source.Append(inputExpr, ".AsProjection()"),
- MarshalType.AABB => source.Append(inputExpr, ".AsAABB()"),
- MarshalType.Color => source.Append(inputExpr, ".AsColor()"),
- MarshalType.Plane => source.Append(inputExpr, ".AsPlane()"),
- MarshalType.Callable => source.Append(inputExpr, ".AsCallable()"),
- MarshalType.SignalInfo => source.Append(inputExpr, ".AsSignalInfo()"),
- MarshalType.Enum =>
- source.Append("(", typeSymbol.FullQualifiedNameIncludeGlobal(), ")", inputExpr, ".AsInt64()"),
- MarshalType.ByteArray => source.Append(inputExpr, ".AsByteArray()"),
- MarshalType.Int32Array => source.Append(inputExpr, ".AsInt32Array()"),
- MarshalType.Int64Array => source.Append(inputExpr, ".AsInt64Array()"),
- MarshalType.Float32Array => source.Append(inputExpr, ".AsFloat32Array()"),
- MarshalType.Float64Array => source.Append(inputExpr, ".AsFloat64Array()"),
- MarshalType.StringArray => source.Append(inputExpr, ".AsStringArray()"),
- MarshalType.Vector2Array => source.Append(inputExpr, ".AsVector2Array()"),
- MarshalType.Vector3Array => source.Append(inputExpr, ".AsVector3Array()"),
- MarshalType.ColorArray => source.Append(inputExpr, ".AsColorArray()"),
- MarshalType.GodotObjectOrDerivedArray => source.Append(inputExpr, ".AsGodotObjectArray<",
- ((IArrayTypeSymbol)typeSymbol).ElementType.FullQualifiedNameIncludeGlobal(), ">()"),
- MarshalType.SystemArrayOfStringName => source.Append(inputExpr, ".AsSystemArrayOfStringName()"),
- MarshalType.SystemArrayOfNodePath => source.Append(inputExpr, ".AsSystemArrayOfNodePath()"),
- MarshalType.SystemArrayOfRID => source.Append(inputExpr, ".AsSystemArrayOfRID()"),
- MarshalType.Variant => source.Append(inputExpr),
- MarshalType.GodotObjectOrDerived => source.Append("(",
- typeSymbol.FullQualifiedNameIncludeGlobal(), ")", inputExpr, ".AsGodotObject()"),
- MarshalType.StringName => source.Append(inputExpr, ".AsStringName()"),
- MarshalType.NodePath => source.Append(inputExpr, ".AsNodePath()"),
- MarshalType.RID => source.Append(inputExpr, ".AsRID()"),
- MarshalType.GodotDictionary => source.Append(inputExpr, ".AsGodotDictionary()"),
- MarshalType.GodotArray => source.Append(inputExpr, ".AsGodotArray()"),
- MarshalType.GodotGenericDictionary => source.Append(inputExpr, ".AsGodotDictionary<",
- ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedNameIncludeGlobal(), ", ",
- ((INamedTypeSymbol)typeSymbol).TypeArguments[1].FullQualifiedNameIncludeGlobal(), ">()"),
- MarshalType.GodotGenericArray => source.Append(inputExpr, ".AsGodotArray<",
- ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedNameIncludeGlobal(), ">()"),
- _ => throw new ArgumentOutOfRangeException(nameof(marshalType), marshalType,
- "Received unexpected marshal type")
+ // For generic Godot collections, Variant.As<T> is slower, so we need this special case
+ MarshalType.GodotGenericDictionary =>
+ source.Append(inputExpr, ".AsGodotDictionary<",
+ ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedNameIncludeGlobal(), ", ",
+ ((INamedTypeSymbol)typeSymbol).TypeArguments[1].FullQualifiedNameIncludeGlobal(), ">()"),
+ MarshalType.GodotGenericArray =>
+ source.Append(inputExpr, ".AsGodotArray<",
+ ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedNameIncludeGlobal(), ">()"),
+ _ => source.Append(inputExpr, ".As<",
+ typeSymbol.FullQualifiedNameIncludeGlobal(), ">()")
};
}
public static StringBuilder AppendManagedToVariantExpr(this StringBuilder source,
- string inputExpr, MarshalType marshalType)
+ string inputExpr, ITypeSymbol typeSymbol, MarshalType marshalType)
{
- switch (marshalType)
+ return marshalType switch
{
- case MarshalType.Boolean:
- case MarshalType.Char:
- case MarshalType.SByte:
- case MarshalType.Int16:
- case MarshalType.Int32:
- case MarshalType.Int64:
- case MarshalType.Byte:
- case MarshalType.UInt16:
- case MarshalType.UInt32:
- case MarshalType.UInt64:
- case MarshalType.Single:
- case MarshalType.Double:
- case MarshalType.String:
- case MarshalType.Vector2:
- case MarshalType.Vector2i:
- case MarshalType.Rect2:
- case MarshalType.Rect2i:
- case MarshalType.Transform2D:
- case MarshalType.Vector3:
- case MarshalType.Vector3i:
- case MarshalType.Basis:
- case MarshalType.Quaternion:
- case MarshalType.Transform3D:
- case MarshalType.Vector4:
- case MarshalType.Vector4i:
- case MarshalType.Projection:
- case MarshalType.AABB:
- case MarshalType.Color:
- case MarshalType.Plane:
- case MarshalType.Callable:
- case MarshalType.SignalInfo:
- case MarshalType.ByteArray:
- case MarshalType.Int32Array:
- case MarshalType.Int64Array:
- case MarshalType.Float32Array:
- case MarshalType.Float64Array:
- case MarshalType.StringArray:
- case MarshalType.Vector2Array:
- case MarshalType.Vector3Array:
- case MarshalType.ColorArray:
- case MarshalType.GodotObjectOrDerivedArray:
- case MarshalType.SystemArrayOfStringName:
- case MarshalType.SystemArrayOfNodePath:
- case MarshalType.SystemArrayOfRID:
- case MarshalType.GodotObjectOrDerived:
- case MarshalType.StringName:
- case MarshalType.NodePath:
- case MarshalType.RID:
- case MarshalType.GodotDictionary:
- case MarshalType.GodotArray:
- case MarshalType.GodotGenericDictionary:
- case MarshalType.GodotGenericArray:
- return source.Append("Variant.CreateFrom(", inputExpr, ")");
- case MarshalType.Enum:
- return source.Append("Variant.CreateFrom((long)", inputExpr, ")");
- case MarshalType.Variant:
- return source.Append(inputExpr);
- default:
- throw new ArgumentOutOfRangeException(nameof(marshalType), marshalType,
- "Received unexpected marshal type");
- }
+ // For generic Godot collections, Variant.From<T> is slower, so we need this special case
+ MarshalType.GodotGenericDictionary or MarshalType.GodotGenericArray =>
+ source.Append("global::Godot.Variant.CreateFrom(", inputExpr, ")"),
+ _ => source.Append("global::Godot.Variant.From<",
+ typeSymbol.FullQualifiedNameIncludeGlobal(), ">(", inputExpr, ")")
+ };
}
}
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs
index 2f51018293..f79909589e 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs
@@ -135,7 +135,8 @@ namespace Godot.SourceGenerators
source.Append("#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword\n");
- source.Append($" public new class MethodName : {symbol.BaseType.FullQualifiedNameIncludeGlobal()}.MethodName {{\n");
+ source.Append(
+ $" public new class MethodName : {symbol.BaseType.FullQualifiedNameIncludeGlobal()}.MethodName {{\n");
// Generate cached StringNames for methods and properties, for fast lookup
@@ -297,7 +298,7 @@ namespace Godot.SourceGenerators
if (method.RetType != null)
{
- returnVal = DeterminePropertyInfo(method.RetType.Value, name: string.Empty);
+ returnVal = DeterminePropertyInfo(method.RetType.Value.MarshalType, name: string.Empty);
}
else
{
@@ -391,7 +392,8 @@ namespace Godot.SourceGenerators
{
source.Append(" ret = ");
- source.AppendManagedToNativeVariantExpr("callRet", method.RetType.Value);
+ source.AppendManagedToNativeVariantExpr("callRet",
+ method.RetType.Value.TypeSymbol, method.RetType.Value.MarshalType);
source.Append(";\n");
source.Append(" return true;\n");
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs
index 252f162b0c..6c4206a575 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs
@@ -124,7 +124,8 @@ namespace Godot.SourceGenerators
source.Append("#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword\n");
- source.Append($" public new class PropertyName : {symbol.BaseType.FullQualifiedNameIncludeGlobal()}.PropertyName {{\n");
+ source.Append(
+ $" public new class PropertyName : {symbol.BaseType.FullQualifiedNameIncludeGlobal()}.PropertyName {{\n");
// Generate cached StringNames for methods and properties, for fast lookup
@@ -199,14 +200,14 @@ namespace Godot.SourceGenerators
foreach (var property in godotClassProperties)
{
GeneratePropertyGetter(property.PropertySymbol.Name,
- property.Type, source, isFirstEntry);
+ property.PropertySymbol.Type, property.Type, source, isFirstEntry);
isFirstEntry = false;
}
foreach (var field in godotClassFields)
{
GeneratePropertyGetter(field.FieldSymbol.Name,
- field.Type, source, isFirstEntry);
+ field.FieldSymbol.Type, field.Type, source, isFirstEntry);
isFirstEntry = false;
}
@@ -303,6 +304,7 @@ namespace Godot.SourceGenerators
private static void GeneratePropertyGetter(
string propertyMemberName,
+ ITypeSymbol propertyTypeSymbol,
MarshalType propertyMarshalType,
StringBuilder source,
bool isFirstEntry
@@ -317,7 +319,8 @@ namespace Godot.SourceGenerators
.Append(propertyMemberName)
.Append(") {\n")
.Append(" value = ")
- .AppendManagedToNativeVariantExpr("this." + propertyMemberName, propertyMarshalType)
+ .AppendManagedToNativeVariantExpr("this." + propertyMemberName,
+ propertyTypeSymbol, propertyMarshalType)
.Append(";\n")
.Append(" return true;\n")
.Append(" }\n");
@@ -376,7 +379,8 @@ namespace Godot.SourceGenerators
if (propertyUsage != PropertyUsageFlags.Category && attr.ConstructorArguments.Length > 1)
hintString = attr.ConstructorArguments[1].Value?.ToString();
- yield return new PropertyInfo(VariantType.Nil, name, PropertyHint.None, hintString, propertyUsage.Value, true);
+ yield return new PropertyInfo(VariantType.Nil, name, PropertyHint.None, hintString,
+ propertyUsage.Value, true);
}
}
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs
index 63339b3487..aa9dd9583e 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs
@@ -174,7 +174,8 @@ namespace Godot.SourceGenerators
}
else
{
- var propertyGet = propertyDeclarationSyntax.AccessorList?.Accessors.Where(a => a.Keyword.IsKind(SyntaxKind.GetKeyword)).FirstOrDefault();
+ var propertyGet = propertyDeclarationSyntax.AccessorList?.Accessors
+ .Where(a => a.Keyword.IsKind(SyntaxKind.GetKeyword)).FirstOrDefault();
if (propertyGet != null)
{
if (propertyGet.ExpressionBody != null)
@@ -200,7 +201,8 @@ namespace Godot.SourceGenerators
{
var returns = propertyGet.DescendantNodes().OfType<ReturnStatementSyntax>();
if (returns.Count() == 1)
- {// Generate only single return
+ {
+ // Generate only single return
var returnStatementSyntax = returns.Single();
if (returnStatementSyntax.Expression is IdentifierNameSyntax identifierNameSyntax)
{
@@ -277,7 +279,8 @@ namespace Godot.SourceGenerators
{
source.Append("#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword\n");
- string dictionaryType = "System.Collections.Generic.Dictionary<Godot.StringName, object>";
+ string dictionaryType =
+ "global::System.Collections.Generic.Dictionary<global::Godot.StringName, global::Godot.Variant>";
source.Append("#if TOOLS\n");
source.Append(" internal new static ");
@@ -304,7 +307,8 @@ namespace Godot.SourceGenerators
source.Append(" values.Add(PropertyName.");
source.Append(exportedMember.Name);
source.Append(", ");
- source.Append(defaultValueLocalName);
+ source.AppendManagedToVariantExpr(defaultValueLocalName,
+ exportedMember.TypeSymbol, exportedMember.Type);
source.Append(");\n");
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs
index ed877cbd17..821f3af75f 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs
@@ -162,7 +162,8 @@ namespace Godot.SourceGenerators
source.Append(" info.AddProperty(PropertyName.")
.Append(propertyName)
.Append(", ")
- .AppendManagedToVariantExpr(string.Concat("this.", propertyName), property.Type)
+ .AppendManagedToVariantExpr(string.Concat("this.", propertyName),
+ property.PropertySymbol.Type, property.Type)
.Append(");\n");
}
@@ -175,7 +176,8 @@ namespace Godot.SourceGenerators
source.Append(" info.AddProperty(PropertyName.")
.Append(fieldName)
.Append(", ")
- .AppendManagedToVariantExpr(string.Concat("this.", fieldName), field.Type)
+ .AppendManagedToVariantExpr(string.Concat("this.", fieldName),
+ field.FieldSymbol.Type, field.Type)
.Append(");\n");
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs
index 119cc9d4f0..ba6c10aa31 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs
@@ -176,7 +176,8 @@ namespace Godot.SourceGenerators
source.Append("#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword\n");
- source.Append($" public new class SignalName : {symbol.BaseType.FullQualifiedNameIncludeGlobal()}.SignalName {{\n");
+ source.Append(
+ $" public new class SignalName : {symbol.BaseType.FullQualifiedNameIncludeGlobal()}.SignalName {{\n");
// Generate cached StringNames for methods and properties, for fast lookup
@@ -236,7 +237,8 @@ namespace Godot.SourceGenerators
.Append(signalName)
.Append(";\n");
- source.Append($" /// <inheritdoc cref=\"{signalDelegate.DelegateSymbol.FullQualifiedNameIncludeGlobal()}\"/>\n");
+ source.Append(
+ $" /// <inheritdoc cref=\"{signalDelegate.DelegateSymbol.FullQualifiedNameIncludeGlobal()}\"/>\n");
source.Append(" public event ")
.Append(signalDelegate.DelegateSymbol.FullQualifiedNameIncludeGlobal())
@@ -351,7 +353,7 @@ namespace Godot.SourceGenerators
if (invokeMethodData.RetType != null)
{
- returnVal = DeterminePropertyInfo(invokeMethodData.RetType.Value, name: string.Empty);
+ returnVal = DeterminePropertyInfo(invokeMethodData.RetType.Value.MarshalType, name: string.Empty);
}
else
{
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
index 89364d1c02..de10c04e31 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
@@ -57,24 +57,22 @@ namespace GodotTools
{
pr.Step("Generating C# project...".TTR());
- string resourceDir = ProjectSettings.GlobalizePath("res://");
-
- string path = resourceDir;
+ string csprojDir = Path.GetDirectoryName(GodotSharpDirs.ProjectCsProjPath);
+ string slnDir = Path.GetDirectoryName(GodotSharpDirs.ProjectSlnPath);
string name = GodotSharpDirs.ProjectAssemblyName;
-
- string guid = CsProjOperations.GenerateGameProject(path, name);
+ string guid = CsProjOperations.GenerateGameProject(csprojDir, name);
if (guid.Length > 0)
{
var solution = new DotNetSolution(name)
{
- DirectoryPath = path
+ DirectoryPath = slnDir
};
var projectInfo = new DotNetSolution.ProjectInfo
{
Guid = guid,
- PathRelativeToSolution = name + ".csproj",
+ PathRelativeToSolution = Path.GetRelativePath(slnDir, GodotSharpDirs.ProjectCsProjPath),
Configs = new List<string> { "Debug", "ExportDebug", "ExportRelease" }
};
@@ -375,6 +373,8 @@ namespace GodotTools
{
base._EnablePlugin();
+ ProjectSettingsChanged += GodotSharpDirs.DetermineProjectLocation;
+
if (Instance != null)
throw new InvalidOperationException();
Instance = this;
@@ -455,7 +455,7 @@ namespace GodotTools
_menuPopup.IdPressed += _MenuOptionPressed;
// External editor settings
- EditorDef("mono/editor/external_editor", ExternalEditorId.None);
+ EditorDef("mono/editor/external_editor", Variant.From(ExternalEditorId.None));
string settingsHintStr = "Disabled";
diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs
index acb7cc3ab0..45ae7eb86b 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs
@@ -1,3 +1,4 @@
+using Godot;
using Godot.NativeInterop;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
@@ -8,30 +9,31 @@ namespace GodotTools.Internals
{
public static float EditorScale => Internal.godot_icall_Globals_EditorScale();
- public static unsafe object GlobalDef(string setting, object defaultValue, bool restartIfChanged = false)
+ // ReSharper disable once UnusedMethodReturnValue.Global
+ public static Variant GlobalDef(string setting, Variant defaultValue, bool restartIfChanged = false)
{
using godot_string settingIn = Marshaling.ConvertStringToNative(setting);
- using godot_variant defaultValueIn = Marshaling.ConvertManagedObjectToVariant(defaultValue);
- Internal.godot_icall_Globals_GlobalDef(settingIn, defaultValueIn, restartIfChanged, out godot_variant result);
- using (result)
- return Marshaling.ConvertVariantToManagedObject(result);
+ using godot_variant defaultValueIn = defaultValue.CopyNativeVariant();
+ Internal.godot_icall_Globals_GlobalDef(settingIn, defaultValueIn, restartIfChanged,
+ out godot_variant result);
+ return Variant.CreateTakingOwnershipOfDisposableValue(result);
}
- public static unsafe object EditorDef(string setting, object defaultValue, bool restartIfChanged = false)
+ // ReSharper disable once UnusedMethodReturnValue.Global
+ public static Variant EditorDef(string setting, Variant defaultValue, bool restartIfChanged = false)
{
using godot_string settingIn = Marshaling.ConvertStringToNative(setting);
- using godot_variant defaultValueIn = Marshaling.ConvertManagedObjectToVariant(defaultValue);
- Internal.godot_icall_Globals_EditorDef(settingIn, defaultValueIn, restartIfChanged, out godot_variant result);
- using (result)
- return Marshaling.ConvertVariantToManagedObject(result);
+ using godot_variant defaultValueIn = defaultValue.CopyNativeVariant();
+ Internal.godot_icall_Globals_EditorDef(settingIn, defaultValueIn, restartIfChanged,
+ out godot_variant result);
+ return Variant.CreateTakingOwnershipOfDisposableValue(result);
}
- public static object EditorShortcut(string setting)
+ public static Variant EditorShortcut(string setting)
{
using godot_string settingIn = Marshaling.ConvertStringToNative(setting);
Internal.godot_icall_Globals_EditorShortcut(settingIn, out godot_variant result);
- using (result)
- return Marshaling.ConvertVariantToManagedObject(result);
+ return Variant.CreateTakingOwnershipOfDisposableValue(result);
}
[SuppressMessage("ReSharper", "InconsistentNaming")]
diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs
index 14285cc0f1..4e892be55c 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs
@@ -52,10 +52,9 @@ namespace GodotTools.Internals
{
GlobalDef("dotnet/project/assembly_name", "");
GlobalDef("dotnet/project/solution_directory", "");
- GlobalDef("dotnet/project/c#_project_directory", "");
}
- private static void DetermineProjectLocation()
+ public static void DetermineProjectLocation()
{
static string DetermineProjectName()
{
@@ -76,10 +75,11 @@ namespace GodotTools.Internals
string slnParentDir = (string)ProjectSettings.GetSetting("dotnet/project/solution_directory");
if (string.IsNullOrEmpty(slnParentDir))
slnParentDir = "res://";
+ else if (!slnParentDir.StartsWith("res://"))
+ slnParentDir = "res://" + slnParentDir;
- string csprojParentDir = (string)ProjectSettings.GetSetting("dotnet/project/c#_project_directory");
- if (string.IsNullOrEmpty(csprojParentDir))
- csprojParentDir = "res://";
+ // The csproj should be in the same folder as project.godot.
+ string csprojParentDir = "res://";
_projectSlnPath = Path.Combine(ProjectSettings.GlobalizePath(slnParentDir),
string.Concat(_projectAssemblyName, ".sln"));
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index 9185506776..9f0bc3fbe3 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -2837,9 +2837,6 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
itype.is_ref_counted = ClassDB::is_parent_class(type_cname, name_cache.type_RefCounted);
itype.memory_own = itype.is_ref_counted;
- itype.cs_variant_to_managed = "(%1)VariantUtils.ConvertToGodotObject(%0)";
- itype.cs_managed_to_variant = "VariantUtils.CreateFromGodotObject(%0)";
-
itype.c_out = "%5return ";
itype.c_out += C_METHOD_UNMANAGED_GET_MANAGED;
itype.c_out += itype.is_ref_counted ? "(%1.Reference);\n" : "(%1);\n";
@@ -3218,8 +3215,6 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
enum_itype.cname = StringName(enum_itype.name);
enum_itype.proxy_name = itype.proxy_name + "." + enum_proxy_name;
TypeInterface::postsetup_enum_type(enum_itype);
- enum_itype.cs_variant_to_managed = "(%1)VariantUtils.ConvertToInt32(%0)";
- enum_itype.cs_managed_to_variant = "VariantUtils.CreateFromInt((int)%0)";
enum_types.insert(enum_itype.cname, enum_itype);
}
@@ -3448,16 +3443,14 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
TypeInterface itype;
-#define INSERT_STRUCT_TYPE(m_type) \
- { \
- itype = TypeInterface::create_value_type(String(#m_type)); \
- itype.c_type_in = #m_type "*"; \
- itype.c_type_out = itype.cs_type; \
- itype.cs_in_expr = "&%0"; \
- itype.cs_in_expr_is_unsafe = true; \
- itype.cs_variant_to_managed = "VariantUtils.ConvertTo%2(%0)"; \
- itype.cs_managed_to_variant = "VariantUtils.CreateFrom%2(%0)"; \
- builtin_types.insert(itype.cname, itype); \
+#define INSERT_STRUCT_TYPE(m_type) \
+ { \
+ itype = TypeInterface::create_value_type(String(#m_type)); \
+ itype.c_type_in = #m_type "*"; \
+ itype.c_type_out = itype.cs_type; \
+ itype.cs_in_expr = "&%0"; \
+ itype.cs_in_expr_is_unsafe = true; \
+ builtin_types.insert(itype.cname, itype); \
}
INSERT_STRUCT_TYPE(Vector2)
@@ -3488,8 +3481,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.c_type_out = itype.c_type;
itype.c_arg_in = "&%s";
itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromBool(%1);\n";
- itype.cs_variant_to_managed = "VariantUtils.ConvertToBool(%0)";
- itype.cs_managed_to_variant = "VariantUtils.CreateFromBool(%0)";
builtin_types.insert(itype.cname, itype);
// Integer types
@@ -3510,8 +3501,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.c_type_in = itype.name; \
itype.c_type_out = itype.name; \
itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromInt(%1);\n"; \
- itype.cs_variant_to_managed = "VariantUtils.ConvertTo" m_int_struct_name "(%0)"; \
- itype.cs_managed_to_variant = "VariantUtils.CreateFromInt(%0)"; \
builtin_types.insert(itype.cname, itype); \
}
@@ -3547,8 +3536,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.c_type_in = itype.proxy_name;
itype.c_type_out = itype.proxy_name;
itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromFloat(%1);\n";
- itype.cs_variant_to_managed = "VariantUtils.ConvertToFloat32(%0)";
- itype.cs_managed_to_variant = "VariantUtils.CreateFromFloat(%0)";
builtin_types.insert(itype.cname, itype);
// double
@@ -3562,8 +3549,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.c_type_in = itype.proxy_name;
itype.c_type_out = itype.proxy_name;
itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromFloat(%1);\n";
- itype.cs_variant_to_managed = "VariantUtils.ConvertToFloat64(%0)";
- itype.cs_managed_to_variant = "VariantUtils.CreateFromFloat(%0)";
builtin_types.insert(itype.cname, itype);
}
@@ -3581,8 +3566,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.c_type_out = itype.cs_type;
itype.c_type_is_disposable_struct = true;
itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromString(%1);\n";
- itype.cs_variant_to_managed = "VariantUtils.ConvertToStringObject(%0)";
- itype.cs_managed_to_variant = "VariantUtils.CreateFromString(%0)";
builtin_types.insert(itype.cname, itype);
// StringName
@@ -3601,8 +3584,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromStringName(%1);\n";
itype.c_type_is_disposable_struct = false; // [c_out] takes ownership
itype.c_ret_needs_default_initialization = true;
- itype.cs_variant_to_managed = "VariantUtils.ConvertToStringNameObject(%0)";
- itype.cs_managed_to_variant = "VariantUtils.CreateFromStringName(%0)";
builtin_types.insert(itype.cname, itype);
// NodePath
@@ -3620,8 +3601,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.c_type_out = itype.cs_type;
itype.c_type_is_disposable_struct = false; // [c_out] takes ownership
itype.c_ret_needs_default_initialization = true;
- itype.cs_variant_to_managed = "VariantUtils.ConvertToNodePathObject(%0)";
- itype.cs_managed_to_variant = "VariantUtils.CreateFromNodePath(%0)";
builtin_types.insert(itype.cname, itype);
// RID
@@ -3634,8 +3613,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.c_type = itype.cs_type;
itype.c_type_in = itype.c_type;
itype.c_type_out = itype.c_type;
- itype.cs_variant_to_managed = "VariantUtils.ConvertToRID(%0)";
- itype.cs_managed_to_variant = "VariantUtils.CreateFromRID(%0)";
builtin_types.insert(itype.cname, itype);
// Variant
@@ -3652,8 +3629,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.c_type_out = itype.cs_type;
itype.c_type_is_disposable_struct = false; // [c_out] takes ownership
itype.c_ret_needs_default_initialization = true;
- itype.cs_variant_to_managed = "Variant.CreateCopyingBorrowed(%0)";
- itype.cs_managed_to_variant = "%0.CopyNativeVariant()";
builtin_types.insert(itype.cname, itype);
// Callable
@@ -3666,8 +3641,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.c_type_in = "in " + itype.cs_type;
itype.c_type_out = itype.cs_type;
itype.c_type_is_disposable_struct = true;
- itype.cs_variant_to_managed = "VariantUtils.ConvertToCallableManaged(%0)";
- itype.cs_managed_to_variant = "VariantUtils.CreateFromCallable(%0)";
builtin_types.insert(itype.cname, itype);
// Signal
@@ -3684,8 +3657,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.c_type_in = "in " + itype.cs_type;
itype.c_type_out = itype.cs_type;
itype.c_type_is_disposable_struct = true;
- itype.cs_variant_to_managed = "VariantUtils.ConvertToSignalInfo(%0)";
- itype.cs_managed_to_variant = "VariantUtils.CreateFromSignalInfo(%0)";
builtin_types.insert(itype.cname, itype);
// VarArg (fictitious type to represent variable arguments)
@@ -3715,8 +3686,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.c_type_in = itype.proxy_name; \
itype.c_type_out = itype.proxy_name; \
itype.c_type_is_disposable_struct = true; \
- itype.cs_variant_to_managed = "VariantUtils.ConvertAs%2ToSystemArray(%0)"; \
- itype.cs_managed_to_variant = "VariantUtils.CreateFrom%2(%0)"; \
builtin_types.insert(itype.name, itype); \
}
@@ -3752,8 +3721,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.c_type_out = itype.cs_type;
itype.c_type_is_disposable_struct = false; // [c_out] takes ownership
itype.c_ret_needs_default_initialization = true;
- itype.cs_variant_to_managed = "VariantUtils.ConvertToArrayObject(%0)";
- itype.cs_managed_to_variant = "VariantUtils.CreateFromArray(%0)";
builtin_types.insert(itype.cname, itype);
// Array_@generic
@@ -3761,6 +3728,9 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.name = "Array_@generic";
itype.cname = itype.name;
itype.cs_out = "%5return new %2(%0(%1));";
+ // For generic Godot collections, Variant.From<T>/As<T> is slower, so we need this special case
+ itype.cs_variant_to_managed = "VariantUtils.ConvertToArrayObject(%0)";
+ itype.cs_managed_to_variant = "VariantUtils.CreateFromArray(%0)";
builtin_types.insert(itype.cname, itype);
// Dictionary
@@ -3778,8 +3748,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.c_type_out = itype.cs_type;
itype.c_type_is_disposable_struct = false; // [c_out] takes ownership
itype.c_ret_needs_default_initialization = true;
- itype.cs_variant_to_managed = "VariantUtils.ConvertToDictionaryObject(%0)";
- itype.cs_managed_to_variant = "VariantUtils.CreateFromDictionary(%0)";
builtin_types.insert(itype.cname, itype);
// Dictionary_@generic
@@ -3787,6 +3755,9 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.name = "Dictionary_@generic";
itype.cname = itype.name;
itype.cs_out = "%5return new %2(%0(%1));";
+ // For generic Godot collections, Variant.From<T>/As<T> is slower, so we need this special case
+ itype.cs_variant_to_managed = "VariantUtils.ConvertToDictionaryObject(%0)";
+ itype.cs_managed_to_variant = "VariantUtils.CreateFromDictionary(%0)";
builtin_types.insert(itype.cname, itype);
// void (fictitious type to represent the return type of methods that do not return anything)
@@ -3852,8 +3823,6 @@ void BindingsGenerator::_populate_global_constants() {
enum_itype.cname = ienum.cname;
enum_itype.proxy_name = enum_itype.name;
TypeInterface::postsetup_enum_type(enum_itype);
- enum_itype.cs_variant_to_managed = "(%1)VariantUtils.ConvertToInt32(%0)";
- enum_itype.cs_managed_to_variant = "VariantUtils.CreateFromInt((int)%0)";
enum_types.insert(enum_itype.cname, enum_itype);
int prefix_length = _determine_enum_prefix(ienum);
@@ -3886,8 +3855,6 @@ void BindingsGenerator::_populate_global_constants() {
enum_itype.cname = enum_cname;
enum_itype.proxy_name = enum_itype.name;
TypeInterface::postsetup_enum_type(enum_itype);
- enum_itype.cs_variant_to_managed = "(%1)VariantUtils.ConvertToInt32(%0)";
- enum_itype.cs_managed_to_variant = "VariantUtils.CreateFromInt((int)%0)";
enum_types.insert(enum_itype.cname, enum_itype);
}
}
diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h
index a479c44368..6d172f4fb8 100644
--- a/modules/mono/editor/bindings_generator.h
+++ b/modules/mono/editor/bindings_generator.h
@@ -209,7 +209,7 @@ class BindingsGenerator {
String name;
StringName cname;
- int type_parameter_count;
+ int type_parameter_count = 0;
/**
* Identifier name of the base class.
@@ -514,7 +514,12 @@ class BindingsGenerator {
static void postsetup_enum_type(TypeInterface &r_enum_itype);
- TypeInterface() {}
+ TypeInterface() {
+ static String default_cs_variant_to_managed = "VariantUtils.ConvertTo<%1>(%0)";
+ static String default_cs_managed_to_variant = "VariantUtils.CreateFrom<%1>(%0)";
+ cs_variant_to_managed = default_cs_variant_to_managed;
+ cs_managed_to_variant = default_cs_managed_to_variant;
+ }
};
struct InternalCall {
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs
index 1b9f63c22d..130776499b 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs
@@ -418,8 +418,8 @@ namespace Godot.Collections
{
for (int i = 0; i < count; i++)
{
- object obj = Marshaling.ConvertVariantToManagedObject(NativeValue.DangerousSelfRef.Elements[i]);
- array.SetValue(obj, index);
+ object boxedVariant = Variant.CreateCopyingBorrowed(NativeValue.DangerousSelfRef.Elements[i]);
+ array.SetValue(boxedVariant, index);
index++;
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs
index d6fad391b6..e6a8054ae2 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs
@@ -159,7 +159,7 @@ namespace Godot.Bridge
for (int i = 0; i < paramCount; i++)
{
- invokeParams[i] = Marshaling.ConvertVariantToManagedObjectOfType(
+ invokeParams[i] = DelegateUtils.RuntimeTypeConversionHelper.ConvertToObjectOfType(
*args[i], parameters[i].ParameterType);
}
@@ -832,7 +832,8 @@ namespace Godot.Bridge
}
else
{
- interopProperties = ((godotsharp_property_info*)NativeMemory.Alloc((nuint)length, (nuint)sizeof(godotsharp_property_info)))!;
+ interopProperties = ((godotsharp_property_info*)NativeMemory.Alloc(
+ (nuint)length, (nuint)sizeof(godotsharp_property_info)))!;
}
try
@@ -858,8 +859,8 @@ namespace Godot.Bridge
addPropInfoFunc(scriptPtr, &currentClassName, interopProperties, length);
- // We're borrowing the StringName's without making an owning copy, so the
- // managed collection needs to be kept alive until `addPropInfoFunc` returns.
+ // We're borrowing the native value of the StringName entries.
+ // The dictionary needs to be kept alive until `addPropInfoFunc` returns.
GC.KeepAlive(properties);
}
finally
@@ -884,12 +885,7 @@ namespace Godot.Bridge
{
// Careful with padding...
public godot_string_name Name; // Not owned
- public godot_variant Value;
-
- public void Dispose()
- {
- Value.Dispose();
- }
+ public godot_variant Value; // Not owned
}
[UnmanagedCallersOnly]
@@ -928,10 +924,35 @@ namespace Godot.Bridge
if (getGodotPropertyDefaultValuesMethod == null)
return;
- var defaultValues = (Dictionary<StringName, object>?)
- getGodotPropertyDefaultValuesMethod.Invoke(null, null);
+ var defaultValuesObj = getGodotPropertyDefaultValuesMethod.Invoke(null, null);
+
+ if (defaultValuesObj == null)
+ return;
+
+ Dictionary<StringName, Variant> defaultValues;
+
+ if (defaultValuesObj is Dictionary<StringName, object> defaultValuesLegacy)
+ {
+ // We have to support this for some time, otherwise this could cause data loss for projects
+ // built with previous releases. Ideally, we should remove this before Godot 4.0 stable.
+
+ if (defaultValuesLegacy.Count <= 0)
+ return;
- if (defaultValues == null || defaultValues.Count <= 0)
+ defaultValues = new();
+
+ foreach (var pair in defaultValuesLegacy)
+ {
+ defaultValues[pair.Key] = Variant.CreateTakingOwnershipOfDisposableValue(
+ DelegateUtils.RuntimeTypeConversionHelper.ConvertToVariant(pair.Value));
+ }
+ }
+ else
+ {
+ defaultValues = (Dictionary<StringName, Variant>)defaultValuesObj;
+ }
+
+ if (defaultValues.Count <= 0)
return;
int length = defaultValues.Count;
@@ -952,7 +973,8 @@ namespace Godot.Bridge
}
else
{
- interopDefaultValues = ((godotsharp_property_def_val_pair*)NativeMemory.Alloc((nuint)length, (nuint)sizeof(godotsharp_property_def_val_pair)))!;
+ interopDefaultValues = ((godotsharp_property_def_val_pair*)NativeMemory.Alloc(
+ (nuint)length, (nuint)sizeof(godotsharp_property_def_val_pair)))!;
}
try
@@ -963,7 +985,7 @@ namespace Godot.Bridge
godotsharp_property_def_val_pair interopProperty = new()
{
Name = (godot_string_name)defaultValuePair.Key.NativeValue, // Not owned
- Value = Marshaling.ConvertManagedObjectToVariant(defaultValuePair.Value)
+ Value = (godot_variant)defaultValuePair.Value.NativeVar // Not owned
};
interopDefaultValues[i] = interopProperty;
@@ -973,15 +995,12 @@ namespace Godot.Bridge
addDefValFunc(scriptPtr, interopDefaultValues, length);
- // We're borrowing the StringName's without making an owning copy, so the
- // managed collection needs to be kept alive until `addDefValFunc` returns.
+ // We're borrowing the native value of the StringName and Variant entries.
+ // The dictionary needs to be kept alive until `addDefValFunc` returns.
GC.KeepAlive(defaultValues);
}
finally
{
- for (int i = 0; i < length; i++)
- interopDefaultValues[i].Dispose();
-
if (!useStack)
NativeMemory.Free(interopDefaultValues);
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs
index d19e0c08f2..a3cfecfaa6 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs
@@ -186,7 +186,7 @@ namespace Godot
writer.Write(field.Name);
var fieldValue = field.GetValue(target);
- using var fieldValueVariant = Marshaling.ConvertManagedObjectToVariant(fieldValue);
+ using var fieldValueVariant = RuntimeTypeConversionHelper.ConvertToVariant(fieldValue);
byte[] valueBuffer = VarToBytes(fieldValueVariant);
writer.Write(valueBuffer.Length);
writer.Write(valueBuffer);
@@ -443,7 +443,14 @@ namespace Godot
FieldInfo? fieldInfo = targetType.GetField(name,
BindingFlags.Instance | BindingFlags.Public);
- fieldInfo?.SetValue(recreatedTarget, GD.BytesToVar(valueBuffer));
+
+ if (fieldInfo != null)
+ {
+ var variantValue = GD.BytesToVar(valueBuffer);
+ object? managedValue = RuntimeTypeConversionHelper.ConvertToObjectOfType(
+ (godot_variant)variantValue.NativeVar, fieldInfo.FieldType);
+ fieldInfo.SetValue(recreatedTarget, managedValue);
+ }
}
@delegate = Delegate.CreateDelegate(delegateType, recreatedTarget, methodInfo,
@@ -537,5 +544,269 @@ namespace Godot
return type;
}
+
+ internal static class RuntimeTypeConversionHelper
+ {
+ [SuppressMessage("ReSharper", "RedundantNameQualifier")]
+ public static godot_variant ConvertToVariant(object? obj)
+ {
+ if (obj == null)
+ return default;
+
+ switch (obj)
+ {
+ case bool @bool:
+ return VariantUtils.CreateFrom(@bool);
+ case char @char:
+ return VariantUtils.CreateFrom(@char);
+ case sbyte int8:
+ return VariantUtils.CreateFrom(int8);
+ case short int16:
+ return VariantUtils.CreateFrom(int16);
+ case int int32:
+ return VariantUtils.CreateFrom(int32);
+ case long int64:
+ return VariantUtils.CreateFrom(int64);
+ case byte uint8:
+ return VariantUtils.CreateFrom(uint8);
+ case ushort uint16:
+ return VariantUtils.CreateFrom(uint16);
+ case uint uint32:
+ return VariantUtils.CreateFrom(uint32);
+ case ulong uint64:
+ return VariantUtils.CreateFrom(uint64);
+ case float @float:
+ return VariantUtils.CreateFrom(@float);
+ case double @double:
+ return VariantUtils.CreateFrom(@double);
+ case Vector2 vector2:
+ return VariantUtils.CreateFrom(vector2);
+ case Vector2i vector2I:
+ return VariantUtils.CreateFrom(vector2I);
+ case Rect2 rect2:
+ return VariantUtils.CreateFrom(rect2);
+ case Rect2i rect2I:
+ return VariantUtils.CreateFrom(rect2I);
+ case Transform2D transform2D:
+ return VariantUtils.CreateFrom(transform2D);
+ case Vector3 vector3:
+ return VariantUtils.CreateFrom(vector3);
+ case Vector3i vector3I:
+ return VariantUtils.CreateFrom(vector3I);
+ case Vector4 vector4:
+ return VariantUtils.CreateFrom(vector4);
+ case Vector4i vector4I:
+ return VariantUtils.CreateFrom(vector4I);
+ case Basis basis:
+ return VariantUtils.CreateFrom(basis);
+ case Quaternion quaternion:
+ return VariantUtils.CreateFrom(quaternion);
+ case Transform3D transform3d:
+ return VariantUtils.CreateFrom(transform3d);
+ case Projection projection:
+ return VariantUtils.CreateFrom(projection);
+ case AABB aabb:
+ return VariantUtils.CreateFrom(aabb);
+ case Color color:
+ return VariantUtils.CreateFrom(color);
+ case Plane plane:
+ return VariantUtils.CreateFrom(plane);
+ case Callable callable:
+ return VariantUtils.CreateFrom(callable);
+ case SignalInfo signalInfo:
+ return VariantUtils.CreateFrom(signalInfo);
+ case string @string:
+ return VariantUtils.CreateFrom(@string);
+ case byte[] byteArray:
+ return VariantUtils.CreateFrom(byteArray);
+ case int[] int32Array:
+ return VariantUtils.CreateFrom(int32Array);
+ case long[] int64Array:
+ return VariantUtils.CreateFrom(int64Array);
+ case float[] floatArray:
+ return VariantUtils.CreateFrom(floatArray);
+ case double[] doubleArray:
+ return VariantUtils.CreateFrom(doubleArray);
+ case string[] stringArray:
+ return VariantUtils.CreateFrom(stringArray);
+ case Vector2[] vector2Array:
+ return VariantUtils.CreateFrom(vector2Array);
+ case Vector3[] vector3Array:
+ return VariantUtils.CreateFrom(vector3Array);
+ case Color[] colorArray:
+ return VariantUtils.CreateFrom(colorArray);
+ case StringName[] stringNameArray:
+ return VariantUtils.CreateFrom(stringNameArray);
+ case NodePath[] nodePathArray:
+ return VariantUtils.CreateFrom(nodePathArray);
+ case RID[] ridArray:
+ return VariantUtils.CreateFrom(ridArray);
+ case Godot.Object[] godotObjectArray:
+ return VariantUtils.CreateFrom(godotObjectArray);
+ case StringName stringName:
+ return VariantUtils.CreateFrom(stringName);
+ case NodePath nodePath:
+ return VariantUtils.CreateFrom(nodePath);
+ case RID rid:
+ return VariantUtils.CreateFrom(rid);
+ case Collections.Dictionary godotDictionary:
+ return VariantUtils.CreateFrom(godotDictionary);
+ case Collections.Array godotArray:
+ return VariantUtils.CreateFrom(godotArray);
+ case Variant variant:
+ return VariantUtils.CreateFrom(variant);
+ case Godot.Object godotObject:
+ return VariantUtils.CreateFrom(godotObject);
+ case Enum @enum:
+ return VariantUtils.CreateFrom(Convert.ToInt64(@enum));
+ case Collections.IGenericGodotDictionary godotDictionary:
+ return VariantUtils.CreateFrom(godotDictionary.UnderlyingDictionary);
+ case Collections.IGenericGodotArray godotArray:
+ return VariantUtils.CreateFrom(godotArray.UnderlyingArray);
+ }
+
+ GD.PushError("Attempted to convert an unmarshallable managed type to Variant. Name: '" +
+ obj.GetType().FullName + ".");
+ return new godot_variant();
+ }
+
+ private delegate object? ConvertToSystemObjectFunc(in godot_variant managed);
+
+ [SuppressMessage("ReSharper", "RedundantNameQualifier")]
+ // ReSharper disable once RedundantNameQualifier
+ private static readonly System.Collections.Generic.Dictionary<Type, ConvertToSystemObjectFunc>
+ ToSystemObjectFuncByType = new()
+ {
+ [typeof(bool)] = (in godot_variant variant) => VariantUtils.ConvertTo<bool>(variant),
+ [typeof(char)] = (in godot_variant variant) => VariantUtils.ConvertTo<char>(variant),
+ [typeof(sbyte)] = (in godot_variant variant) => VariantUtils.ConvertTo<sbyte>(variant),
+ [typeof(short)] = (in godot_variant variant) => VariantUtils.ConvertTo<short>(variant),
+ [typeof(int)] = (in godot_variant variant) => VariantUtils.ConvertTo<int>(variant),
+ [typeof(long)] = (in godot_variant variant) => VariantUtils.ConvertTo<long>(variant),
+ [typeof(byte)] = (in godot_variant variant) => VariantUtils.ConvertTo<byte>(variant),
+ [typeof(ushort)] = (in godot_variant variant) => VariantUtils.ConvertTo<ushort>(variant),
+ [typeof(uint)] = (in godot_variant variant) => VariantUtils.ConvertTo<uint>(variant),
+ [typeof(ulong)] = (in godot_variant variant) => VariantUtils.ConvertTo<ulong>(variant),
+ [typeof(float)] = (in godot_variant variant) => VariantUtils.ConvertTo<float>(variant),
+ [typeof(double)] = (in godot_variant variant) => VariantUtils.ConvertTo<double>(variant),
+ [typeof(Vector2)] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector2>(variant),
+ [typeof(Vector2i)] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector2i>(variant),
+ [typeof(Rect2)] = (in godot_variant variant) => VariantUtils.ConvertTo<Rect2>(variant),
+ [typeof(Rect2i)] = (in godot_variant variant) => VariantUtils.ConvertTo<Rect2i>(variant),
+ [typeof(Transform2D)] = (in godot_variant variant) => VariantUtils.ConvertTo<Transform2D>(variant),
+ [typeof(Vector3)] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector3>(variant),
+ [typeof(Vector3i)] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector3i>(variant),
+ [typeof(Basis)] = (in godot_variant variant) => VariantUtils.ConvertTo<Basis>(variant),
+ [typeof(Quaternion)] = (in godot_variant variant) => VariantUtils.ConvertTo<Quaternion>(variant),
+ [typeof(Transform3D)] = (in godot_variant variant) => VariantUtils.ConvertTo<Transform3D>(variant),
+ [typeof(Vector4)] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector4>(variant),
+ [typeof(Vector4i)] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector4i>(variant),
+ [typeof(AABB)] = (in godot_variant variant) => VariantUtils.ConvertTo<AABB>(variant),
+ [typeof(Color)] = (in godot_variant variant) => VariantUtils.ConvertTo<Color>(variant),
+ [typeof(Plane)] = (in godot_variant variant) => VariantUtils.ConvertTo<Plane>(variant),
+ [typeof(Callable)] = (in godot_variant variant) => VariantUtils.ConvertTo<Callable>(variant),
+ [typeof(SignalInfo)] = (in godot_variant variant) => VariantUtils.ConvertTo<SignalInfo>(variant),
+ [typeof(string)] = (in godot_variant variant) => VariantUtils.ConvertTo<string>(variant),
+ [typeof(byte[])] = (in godot_variant variant) => VariantUtils.ConvertTo<byte[]>(variant),
+ [typeof(int[])] = (in godot_variant variant) => VariantUtils.ConvertTo<int[]>(variant),
+ [typeof(long[])] = (in godot_variant variant) => VariantUtils.ConvertTo<long[]>(variant),
+ [typeof(float[])] = (in godot_variant variant) => VariantUtils.ConvertTo<float[]>(variant),
+ [typeof(double[])] = (in godot_variant variant) => VariantUtils.ConvertTo<double[]>(variant),
+ [typeof(string[])] = (in godot_variant variant) => VariantUtils.ConvertTo<string[]>(variant),
+ [typeof(Vector2[])] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector2[]>(variant),
+ [typeof(Vector3[])] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector3[]>(variant),
+ [typeof(Color[])] = (in godot_variant variant) => VariantUtils.ConvertTo<Color[]>(variant),
+ [typeof(StringName[])] =
+ (in godot_variant variant) => VariantUtils.ConvertTo<StringName[]>(variant),
+ [typeof(NodePath[])] = (in godot_variant variant) => VariantUtils.ConvertTo<NodePath[]>(variant),
+ [typeof(RID[])] = (in godot_variant variant) => VariantUtils.ConvertTo<RID[]>(variant),
+ [typeof(StringName)] = (in godot_variant variant) => VariantUtils.ConvertTo<StringName>(variant),
+ [typeof(NodePath)] = (in godot_variant variant) => VariantUtils.ConvertTo<NodePath>(variant),
+ [typeof(RID)] = (in godot_variant variant) => VariantUtils.ConvertTo<RID>(variant),
+ [typeof(Godot.Collections.Dictionary)] = (in godot_variant variant) =>
+ VariantUtils.ConvertTo<Godot.Collections.Dictionary>(variant),
+ [typeof(Godot.Collections.Array)] =
+ (in godot_variant variant) => VariantUtils.ConvertTo<Godot.Collections.Array>(variant),
+ [typeof(Variant)] = (in godot_variant variant) => VariantUtils.ConvertTo<Variant>(variant),
+ };
+
+ [SuppressMessage("ReSharper", "RedundantNameQualifier")]
+ public static object? ConvertToObjectOfType(in godot_variant variant, Type type)
+ {
+ if (ToSystemObjectFuncByType.TryGetValue(type, out var func))
+ return func(variant);
+
+ if (typeof(Godot.Object).IsAssignableFrom(type))
+ return Convert.ChangeType(VariantUtils.ConvertTo<Godot.Object>(variant), type);
+
+ if (type.IsEnum)
+ {
+ var enumUnderlyingType = type.GetEnumUnderlyingType();
+
+ switch (Type.GetTypeCode(enumUnderlyingType))
+ {
+ case TypeCode.SByte:
+ return Enum.ToObject(type, VariantUtils.ConvertToInt8(variant));
+ case TypeCode.Int16:
+ return Enum.ToObject(type, VariantUtils.ConvertToInt16(variant));
+ case TypeCode.Int32:
+ return Enum.ToObject(type, VariantUtils.ConvertToInt32(variant));
+ case TypeCode.Int64:
+ return Enum.ToObject(type, VariantUtils.ConvertToInt64(variant));
+ case TypeCode.Byte:
+ return Enum.ToObject(type, VariantUtils.ConvertToUInt8(variant));
+ case TypeCode.UInt16:
+ return Enum.ToObject(type, VariantUtils.ConvertToUInt16(variant));
+ case TypeCode.UInt32:
+ return Enum.ToObject(type, VariantUtils.ConvertToUInt32(variant));
+ case TypeCode.UInt64:
+ return Enum.ToObject(type, VariantUtils.ConvertToUInt64(variant));
+ default:
+ {
+ GD.PushError(
+ "Attempted to convert Variant to enum value of unsupported underlying type. Name: " +
+ type.FullName + " : " + enumUnderlyingType.FullName + ".");
+ return null;
+ }
+ }
+ }
+
+ if (type.IsGenericType)
+ {
+ var genericTypeDef = type.GetGenericTypeDefinition();
+
+ if (genericTypeDef == typeof(Godot.Collections.Dictionary<,>))
+ {
+ var ctor = type.GetConstructor(BindingFlags.Default,
+ new[] { typeof(Godot.Collections.Dictionary) });
+
+ if (ctor == null)
+ throw new InvalidOperationException("Dictionary constructor not found");
+
+ return ctor.Invoke(new object?[]
+ {
+ VariantUtils.ConvertTo<Godot.Collections.Dictionary>(variant)
+ });
+ }
+
+ if (genericTypeDef == typeof(Godot.Collections.Array<>))
+ {
+ var ctor = type.GetConstructor(BindingFlags.Default,
+ new[] { typeof(Godot.Collections.Array) });
+
+ if (ctor == null)
+ throw new InvalidOperationException("Array constructor not found");
+
+ return ctor.Invoke(new object?[]
+ {
+ VariantUtils.ConvertTo<Godot.Collections.Array>(variant)
+ });
+ }
+ }
+
+ GD.PushError($"Attempted to convert Variant to unsupported type. Name: {type.FullName}.");
+ return null;
+ }
+ }
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs
index d2a80d0e92..6176093bc1 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs
@@ -1,5 +1,7 @@
using System;
using System.Runtime.InteropServices;
+using Godot.Collections;
+using Array = System.Array;
// ReSharper disable InconsistentNaming
@@ -148,6 +150,15 @@ namespace Godot.NativeInterop
{
if (typeof(Godot.Object).IsAssignableFrom(type))
return Variant.Type.Object;
+
+ // We use `IsAssignableFrom` with our helper interfaces to detect generic Godot collections
+ // because `GetGenericTypeDefinition` is not supported in NativeAOT reflection-free mode.
+
+ if (typeof(IGenericGodotDictionary).IsAssignableFrom(type))
+ return Variant.Type.Dictionary;
+
+ if (typeof(IGenericGodotArray).IsAssignableFrom(type))
+ return Variant.Type.Array;
}
else if (type == typeof(Variant))
{
@@ -183,512 +194,6 @@ namespace Godot.NativeInterop
return Variant.Type.Nil;
}
- /* TODO: Reflection and type checking each time is slow. This will be replaced with source generators. */
- public static godot_variant ConvertManagedObjectToVariant(object? p_obj)
- {
- if (p_obj == null)
- return new godot_variant();
-
- switch (p_obj)
- {
- case bool @bool:
- return VariantUtils.CreateFromBool(@bool);
- case char @char:
- return VariantUtils.CreateFromInt(@char);
- case sbyte @int8:
- return VariantUtils.CreateFromInt(@int8);
- case short @int16:
- return VariantUtils.CreateFromInt(@int16);
- case int @int32:
- return VariantUtils.CreateFromInt(@int32);
- case long @int64:
- return VariantUtils.CreateFromInt(@int64);
- case byte @uint8:
- return VariantUtils.CreateFromInt(@uint8);
- case ushort @uint16:
- return VariantUtils.CreateFromInt(@uint16);
- case uint @uint32:
- return VariantUtils.CreateFromInt(@uint32);
- case ulong @uint64:
- return VariantUtils.CreateFromInt(@uint64);
- case float @float:
- return VariantUtils.CreateFromFloat(@float);
- case double @double:
- return VariantUtils.CreateFromFloat(@double);
- case Vector2 @vector2:
- return VariantUtils.CreateFromVector2(@vector2);
- case Vector2i @vector2i:
- return VariantUtils.CreateFromVector2i(@vector2i);
- case Rect2 @rect2:
- return VariantUtils.CreateFromRect2(@rect2);
- case Rect2i @rect2i:
- return VariantUtils.CreateFromRect2i(@rect2i);
- case Transform2D @transform2D:
- return VariantUtils.CreateFromTransform2D(@transform2D);
- case Vector3 @vector3:
- return VariantUtils.CreateFromVector3(@vector3);
- case Vector3i @vector3i:
- return VariantUtils.CreateFromVector3i(@vector3i);
- case Vector4 @vector4:
- return VariantUtils.CreateFromVector4(@vector4);
- case Vector4i @vector4i:
- return VariantUtils.CreateFromVector4i(@vector4i);
- case Basis @basis:
- return VariantUtils.CreateFromBasis(@basis);
- case Quaternion @quaternion:
- return VariantUtils.CreateFromQuaternion(@quaternion);
- case Transform3D @transform3d:
- return VariantUtils.CreateFromTransform3D(@transform3d);
- case Projection @projection:
- return VariantUtils.CreateFromProjection(@projection);
- case AABB @aabb:
- return VariantUtils.CreateFromAABB(@aabb);
- case Color @color:
- return VariantUtils.CreateFromColor(@color);
- case Plane @plane:
- return VariantUtils.CreateFromPlane(@plane);
- case Callable @callable:
- return VariantUtils.CreateFromCallable(@callable);
- case SignalInfo @signalInfo:
- return VariantUtils.CreateFromSignalInfo(@signalInfo);
- case Enum @enum:
- return VariantUtils.CreateFromInt(Convert.ToInt64(@enum));
- case string @string:
- return VariantUtils.CreateFromString(@string);
- case byte[] byteArray:
- return VariantUtils.CreateFromPackedByteArray(byteArray);
- case int[] int32Array:
- return VariantUtils.CreateFromPackedInt32Array(int32Array);
- case long[] int64Array:
- return VariantUtils.CreateFromPackedInt64Array(int64Array);
- case float[] floatArray:
- return VariantUtils.CreateFromPackedFloat32Array(floatArray);
- case double[] doubleArray:
- return VariantUtils.CreateFromPackedFloat64Array(doubleArray);
- case string[] stringArray:
- return VariantUtils.CreateFromPackedStringArray(stringArray);
- case Vector2[] vector2Array:
- return VariantUtils.CreateFromPackedVector2Array(vector2Array);
- case Vector3[] vector3Array:
- return VariantUtils.CreateFromPackedVector3Array(vector3Array);
- case Color[] colorArray:
- return VariantUtils.CreateFromPackedColorArray(colorArray);
- case StringName[] stringNameArray:
- return VariantUtils.CreateFromSystemArrayOfStringName(stringNameArray);
- case NodePath[] nodePathArray:
- return VariantUtils.CreateFromSystemArrayOfNodePath(nodePathArray);
- case RID[] ridArray:
- return VariantUtils.CreateFromSystemArrayOfRID(ridArray);
- case Godot.Object[] godotObjectArray:
- return VariantUtils.CreateFromSystemArrayOfGodotObject(godotObjectArray);
- case Godot.Object godotObject:
- return VariantUtils.CreateFromGodotObject(godotObject);
- case StringName stringName:
- return VariantUtils.CreateFromStringName(stringName);
- case NodePath nodePath:
- return VariantUtils.CreateFromNodePath(nodePath);
- case RID rid:
- return VariantUtils.CreateFromRID(rid);
- case Collections.Dictionary godotDictionary:
- return VariantUtils.CreateFromDictionary(godotDictionary);
- case Collections.Array godotArray:
- return VariantUtils.CreateFromArray(godotArray);
- case Collections.IGenericGodotDictionary godotDictionary:
- return VariantUtils.CreateFromDictionary(godotDictionary.UnderlyingDictionary);
- case Collections.IGenericGodotArray godotArray:
- return VariantUtils.CreateFromArray(godotArray.UnderlyingArray);
- case Variant variant:
- return NativeFuncs.godotsharp_variant_new_copy((godot_variant)variant.NativeVar);
- }
-
- GD.PushError("Attempted to convert an unmarshallable managed type to Variant. Name: '" +
- p_obj.GetType().FullName + ".");
- return new godot_variant();
- }
-
- public static object? ConvertVariantToManagedObjectOfType(in godot_variant p_var, Type type)
- {
- // This function is only needed to set the value of properties. Fields have their own implementation, set_value_from_variant.
- switch (Type.GetTypeCode(type))
- {
- case TypeCode.Boolean:
- return VariantUtils.ConvertToBool(p_var);
- case TypeCode.Char:
- return VariantUtils.ConvertToChar(p_var);
- case TypeCode.SByte:
- return VariantUtils.ConvertToInt8(p_var);
- case TypeCode.Int16:
- return VariantUtils.ConvertToInt16(p_var);
- case TypeCode.Int32:
- return VariantUtils.ConvertToInt32(p_var);
- case TypeCode.Int64:
- return VariantUtils.ConvertToInt64(p_var);
- case TypeCode.Byte:
- return VariantUtils.ConvertToUInt8(p_var);
- case TypeCode.UInt16:
- return VariantUtils.ConvertToUInt16(p_var);
- case TypeCode.UInt32:
- return VariantUtils.ConvertToUInt32(p_var);
- case TypeCode.UInt64:
- return VariantUtils.ConvertToUInt64(p_var);
- case TypeCode.Single:
- return VariantUtils.ConvertToFloat32(p_var);
- case TypeCode.Double:
- return VariantUtils.ConvertToFloat64(p_var);
- case TypeCode.String:
- return VariantUtils.ConvertToStringObject(p_var);
- default:
- {
- if (type == typeof(Vector2))
- return VariantUtils.ConvertToVector2(p_var);
-
- if (type == typeof(Vector2i))
- return VariantUtils.ConvertToVector2i(p_var);
-
- if (type == typeof(Rect2))
- return VariantUtils.ConvertToRect2(p_var);
-
- if (type == typeof(Rect2i))
- return VariantUtils.ConvertToRect2i(p_var);
-
- if (type == typeof(Transform2D))
- return VariantUtils.ConvertToTransform2D(p_var);
-
- if (type == typeof(Vector3))
- return VariantUtils.ConvertToVector3(p_var);
-
- if (type == typeof(Vector3i))
- return VariantUtils.ConvertToVector3i(p_var);
-
- if (type == typeof(Vector4))
- return VariantUtils.ConvertToVector4(p_var);
-
- if (type == typeof(Vector4i))
- return VariantUtils.ConvertToVector4i(p_var);
-
- if (type == typeof(Basis))
- return VariantUtils.ConvertToBasis(p_var);
-
- if (type == typeof(Quaternion))
- return VariantUtils.ConvertToQuaternion(p_var);
-
- if (type == typeof(Transform3D))
- return VariantUtils.ConvertToTransform3D(p_var);
-
- if (type == typeof(Projection))
- return VariantUtils.ConvertToProjection(p_var);
-
- if (type == typeof(AABB))
- return VariantUtils.ConvertToAABB(p_var);
-
- if (type == typeof(Color))
- return VariantUtils.ConvertToColor(p_var);
-
- if (type == typeof(Plane))
- return VariantUtils.ConvertToPlane(p_var);
-
- if (type == typeof(Callable))
- return VariantUtils.ConvertToCallableManaged(p_var);
-
- if (type == typeof(SignalInfo))
- return VariantUtils.ConvertToSignalInfo(p_var);
-
- if (type.IsEnum)
- {
- var enumUnderlyingType = type.GetEnumUnderlyingType();
- switch (Type.GetTypeCode(enumUnderlyingType))
- {
- case TypeCode.SByte:
- return VariantUtils.ConvertToInt8(p_var);
- case TypeCode.Int16:
- return VariantUtils.ConvertToInt16(p_var);
- case TypeCode.Int32:
- return VariantUtils.ConvertToInt32(p_var);
- case TypeCode.Int64:
- return VariantUtils.ConvertToInt64(p_var);
- case TypeCode.Byte:
- return VariantUtils.ConvertToUInt8(p_var);
- case TypeCode.UInt16:
- return VariantUtils.ConvertToUInt16(p_var);
- case TypeCode.UInt32:
- return VariantUtils.ConvertToUInt32(p_var);
- case TypeCode.UInt64:
- return VariantUtils.ConvertToUInt64(p_var);
- default:
- {
- GD.PushError(
- "Attempted to convert Variant to enum value of unsupported underlying type. Name: " +
- type.FullName + " : " + enumUnderlyingType.FullName + ".");
- return null;
- }
- }
- }
-
- if (type.IsArray || type.IsSZArray)
- {
- return ConvertVariantToSystemArrayOfType(p_var, type);
- }
- else if (type.IsGenericType)
- {
- if (typeof(Godot.Object).IsAssignableFrom(type))
- {
- var godotObject = VariantUtils.ConvertToGodotObject(p_var);
-
- if (!type.IsInstanceOfType(godotObject))
- {
- GD.PushError("Invalid cast when marshaling Godot.Object type." +
- $" `{godotObject.GetType()}` is not assignable to `{type.FullName}`.");
- return null;
- }
-
- return godotObject;
- }
-
- return null;
- }
- else if (type == typeof(Variant))
- {
- return Variant.CreateCopyingBorrowed(p_var);
- }
-
- if (ConvertVariantToManagedObjectOfClass(p_var, type, out object? res))
- return res;
-
- break;
- }
- }
-
- GD.PushError("Attempted to convert Variant to unsupported type. Name: " +
- type.FullName + ".");
- return null;
- }
-
- private static object? ConvertVariantToSystemArrayOfType(in godot_variant p_var, Type type)
- {
- if (type == typeof(byte[]))
- return VariantUtils.ConvertAsPackedByteArrayToSystemArray(p_var);
-
- if (type == typeof(int[]))
- return VariantUtils.ConvertAsPackedInt32ArrayToSystemArray(p_var);
-
- if (type == typeof(long[]))
- return VariantUtils.ConvertAsPackedInt64ArrayToSystemArray(p_var);
-
- if (type == typeof(float[]))
- return VariantUtils.ConvertAsPackedFloat32ArrayToSystemArray(p_var);
-
- if (type == typeof(double[]))
- return VariantUtils.ConvertAsPackedFloat64ArrayToSystemArray(p_var);
-
- if (type == typeof(string[]))
- return VariantUtils.ConvertAsPackedStringArrayToSystemArray(p_var);
-
- if (type == typeof(Vector2[]))
- return VariantUtils.ConvertAsPackedVector2ArrayToSystemArray(p_var);
-
- if (type == typeof(Vector3[]))
- return VariantUtils.ConvertAsPackedVector3ArrayToSystemArray(p_var);
-
- if (type == typeof(Color[]))
- return VariantUtils.ConvertAsPackedColorArrayToSystemArray(p_var);
-
- if (type == typeof(StringName[]))
- return VariantUtils.ConvertToSystemArrayOfStringName(p_var);
-
- if (type == typeof(NodePath[]))
- return VariantUtils.ConvertToSystemArrayOfNodePath(p_var);
-
- if (type == typeof(RID[]))
- return VariantUtils.ConvertToSystemArrayOfRID(p_var);
-
- if (typeof(Godot.Object[]).IsAssignableFrom(type))
- return VariantUtils.ConvertToSystemArrayOfGodotObject(p_var, type);
-
- GD.PushError("Attempted to convert Variant to array of unsupported element type. Name: " +
- type.GetElementType()!.FullName + ".");
- return null;
- }
-
- private static bool ConvertVariantToManagedObjectOfClass(in godot_variant p_var, Type type,
- out object? res)
- {
- if (typeof(Godot.Object).IsAssignableFrom(type))
- {
- if (p_var.Type == Variant.Type.Nil)
- {
- res = null;
- return true;
- }
-
- if (p_var.Type != Variant.Type.Object)
- {
- GD.PushError("Invalid cast when marshaling Godot.Object type." +
- $" Variant type is `{p_var.Type}`; expected `{p_var.Object}`.");
- res = null;
- return true;
- }
-
- var godotObjectPtr = VariantUtils.ConvertToGodotObjectPtr(p_var);
-
- if (godotObjectPtr == IntPtr.Zero)
- {
- res = null;
- return true;
- }
-
- var godotObject = InteropUtils.UnmanagedGetManaged(godotObjectPtr);
-
- if (!type.IsInstanceOfType(godotObject))
- {
- GD.PushError("Invalid cast when marshaling Godot.Object type." +
- $" `{godotObject.GetType()}` is not assignable to `{type.FullName}`.");
- res = null;
- return false;
- }
-
- res = godotObject;
- return true;
- }
-
- if (typeof(StringName) == type)
- {
- res = VariantUtils.ConvertToStringNameObject(p_var);
- return true;
- }
-
- if (typeof(NodePath) == type)
- {
- res = VariantUtils.ConvertToNodePathObject(p_var);
- return true;
- }
-
- if (typeof(RID) == type)
- {
- res = VariantUtils.ConvertToRID(p_var);
- return true;
- }
-
- if (typeof(Collections.Dictionary) == type)
- {
- res = VariantUtils.ConvertToDictionaryObject(p_var);
- return true;
- }
-
- if (typeof(Collections.Array) == type)
- {
- res = VariantUtils.ConvertToArrayObject(p_var);
- return true;
- }
-
- res = null;
- return false;
- }
-
- public static unsafe object? ConvertVariantToManagedObject(in godot_variant p_var)
- {
- switch (p_var.Type)
- {
- case Variant.Type.Bool:
- return p_var.Bool.ToBool();
- case Variant.Type.Int:
- return p_var.Int;
- case Variant.Type.Float:
- {
-#if REAL_T_IS_DOUBLE
- return p_var.Float;
-#else
- return (float)p_var.Float;
-#endif
- }
- case Variant.Type.String:
- return ConvertStringToManaged(p_var.String);
- case Variant.Type.Vector2:
- return p_var.Vector2;
- case Variant.Type.Vector2i:
- return p_var.Vector2i;
- case Variant.Type.Rect2:
- return p_var.Rect2;
- case Variant.Type.Rect2i:
- return p_var.Rect2i;
- case Variant.Type.Vector3:
- return p_var.Vector3;
- case Variant.Type.Vector3i:
- return p_var.Vector3i;
- case Variant.Type.Transform2d:
- return *p_var.Transform2D;
- case Variant.Type.Vector4:
- return p_var.Vector4;
- case Variant.Type.Vector4i:
- return p_var.Vector4i;
- case Variant.Type.Plane:
- return p_var.Plane;
- case Variant.Type.Quaternion:
- return p_var.Quaternion;
- case Variant.Type.Aabb:
- return *p_var.AABB;
- case Variant.Type.Basis:
- return *p_var.Basis;
- case Variant.Type.Transform3d:
- return *p_var.Transform3D;
- case Variant.Type.Projection:
- return *p_var.Projection;
- case Variant.Type.Color:
- return p_var.Color;
- case Variant.Type.StringName:
- {
- // The Variant owns the value, so we need to make a copy
- return StringName.CreateTakingOwnershipOfDisposableValue(
- NativeFuncs.godotsharp_string_name_new_copy(p_var.StringName));
- }
- case Variant.Type.NodePath:
- {
- // The Variant owns the value, so we need to make a copy
- return NodePath.CreateTakingOwnershipOfDisposableValue(
- NativeFuncs.godotsharp_node_path_new_copy(p_var.NodePath));
- }
- case Variant.Type.Rid:
- return p_var.RID;
- case Variant.Type.Object:
- return InteropUtils.UnmanagedGetManaged(p_var.Object);
- case Variant.Type.Callable:
- return ConvertCallableToManaged(p_var.Callable);
- case Variant.Type.Signal:
- return ConvertSignalToManaged(p_var.Signal);
- case Variant.Type.Dictionary:
- {
- // The Variant owns the value, so we need to make a copy
- return Collections.Dictionary.CreateTakingOwnershipOfDisposableValue(
- NativeFuncs.godotsharp_dictionary_new_copy(p_var.Dictionary));
- }
- case Variant.Type.Array:
- {
- // The Variant owns the value, so we need to make a copy
- return Collections.Array.CreateTakingOwnershipOfDisposableValue(
- NativeFuncs.godotsharp_array_new_copy(p_var.Array));
- }
- case Variant.Type.PackedByteArray:
- return VariantUtils.ConvertAsPackedByteArrayToSystemArray(p_var);
- case Variant.Type.PackedInt32Array:
- return VariantUtils.ConvertAsPackedInt32ArrayToSystemArray(p_var);
- case Variant.Type.PackedInt64Array:
- return VariantUtils.ConvertAsPackedInt64ArrayToSystemArray(p_var);
- case Variant.Type.PackedFloat32Array:
- return VariantUtils.ConvertAsPackedFloat32ArrayToSystemArray(p_var);
- case Variant.Type.PackedFloat64Array:
- return VariantUtils.ConvertAsPackedFloat64ArrayToSystemArray(p_var);
- case Variant.Type.PackedStringArray:
- return VariantUtils.ConvertAsPackedStringArrayToSystemArray(p_var);
- case Variant.Type.PackedVector2Array:
- return VariantUtils.ConvertAsPackedVector2ArrayToSystemArray(p_var);
- case Variant.Type.PackedVector3Array:
- return VariantUtils.ConvertAsPackedVector3ArrayToSystemArray(p_var);
- case Variant.Type.PackedColorArray:
- return VariantUtils.ConvertAsPackedColorArrayToSystemArray(p_var);
- default:
- return null;
- }
- }
-
// String
public static unsafe godot_string ConvertStringToNative(string? p_mono_string)
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.generic.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.generic.cs
index 694da6db77..80ef2a1ea1 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.generic.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.generic.cs
@@ -4,6 +4,8 @@ using System.Runtime.CompilerServices;
namespace Godot.NativeInterop;
+#nullable enable
+
public partial class VariantUtils
{
private static Exception UnsupportedType<T>() => new InvalidOperationException(
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs
index 237a4da364..49a363cef2 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs
@@ -109,16 +109,50 @@ public partial struct Variant : IDisposable
public override string ToString() => AsString();
- public object? Obj
- {
- get
+ public object? Obj =>
+ _obj ??= NativeVar.DangerousSelfRef.Type switch
{
- if (_obj == null)
- _obj = Marshaling.ConvertVariantToManagedObject((godot_variant)NativeVar);
-
- return _obj;
- }
- }
+ Type.Bool => AsBool(),
+ Type.Int => AsInt64(),
+ Type.Float => AsDouble(),
+ Type.String => AsString(),
+ Type.Vector2 => AsVector2(),
+ Type.Vector2i => AsVector2i(),
+ Type.Rect2 => AsRect2(),
+ Type.Rect2i => AsRect2i(),
+ Type.Vector3 => AsVector3(),
+ Type.Vector3i => AsVector3i(),
+ Type.Transform2d => AsTransform2D(),
+ Type.Vector4 => AsVector4(),
+ Type.Vector4i => AsVector4i(),
+ Type.Plane => AsPlane(),
+ Type.Quaternion => AsQuaternion(),
+ Type.Aabb => AsAABB(),
+ Type.Basis => AsBasis(),
+ Type.Transform3d => AsTransform3D(),
+ Type.Projection => AsProjection(),
+ Type.Color => AsColor(),
+ Type.StringName => AsStringName(),
+ Type.NodePath => AsNodePath(),
+ Type.Rid => AsRID(),
+ Type.Object => AsGodotObject(),
+ Type.Callable => AsCallable(),
+ Type.Signal => AsSignalInfo(),
+ Type.Dictionary => AsGodotDictionary(),
+ Type.Array => AsGodotArray(),
+ Type.PackedByteArray => AsByteArray(),
+ Type.PackedInt32Array => AsInt32Array(),
+ Type.PackedInt64Array => AsInt64Array(),
+ Type.PackedFloat32Array => AsFloat32Array(),
+ Type.PackedFloat64Array => AsFloat64Array(),
+ Type.PackedStringArray => AsStringArray(),
+ Type.PackedVector2Array => AsVector2Array(),
+ Type.PackedVector3Array => AsVector3Array(),
+ Type.PackedColorArray => AsColorArray(),
+ Type.Nil => null,
+ Type.Max or _ =>
+ throw new InvalidOperationException($"Invalid Variant type: {NativeVar.DangerousSelfRef.Type}"),
+ };
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant From<[MustBeVariant] T>(in T from) =>
diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp
index cf2d8c9986..27fab88956 100644
--- a/modules/text_server_adv/text_server_adv.cpp
+++ b/modules/text_server_adv/text_server_adv.cpp
@@ -34,6 +34,7 @@
// Headers for building as GDExtension plug-in.
#include <godot_cpp/classes/file_access.hpp>
+#include <godot_cpp/classes/os.hpp>
#include <godot_cpp/classes/project_settings.hpp>
#include <godot_cpp/classes/rendering_server.hpp>
#include <godot_cpp/classes/translation_server.hpp>
@@ -1437,11 +1438,13 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_f
if (fd->face->style_name != nullptr) {
p_font_data->style_name = String::utf8((const char *)fd->face->style_name);
}
+ p_font_data->weight = _font_get_weight_by_name(p_font_data->style_name.to_lower());
+ p_font_data->stretch = _font_get_stretch_by_name(p_font_data->style_name.to_lower());
p_font_data->style_flags = 0;
- if (fd->face->style_flags & FT_STYLE_FLAG_BOLD) {
+ if ((fd->face->style_flags & FT_STYLE_FLAG_BOLD) || p_font_data->weight >= 700) {
p_font_data->style_flags.set_flag(FONT_BOLD);
}
- if (fd->face->style_flags & FT_STYLE_FLAG_ITALIC) {
+ if ((fd->face->style_flags & FT_STYLE_FLAG_ITALIC) || _is_ital_style(p_font_data->style_name.to_lower())) {
p_font_data->style_flags.set_flag(FONT_ITALIC);
}
if (fd->face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) {
@@ -1967,6 +1970,46 @@ String TextServerAdvanced::_font_get_style_name(const RID &p_font_rid) const {
return fd->style_name;
}
+void TextServerAdvanced::_font_set_weight(const RID &p_font_rid, int64_t p_weight) {
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->weight = CLAMP(p_weight, 100, 999);
+}
+
+int64_t TextServerAdvanced::_font_get_weight(const RID &p_font_rid) const {
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, 400);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 400);
+ return fd->weight;
+}
+
+void TextServerAdvanced::_font_set_stretch(const RID &p_font_rid, int64_t p_stretch) {
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->stretch = CLAMP(p_stretch, 50, 200);
+}
+
+int64_t TextServerAdvanced::_font_get_stretch(const RID &p_font_rid) const {
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, 100);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 100);
+ return fd->stretch;
+}
+
void TextServerAdvanced::_font_set_name(const RID &p_font_rid, const String &p_name) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -2103,6 +2146,25 @@ int64_t TextServerAdvanced::_font_get_fixed_size(const RID &p_font_rid) const {
return fd->fixed_size;
}
+void TextServerAdvanced::_font_set_allow_system_fallback(const RID &p_font_rid, bool p_allow_system_fallback) {
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ if (fd->allow_system_fallback != p_allow_system_fallback) {
+ _font_clear_cache(fd);
+ fd->allow_system_fallback = p_allow_system_fallback;
+ }
+}
+
+bool TextServerAdvanced::_font_is_allow_system_fallback(const RID &p_font_rid) const {
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, false);
+
+ MutexLock lock(fd->mutex);
+ return fd->allow_system_fallback;
+}
+
void TextServerAdvanced::_font_set_force_autohinter(const RID &p_font_rid, bool p_force_autohinter) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -4632,12 +4694,11 @@ bool TextServerAdvanced::_shaped_text_update_breaks(const RID &p_shaped) {
sd->breaks[pos] = true;
} else if ((ubrk_getRuleStatus(bi) >= UBRK_LINE_SOFT) && (ubrk_getRuleStatus(bi) < UBRK_LINE_SOFT_LIMIT)) {
sd->breaks[pos] = false;
-
- int pos_p = pos - 1 - sd->start;
- char32_t c = sd->text[pos_p];
- if (pos - sd->start != sd->end && !is_whitespace(c) && (c != 0xfffc)) {
- sd->break_inserts++;
- }
+ }
+ int pos_p = pos - 1 - sd->start;
+ char32_t c = sd->text[pos_p];
+ if (pos - sd->start != sd->end && !is_whitespace(c) && (c != 0xfffc)) {
+ sd->break_inserts++;
}
}
}
@@ -5066,10 +5127,177 @@ _FORCE_INLINE_ void TextServerAdvanced::_add_featuers(const Dictionary &p_source
}
}
-void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_start, int64_t p_end, hb_script_t p_script, hb_direction_t p_direction, TypedArray<RID> p_fonts, int64_t p_span, int64_t p_fb_index) {
+void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_start, int64_t p_end, hb_script_t p_script, hb_direction_t p_direction, TypedArray<RID> p_fonts, int64_t p_span, int64_t p_fb_index, int64_t p_prev_start, int64_t p_prev_end) {
+ RID f;
int fs = p_sd->spans[p_span].font_size;
- if (p_fb_index >= p_fonts.size()) {
- // Add fallback glyphs.
+
+ if (p_fb_index >= 0 && p_fb_index < p_fonts.size()) {
+ // Try font from list.
+ f = p_fonts[p_fb_index];
+ } else if (OS::get_singleton()->has_feature("system_fonts") && p_fonts.size() > 0 && ((p_fb_index == p_fonts.size()) || (p_fb_index > p_fonts.size() && p_start != p_prev_start))) {
+ // Try system fallback.
+ RID fdef = p_fonts[0];
+ if (_font_is_allow_system_fallback(fdef)) {
+ String text = p_sd->text.substr(p_start, 1);
+ String font_name = _font_get_name(fdef);
+ BitField<FontStyle> font_style = _font_get_style(fdef);
+ int font_weight = _font_get_weight(fdef);
+ int font_stretch = _font_get_stretch(fdef);
+ Dictionary dvar = _font_get_variation_coordinates(fdef);
+ static int64_t wgth_tag = _name_to_tag("weight");
+ static int64_t wdth_tag = _name_to_tag("width");
+ static int64_t ital_tag = _name_to_tag("italic");
+ if (dvar.has(wgth_tag)) {
+ font_weight = dvar[wgth_tag].operator int();
+ }
+ if (dvar.has(wdth_tag)) {
+ font_stretch = dvar[wdth_tag].operator int();
+ }
+ if (dvar.has(ital_tag) && dvar[ital_tag].operator int() == 1) {
+ font_style.set_flag(TextServer::FONT_ITALIC);
+ }
+
+ char scr_buffer[5] = { 0, 0, 0, 0, 0 };
+ hb_tag_to_string(hb_script_to_iso15924_tag(p_script), scr_buffer);
+ String script_code = String(scr_buffer);
+ String locale = (p_sd->spans[p_span].language.is_empty()) ? TranslationServer::get_singleton()->get_tool_locale() : p_sd->spans[p_span].language;
+
+ PackedStringArray fallback_font_name = OS::get_singleton()->get_system_font_path_for_text(font_name, text, locale, script_code, font_weight, font_stretch, font_style & TextServer::FONT_ITALIC);
+#ifdef GDEXTENSION
+ for (int fb = 0; fb < fallback_font_name.size(); fb++) {
+ const String &E = fallback_font_name[fb];
+#else
+ for (const String &E : fallback_font_name) {
+#endif
+ SystemFontKey key = SystemFontKey(E, font_style & TextServer::FONT_ITALIC, font_weight, font_stretch, fdef, this);
+ if (system_fonts.has(key)) {
+ const SystemFontCache &sysf_cache = system_fonts[key];
+ int best_score = 0;
+ int best_match = -1;
+ for (int face_idx = 0; face_idx < sysf_cache.var.size(); face_idx++) {
+ const SystemFontCacheRec &F = sysf_cache.var[face_idx];
+ if (unlikely(!_font_has_char(F.rid, text[0]))) {
+ continue;
+ }
+ BitField<FontStyle> style = _font_get_style(F.rid);
+ int weight = _font_get_weight(F.rid);
+ int stretch = _font_get_stretch(F.rid);
+ int score = (20 - Math::abs(weight - font_weight) / 50);
+ score += (20 - Math::abs(stretch - font_stretch) / 10);
+ if (bool(style & TextServer::FONT_ITALIC) == bool(font_style & TextServer::FONT_ITALIC)) {
+ score += 30;
+ }
+ if (score >= best_score) {
+ best_score = score;
+ best_match = face_idx;
+ }
+ if (best_score == 70) {
+ break;
+ }
+ }
+ if (best_match != -1) {
+ f = sysf_cache.var[best_match].rid;
+ }
+ }
+ if (!f.is_valid()) {
+ if (system_fonts.has(key)) {
+ const SystemFontCache &sysf_cache = system_fonts[key];
+ if (sysf_cache.max_var == sysf_cache.var.size()) {
+ // All subfonts already tested, skip.
+ continue;
+ }
+ }
+
+ if (!system_font_data.has(E)) {
+ system_font_data[E] = FileAccess::get_file_as_bytes(E);
+ }
+
+ const PackedByteArray &font_data = system_font_data[E];
+
+ SystemFontCacheRec sysf;
+ sysf.rid = _create_font();
+ _font_set_data_ptr(sysf.rid, font_data.ptr(), font_data.size());
+
+ Dictionary var = dvar;
+ // Select matching style from collection.
+ int best_score = 0;
+ int best_match = -1;
+ for (int face_idx = 0; face_idx < _font_get_face_count(sysf.rid); face_idx++) {
+ _font_set_face_index(sysf.rid, face_idx);
+ if (unlikely(!_font_has_char(sysf.rid, text[0]))) {
+ continue;
+ }
+ BitField<FontStyle> style = _font_get_style(sysf.rid);
+ int weight = _font_get_weight(sysf.rid);
+ int stretch = _font_get_stretch(sysf.rid);
+ int score = (20 - Math::abs(weight - font_weight) / 50);
+ score += (20 - Math::abs(stretch - font_stretch) / 10);
+ if (bool(style & TextServer::FONT_ITALIC) == bool(font_style & TextServer::FONT_ITALIC)) {
+ score += 30;
+ }
+ if (score >= best_score) {
+ best_score = score;
+ best_match = face_idx;
+ }
+ if (best_score == 70) {
+ break;
+ }
+ }
+ if (best_match == -1) {
+ _free_rid(sysf.rid);
+ continue;
+ } else {
+ _font_set_face_index(sysf.rid, best_match);
+ }
+ sysf.index = best_match;
+
+ // If it's a variable font, apply weight, stretch and italic coordinates to match requested style.
+ if (best_score != 70) {
+ Dictionary ftr = _font_supported_variation_list(sysf.rid);
+ if (ftr.has(wdth_tag)) {
+ var[wdth_tag] = font_stretch;
+ _font_set_stretch(sysf.rid, font_stretch);
+ }
+ if (ftr.has(wgth_tag)) {
+ var[wgth_tag] = font_weight;
+ _font_set_weight(sysf.rid, font_weight);
+ }
+ if ((font_style & TextServer::FONT_ITALIC) && ftr.has(ital_tag)) {
+ var[ital_tag] = 1;
+ _font_set_style(sysf.rid, _font_get_style(sysf.rid) | TextServer::FONT_ITALIC);
+ }
+ }
+
+ _font_set_antialiasing(sysf.rid, key.antialiasing);
+ _font_set_generate_mipmaps(sysf.rid, key.mipmaps);
+ _font_set_multichannel_signed_distance_field(sysf.rid, key.msdf);
+ _font_set_msdf_pixel_range(sysf.rid, key.msdf_range);
+ _font_set_msdf_size(sysf.rid, key.msdf_source_size);
+ _font_set_fixed_size(sysf.rid, key.fixed_size);
+ _font_set_force_autohinter(sysf.rid, key.force_autohinter);
+ _font_set_hinting(sysf.rid, key.hinting);
+ _font_set_subpixel_positioning(sysf.rid, key.subpixel_positioning);
+ _font_set_variation_coordinates(sysf.rid, var);
+ _font_set_oversampling(sysf.rid, key.oversampling);
+ _font_set_embolden(sysf.rid, key.embolden);
+ _font_set_transform(sysf.rid, key.transform);
+
+ if (system_fonts.has(key)) {
+ system_fonts[key].var.push_back(sysf);
+ } else {
+ SystemFontCache &sysf_cache = system_fonts[key];
+ sysf_cache.max_var = _font_get_face_count(sysf.rid);
+ sysf_cache.var.push_back(sysf);
+ }
+ f = sysf.rid;
+ }
+ break;
+ }
+ }
+ }
+
+ if (!f.is_valid()) {
+ // No valid font, use fallback hex code boxes.
for (int i = p_start; i < p_end; i++) {
if (p_sd->preserve_invalid || (p_sd->preserve_control && is_control(p_sd->text[i]))) {
Glyph gl;
@@ -5100,7 +5328,6 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star
return;
}
- RID f = p_fonts[p_fb_index];
FontAdvanced *fd = font_owner.get_or_null(f);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -5195,7 +5422,7 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star
gl.end = end;
gl.count = 0;
- gl.font_rid = p_fonts[p_fb_index];
+ gl.font_rid = f;
gl.font_size = fs;
if (glyph_info[i].mask & HB_GLYPH_FLAG_UNSAFE_TO_BREAK) {
@@ -5262,7 +5489,7 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star
for (unsigned int i = 0; i < glyph_count; i++) {
if ((w[i].flags & GRAPHEME_IS_VALID) == GRAPHEME_IS_VALID) {
if (failed_subrun_start != p_end + 1) {
- _shape_run(p_sd, failed_subrun_start, failed_subrun_end, p_script, p_direction, p_fonts, p_span, p_fb_index + 1);
+ _shape_run(p_sd, failed_subrun_start, failed_subrun_end, p_script, p_direction, p_fonts, p_span, p_fb_index + 1, p_start, p_end);
failed_subrun_start = p_end + 1;
failed_subrun_end = p_start;
}
@@ -5292,7 +5519,7 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star
}
memfree(w);
if (failed_subrun_start != p_end + 1) {
- _shape_run(p_sd, failed_subrun_start, failed_subrun_end, p_script, p_direction, p_fonts, p_span, p_fb_index + 1);
+ _shape_run(p_sd, failed_subrun_start, failed_subrun_end, p_script, p_direction, p_fonts, p_span, p_fb_index + 1, p_start, p_end);
}
p_sd->ascent = MAX(p_sd->ascent, _font_get_ascent(f, fs));
p_sd->descent = MAX(p_sd->descent, _font_get_descent(f, fs));
@@ -5464,7 +5691,7 @@ bool TextServerAdvanced::_shaped_text_shape(const RID &p_shaped) {
}
fonts.append_array(fonts_scr_only);
fonts.append_array(fonts_no_match);
- _shape_run(sd, MAX(sd->spans[k].start - sd->start, script_run_start), MIN(sd->spans[k].end - sd->start, script_run_end), sd->script_iter->script_ranges[j].script, bidi_run_direction, fonts, k, 0);
+ _shape_run(sd, MAX(sd->spans[k].start - sd->start, script_run_start), MIN(sd->spans[k].end - sd->start, script_run_end), sd->script_iter->script_ranges[j].script, bidi_run_direction, fonts, k, 0, 0, 0);
}
}
}
@@ -5961,7 +6188,11 @@ String TextServerAdvanced::_strip_diacritics(const String &p_string) const {
String result;
for (int i = 0; i < normalized_string.length(); i++) {
if (u_getCombiningClass(normalized_string[i]) == 0) {
+#ifdef GDEXTENSION
+ result = result + String::chr(normalized_string[i]);
+#else
result = result + normalized_string[i];
+#endif
}
}
return result;
@@ -6243,6 +6474,17 @@ TextServerAdvanced::TextServerAdvanced() {
_bmp_create_font_funcs();
}
+void TextServerAdvanced::_cleanup() {
+ for (const KeyValue<SystemFontKey, SystemFontCache> &E : system_fonts) {
+ const Vector<SystemFontCacheRec> &sysf_cache = E.value.var;
+ for (const SystemFontCacheRec &F : sysf_cache) {
+ _free_rid(F.rid);
+ }
+ }
+ system_fonts.clear();
+ system_font_data.clear();
+}
+
TextServerAdvanced::~TextServerAdvanced() {
_bmp_free_font_funcs();
#ifdef MODULE_FREETYPE_ENABLED
diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h
index 10fe3c2316..5e6d2cc2c0 100644
--- a/modules/text_server_adv/text_server_adv.h
+++ b/modules/text_server_adv/text_server_adv.h
@@ -300,6 +300,7 @@ class TextServerAdvanced : public TextServerExtension {
int msdf_range = 14;
int msdf_source_size = 48;
int fixed_size = 0;
+ bool allow_system_fallback = true;
bool force_autohinter = false;
TextServer::Hinting hinting = TextServer::HINTING_LIGHT;
TextServer::SubpixelPositioning subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_AUTO;
@@ -311,6 +312,8 @@ class TextServerAdvanced : public TextServerExtension {
BitField<TextServer::FontStyle> style_flags = 0;
String font_name;
String style_name;
+ int weight = 400;
+ int stretch = 100;
HashMap<Vector2i, FontForSizeAdvanced *, VariantHasher, VariantComparator> cache;
@@ -372,6 +375,57 @@ class TextServerAdvanced : public TextServerExtension {
_FORCE_INLINE_ double _get_extra_advance(RID p_font_rid, int p_font_size) const;
_FORCE_INLINE_ Variant::Type _get_tag_type(int64_t p_tag) const;
_FORCE_INLINE_ bool _get_tag_hidden(int64_t p_tag) const;
+ _FORCE_INLINE_ int _font_get_weight_by_name(const String &p_sty_name) const {
+ String sty_name = p_sty_name.replace(" ", "").replace("-", "");
+ if (sty_name.find("thin") >= 0 || sty_name.find("hairline") >= 0) {
+ return 100;
+ } else if (sty_name.find("extralight") >= 0 || sty_name.find("ultralight") >= 0) {
+ return 200;
+ } else if (sty_name.find("light") >= 0) {
+ return 300;
+ } else if (sty_name.find("semilight") >= 0) {
+ return 350;
+ } else if (sty_name.find("regular") >= 0) {
+ return 400;
+ } else if (sty_name.find("medium") >= 0) {
+ return 500;
+ } else if (sty_name.find("semibold") >= 0 || sty_name.find("demibold") >= 0) {
+ return 600;
+ } else if (sty_name.find("bold") >= 0) {
+ return 700;
+ } else if (sty_name.find("extrabold") >= 0 || sty_name.find("ultrabold") >= 0) {
+ return 800;
+ } else if (sty_name.find("black") >= 0 || sty_name.find("heavy") >= 0) {
+ return 900;
+ } else if (sty_name.find("extrablack") >= 0 || sty_name.find("ultrablack") >= 0) {
+ return 950;
+ }
+ return 400;
+ }
+ _FORCE_INLINE_ int _font_get_stretch_by_name(const String &p_sty_name) const {
+ String sty_name = p_sty_name.replace(" ", "").replace("-", "");
+ if (sty_name.find("ultracondensed") >= 0) {
+ return 50;
+ } else if (sty_name.find("extracondensed") >= 0) {
+ return 63;
+ } else if (sty_name.find("condensed") >= 0) {
+ return 75;
+ } else if (sty_name.find("semicondensed") >= 0) {
+ return 87;
+ } else if (sty_name.find("semiexpanded") >= 0) {
+ return 113;
+ } else if (sty_name.find("expanded") >= 0) {
+ return 125;
+ } else if (sty_name.find("extraexpanded") >= 0) {
+ return 150;
+ } else if (sty_name.find("ultraexpanded") >= 0) {
+ return 200;
+ }
+ return 100;
+ }
+ _FORCE_INLINE_ bool _is_ital_style(const String &p_sty_name) const {
+ return (p_sty_name.find("italic") >= 0) || (p_sty_name.find("oblique") >= 0);
+ }
// Shaped text cache data.
struct TrimData {
@@ -474,12 +528,87 @@ class TextServerAdvanced : public TextServerExtension {
mutable RID_PtrOwner<FontAdvanced> font_owner;
mutable RID_PtrOwner<ShapedTextDataAdvanced> shaped_owner;
+ struct SystemFontKey {
+ String font_name;
+ TextServer::FontAntialiasing antialiasing = TextServer::FONT_ANTIALIASING_GRAY;
+ bool italic = false;
+ bool mipmaps = false;
+ bool msdf = false;
+ bool force_autohinter = false;
+ int weight = 400;
+ int stretch = 100;
+ int msdf_range = 14;
+ int msdf_source_size = 48;
+ int fixed_size = 0;
+ TextServer::Hinting hinting = TextServer::HINTING_LIGHT;
+ TextServer::SubpixelPositioning subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_AUTO;
+ Dictionary variation_coordinates;
+ double oversampling = 0.0;
+ double embolden = 0.0;
+ Transform2D transform;
+
+ bool operator==(const SystemFontKey &p_b) const {
+ return (font_name == p_b.font_name) && (antialiasing == p_b.antialiasing) && (italic == p_b.italic) && (mipmaps == p_b.mipmaps) && (msdf == p_b.msdf) && (force_autohinter == p_b.force_autohinter) && (weight == p_b.weight) && (stretch == p_b.stretch) && (msdf_range == p_b.msdf_range) && (msdf_source_size == p_b.msdf_source_size) && (fixed_size == p_b.fixed_size) && (hinting == p_b.hinting) && (subpixel_positioning == p_b.subpixel_positioning) && (variation_coordinates == p_b.variation_coordinates) && (oversampling == p_b.oversampling) && (embolden == p_b.embolden) && (transform == p_b.transform);
+ }
+
+ SystemFontKey(const String &p_font_name, bool p_italic, int p_weight, int p_stretch, RID p_font, const TextServerAdvanced *p_fb) {
+ font_name = p_font_name;
+ italic = p_italic;
+ weight = p_weight;
+ stretch = p_stretch;
+ antialiasing = p_fb->_font_get_antialiasing(p_font);
+ mipmaps = p_fb->_font_get_generate_mipmaps(p_font);
+ msdf = p_fb->_font_is_multichannel_signed_distance_field(p_font);
+ msdf_range = p_fb->_font_get_msdf_pixel_range(p_font);
+ msdf_source_size = p_fb->_font_get_msdf_size(p_font);
+ fixed_size = p_fb->_font_get_fixed_size(p_font);
+ force_autohinter = p_fb->_font_is_force_autohinter(p_font);
+ hinting = p_fb->_font_get_hinting(p_font);
+ subpixel_positioning = p_fb->_font_get_subpixel_positioning(p_font);
+ variation_coordinates = p_fb->_font_get_variation_coordinates(p_font);
+ oversampling = p_fb->_font_get_oversampling(p_font);
+ embolden = p_fb->_font_get_embolden(p_font);
+ transform = p_fb->_font_get_transform(p_font);
+ }
+ };
+
+ struct SystemFontCacheRec {
+ RID rid;
+ int index = 0;
+ };
+
+ struct SystemFontCache {
+ Vector<SystemFontCacheRec> var;
+ int max_var = 0;
+ };
+
+ struct SystemFontKeyHasher {
+ _FORCE_INLINE_ static uint32_t hash(const SystemFontKey &p_a) {
+ uint32_t hash = p_a.font_name.hash();
+ hash = hash_murmur3_one_32(p_a.variation_coordinates.hash(), hash);
+ hash = hash_murmur3_one_32(p_a.weight, hash);
+ hash = hash_murmur3_one_32(p_a.stretch, hash);
+ hash = hash_murmur3_one_32(p_a.msdf_range, hash);
+ hash = hash_murmur3_one_32(p_a.msdf_source_size, hash);
+ hash = hash_murmur3_one_32(p_a.fixed_size, hash);
+ hash = hash_murmur3_one_double(p_a.oversampling, hash);
+ hash = hash_murmur3_one_double(p_a.embolden, hash);
+ hash = hash_murmur3_one_real(p_a.transform[0].x, hash);
+ hash = hash_murmur3_one_real(p_a.transform[0].y, hash);
+ hash = hash_murmur3_one_real(p_a.transform[1].x, hash);
+ hash = hash_murmur3_one_real(p_a.transform[1].y, hash);
+ return hash_fmix32(hash_murmur3_one_32(((int)p_a.mipmaps) | ((int)p_a.msdf << 1) | ((int)p_a.italic << 2) | ((int)p_a.force_autohinter << 3) | ((int)p_a.hinting << 4) | ((int)p_a.subpixel_positioning << 8) | ((int)p_a.antialiasing << 12), hash));
+ }
+ };
+ mutable HashMap<SystemFontKey, SystemFontCache, SystemFontKeyHasher> system_fonts;
+ mutable HashMap<String, PackedByteArray> system_font_data;
+
void _realign(ShapedTextDataAdvanced *p_sd) const;
int64_t _convert_pos(const String &p_utf32, const Char16String &p_utf16, int64_t p_pos) const;
int64_t _convert_pos(const ShapedTextDataAdvanced *p_sd, int64_t p_pos) const;
int64_t _convert_pos_inv(const ShapedTextDataAdvanced *p_sd, int64_t p_pos) const;
bool _shape_substr(ShapedTextDataAdvanced *p_new_sd, const ShapedTextDataAdvanced *p_sd, int64_t p_start, int64_t p_length) const;
- void _shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_start, int64_t p_end, hb_script_t p_script, hb_direction_t p_direction, TypedArray<RID> p_fonts, int64_t p_span, int64_t p_fb_index);
+ void _shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_start, int64_t p_end, hb_script_t p_script, hb_direction_t p_direction, TypedArray<RID> p_fonts, int64_t p_span, int64_t p_fb_index, int64_t p_prev_start, int64_t p_prev_end);
Glyph _shape_single_glyph(ShapedTextDataAdvanced *p_sd, char32_t p_char, hb_script_t p_script, hb_direction_t p_direction, const RID &p_font, int64_t p_font_size);
_FORCE_INLINE_ void _add_featuers(const Dictionary &p_source, Vector<hb_feature_t> &r_ftrs);
@@ -568,6 +697,12 @@ public:
MODBIND2(font_set_style_name, const RID &, const String &);
MODBIND1RC(String, font_get_style_name, const RID &);
+ MODBIND2(font_set_weight, const RID &, int64_t);
+ MODBIND1RC(int64_t, font_get_weight, const RID &);
+
+ MODBIND2(font_set_stretch, const RID &, int64_t);
+ MODBIND1RC(int64_t, font_get_stretch, const RID &);
+
MODBIND2(font_set_name, const RID &, const String &);
MODBIND1RC(String, font_get_name, const RID &);
@@ -589,6 +724,9 @@ public:
MODBIND2(font_set_fixed_size, const RID &, int64_t);
MODBIND1RC(int64_t, font_get_fixed_size, const RID &);
+ MODBIND2(font_set_allow_system_fallback, const RID &, bool);
+ MODBIND1RC(bool, font_is_allow_system_fallback, const RID &);
+
MODBIND2(font_set_force_autohinter, const RID &, bool);
MODBIND1RC(bool, font_is_force_autohinter, const RID &);
@@ -787,6 +925,8 @@ public:
MODBIND2RC(String, string_to_upper, const String &, const String &);
MODBIND2RC(String, string_to_lower, const String &, const String &);
+ MODBIND0(cleanup);
+
TextServerAdvanced();
~TextServerAdvanced();
};
diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp
index aaef9c9a3d..9133c277bb 100644
--- a/modules/text_server_fb/text_server_fb.cpp
+++ b/modules/text_server_fb/text_server_fb.cpp
@@ -34,6 +34,7 @@
// Headers for building as GDExtension plug-in.
#include <godot_cpp/classes/file_access.hpp>
+#include <godot_cpp/classes/os.hpp>
#include <godot_cpp/classes/project_settings.hpp>
#include <godot_cpp/classes/rendering_server.hpp>
#include <godot_cpp/classes/translation_server.hpp>
@@ -49,6 +50,7 @@ using namespace godot;
#include "core/config/project_settings.h"
#include "core/error/error_macros.h"
#include "core/string/print_string.h"
+#include "core/string/translation.h"
#include "core/string/ucaps.h"
#include "modules/modules_enabled.gen.h" // For freetype, msdfgen, svg.
@@ -852,11 +854,13 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontFallback *p_f
if (fd->face->style_name != nullptr) {
p_font_data->style_name = String::utf8((const char *)fd->face->style_name);
}
+ p_font_data->weight = _font_get_weight_by_name(p_font_data->style_name.to_lower());
+ p_font_data->stretch = _font_get_stretch_by_name(p_font_data->style_name.to_lower());
p_font_data->style_flags = 0;
- if (fd->face->style_flags & FT_STYLE_FLAG_BOLD) {
+ if ((fd->face->style_flags & FT_STYLE_FLAG_BOLD) || p_font_data->weight >= 700) {
p_font_data->style_flags.set_flag(FONT_BOLD);
}
- if (fd->face->style_flags & FT_STYLE_FLAG_ITALIC) {
+ if ((fd->face->style_flags & FT_STYLE_FLAG_ITALIC) || _is_ital_style(p_font_data->style_name.to_lower())) {
p_font_data->style_flags.set_flag(FONT_ITALIC);
}
if (fd->face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) {
@@ -1061,6 +1065,46 @@ String TextServerFallback::_font_get_style_name(const RID &p_font_rid) const {
return fd->style_name;
}
+void TextServerFallback::_font_set_weight(const RID &p_font_rid, int64_t p_weight) {
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->weight = CLAMP(p_weight, 100, 999);
+}
+
+int64_t TextServerFallback::_font_get_weight(const RID &p_font_rid) const {
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, 400);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 400);
+ return fd->weight;
+}
+
+void TextServerFallback::_font_set_stretch(const RID &p_font_rid, int64_t p_stretch) {
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->stretch = CLAMP(p_stretch, 50, 200);
+}
+
+int64_t TextServerFallback::_font_get_stretch(const RID &p_font_rid) const {
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, 100);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 100);
+ return fd->stretch;
+}
+
void TextServerFallback::_font_set_name(const RID &p_font_rid, const String &p_name) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -1197,6 +1241,25 @@ int64_t TextServerFallback::_font_get_fixed_size(const RID &p_font_rid) const {
return fd->fixed_size;
}
+void TextServerFallback::_font_set_allow_system_fallback(const RID &p_font_rid, bool p_allow_system_fallback) {
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ if (fd->allow_system_fallback != p_allow_system_fallback) {
+ _font_clear_cache(fd);
+ fd->allow_system_fallback = p_allow_system_fallback;
+ }
+}
+
+bool TextServerFallback::_font_is_allow_system_fallback(const RID &p_font_rid) const {
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, false);
+
+ MutexLock lock(fd->mutex);
+ return fd->allow_system_fallback;
+}
+
void TextServerFallback::_font_set_force_autohinter(const RID &p_font_rid, bool p_force_autohinter) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -3603,6 +3666,7 @@ bool TextServerFallback::_shaped_text_shape(const RID &p_shaped) {
sd->glyphs.push_back(gl);
} else {
// Text span.
+ RID prev_font;
for (int j = span.start; j < span.end; j++) {
Glyph gl;
gl.start = j;
@@ -3623,6 +3687,170 @@ bool TextServerFallback::_shaped_text_shape(const RID &p_shaped) {
break;
}
}
+ if (!gl.font_rid.is_valid() && prev_font.is_valid()) {
+ if (_font_has_char(prev_font, gl.index)) {
+ gl.font_rid = prev_font;
+ }
+ }
+ if (!gl.font_rid.is_valid() && OS::get_singleton()->has_feature("system_fonts") && span.fonts.size() > 0) {
+ // Try system fallback.
+ RID fdef = span.fonts[0];
+ if (_font_is_allow_system_fallback(fdef)) {
+ String text = sd->text.substr(j, 1);
+ String font_name = _font_get_name(fdef);
+ BitField<FontStyle> font_style = _font_get_style(fdef);
+ int font_weight = _font_get_weight(fdef);
+ int font_stretch = _font_get_stretch(fdef);
+ Dictionary dvar = _font_get_variation_coordinates(fdef);
+ static int64_t wgth_tag = _name_to_tag("weight");
+ static int64_t wdth_tag = _name_to_tag("width");
+ static int64_t ital_tag = _name_to_tag("italic");
+ if (dvar.has(wgth_tag)) {
+ font_weight = dvar[wgth_tag].operator int();
+ }
+ if (dvar.has(wdth_tag)) {
+ font_stretch = dvar[wdth_tag].operator int();
+ }
+ if (dvar.has(ital_tag) && dvar[ital_tag].operator int() == 1) {
+ font_style.set_flag(TextServer::FONT_ITALIC);
+ }
+
+ String locale = (span.language.is_empty()) ? TranslationServer::get_singleton()->get_tool_locale() : span.language;
+
+ PackedStringArray fallback_font_name = OS::get_singleton()->get_system_font_path_for_text(font_name, text, locale, String(), font_weight, font_stretch, font_style & TextServer::FONT_ITALIC);
+#ifdef GDEXTENSION
+ for (int fb = 0; fb < fallback_font_name.size(); fb++) {
+ const String &E = fallback_font_name[fb];
+#else
+ for (const String &E : fallback_font_name) {
+#endif
+ SystemFontKey key = SystemFontKey(E, font_style & TextServer::FONT_ITALIC, font_weight, font_stretch, fdef, this);
+ if (system_fonts.has(key)) {
+ const SystemFontCache &sysf_cache = system_fonts[key];
+ int best_score = 0;
+ int best_match = -1;
+ for (int face_idx = 0; face_idx < sysf_cache.var.size(); face_idx++) {
+ const SystemFontCacheRec &F = sysf_cache.var[face_idx];
+ if (unlikely(!_font_has_char(F.rid, text[0]))) {
+ continue;
+ }
+ BitField<FontStyle> style = _font_get_style(F.rid);
+ int weight = _font_get_weight(F.rid);
+ int stretch = _font_get_stretch(F.rid);
+ int score = (20 - Math::abs(weight - font_weight) / 50);
+ score += (20 - Math::abs(stretch - font_stretch) / 10);
+ if (bool(style & TextServer::FONT_ITALIC) == bool(font_style & TextServer::FONT_ITALIC)) {
+ score += 30;
+ }
+ if (score >= best_score) {
+ best_score = score;
+ best_match = face_idx;
+ }
+ if (best_score == 70) {
+ break;
+ }
+ }
+ if (best_match != -1) {
+ gl.font_rid = sysf_cache.var[best_match].rid;
+ }
+ }
+ if (!gl.font_rid.is_valid()) {
+ if (system_fonts.has(key)) {
+ const SystemFontCache &sysf_cache = system_fonts[key];
+ if (sysf_cache.max_var == sysf_cache.var.size()) {
+ // All subfonts already tested, skip.
+ continue;
+ }
+ }
+
+ if (!system_font_data.has(E)) {
+ system_font_data[E] = FileAccess::get_file_as_bytes(E);
+ }
+
+ const PackedByteArray &font_data = system_font_data[E];
+
+ SystemFontCacheRec sysf;
+ sysf.rid = _create_font();
+ _font_set_data_ptr(sysf.rid, font_data.ptr(), font_data.size());
+
+ Dictionary var = dvar;
+ // Select matching style from collection.
+ int best_score = 0;
+ int best_match = -1;
+ for (int face_idx = 0; face_idx < _font_get_face_count(sysf.rid); face_idx++) {
+ _font_set_face_index(sysf.rid, face_idx);
+ if (unlikely(!_font_has_char(sysf.rid, text[0]))) {
+ continue;
+ }
+ BitField<FontStyle> style = _font_get_style(sysf.rid);
+ int weight = _font_get_weight(sysf.rid);
+ int stretch = _font_get_stretch(sysf.rid);
+ int score = (20 - Math::abs(weight - font_weight) / 50);
+ score += (20 - Math::abs(stretch - font_stretch) / 10);
+ if (bool(style & TextServer::FONT_ITALIC) == bool(font_style & TextServer::FONT_ITALIC)) {
+ score += 30;
+ }
+ if (score >= best_score) {
+ best_score = score;
+ best_match = face_idx;
+ }
+ if (best_score == 70) {
+ break;
+ }
+ }
+ if (best_match == -1) {
+ _free_rid(sysf.rid);
+ continue;
+ } else {
+ _font_set_face_index(sysf.rid, best_match);
+ }
+ sysf.index = best_match;
+
+ // If it's a variable font, apply weight, stretch and italic coordinates to match requested style.
+ if (best_score != 70) {
+ Dictionary ftr = _font_supported_variation_list(sysf.rid);
+ if (ftr.has(wdth_tag)) {
+ var[wdth_tag] = font_stretch;
+ _font_set_stretch(sysf.rid, font_stretch);
+ }
+ if (ftr.has(wgth_tag)) {
+ var[wgth_tag] = font_weight;
+ _font_set_weight(sysf.rid, font_weight);
+ }
+ if ((font_style & TextServer::FONT_ITALIC) && ftr.has(ital_tag)) {
+ var[ital_tag] = 1;
+ _font_set_style(sysf.rid, _font_get_style(sysf.rid) | TextServer::FONT_ITALIC);
+ }
+ }
+
+ _font_set_antialiasing(sysf.rid, key.antialiasing);
+ _font_set_generate_mipmaps(sysf.rid, key.mipmaps);
+ _font_set_multichannel_signed_distance_field(sysf.rid, key.msdf);
+ _font_set_msdf_pixel_range(sysf.rid, key.msdf_range);
+ _font_set_msdf_size(sysf.rid, key.msdf_source_size);
+ _font_set_fixed_size(sysf.rid, key.fixed_size);
+ _font_set_force_autohinter(sysf.rid, key.force_autohinter);
+ _font_set_hinting(sysf.rid, key.hinting);
+ _font_set_subpixel_positioning(sysf.rid, key.subpixel_positioning);
+ _font_set_variation_coordinates(sysf.rid, var);
+ _font_set_oversampling(sysf.rid, key.oversampling);
+ _font_set_embolden(sysf.rid, key.embolden);
+ _font_set_transform(sysf.rid, key.transform);
+
+ if (system_fonts.has(key)) {
+ system_fonts[key].var.push_back(sysf);
+ } else {
+ SystemFontCache &sysf_cache = system_fonts[key];
+ sysf_cache.max_var = _font_get_face_count(sysf.rid);
+ sysf_cache.var.push_back(sysf);
+ }
+ gl.font_rid = sysf.rid;
+ }
+ break;
+ }
+ }
+ }
+ prev_font = gl.font_rid;
double scale = _font_get_scale(gl.font_rid, gl.font_size);
if (gl.font_rid.is_valid()) {
@@ -3893,6 +4121,17 @@ TextServerFallback::TextServerFallback() {
_insert_feature_sets();
};
+void TextServerFallback::_cleanup() {
+ for (const KeyValue<SystemFontKey, SystemFontCache> &E : system_fonts) {
+ const Vector<SystemFontCacheRec> &sysf_cache = E.value.var;
+ for (const SystemFontCacheRec &F : sysf_cache) {
+ _free_rid(F.rid);
+ }
+ }
+ system_fonts.clear();
+ system_font_data.clear();
+}
+
TextServerFallback::~TextServerFallback() {
#ifdef MODULE_FREETYPE_ENABLED
if (ft_library != nullptr) {
diff --git a/modules/text_server_fb/text_server_fb.h b/modules/text_server_fb/text_server_fb.h
index 7e0bc99618..f8a05f9433 100644
--- a/modules/text_server_fb/text_server_fb.h
+++ b/modules/text_server_fb/text_server_fb.h
@@ -256,6 +256,7 @@ class TextServerFallback : public TextServerExtension {
int msdf_source_size = 48;
int fixed_size = 0;
bool force_autohinter = false;
+ bool allow_system_fallback = true;
TextServer::Hinting hinting = TextServer::HINTING_LIGHT;
TextServer::SubpixelPositioning subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_AUTO;
Dictionary variation_coordinates;
@@ -266,6 +267,8 @@ class TextServerFallback : public TextServerExtension {
BitField<TextServer::FontStyle> style_flags = 0;
String font_name;
String style_name;
+ int weight = 400;
+ int stretch = 100;
HashMap<Vector2i, FontForSizeFallback *, VariantHasher, VariantComparator> cache;
@@ -322,6 +325,58 @@ class TextServerFallback : public TextServerExtension {
}
}
+ _FORCE_INLINE_ int _font_get_weight_by_name(const String &p_sty_name) const {
+ String sty_name = p_sty_name.replace(" ", "").replace("-", "");
+ if (sty_name.find("thin") >= 0 || sty_name.find("hairline") >= 0) {
+ return 100;
+ } else if (sty_name.find("extralight") >= 0 || sty_name.find("ultralight") >= 0) {
+ return 200;
+ } else if (sty_name.find("light") >= 0) {
+ return 300;
+ } else if (sty_name.find("semilight") >= 0) {
+ return 350;
+ } else if (sty_name.find("regular") >= 0) {
+ return 400;
+ } else if (sty_name.find("medium") >= 0) {
+ return 500;
+ } else if (sty_name.find("semibold") >= 0 || sty_name.find("demibold") >= 0) {
+ return 600;
+ } else if (sty_name.find("bold") >= 0) {
+ return 700;
+ } else if (sty_name.find("extrabold") >= 0 || sty_name.find("ultrabold") >= 0) {
+ return 800;
+ } else if (sty_name.find("black") >= 0 || sty_name.find("heavy") >= 0) {
+ return 900;
+ } else if (sty_name.find("extrablack") >= 0 || sty_name.find("ultrablack") >= 0) {
+ return 950;
+ }
+ return 400;
+ }
+ _FORCE_INLINE_ int _font_get_stretch_by_name(const String &p_sty_name) const {
+ String sty_name = p_sty_name.replace(" ", "").replace("-", "");
+ if (sty_name.find("ultracondensed") >= 0) {
+ return 50;
+ } else if (sty_name.find("extracondensed") >= 0) {
+ return 63;
+ } else if (sty_name.find("condensed") >= 0) {
+ return 75;
+ } else if (sty_name.find("semicondensed") >= 0) {
+ return 87;
+ } else if (sty_name.find("semiexpanded") >= 0) {
+ return 113;
+ } else if (sty_name.find("expanded") >= 0) {
+ return 125;
+ } else if (sty_name.find("extraexpanded") >= 0) {
+ return 150;
+ } else if (sty_name.find("ultraexpanded") >= 0) {
+ return 200;
+ }
+ return 100;
+ }
+ _FORCE_INLINE_ bool _is_ital_style(const String &p_sty_name) const {
+ return (p_sty_name.find("italic") >= 0) || (p_sty_name.find("oblique") >= 0);
+ }
+
// Shaped text cache data.
struct TrimData {
int trim_pos = -1;
@@ -398,6 +453,81 @@ class TextServerFallback : public TextServerExtension {
mutable RID_PtrOwner<FontFallback> font_owner;
mutable RID_PtrOwner<ShapedTextDataFallback> shaped_owner;
+ struct SystemFontKey {
+ String font_name;
+ TextServer::FontAntialiasing antialiasing = TextServer::FONT_ANTIALIASING_GRAY;
+ bool italic = false;
+ bool mipmaps = false;
+ bool msdf = false;
+ bool force_autohinter = false;
+ int weight = 400;
+ int stretch = 100;
+ int msdf_range = 14;
+ int msdf_source_size = 48;
+ int fixed_size = 0;
+ TextServer::Hinting hinting = TextServer::HINTING_LIGHT;
+ TextServer::SubpixelPositioning subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_AUTO;
+ Dictionary variation_coordinates;
+ double oversampling = 0.0;
+ double embolden = 0.0;
+ Transform2D transform;
+
+ bool operator==(const SystemFontKey &p_b) const {
+ return (font_name == p_b.font_name) && (antialiasing == p_b.antialiasing) && (italic == p_b.italic) && (mipmaps == p_b.mipmaps) && (msdf == p_b.msdf) && (force_autohinter == p_b.force_autohinter) && (weight == p_b.weight) && (stretch == p_b.stretch) && (msdf_range == p_b.msdf_range) && (msdf_source_size == p_b.msdf_source_size) && (fixed_size == p_b.fixed_size) && (hinting == p_b.hinting) && (subpixel_positioning == p_b.subpixel_positioning) && (variation_coordinates == p_b.variation_coordinates) && (oversampling == p_b.oversampling) && (embolden == p_b.embolden) && (transform == p_b.transform);
+ }
+
+ SystemFontKey(const String &p_font_name, bool p_italic, int p_weight, int p_stretch, RID p_font, const TextServerFallback *p_fb) {
+ font_name = p_font_name;
+ italic = p_italic;
+ weight = p_weight;
+ stretch = p_stretch;
+ antialiasing = p_fb->_font_get_antialiasing(p_font);
+ mipmaps = p_fb->_font_get_generate_mipmaps(p_font);
+ msdf = p_fb->_font_is_multichannel_signed_distance_field(p_font);
+ msdf_range = p_fb->_font_get_msdf_pixel_range(p_font);
+ msdf_source_size = p_fb->_font_get_msdf_size(p_font);
+ fixed_size = p_fb->_font_get_fixed_size(p_font);
+ force_autohinter = p_fb->_font_is_force_autohinter(p_font);
+ hinting = p_fb->_font_get_hinting(p_font);
+ subpixel_positioning = p_fb->_font_get_subpixel_positioning(p_font);
+ variation_coordinates = p_fb->_font_get_variation_coordinates(p_font);
+ oversampling = p_fb->_font_get_oversampling(p_font);
+ embolden = p_fb->_font_get_embolden(p_font);
+ transform = p_fb->_font_get_transform(p_font);
+ }
+ };
+
+ struct SystemFontCacheRec {
+ RID rid;
+ int index = 0;
+ };
+
+ struct SystemFontCache {
+ Vector<SystemFontCacheRec> var;
+ int max_var = 0;
+ };
+
+ struct SystemFontKeyHasher {
+ _FORCE_INLINE_ static uint32_t hash(const SystemFontKey &p_a) {
+ uint32_t hash = p_a.font_name.hash();
+ hash = hash_murmur3_one_32(p_a.variation_coordinates.hash(), hash);
+ hash = hash_murmur3_one_32(p_a.weight, hash);
+ hash = hash_murmur3_one_32(p_a.stretch, hash);
+ hash = hash_murmur3_one_32(p_a.msdf_range, hash);
+ hash = hash_murmur3_one_32(p_a.msdf_source_size, hash);
+ hash = hash_murmur3_one_32(p_a.fixed_size, hash);
+ hash = hash_murmur3_one_double(p_a.oversampling, hash);
+ hash = hash_murmur3_one_double(p_a.embolden, hash);
+ hash = hash_murmur3_one_real(p_a.transform[0].x, hash);
+ hash = hash_murmur3_one_real(p_a.transform[0].y, hash);
+ hash = hash_murmur3_one_real(p_a.transform[1].x, hash);
+ hash = hash_murmur3_one_real(p_a.transform[1].y, hash);
+ return hash_fmix32(hash_murmur3_one_32(((int)p_a.mipmaps) | ((int)p_a.msdf << 1) | ((int)p_a.italic << 2) | ((int)p_a.force_autohinter << 3) | ((int)p_a.hinting << 4) | ((int)p_a.subpixel_positioning << 8) | ((int)p_a.antialiasing << 12), hash));
+ }
+ };
+ mutable HashMap<SystemFontKey, SystemFontCache, SystemFontKeyHasher> system_fonts;
+ mutable HashMap<String, PackedByteArray> system_font_data;
+
void _realign(ShapedTextDataFallback *p_sd) const;
protected:
@@ -442,6 +572,12 @@ public:
MODBIND2(font_set_style_name, const RID &, const String &);
MODBIND1RC(String, font_get_style_name, const RID &);
+ MODBIND2(font_set_weight, const RID &, int64_t);
+ MODBIND1RC(int64_t, font_get_weight, const RID &);
+
+ MODBIND2(font_set_stretch, const RID &, int64_t);
+ MODBIND1RC(int64_t, font_get_stretch, const RID &);
+
MODBIND2(font_set_name, const RID &, const String &);
MODBIND1RC(String, font_get_name, const RID &);
@@ -463,6 +599,9 @@ public:
MODBIND2(font_set_fixed_size, const RID &, int64_t);
MODBIND1RC(int64_t, font_get_fixed_size, const RID &);
+ MODBIND2(font_set_allow_system_fallback, const RID &, bool);
+ MODBIND1RC(bool, font_is_allow_system_fallback, const RID &);
+
MODBIND2(font_set_force_autohinter, const RID &, bool);
MODBIND1RC(bool, font_is_force_autohinter, const RID &);
@@ -651,6 +790,8 @@ public:
MODBIND2RC(String, string_to_upper, const String &, const String &);
MODBIND2RC(String, string_to_lower, const String &, const String &);
+ MODBIND0(cleanup);
+
TextServerFallback();
~TextServerFallback();
};