Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add GObject-2.0.Integration.csproj #1158

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<PackageId>GirCore.GObject-2.0.Integration</PackageId>
<RootNamespace>GObject.Integration</RootNamespace>
<Description>Source Generators to make it easy to integrate C# with the GObject type system.</Description>

<IncludeBuildOutput>false</IncludeBuildOutput>
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Common" Version="4.12.0" />
</ItemGroup>

<!--TODO VERIFY THIS -->
<!-- This ensures the library will be packaged as a source generator when we use `dotnet pack` -->
<ItemGroup>
<None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true"
PackagePath="analyzers/dotnet/cs" Visible="false" />
</ItemGroup>
</Project>
12 changes: 12 additions & 0 deletions src/Extensions/GObject-2.0.Integration/Integration/Generator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Microsoft.CodeAnalysis;

namespace GObject.Integration;

[Generator]
public class Generator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
context.EnableSubclassSupport();
}
}
16 changes: 16 additions & 0 deletions src/Extensions/GObject-2.0.Integration/Integration/Subclass.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
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/

Check warning on line 10 in src/Extensions/GObject-2.0.Integration/Integration/Subclass.cs

View workflow job for this annotation

GitHub Actions / Build (Linux)

Fix formatting
context.RegisterImplementationSourceOutput(
source: context.GetSubclassValuesProvider(),

Check warning on line 12 in src/Extensions/GObject-2.0.Integration/Integration/Subclass.cs

View workflow job for this annotation

GitHub Actions / Build (Linux)

Fix formatting
action: SubclassCode.Generate

Check warning on line 13 in src/Extensions/GObject-2.0.Integration/Integration/Subclass.cs

View workflow job for this annotation

GitHub Actions / Build (Linux)

Fix formatting
);

Check warning on line 14 in src/Extensions/GObject-2.0.Integration/Integration/Subclass.cs

View workflow job for this annotation

GitHub Actions / Build (Linux)

Fix formatting
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace GObject.Integration;

public static class SubclassAttribute
{
public const string FullyQualifiedName = "GObject.SubclassAttribute`1";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using Microsoft.CodeAnalysis;

namespace GObject.Integration;

public static class SubclassCode
{
public static void Generate(SourceProductionContext context, SubclassData subclassData)
{
context.AddSource(
hintName:$"{subclassData.FileName}.Subclass.g.cs",

Check warning on line 10 in src/Extensions/GObject-2.0.Integration/Integration/Subclass/SubclassCode.cs

View workflow job for this annotation

GitHub Actions / Build (Linux)

Fix formatting
source: ToCode(subclassData)
);
}
private static string ToCode(SubclassData subclassData)
{
return $$"""
namespace {{subclassData.Namespace}};

{{subclassData.Accessibility}} partial class {{subclassData.NameGenericArguments}}({{subclassData.ParentHandle}} handle) : {{subclassData.Parent}}(handle), GObject.GTypeProvider, GObject.InstanceFactory
{
private static readonly GObject.Type GType = GObject.Internal.SubclassRegistrar.Register<{{subclassData.NameGenericArguments}}, {{subclassData.Parent}}>();
public static new GObject.Type GetGType() => GType;

static object GObject.InstanceFactory.Create(System.IntPtr handle, bool ownsHandle)
{
return new {{subclassData.NameGenericArguments}}(new {{subclassData.ParentHandle}}(handle, ownsHandle));
}

public {{subclassData.Name}}(params GObject.ConstructArgument[] constructArguments) : this({{subclassData.ParentHandle}}.For<{{subclassData.NameGenericArguments}}>(constructArguments)) { }
}
""";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
namespace GObject.Integration;

public record SubclassData(string Name, string NameGenericArguments, string Parent, string ParentHandle, string Namespace, string Accessibility, string FileName);
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis;

namespace GObject.Integration;

public static class SubclassValuesProvider
{
public static IncrementalValuesProvider<SubclassData> 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)
{
if (context.TargetSymbol is not INamedTypeSymbol subclass)
return null;

var subclassAttribute = context.Attributes.First().AttributeClass;

if (subclassAttribute is null)
return null;

var parentType = subclassAttribute.TypeArguments.First();

var parentHandleAttributeData = parentType
.GetAttributes()
.FirstOrDefault(x => x.AttributeClass?.OriginalDefinition.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) == "global::GObject.HandleAttribute<T>");

if (parentHandleAttributeData?.AttributeClass is not { } parentHandle)
return null;

var accessibility = GetAccessibility(context.TargetSymbol);

if (accessibility is null)
return null;

return new SubclassData(
Name: subclass.Name,
NameGenericArguments: subclass.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat),
Parent: parentType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat),
ParentHandle: parentHandle.TypeArguments.First().ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat),
Namespace: context.TargetSymbol.ContainingNamespace.ToDisplayString(),
Accessibility: accessibility,
FileName: GetFileName(subclass)
);
}

private static string GetFileName(INamedTypeSymbol typeSymbol)
{
var suffix = typeSymbol.Arity == 0 ? string.Empty : $"_{typeSymbol.Arity}";

return typeSymbol.Name + suffix;
}

private static string? GetAccessibility(ISymbol parentType)
{
var accessibility = parentType.DeclaredAccessibility switch
{
Accessibility.Public => "public",
Accessibility.Internal => "internal",
Accessibility.Private => "private",
Accessibility.NotApplicable => "internal",
_ => null
};
return accessibility;
}
}
Original file line number Diff line number Diff line change
@@ -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"
}
}
}
26 changes: 7 additions & 19 deletions src/Generation/Generator/Renderer/Internal/Class/ClassHandle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ public static string Render(GirModel.Class cls)
private static string RenderFinalClassHandle(GirModel.Class cls)
{
var handleName = Class.GetInternalHandleName(cls);

var owned = Class.IsInitiallyUnowned(cls) ? "false" : "true";

return $$"""
using System;
using System.Linq;
Expand All @@ -30,23 +31,16 @@ public partial class {{handleName}} : {{RenderParent(cls)}}
{
public {{handleName}}(IntPtr handle, bool ownsHandle) : base(handle, ownsHandle){ }

public static {{handleName}} Create(bool owned, GObject.ConstructArgument[] constructArguments)
public static {{handleName}} Create(GObject.ConstructArgument[] constructArguments)
{
// We can't check if a reference is floating via "g_object_is_floating" here
// as the function could be "lying" depending on the intent of framework writers.
// E.g. A Gtk.Window created via "g_object_new_with_properties" returns an unowned
// reference which is not marked as floating as the gtk toolkit "owns" it.
// For this reason we just delegate the problem to the caller and require a
// definition whether the ownership of the new object will be transferred to us or not.

var ptr = GObject.Internal.Object.NewWithProperties(
objectType: {{Class.GetFullyQualifiedInternalName(cls)}}.GetGType(),
nProperties: (uint) constructArguments.Length,
names: constructArguments.Select(x => x.Name).ToArray(),
values: GObject.Internal.ValueArray2OwnedHandle.Create(constructArguments.Select(x => x.Value).ToArray())
);

return new {{handleName}}(ptr, owned);
return new {{handleName}}(ptr, {{owned}});
}
}
""";
Expand All @@ -55,6 +49,7 @@ public partial class {{handleName}} : {{RenderParent(cls)}}
private static string RenderStandardClassHandle(GirModel.Class cls)
{
var handleName = Class.GetInternalHandleName(cls);
var owned = Class.IsInitiallyUnowned(cls) ? "false" : "true";

return $$"""
using System;
Expand All @@ -70,23 +65,16 @@ public partial class {{handleName}} : {{RenderParent(cls)}}
{
public {{handleName}}(IntPtr handle, bool ownsHandle) : base(handle, ownsHandle){ }

public static new {{handleName}} For<T>(bool owned, GObject.ConstructArgument[] constructArguments) where T : {{Class.GetFullyQualifiedPublicName(cls)}}, GObject.GTypeProvider
public static new {{handleName}} For<T>(GObject.ConstructArgument[] constructArguments) where T : {{Class.GetFullyQualifiedPublicName(cls)}}, GObject.GTypeProvider
{
// We can't check if a reference is floating via "g_object_is_floating" here
// as the function could be "lying" depending on the intent of framework writers.
// E.g. A Gtk.Window created via "g_object_new_with_properties" returns an unowned
// reference which is not marked as floating as the gtk toolkit "owns" it.
// For this reason we just delegate the problem to the caller and require a
// definition whether the ownership of the new object will be transferred to us or not.

var ptr = GObject.Internal.Object.NewWithProperties(
objectType: T.GetGType(),
nProperties: (uint) constructArguments.Length,
names: constructArguments.Select(x => x.Name).ToArray(),
values: GObject.Internal.ValueArray2OwnedHandle.Create(constructArguments.Select(x => x.Value).ToArray())
);

return new {{handleName}}(ptr, owned);
return new {{handleName}}(ptr, {{owned}});
}
}
""";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ namespace {Namespace.GetPublicName(cls.Namespace)};
// AUTOGENERATED FILE - DO NOT MODIFY

{PlatformSupportAttribute.Render(cls as GirModel.PlatformDependent)}
[GObject.Handle<{Class.GetFullyQualifiedInternalHandleName(cls)}>]
public {@sealed}partial class {cls.Name}({Class.GetFullyQualifiedInternalHandleName(cls)} handle) {RenderInheritance(cls)}
{{
{RenderPublicConstructor(cls)}
Expand All @@ -46,10 +47,8 @@ private static string RenderInheritance(GirModel.Class cls)

private static string RenderPublicConstructor(GirModel.Class cls)
{
var owned = Class.IsInitiallyUnowned(cls) ? "false" : "true";

return cls.Final
? $" public {cls.Name}(params GObject.ConstructArgument[] constructArguments) : this({Class.GetFullyQualifiedInternalHandleName(cls)}.Create({owned}, constructArguments)) {{ }}"
: $" public {cls.Name}(params GObject.ConstructArgument[] constructArguments) : this({Class.GetFullyQualifiedInternalHandleName(cls)}.For<{cls.Name}>({owned}, constructArguments)) {{ }}";
? $" public {cls.Name}(params GObject.ConstructArgument[] constructArguments) : this({Class.GetFullyQualifiedInternalHandleName(cls)}.Create(constructArguments)) {{ }}"
: $" public {cls.Name}(params GObject.ConstructArgument[] constructArguments) : this({Class.GetFullyQualifiedInternalHandleName(cls)}.For<{cls.Name}>(constructArguments)) {{ }}";
}
}
15 changes: 15 additions & 0 deletions src/GirCore.sln
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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}
Expand Down Expand Up @@ -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
4 changes: 2 additions & 2 deletions src/Libs/GObject-2.0/Internal/ObjectHandle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ protected override bool ReleaseHandle()
protected internal virtual void AddMemoryPressure() { }
protected virtual void RemoveMemoryPressure() { }

public static ObjectHandle For<T>(bool owned, ConstructArgument[] constructArguments) where T : GObject.Object, GTypeProvider
public static ObjectHandle For<T>(ConstructArgument[] constructArguments) where T : GObject.Object, GTypeProvider
{
// We can't check if a reference is floating via "g_object_is_floating" here
// as the function could be "lying" depending on the intent of framework writers.
Expand All @@ -67,6 +67,6 @@ public static ObjectHandle For<T>(bool owned, ConstructArgument[] constructArgum
values: GObject.Internal.ValueArray2OwnedHandle.Create(constructArguments.Select(x => x.Value).ToArray())
);

return new ObjectHandle(ptr, owned);
return new ObjectHandle(ptr, true);
}
}
6 changes: 6 additions & 0 deletions src/Libs/GObject-2.0/Public/HandleAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
using System;

namespace GObject;

[AttributeUsage(AttributeTargets.Class)]
public class HandleAttribute<T> : Attribute where T : Internal.ObjectHandle;
1 change: 1 addition & 0 deletions src/Libs/GObject-2.0/Public/Object.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace GObject;

[GObject.Handle<ObjectHandle>]
public partial class Object : IDisposable
{
public ObjectHandle Handle { get; }
Expand Down
4 changes: 4 additions & 0 deletions src/Libs/GObject-2.0/Public/SubclassAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
namespace GObject;

[System.AttributeUsage(System.AttributeTargets.Class)]
public class SubclassAttribute<T> : System.Attribute where T : GObject.Object;
16 changes: 3 additions & 13 deletions src/Samples/Gtk-4.0/GridView/CustomObjectGridViewWindow.cs
Original file line number Diff line number Diff line change
@@ -1,35 +1,25 @@
using System;
using GObject;
using GObject.Internal;
using Gtk;
using static Gtk.GridView;
using static Gtk.SignalListItemFactory;
using ListStore = Gio.ListStore;
using Type = GObject.Type;

namespace GridViewSample;

public class ItemData : GObject.Object, GTypeProvider, InstanceFactory
[Subclass<GObject.Object>]
public partial class ItemData
{
private static readonly Type GType = SubclassRegistrar.Register<ItemData, GObject.Object>();
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; }

public ItemData(string imagePath, string text, string description) : base(ObjectHandle.For<ItemData>(true, []))
public ItemData(string imagePath, string text, string description) : this()
{
ImagePath = imagePath;
Text = text;
Description = description;
}

private ItemData(IntPtr ptr, bool ownsHandle) : base(new ObjectHandle(ptr, ownsHandle)) { }
}

public class CustomObjectGridViewWindow : Window
Expand Down
1 change: 1 addition & 0 deletions src/Samples/Gtk-4.0/GridView/GridView.csproj
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<ItemGroup>
<ProjectReference Include="..\..\..\Extensions\GObject-2.0.Integration\GObject-2.0.Integration.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
<ProjectReference Include="..\..\..\Libs\Gtk-4.0\Gtk-4.0.csproj" />
</ItemGroup>

Expand Down
Loading
Loading