summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRémi Verschelde <rverschelde@gmail.com>2024-09-27 13:53:11 +0200
committerRémi Verschelde <rverschelde@gmail.com>2024-09-27 13:53:11 +0200
commit543fa16b4ce821a0118da3ef0904a105aaa046e9 (patch)
treead25bcdfee152e36803555033370accbd0793a84
parent506d6e427a4eecfc1e5e5ee1180019a876119701 (diff)
parentda37998dc84dcf05c91fdfe7481445c0cb10af2e (diff)
downloadredot-engine-543fa16b4ce821a0118da3ef0904a105aaa046e9.tar.gz
Merge pull request #68233 from raulsntos/dotnet/raise-events
C#: Generate strongly-typed method to raise signal events and fix event accessibility
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/EventSignals_ScriptSignals.generated.cs4
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs26
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs46
-rw-r--r--modules/mono/editor/bindings_generator.cpp40
4 files changed, 108 insertions, 8 deletions
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/EventSignals_ScriptSignals.generated.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/EventSignals_ScriptSignals.generated.cs
index cc45e5746f..188972e6fe 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/EventSignals_ScriptSignals.generated.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/EventSignals_ScriptSignals.generated.cs
@@ -32,6 +32,10 @@ partial class EventSignals
add => backing_MySignal += value;
remove => backing_MySignal -= value;
}
+ protected void OnMySignal(string str, int num)
+ {
+ EmitSignal(SignalName.MySignal, str, num);
+ }
/// <inheritdoc/>
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
protected override void RaiseGodotClassSignalCallbacks(in godot_string_name signal, NativeVariantPtrArgs args)
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 957d5789df..62fa7b0a36 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs
@@ -155,6 +155,32 @@ namespace Godot.SourceGenerators
};
}
+ public static string GetAccessibilityKeyword(this INamedTypeSymbol namedTypeSymbol)
+ {
+ if (namedTypeSymbol.DeclaredAccessibility == Accessibility.NotApplicable)
+ {
+ // Accessibility not specified. Get the default accessibility.
+ return namedTypeSymbol.ContainingSymbol switch
+ {
+ null or INamespaceSymbol => "internal",
+ ITypeSymbol { TypeKind: TypeKind.Class or TypeKind.Struct } => "private",
+ ITypeSymbol { TypeKind: TypeKind.Interface } => "public",
+ _ => "",
+ };
+ }
+
+ return namedTypeSymbol.DeclaredAccessibility switch
+ {
+ Accessibility.Private => "private",
+ Accessibility.Protected => "protected",
+ Accessibility.Internal => "internal",
+ Accessibility.ProtectedAndInternal => "private",
+ Accessibility.ProtectedOrInternal => "private",
+ Accessibility.Public => "public",
+ _ => "",
+ };
+ }
+
public static string NameWithTypeParameters(this INamedTypeSymbol symbol)
{
return symbol.IsGenericType ?
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 54f2212339..0dda43ab4c 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs
@@ -5,13 +5,6 @@ using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
-// TODO:
-// Determine a proper way to emit the signal.
-// 'Emit(nameof(TheEvent))' creates a StringName every time and has the overhead of string marshaling.
-// I haven't decided on the best option yet. Some possibilities:
-// - Expose the generated StringName fields to the user, for use with 'Emit(...)'.
-// - Generate a 'EmitSignalName' method for each event signal.
-
namespace Godot.SourceGenerators
{
[Generator]
@@ -276,7 +269,7 @@ namespace Godot.SourceGenerators
source.Append(
$" /// <inheritdoc cref=\"{signalDelegate.DelegateSymbol.FullQualifiedNameIncludeGlobal()}\"/>\n");
- source.Append(" public event ")
+ source.Append($" {signalDelegate.DelegateSymbol.GetAccessibilityKeyword()} event ")
.Append(signalDelegate.DelegateSymbol.FullQualifiedNameIncludeGlobal())
.Append(" @")
.Append(signalName)
@@ -288,6 +281,43 @@ namespace Godot.SourceGenerators
.Append(signalName)
.Append(" -= value;\n")
.Append("}\n");
+
+ // Generate On{EventName} method to raise the event
+
+ var invokeMethodSymbol = signalDelegate.InvokeMethodData.Method;
+ int paramCount = invokeMethodSymbol.Parameters.Length;
+
+ string raiseMethodModifiers = signalDelegate.DelegateSymbol.ContainingType.IsSealed ?
+ "private" :
+ "protected";
+
+ source.Append($" {raiseMethodModifiers} void On{signalName}(");
+ for (int i = 0; i < paramCount; i++)
+ {
+ var paramSymbol = invokeMethodSymbol.Parameters[i];
+ source.Append($"{paramSymbol.Type.FullQualifiedNameIncludeGlobal()} {paramSymbol.Name}");
+ if (i < paramCount - 1)
+ {
+ source.Append(", ");
+ }
+ }
+ source.Append(")\n");
+ source.Append(" {\n");
+ source.Append($" EmitSignal(SignalName.{signalName}");
+ foreach (var paramSymbol in invokeMethodSymbol.Parameters)
+ {
+ // Enums must be converted to the underlying type before they can be implicitly converted to Variant
+ if (paramSymbol.Type.TypeKind == TypeKind.Enum)
+ {
+ var underlyingType = ((INamedTypeSymbol)paramSymbol.Type).EnumUnderlyingType;
+ source.Append($", ({underlyingType.FullQualifiedNameIncludeGlobal()}){paramSymbol.Name}");
+ continue;
+ }
+
+ source.Append($", {paramSymbol.Name}");
+ }
+ source.Append(");\n");
+ source.Append(" }\n");
}
// Generate RaiseGodotClassSignalCallbacks
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index a467aae2e9..89655e0b56 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -3227,6 +3227,46 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf
}
p_output.append(CLOSE_BLOCK_L1);
+
+ // Generate On{EventName} method to raise the event.
+ if (!p_itype.is_singleton) {
+ p_output.append(MEMBER_BEGIN "protected void ");
+ p_output << "On" << p_isignal.proxy_name;
+ if (is_parameterless) {
+ p_output.append("()\n" OPEN_BLOCK_L1 INDENT2);
+ p_output << "EmitSignal(SignalName." << p_isignal.proxy_name << ");\n";
+ p_output.append(CLOSE_BLOCK_L1);
+ } else {
+ p_output.append("(");
+
+ StringBuilder cs_emitsignal_params;
+
+ int idx = 0;
+ for (const ArgumentInterface &iarg : p_isignal.arguments) {
+ const TypeInterface *arg_type = _get_type_or_null(iarg.type);
+ ERR_FAIL_NULL_V_MSG(arg_type, ERR_BUG, "Argument type '" + iarg.type.cname + "' was not found.");
+
+ if (idx != 0) {
+ p_output << ", ";
+ cs_emitsignal_params << ", ";
+ }
+
+ p_output << arg_type->cs_type << " " << iarg.name;
+
+ if (arg_type->is_enum) {
+ cs_emitsignal_params << "(long)";
+ }
+
+ cs_emitsignal_params << iarg.name;
+
+ idx++;
+ }
+
+ p_output.append(")\n" OPEN_BLOCK_L1 INDENT2);
+ p_output << "EmitSignal(SignalName." << p_isignal.proxy_name << ", " << cs_emitsignal_params << ");\n";
+ p_output.append(CLOSE_BLOCK_L1);
+ }
+ }
}
return OK;