C#: Lookup signals and methods in Get method

Allows to retrieve `Callable`s and `Signal`s using `Get` like it works in GDScript.
This commit is contained in:
Raul Santos 2023-01-13 20:59:02 +01:00
parent 3c9bf4bc21
commit a6ba914f15
No known key found for this signature in database
GPG Key ID: B532473AE3A803E4
3 changed files with 90 additions and 0 deletions

View File

@ -272,6 +272,25 @@ namespace Godot.SourceGenerators
source.Append(" }\n");
}
// Generate HasGodotClassSignal
if (godotSignalDelegates.Count > 0)
{
source.Append(
" protected override bool HasGodotClassSignal(in godot_string_name signal)\n {\n");
bool isFirstEntry = true;
foreach (var signal in godotSignalDelegates)
{
GenerateHasSignalEntry(signal.Name, source, isFirstEntry);
isFirstEntry = false;
}
source.Append(" return base.HasGodotClassSignal(signal);\n");
source.Append(" }\n");
}
source.Append("}\n"); // partial class
if (isInnerClass)
@ -397,6 +416,20 @@ namespace Godot.SourceGenerators
PropertyHint.None, string.Empty, propUsage, exported: false);
}
private static void GenerateHasSignalEntry(
string signalName,
StringBuilder source,
bool isFirstEntry
)
{
source.Append(" ");
if (!isFirstEntry)
source.Append("else ");
source.Append("if (signal == SignalName.");
source.Append(signalName);
source.Append(") {\n return true;\n }\n");
}
private static void GenerateSignalEventInvoker(
GodotSignalDelegateData signal,
StringBuilder source

View File

@ -84,10 +84,12 @@ StringBuilder &operator<<(StringBuilder &r_sb, const char *p_cstring) {
#define CS_PROPERTY_SINGLETON "Singleton"
#define CS_METHOD_INVOKE_GODOT_CLASS_METHOD "InvokeGodotClassMethod"
#define CS_METHOD_HAS_GODOT_CLASS_METHOD "HasGodotClassMethod"
#define CS_METHOD_HAS_GODOT_CLASS_SIGNAL "HasGodotClassSignal"
#define CS_STATIC_FIELD_NATIVE_CTOR "NativeCtor"
#define CS_STATIC_FIELD_METHOD_BIND_PREFIX "MethodBind"
#define CS_STATIC_FIELD_METHOD_PROXY_NAME_PREFIX "MethodProxyName_"
#define CS_STATIC_FIELD_SIGNAL_PROXY_NAME_PREFIX "SignalProxyName_"
#define ICALL_PREFIX "godot_icall_"
#define ICALL_CLASSDB_GET_METHOD "ClassDB_get_method"
@ -1608,6 +1610,16 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
<< " = \"" << imethod.proxy_name << "\";\n";
}
// Generate signal names cache fields
for (const SignalInterface &isignal : itype.signals_) {
output << MEMBER_BEGIN "// ReSharper disable once InconsistentNaming\n"
<< INDENT1 "[DebuggerBrowsable(DebuggerBrowsableState.Never)]\n"
<< INDENT1 "private static readonly StringName "
<< CS_STATIC_FIELD_SIGNAL_PROXY_NAME_PREFIX << isignal.name
<< " = \"" << isignal.proxy_name << "\";\n";
}
// TODO: Only generate HasGodotClassMethod and InvokeGodotClassMethod if there's any method
// Generate InvokeGodotClassMethod
@ -1721,6 +1733,34 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
}
output << INDENT1 "}\n";
// Generate HasGodotClassSignal
output << MEMBER_BEGIN "protected internal " << (is_derived_type ? "override" : "virtual")
<< " bool " CS_METHOD_HAS_GODOT_CLASS_SIGNAL "(in godot_string_name signal)\n"
<< INDENT1 "{\n";
for (const SignalInterface &isignal : itype.signals_) {
// We check for native names (snake_case). If we detect one, we call HasGodotClassSignal
// again, but this time with the respective proxy name (PascalCase). It's the job of
// user derived classes to override the method and check for those. Our C# source
// generators take care of generating those override methods.
output << INDENT2 "if (signal == SignalName." << isignal.proxy_name
<< ")\n" INDENT2 "{\n"
<< INDENT3 "if (" CS_METHOD_HAS_GODOT_CLASS_SIGNAL "("
<< CS_STATIC_FIELD_SIGNAL_PROXY_NAME_PREFIX << isignal.name
<< ".NativeValue.DangerousSelfRef))\n" INDENT3 "{\n"
<< INDENT4 "return true;\n"
<< INDENT3 "}\n" INDENT2 "}\n";
}
if (is_derived_type) {
output << INDENT2 "return base." CS_METHOD_HAS_GODOT_CLASS_SIGNAL "(signal);\n";
} else {
output << INDENT2 "return false;\n";
}
output << INDENT1 "}\n";
}
//Generate StringName for all class members

View File

@ -84,12 +84,29 @@ namespace Godot.Bridge
if (godotObject == null)
throw new InvalidOperationException();
// Properties
if (godotObject.GetGodotClassPropertyValue(CustomUnsafe.AsRef(name), out godot_variant outRetValue))
{
*outRet = outRetValue;
return godot_bool.True;
}
// Signals
if (godotObject.HasGodotClassSignal(CustomUnsafe.AsRef(name)))
{
godot_signal signal = new godot_signal(*name, godotObject.GetInstanceId());
*outRet = VariantUtils.CreateFromSignalTakingOwnershipOfDisposableValue(signal);
return godot_bool.True;
}
// Methods
if (godotObject.HasGodotClassMethod(CustomUnsafe.AsRef(name)))
{
godot_callable method = new godot_callable(*name, godotObject.GetInstanceId());
*outRet = VariantUtils.CreateFromCallableTakingOwnershipOfDisposableValue(method);
return godot_bool.True;
}
var nameManaged = StringName.CreateTakingOwnershipOfDisposableValue(
NativeFuncs.godotsharp_string_name_new_copy(CustomUnsafe.AsRef(name)));