Skip to content
This repository has been archived by the owner on Jun 10, 2024. It is now read-only.

Commit

Permalink
fix: wm notifications (#16)
Browse files Browse the repository at this point in the history
* feat: super objects

* fix: rename WM notifications

* chore: bump version
  • Loading branch information
jolexxa authored Nov 28, 2023
1 parent 64526be commit 4c2d0f5
Show file tree
Hide file tree
Showing 27 changed files with 991 additions and 214 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ SuperNodes is a C# source generator that gives superpowers to Godot node scripts

SuperNodes can do a LOT — [check out the official documentation][docs] for usage details.

- ✅ Apply PowerUps (essentially mixins for C#) to any class that extends a Godot `Node` or `GodotObject`.
- ✅ Apply PowerUps (essentially mixins for C#) to any class or record.
- ✅ PowerUps applied to a `GodotObject` or Godot `Node` can hook into the node's lifecycle, observing events and running code before user script callbacks.
- ✅ Use third-party source generators alongside Godot's official source generators.
- ✅ Get and set the value of script properties and fields at runtime, without using reflection.
- ✅ Examine the attributes and types of script properties and fields at runtime, without using reflection.
Expand Down
65 changes: 65 additions & 0 deletions SuperNodes.TestCases/test/test_cases/AllNotifications.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@

using Godot;
using SuperNodes.Types;

namespace AllNotifications;

[SuperNode]
public partial class Example : Node {
private void OnPostinitialize() { }
private void OnPredelete() { }
private void OnNotification(int what) { }
private void OnEnterTree() { }
// not working
private void OnWMWindowFocusIn() { }
// not working
private void OnWMWindowFocusOut() { }
// not working
private void OnWMCloseRequest() { }
// not working
private void OnWMSizeChanged() { }
// not working
private void OnWMDpiChange() { }
private void OnVpMouseEnter() { }
private void OnVpMouseExit() { }
private void OnOsMemoryWarning() { }
private void OnTranslationChanged() { }
// not working
private void OnWMAbout() { }
private void OnCrash() { }
private void OnOsImeUpdate() { }
private void OnApplicationResumed() { }
private void OnApplicationPaused() { }
private void OnApplicationFocusIn() { }
private void OnApplicationFocusOut() { }
private void OnTextServerChanged() { }
// not working
private void OnWMMouseExit() { }
// not working
private void OnWMMouseEnter() { }
// not working
private void OnWMGoBackRequest() { }
private void OnEditorPreSave() { }
private void OnExitTree() { }
private void OnMovedInParent() { }
private void OnReady() { }
private void OnEditorPostSave() { }
private void OnUnpaused() { }
private void OnPhysicsProcess(double delta) { }
private void OnProcess(double delta) { }
private void OnParented() { }
private void OnUnparented() { }
private void OnPaused() { }
private void OnDragBegin() { }
private void OnDragEnd() { }
private void OnPathRenamed() { }
private void OnInternalProcess() { }
private void OnInternalPhysicsProcess() { }
private void OnPostEnterTree() { }
private void OnDisabled() { }
private void OnEnabled() { }
private void OnSceneInstantiated() { }


public override partial void _Notification(int what);
}
48 changes: 48 additions & 0 deletions SuperNodes.TestCases/test/test_cases/PlainOldSuperObjectTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
namespace PlainOldSuperObjectTest;

using System;
using Chickensoft.GoDotTest;
using Godot;
using Shouldly;
using SuperNodes.Types;

public record CommonBaseType { }
public interface IGreeter { }

[SuperObject(typeof(Greeter))]
public partial record MyPlainOldRecordType : CommonBaseType { }

public partial class GrandparentType<T> {
public partial class ParentType {
[SuperObject(typeof(Greeter))]
public partial record MyNestedPlainOldRecordType : CommonBaseType { }
}
}

[PowerUp]
public record Greeter : CommonBaseType, IGreeter {
public void Greet() => Console.WriteLine("Hello, world!");
}

public class PlainOldSuperObjectTest : TestClass {
public PlainOldSuperObjectTest(Node testScene) : base(testScene) { }

[Test]
public void PlainOldObjectsWorkAsSuperObjects() {
var obj = new MyPlainOldRecordType();

obj.ShouldBeOfType<MyPlainOldRecordType>();
obj.ShouldBeAssignableTo<IGreeter>();
}

[Test]
public void NestedPlainOldObjectsWorkAsSuperObjects() {
var obj =
new GrandparentType<string>.ParentType.MyNestedPlainOldRecordType();

obj.ShouldBeOfType<
GrandparentType<string>.ParentType.MyNestedPlainOldRecordType
>();
obj.ShouldBeAssignableTo<IGreeter>();
}
}
2 changes: 1 addition & 1 deletion SuperNodes.Tests/reports/branch_coverage.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion SuperNodes.Tests/reports/line_coverage.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
31 changes: 21 additions & 10 deletions SuperNodes.Tests/tests/PowerUpsFeature/PowerUpGeneratorTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,15 @@ public void OnTestPowerUp(int what) {
HasPartialNotificationMethod: true,
HasOnNotificationMethodHandler: true,
PropsAndFields: ImmutableArray<PropOrField>.Empty,
Usings: ImmutableHashSet<string>.Empty
Usings: ImmutableHashSet<string>.Empty,
ContainingTypes: new ContainingType[] {
new ContainingType(
FullName: "TestContainingType",
Kind: ContainingTypeKind.Class,
Accessibility: Accessibility.Public,
IsPartial: true
)
}.ToImmutableArray()
);

var genService = new Mock<IPowerUpGeneratorService>();
Expand Down Expand Up @@ -136,20 +144,23 @@ public void OnTestPowerUp(int what) {
using SuperNodes.Types;

namespace Tests {
partial class TestSuperNode : global::Tests.ITestPowerUp<bool, int>
{
public string AddedProperty { get; set; } = "Property";
private readonly int _addedField = 10;
public static int MyNumber { get; set; } = 42;
public void OnTestPowerUp(int what)
public partial class TestContainingType {
partial class TestSuperNode : global::Tests.ITestPowerUp<bool, int>
{
TestSuperNode.MyNumber = 666;
if (what == NotificationReady)
public string AddedProperty { get; set; } = "Property";
private readonly int _addedField = 10;
public static int MyNumber { get; set; } = 42;
public void OnTestPowerUp(int what)
{
GD.Print("Hello, TestPowerUp!");
TestSuperNode.MyNumber = 666;
if (what == NotificationReady)
{
GD.Print("Hello, TestPowerUp!");
}
}
}
}

}
#nullable disable
#pragma warning restore
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public void GeneratesSuperNode() {
var notificationHandlers = ImmutableArray.Create<string>();
var powerUps = ImmutableDictionary.Create<string, PowerUp>();

var item = new GenerationItem(
var item = new SuperNodeGenerationItem(
SuperNode: new SuperNode(
Namespace: "global::Tests",
Name: "TestSuperNode",
Expand All @@ -41,7 +41,8 @@ public void GeneratesSuperNode() {
HasPartialNotificationMethod: true,
HasOnNotificationMethodHandler: true,
PropsAndFields: ImmutableArray<PropOrField>.Empty,
Usings: ImmutableHashSet<string>.Empty
Usings: ImmutableHashSet<string>.Empty,
ContainingTypes: ImmutableArray<ContainingType>.Empty
),
PowerUps: powerUps
);
Expand Down Expand Up @@ -73,7 +74,6 @@ public void GeneratesSuperNode() {
source.ShouldBe("""
#pragma warning disable
#nullable enable
using Godot;
using SuperNodes.Types;

namespace global::Tests {
Expand Down Expand Up @@ -194,10 +194,11 @@ public void GeneratesSuperNodeStaticReflectionTables() {
HasPartialNotificationMethod: true,
HasOnNotificationMethodHandler: true,
PropsAndFields: propsAndFields,
Usings: usings
Usings: usings,
ContainingTypes: ImmutableArray<ContainingType>.Empty
);

var item = new GenerationItem(
var item = new SuperNodeGenerationItem(
SuperNode: superNode,
PowerUps: powerUps
);
Expand Down Expand Up @@ -250,7 +251,7 @@ public void GeneratesSuperNodeStaticReflectionTables() {
)
).Returns(setPropertyOrFieldFn);

var source = generator.GenerateSuperNodeStatic(item, appliedPowerUps);
var source = generator.GenerateStaticReflection(item, appliedPowerUps);

source.ShouldBe("""
#pragma warning disable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ public void OnReady() { }
cs => cs.GetAttribute(symbol, Constants.SUPER_NODE_ATTRIBUTE_NAME_FULL)
).Returns((AttributeData?)null);

codeService.Setup(cs => cs.GetLifecycleHooks(null))
codeService.Setup(cs => cs.GetLifecycleHooks(null, true))
.Returns(LifecycleHooksResponse.Empty);

var members = ImmutableArray<ISymbol>.Empty;
Expand Down
8 changes: 7 additions & 1 deletion SuperNodes.Tests/tests/SuperNodesGeneratorTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,18 @@ public partial class GeneralFeatureSuperNode : Node {
public void OnReady() { }
}

[SuperObject(typeof(PlainGeneralFeaturePowerUp))]
public partial record PlainObject { }

[PowerUp]
public partial class GeneralFeaturePowerUp<T>
: Node, IGeneralFeaturePowerUp<T> {
T IGeneralFeaturePowerUp<T>.TestValue { get; } = default!;
}

[PowerUp]
public record PlainGeneralFeaturePowerUp { }

public interface IGeneralFeaturePowerUp<T> {
T TestValue { get; }
}
Expand All @@ -50,7 +56,7 @@ public void OtherGenerator(int what) { }
var result = Tester.Generate(source);

result.Diagnostics.ShouldBeEmpty();
result.Outputs.Count.ShouldBe(3);
result.Outputs.Count.ShouldBe(5);
}

[Fact]
Expand Down
71 changes: 71 additions & 0 deletions SuperNodes.Tests/tests/common/models/ContainingTypeTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
namespace SuperNodes.Tests.Common.Models;

using System;
using Shouldly;
using SuperNodes.Common.Models;
using Xunit;

public class ContainingTypeTest {
[Fact]
public void Initializes() {
var containingType = new ContainingType(
FullName: "TestClass",
Kind: ContainingTypeKind.Record,
Accessibility: Microsoft.CodeAnalysis.Accessibility.Public,
IsPartial: false
);

containingType.TypeDeclarationKeyword.ShouldBe("record");
containingType.AccessibilityKeywords.ShouldBe("public");
}

[Fact]
public void GetTypeDeclarationKeyword() {
ContainingType.GetTypeDeclarationKeyword(
ContainingTypeKind.Record
).ShouldBe("record");

ContainingType.GetTypeDeclarationKeyword(
ContainingTypeKind.Class
).ShouldBe("class");

Should.Throw<ArgumentException>(
() => ContainingType.GetTypeDeclarationKeyword(
(ContainingTypeKind)3
)
);
}

[Fact]
public void GetAccessibilityKeywords() {
ContainingType.GetAccessibilityKeywords(
Microsoft.CodeAnalysis.Accessibility.Public
).ShouldBe("public");

ContainingType.GetAccessibilityKeywords(
Microsoft.CodeAnalysis.Accessibility.Protected
).ShouldBe("protected");

ContainingType.GetAccessibilityKeywords(
Microsoft.CodeAnalysis.Accessibility.Internal
).ShouldBe("internal");

ContainingType.GetAccessibilityKeywords(
Microsoft.CodeAnalysis.Accessibility.ProtectedOrInternal
).ShouldBe("protected internal");

ContainingType.GetAccessibilityKeywords(
Microsoft.CodeAnalysis.Accessibility.Private
).ShouldBe("private");

ContainingType.GetAccessibilityKeywords(
Microsoft.CodeAnalysis.Accessibility.ProtectedAndInternal
).ShouldBe("private protected");

Should.Throw<ArgumentException>(
() => ContainingType.GetAccessibilityKeywords(
(Microsoft.CodeAnalysis.Accessibility)7
)
);
}
}
35 changes: 30 additions & 5 deletions SuperNodes.Tests/tests/common/models/GenerationItemTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,39 @@ public void Initializes() {
HasPartialNotificationMethod: false,
HasOnNotificationMethodHandler: false,
PropsAndFields: ImmutableArray<PropOrField>.Empty,
Usings: ImmutableHashSet<string>.Empty
Usings: ImmutableHashSet<string>.Empty,
ContainingTypes: ImmutableArray<ContainingType>.Empty
);

var superObject = new SuperObject(
Namespace: "global::Tests",
Name: "SuperObject",
NameWithoutGenerics: "SuperObject",
Location: new Mock<Location>().Object,
BaseClasses: new string[] { "global::Godot.Object" }.ToImmutableArray(),
LifecycleHooks: ImmutableArray<IGodotNodeLifecycleHook>.Empty,
PowerUpHooksByFullName: ImmutableDictionary<string, PowerUpHook>.Empty,
PropsAndFields: ImmutableArray<PropOrField>.Empty,
Usings: ImmutableHashSet<string>.Empty,
IsRecord: false,
ContainingTypes: ImmutableArray<ContainingType>.Empty
);
var powerUps = ImmutableDictionary<string, PowerUp>.Empty;

var generationItem = new GenerationItem(superNode, powerUps);
var superNodeGenerationItem =
new SuperNodeGenerationItem(superNode, powerUps);

var superObjectGenerationItem = new SuperObjectGenerationItem(
superObject,
powerUps
);

superNodeGenerationItem.ShouldBeOfType<SuperNodeGenerationItem>();
superNodeGenerationItem.SuperNode.ShouldBe(superNode);
superNodeGenerationItem.PowerUps.ShouldBe(powerUps);

generationItem.ShouldBeOfType<GenerationItem>();
generationItem.SuperNode.ShouldBe(superNode);
generationItem.PowerUps.ShouldBe(powerUps);
superObjectGenerationItem.ShouldBeOfType<SuperObjectGenerationItem>();
superObjectGenerationItem.SuperObject.ShouldBe(superObject);
superObjectGenerationItem.PowerUps.ShouldBe(powerUps);
}
}
Loading

0 comments on commit 4c2d0f5

Please sign in to comment.