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 cc45e5746fb..188972e6fe3 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); + } /// [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/ScriptSignalsGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs index 23253524ab7..0dda43ab4ce 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] @@ -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 d0adf39fb2b..62a9bf0b3d8 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -3104,6 +3104,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;