Skip to content

Commit

Permalink
Allow exporting generic properties
Browse files Browse the repository at this point in the history
  • Loading branch information
nrgill28 committed May 26, 2024
1 parent be56cab commit e81ac44
Show file tree
Hide file tree
Showing 22 changed files with 672 additions and 140 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/static_checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
fi
echo "$files" >> changed.txt
cat changed.txt
files=$(echo "$files" | grep -v 'thirdparty' | xargs -I {} sh -c 'echo "./{}"' | tr '\n' ' ')
files=$(echo "$files" | grep -v 'thirdparty' | xargs -I {} sh -c 'echo "\"./{}\""' | tr '\n' ' ')
echo "CHANGED_FILES=$files" >> $GITHUB_ENV
# This needs to happen before Python and npm execution; it must happen before any extra files are written.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using Godot.Collections;
using System;

namespace Godot.SourceGenerators.Sample;

public partial class GenericExports<[MustBeVariant] T> : GodotObject
{
[Export] public T RegularField;
[Export] public T RegularProperty { get; set; }

[Export] public Array<T> ArrayProperty { get; set; }

[Signal]
public delegate T GenericSignalEventHandler(T var);

public T GenericMethod(T var)
{
return var;
}
}

public partial class GenericExportsVector2 : GenericExports<Vector2>
{
}

public partial class GenericExportsRect2 : GenericExports<Rect2>
{
}

public partial class GenericExportsMultiple<TSome, [MustBeVariant] TOther> : GodotObject
{
[Export] public Array<TOther> ArrayExport { get; set; }

// This is not valid because TSome is not [MustBeVariant]
// [Export] public TSome AnotherArray { get; set; }

// You can still use TSome, just not exported.
public TSome NonExportField;
}

// Doesn't require the [MustBeVariant] attribute because T is constrained to GodotObject or Enum already.
public partial class InferredVariantConstraintGodotObject<T> : GodotObject
where T : GodotObject
{
[Export] public T Exported;
}

public partial class InferredVariantConstraintEnum<T> : GodotObject
where T : Enum
{
[Export] public T Exported;
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@ await CSharpSourceGeneratorVerifier<ScriptMethodsGenerator>.Verify(
);
}

[Fact]
public async void Generic()
{
await CSharpSourceGeneratorVerifier<ScriptMethodsGenerator>.Verify(
"Generic.cs",
"Generic_T_ScriptMethods.generated.cs"
);
}

[Fact]
public async void ScriptBoilerplate()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public async void Generic()
{
var verifier = CSharpSourceGeneratorVerifier<ScriptPathAttributeGenerator>.MakeVerifier(
new string[] { "Generic.cs" },
new string[] { "Generic(Of T)_ScriptPath.generated.cs" }
new string[] { "Generic_T_ScriptPath.generated.cs" }
);
verifier.TestState.GeneratedSources.Add(MakeAssemblyScriptTypesGeneratedSource(new string[] { "global::Generic<>" }));
await verifier.RunAsync();
Expand All @@ -59,7 +59,7 @@ public async void GenericMultipleClassesSameName()
{
var verifier = CSharpSourceGeneratorVerifier<ScriptPathAttributeGenerator>.MakeVerifier(
Array.Empty<string>(),
new string[] { "Generic(Of T)_ScriptPath.generated.cs" }
new string[] { "Generic_T_ScriptPath.generated.cs" }
);
verifier.TestState.Sources.Add(("Generic.cs", File.ReadAllText(Path.Combine(Constants.SourceFolderPath, "Generic.GD0003.cs"))));
verifier.TestState.GeneratedSources.Add(MakeAssemblyScriptTypesGeneratedSource(new string[] { "global::Generic<>", "global::Generic<,>", "global::Generic" }));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,16 @@ public async void AbstractGenericNode()
{
await CSharpSourceGeneratorVerifier<ScriptPropertiesGenerator>.Verify(
"AbstractGenericNode.cs",
"AbstractGenericNode(Of T)_ScriptProperties.generated.cs"
"AbstractGenericNode_T_ScriptProperties.generated.cs"
);
}

[Fact]
public async void Generic()
{
await CSharpSourceGeneratorVerifier<ScriptPropertiesGenerator>.Verify(
"Generic.cs",
"Generic_T_ScriptProperties.generated.cs"
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,13 @@ await CSharpSourceGeneratorVerifier<ScriptSignalsGenerator>.Verify(
"EventSignals_ScriptSignals.generated.cs"
);
}

[Fact]
public async void Generic()
{
await CSharpSourceGeneratorVerifier<ScriptSignalsGenerator>.Verify(
"Generic.cs",
"Generic_T_ScriptSignals.generated.cs"
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ protected override bool GetGodotClassPropertyValue(in godot_string_name name, ou
internal new static global::System.Collections.Generic.List<global::Godot.Bridge.PropertyInfo> GetGodotPropertyList()
{
var properties = new global::System.Collections.Generic.List<global::Godot.Bridge.PropertyInfo>();
properties.Add(new(type: (global::Godot.Variant.Type)28, name: PropertyName.MyArray, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true));
properties.Add(global::Godot.Bridge.GenericUtils.PropertyInfoFromGenericType<global::Godot.Collections.Array<T>>(name: PropertyName.MyArray, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true));
return properties;
}
#pragma warning restore CS0109
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using Godot;
using Godot.NativeInterop;

partial class Generic<T>
{
#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword
/// <summary>
/// Cached StringNames for the methods contained in this class, for fast lookup.
/// </summary>
public new class MethodName : global::Godot.GodotObject.MethodName {
/// <summary>
/// Cached name for the 'GenericMethod' method.
/// </summary>
public new static readonly global::Godot.StringName GenericMethod = "GenericMethod";
}
/// <summary>
/// Get the method information for all the methods declared in this class.
/// This method is used by Godot to register the available methods in the editor.
/// Do not call this method.
/// </summary>
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
internal new static global::System.Collections.Generic.List<global::Godot.Bridge.MethodInfo> GetGodotMethodList()
{
var methods = new global::System.Collections.Generic.List<global::Godot.Bridge.MethodInfo>(1);
methods.Add(new(name: MethodName.GenericMethod, returnVal: global::Godot.Bridge.GenericUtils.PropertyInfoFromGenericType<T>(name: "", hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)6, exported: false), flags: (global::Godot.MethodFlags)1, arguments: new() { global::Godot.Bridge.GenericUtils.PropertyInfoFromGenericType<T>(name: "var", hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)6, exported: false), }, defaultArguments: null));
return methods;
}
#pragma warning restore CS0109
/// <inheritdoc/>
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
protected override bool InvokeGodotClassMethod(in godot_string_name method, NativeVariantPtrArgs args, out godot_variant ret)
{
if (method == MethodName.GenericMethod && args.Count == 1) {
var callRet = GenericMethod(global::Godot.NativeInterop.VariantUtils.ConvertTo<T>(args[0]));
ret = global::Godot.NativeInterop.VariantUtils.CreateFrom<T>(callRet);
return true;
}
return base.InvokeGodotClassMethod(method, args, out ret);
}
/// <inheritdoc/>
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
protected override bool HasGodotClassMethod(in godot_string_name method)
{
if (method == MethodName.GenericMethod) {
return true;
}
return base.HasGodotClassMethod(method);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using Godot;
using Godot.NativeInterop;

partial class Generic<T>
{
#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword
/// <summary>
/// Cached StringNames for the properties and fields contained in this class, for fast lookup.
/// </summary>
public new class PropertyName : global::Godot.GodotObject.PropertyName {
/// <summary>
/// Cached name for the 'RegularProperty' property.
/// </summary>
public new static readonly global::Godot.StringName RegularProperty = "RegularProperty";
/// <summary>
/// Cached name for the 'ArrayProperty' property.
/// </summary>
public new static readonly global::Godot.StringName ArrayProperty = "ArrayProperty";
/// <summary>
/// Cached name for the 'RegularField' field.
/// </summary>
public new static readonly global::Godot.StringName RegularField = "RegularField";
}
/// <inheritdoc/>
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
protected override bool SetGodotClassPropertyValue(in godot_string_name name, in godot_variant value)
{
if (name == PropertyName.RegularProperty) {
this.RegularProperty = global::Godot.NativeInterop.VariantUtils.ConvertTo<T>(value);
return true;
}
if (name == PropertyName.ArrayProperty) {
this.ArrayProperty = global::Godot.NativeInterop.VariantUtils.ConvertToArray<T>(value);
return true;
}
if (name == PropertyName.RegularField) {
this.RegularField = global::Godot.NativeInterop.VariantUtils.ConvertTo<T>(value);
return true;
}
return base.SetGodotClassPropertyValue(name, value);
}
/// <inheritdoc/>
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
protected override bool GetGodotClassPropertyValue(in godot_string_name name, out godot_variant value)
{
if (name == PropertyName.RegularProperty) {
value = global::Godot.NativeInterop.VariantUtils.CreateFrom<T>(this.RegularProperty);
return true;
}
if (name == PropertyName.ArrayProperty) {
value = global::Godot.NativeInterop.VariantUtils.CreateFromArray(this.ArrayProperty);
return true;
}
if (name == PropertyName.RegularField) {
value = global::Godot.NativeInterop.VariantUtils.CreateFrom<T>(this.RegularField);
return true;
}
return base.GetGodotClassPropertyValue(name, out value);
}
/// <summary>
/// Get the property information for all the properties declared in this class.
/// This method is used by Godot to register the available properties in the editor.
/// Do not call this method.
/// </summary>
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
internal new static global::System.Collections.Generic.List<global::Godot.Bridge.PropertyInfo> GetGodotPropertyList()
{
var properties = new global::System.Collections.Generic.List<global::Godot.Bridge.PropertyInfo>();
properties.Add(global::Godot.Bridge.GenericUtils.PropertyInfoFromGenericType<T>(name: PropertyName.RegularField, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true));
properties.Add(global::Godot.Bridge.GenericUtils.PropertyInfoFromGenericType<T>(name: PropertyName.RegularProperty, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true));
properties.Add(global::Godot.Bridge.GenericUtils.PropertyInfoFromGenericType<global::Godot.Collections.Array<T>>(name: PropertyName.ArrayProperty, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true));
return properties;
}
#pragma warning restore CS0109
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using Godot;
using Godot.NativeInterop;

partial class Generic<T>
{
#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword
/// <summary>
/// Cached StringNames for the signals contained in this class, for fast lookup.
/// </summary>
public new class SignalName : global::Godot.GodotObject.SignalName {
/// <summary>
/// Cached name for the 'GenericSignal' signal.
/// </summary>
public new static readonly global::Godot.StringName GenericSignal = "GenericSignal";
}
/// <summary>
/// Get the signal information for all the signals declared in this class.
/// This method is used by Godot to register the available signals in the editor.
/// Do not call this method.
/// </summary>
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
internal new static global::System.Collections.Generic.List<global::Godot.Bridge.MethodInfo> GetGodotSignalList()
{
var signals = new global::System.Collections.Generic.List<global::Godot.Bridge.MethodInfo>(1);
signals.Add(new(name: SignalName.GenericSignal, returnVal: global::Godot.Bridge.GenericUtils.PropertyInfoFromGenericType<T>(name: "", hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)6, exported: false), flags: (global::Godot.MethodFlags)1, arguments: new() { global::Godot.Bridge.GenericUtils.PropertyInfoFromGenericType<T>(name: "var", hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)6, exported: false), }, defaultArguments: null));
return signals;
}
#pragma warning restore CS0109
private global::Generic<T>.GenericSignalEventHandler backing_GenericSignal;
/// <inheritdoc cref="global::Generic{T}.GenericSignalEventHandler"/>
public event global::Generic<T>.GenericSignalEventHandler GenericSignal {
add => backing_GenericSignal += value;
remove => backing_GenericSignal -= value;
}
/// <inheritdoc/>
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
protected override void RaiseGodotClassSignalCallbacks(in godot_string_name signal, NativeVariantPtrArgs args)
{
if (signal == SignalName.GenericSignal && args.Count == 1) {
backing_GenericSignal?.Invoke(global::Godot.NativeInterop.VariantUtils.ConvertTo<T>(args[0]));
return;
}
base.RaiseGodotClassSignalCallbacks(signal, args);
}
/// <inheritdoc/>
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
protected override bool HasGodotClassSignal(in godot_string_name signal)
{
if (signal == SignalName.GenericSignal) {
return true;
}
return base.HasGodotClassSignal(signal);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
using Godot;
using Godot.Collections;

public partial class Generic<T> : GodotObject
public partial class Generic<[MustBeVariant] T> : GodotObject
{
private int _field;
[Export] public T RegularField;
[Export] public T RegularProperty { get; set; }

[Export] public Array<T> ArrayProperty { get; set; }

[Signal]
public delegate T GenericSignalEventHandler(T var);

public T GenericMethod(T var)
{
return var;
}
}
Loading

0 comments on commit e81ac44

Please sign in to comment.