From a19e8fb1eea9f83cbe4af16c61affa8ab31c428e Mon Sep 17 00:00:00 2001
From: badcel <1218031+badcel@users.noreply.github.com>
Date: Tue, 21 Jan 2025 21:55:20 +0100
Subject: [PATCH] Add GObject-2.0.Integration.csproj
---
.../GObject-2.0.Integration.csproj | 22 +++++++
.../Integration/Generator.cs | 12 ++++
.../Integration/Subclass.cs | 21 +++++++
.../Integration/Subclass/SubclassAttribute.cs | 15 +++++
.../Integration/Subclass/SubclassCode.cs | 40 +++++++++++++
.../Integration/Subclass/SubclassData.cs | 3 +
.../Subclass/SubclassValuesProvider.cs | 28 +++++++++
.../Properties/launchSettings.json | 9 +++
src/GirCore.sln | 15 +++++
.../GridView/CustomObjectGridViewWindow.cs | 12 +---
src/Samples/Gtk-4.0/GridView/GridView.csproj | 1 +
.../GirTest-0.1.Tests.csproj | 3 +
.../SubclassIntegrationTest.cs | 58 +++++++++++++++++++
13 files changed, 229 insertions(+), 10 deletions(-)
create mode 100644 src/Extensions/GObject-2.0.Integration/GObject-2.0.Integration.csproj
create mode 100644 src/Extensions/GObject-2.0.Integration/Integration/Generator.cs
create mode 100644 src/Extensions/GObject-2.0.Integration/Integration/Subclass.cs
create mode 100644 src/Extensions/GObject-2.0.Integration/Integration/Subclass/SubclassAttribute.cs
create mode 100644 src/Extensions/GObject-2.0.Integration/Integration/Subclass/SubclassCode.cs
create mode 100644 src/Extensions/GObject-2.0.Integration/Integration/Subclass/SubclassData.cs
create mode 100644 src/Extensions/GObject-2.0.Integration/Integration/Subclass/SubclassValuesProvider.cs
create mode 100644 src/Extensions/GObject-2.0.Integration/Properties/launchSettings.json
create mode 100644 src/Tests/Libs/GirTest-0.1.Tests/SubclassIntegrationTest.cs
diff --git a/src/Extensions/GObject-2.0.Integration/GObject-2.0.Integration.csproj b/src/Extensions/GObject-2.0.Integration/GObject-2.0.Integration.csproj
new file mode 100644
index 000000000..bc06ed9b0
--- /dev/null
+++ b/src/Extensions/GObject-2.0.Integration/GObject-2.0.Integration.csproj
@@ -0,0 +1,22 @@
+
+
+
+ GirCore.GObject-2.0.Integration
+ GObject.Integration
+ Source Generators to make it easy to integrate C# with the GObject type system.
+
+ false
+ true
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Extensions/GObject-2.0.Integration/Integration/Generator.cs b/src/Extensions/GObject-2.0.Integration/Integration/Generator.cs
new file mode 100644
index 000000000..f1f0f70a4
--- /dev/null
+++ b/src/Extensions/GObject-2.0.Integration/Integration/Generator.cs
@@ -0,0 +1,12 @@
+using Microsoft.CodeAnalysis;
+
+namespace GObject.Integration;
+
+[Generator]
+public class Generator : IIncrementalGenerator
+{
+ public void Initialize(IncrementalGeneratorInitializationContext context)
+ {
+ context.EnableSubclassSupport();
+ }
+}
\ No newline at end of file
diff --git a/src/Extensions/GObject-2.0.Integration/Integration/Subclass.cs b/src/Extensions/GObject-2.0.Integration/Integration/Subclass.cs
new file mode 100644
index 000000000..6bda37516
--- /dev/null
+++ b/src/Extensions/GObject-2.0.Integration/Integration/Subclass.cs
@@ -0,0 +1,21 @@
+using Microsoft.CodeAnalysis;
+
+namespace GObject.Integration;
+
+public static class Subclass
+{
+ public static void EnableSubclassSupport(this IncrementalGeneratorInitializationContext context)
+ {
+ //https://andrewlock.net/creating-a-source-generator-part-1-creating-an-incremental-source-generator/
+
+ context.RegisterPostInitializationOutput(ctx => ctx.AddSource(
+ hintName: SubclassAttribute.FileName,
+ source: SubclassAttribute.Code
+ ));
+
+ context.RegisterImplementationSourceOutput(
+ source: context.GetSubclassValuesProvider(),
+ action: SubclassCode.Generate
+ );
+ }
+}
\ No newline at end of file
diff --git a/src/Extensions/GObject-2.0.Integration/Integration/Subclass/SubclassAttribute.cs b/src/Extensions/GObject-2.0.Integration/Integration/Subclass/SubclassAttribute.cs
new file mode 100644
index 000000000..4aaf044e6
--- /dev/null
+++ b/src/Extensions/GObject-2.0.Integration/Integration/Subclass/SubclassAttribute.cs
@@ -0,0 +1,15 @@
+namespace GObject.Integration;
+
+public static class SubclassAttribute
+{
+ public const string FullyQualifiedName = "GObject.SubclassAttribute";
+
+ public const string FileName = "SubclassAttribute.g.cs";
+
+ public const string Code = """
+ namespace GObject;
+
+ [System.AttributeUsage(System.AttributeTargets.Class)]
+ public class SubclassAttribute : System.Attribute { }
+ """;
+}
\ No newline at end of file
diff --git a/src/Extensions/GObject-2.0.Integration/Integration/Subclass/SubclassCode.cs b/src/Extensions/GObject-2.0.Integration/Integration/Subclass/SubclassCode.cs
new file mode 100644
index 000000000..e0c4438a7
--- /dev/null
+++ b/src/Extensions/GObject-2.0.Integration/Integration/Subclass/SubclassCode.cs
@@ -0,0 +1,40 @@
+using Microsoft.CodeAnalysis;
+
+namespace GObject.Integration;
+
+public static class SubclassCode
+{
+ public static void Generate(SourceProductionContext context, SubclassData subclassData)
+ {
+ context.AddSource(
+ hintName:$"{subclassData.Name}.Subclass.g.cs",
+ source: ToCode(subclassData)
+ );
+ }
+ private static string ToCode(SubclassData subclassData)
+ {
+ return $$"""
+ namespace GridViewSample;
+
+ //TODO: HIER MUSS EIN Primary Constructor rein, der ein Handle braucht und das an die Base weitergibt
+ //public class My(AboutDialogHandle handle) : AboutDialog(handle)
+ //{
+ // public My(params GObject.ConstructArgument[] constructArguments) : this(Gtk.Internal.AboutDialogHandle.For(false, constructArguments)) { }
+ // Dann kann ein User den Constructor so schreiben:
+ // public My(int a) : this()
+
+ public partial class {{subclassData.Name}} : GObject.GTypeProvider, GObject.InstanceFactory
+ {
+ private static readonly GObject.Type GType = GObject.Internal.SubclassRegistrar.Register<{{subclassData.Name}}, GObject.Object>();
+ public static new GObject.Type GetGType() => GType;
+
+ static object GObject.InstanceFactory.Create(System.IntPtr handle, bool ownsHandle)
+ {
+ return new {{subclassData.Name}}(handle, ownsHandle);
+ }
+
+ private {{subclassData.Name}}(System.IntPtr ptr, bool ownsHandle) : base(new GObject.Internal.ObjectHandle(ptr, ownsHandle)) { }
+ }
+ """;
+ }
+}
\ No newline at end of file
diff --git a/src/Extensions/GObject-2.0.Integration/Integration/Subclass/SubclassData.cs b/src/Extensions/GObject-2.0.Integration/Integration/Subclass/SubclassData.cs
new file mode 100644
index 000000000..a359b09e7
--- /dev/null
+++ b/src/Extensions/GObject-2.0.Integration/Integration/Subclass/SubclassData.cs
@@ -0,0 +1,3 @@
+namespace GObject.Integration;
+
+public record SubclassData(string Name, string Parent);
\ No newline at end of file
diff --git a/src/Extensions/GObject-2.0.Integration/Integration/Subclass/SubclassValuesProvider.cs b/src/Extensions/GObject-2.0.Integration/Integration/Subclass/SubclassValuesProvider.cs
new file mode 100644
index 000000000..cce40c0b7
--- /dev/null
+++ b/src/Extensions/GObject-2.0.Integration/Integration/Subclass/SubclassValuesProvider.cs
@@ -0,0 +1,28 @@
+using System.Threading;
+using Microsoft.CodeAnalysis;
+
+namespace GObject.Integration;
+
+public static class SubclassValuesProvider
+{
+ public static IncrementalValuesProvider GetSubclassValuesProvider(this IncrementalGeneratorInitializationContext context)
+ {
+ return context.SyntaxProvider
+ .ForAttributeWithMetadataName(
+ fullyQualifiedMetadataName: SubclassAttribute.FullyQualifiedName,
+ predicate: static (_, _) => true,
+ transform: GetSubclassData)
+ .Where(data => data is not null)!;
+ }
+
+ private static SubclassData? GetSubclassData(GeneratorAttributeSyntaxContext context, CancellationToken cancellationToken)
+ {
+ var semanticModel = context.SemanticModel;
+ var enumDeclarationSyntax = context.TargetNode;
+
+ if (semanticModel.GetDeclaredSymbol(enumDeclarationSyntax, cancellationToken) is not INamedTypeSymbol symbol)
+ return null;
+
+ return new SubclassData(Name: symbol.Name, "Test");
+ }
+}
\ No newline at end of file
diff --git a/src/Extensions/GObject-2.0.Integration/Properties/launchSettings.json b/src/Extensions/GObject-2.0.Integration/Properties/launchSettings.json
new file mode 100644
index 000000000..c9194f6d9
--- /dev/null
+++ b/src/Extensions/GObject-2.0.Integration/Properties/launchSettings.json
@@ -0,0 +1,9 @@
+{
+ "$schema": "http://json.schemastore.org/launchsettings.json",
+ "profiles": {
+ "GObject-2.0.Integration": {
+ "commandName": "DebugRoslynComponent",
+ "targetProject": "../../Samples/Gtk-4.0/GridView/GridView.csproj"
+ }
+ }
+}
diff --git a/src/GirCore.sln b/src/GirCore.sln
index 111e5c639..443c6cf59 100644
--- a/src/GirCore.sln
+++ b/src/GirCore.sln
@@ -260,6 +260,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "BoxLayout", "BoxLayout", "{
..\docs\docs\tutorial\gtk\img\BoxLayout\Windows.png = ..\docs\docs\tutorial\gtk\img\BoxLayout\Windows.png
EndProjectSection
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GObject-2.0.Integration", "Extensions\GObject-2.0.Integration\GObject-2.0.Integration.csproj", "{25255BB4-BB40-4E90-A934-FA91B2E9022D}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -909,6 +911,18 @@ Global
{43F76895-E4F5-42CF-B225-524F53B7660F}.Release|x64.Build.0 = Release|Any CPU
{43F76895-E4F5-42CF-B225-524F53B7660F}.Release|x86.ActiveCfg = Release|Any CPU
{43F76895-E4F5-42CF-B225-524F53B7660F}.Release|x86.Build.0 = Release|Any CPU
+ {25255BB4-BB40-4E90-A934-FA91B2E9022D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {25255BB4-BB40-4E90-A934-FA91B2E9022D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {25255BB4-BB40-4E90-A934-FA91B2E9022D}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {25255BB4-BB40-4E90-A934-FA91B2E9022D}.Debug|x64.Build.0 = Debug|Any CPU
+ {25255BB4-BB40-4E90-A934-FA91B2E9022D}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {25255BB4-BB40-4E90-A934-FA91B2E9022D}.Debug|x86.Build.0 = Debug|Any CPU
+ {25255BB4-BB40-4E90-A934-FA91B2E9022D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {25255BB4-BB40-4E90-A934-FA91B2E9022D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {25255BB4-BB40-4E90-A934-FA91B2E9022D}.Release|x64.ActiveCfg = Release|Any CPU
+ {25255BB4-BB40-4E90-A934-FA91B2E9022D}.Release|x64.Build.0 = Release|Any CPU
+ {25255BB4-BB40-4E90-A934-FA91B2E9022D}.Release|x86.ActiveCfg = Release|Any CPU
+ {25255BB4-BB40-4E90-A934-FA91B2E9022D}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{BF7F9B0B-CB43-4161-BFAD-C6EE479FC86B} = {386AE10F-B7AC-4C97-AC5C-202D3662A868}
@@ -990,5 +1004,6 @@ Global
{0A902770-C2A3-440B-86BD-5E4A9DC92F88} = {D14CDE9E-BE14-47DC-B231-7770A40FE716}
{43F76895-E4F5-42CF-B225-524F53B7660F} = {D14CDE9E-BE14-47DC-B231-7770A40FE716}
{61F2AC19-E853-4FAD-8EF3-6F3707D1F796} = {C6411C41-C911-4942-A108-0EFBE55082D7}
+ {25255BB4-BB40-4E90-A934-FA91B2E9022D} = {82ACABCF-0CE8-40ED-9402-8499407E846F}
EndGlobalSection
EndGlobal
diff --git a/src/Samples/Gtk-4.0/GridView/CustomObjectGridViewWindow.cs b/src/Samples/Gtk-4.0/GridView/CustomObjectGridViewWindow.cs
index 005905608..ba1256928 100644
--- a/src/Samples/Gtk-4.0/GridView/CustomObjectGridViewWindow.cs
+++ b/src/Samples/Gtk-4.0/GridView/CustomObjectGridViewWindow.cs
@@ -9,15 +9,9 @@
namespace GridViewSample;
-public class ItemData : GObject.Object, GTypeProvider, InstanceFactory
+[GObject.Subclass]
+public partial class ItemData : GObject.Object
{
- private static readonly Type GType = SubclassRegistrar.Register();
- public static new Type GetGType() => GType;
- static object InstanceFactory.Create(IntPtr handle, bool ownsHandle)
- {
- return new ItemData(handle, ownsHandle);
- }
-
public string? ImagePath { get; set; }
public string? Text { get; set; }
public string? Description { get; set; }
@@ -28,8 +22,6 @@ public ItemData(string imagePath, string text, string description) : base(Object
Text = text;
Description = description;
}
-
- private ItemData(IntPtr ptr, bool ownsHandle) : base(new ObjectHandle(ptr, ownsHandle)) { }
}
public class CustomObjectGridViewWindow : Window
diff --git a/src/Samples/Gtk-4.0/GridView/GridView.csproj b/src/Samples/Gtk-4.0/GridView/GridView.csproj
index c24d63834..0b9ebc27c 100644
--- a/src/Samples/Gtk-4.0/GridView/GridView.csproj
+++ b/src/Samples/Gtk-4.0/GridView/GridView.csproj
@@ -1,6 +1,7 @@
+
diff --git a/src/Tests/Libs/GirTest-0.1.Tests/GirTest-0.1.Tests.csproj b/src/Tests/Libs/GirTest-0.1.Tests/GirTest-0.1.Tests.csproj
index 9dad3b991..add6f834f 100644
--- a/src/Tests/Libs/GirTest-0.1.Tests/GirTest-0.1.Tests.csproj
+++ b/src/Tests/Libs/GirTest-0.1.Tests/GirTest-0.1.Tests.csproj
@@ -5,6 +5,9 @@
+
+
+
diff --git a/src/Tests/Libs/GirTest-0.1.Tests/SubclassIntegrationTest.cs b/src/Tests/Libs/GirTest-0.1.Tests/SubclassIntegrationTest.cs
new file mode 100644
index 000000000..fb8f419b3
--- /dev/null
+++ b/src/Tests/Libs/GirTest-0.1.Tests/SubclassIntegrationTest.cs
@@ -0,0 +1,58 @@
+using System;
+using System.Collections.Immutable;
+using System.Linq;
+using FluentAssertions;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+
+namespace GirTest.Tests;
+
+[TestClass, TestCategory("BindingTest")]
+public class SubclassIntegrationTest : Test
+{
+ [TestMethod]
+ public void GeneratesCode()
+ {
+ var result = RunGeneratorOn("""
+ [GObject.Subclass]
+ public partial class CustomSubclassOfObject{ }
+ """);
+ var sources = result.Results.First().GeneratedSources;
+
+ sources[0].HintName.Should().Be("SubclassAttribute.g.cs");
+ sources[0].SourceText.ToString().Should().Be("""
+ namespace GObject;
+
+ [System.AttributeUsage(System.AttributeTargets.Class)]
+ public class SubclassAttribute : System.Attribute { }
+ """);
+
+ sources[1].HintName.Should().Be("CustomSubclassOfObject.Subclass.g.cs");
+ sources[1].SourceText.ToString().Should().Be("""
+ namespace GridViewSample;
+
+ public partial class CustomSubclassOfObject : GObject.GTypeProvider, GObject.InstanceFactory
+ {
+ private static readonly GObject.Type GType = GObject.Internal.SubclassRegistrar.Register();
+ public static new GObject.Type GetGType() => GType;
+
+ static object GObject.InstanceFactory.Create(System.IntPtr handle, bool ownsHandle)
+ {
+ return new CustomSubclassOfObject(handle, ownsHandle);
+ }
+ }
+ """);
+ }
+
+ private static GeneratorDriverRunResult RunGeneratorOn(string source)
+ {
+ var inputCompilation = CSharpCompilation.Create("compilation", [CSharpSyntaxTree.ParseText(source)]);
+ var generator = new GObject.Integration.Generator();
+ GeneratorDriver driver = CSharpGeneratorDriver.Create(generator);
+ driver = driver.RunGeneratorsAndUpdateCompilation(inputCompilation, out _, out _);
+
+ return driver.GetRunResult();
+ }
+}
\ No newline at end of file