aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKarsten Heimrich <karsten.heimrich@qt.io>2025-12-02 14:30:52 +0100
committerKarsten Heimrich <karsten.heimrich@qt.io>2025-12-03 19:44:54 +0000
commitc6ea46cb5fca2ca9b86fc0658a33a3f9ac931442 (patch)
treef339c3f3b19c9407d6949034802c848a9d57618a
parentbb0a16c6f1dc90c47ea39a98128212022bac6b82 (diff)
Introduce Qt.Bridge.DotNet dotnet CLI item & project templatesHEADdev
Change-Id: Id950260d748edf2066e099f598da49c47dda3810 Reviewed-by: Miguel Costa <miguel.costa@qt.io>
-rw-r--r--.gitignore2
-rw-r--r--nuget/Qt.Bridge.DotNet.Templates/Qt.Bridge.DotNet.Templates.csproj80
-rw-r--r--nuget/Qt.Bridge.DotNet.Templates/README.template.md103
-rw-r--r--nuget/Qt.Bridge.DotNet.Templates/templates/Qt.Bridge.DotNet.Item/.template.config/template.json25
-rw-r--r--nuget/Qt.Bridge.DotNet.Templates/templates/Qt.Bridge.DotNet.Item/NewFile.qml12
-rw-r--r--nuget/Qt.Bridge.DotNet.Templates/templates/Qt.Bridge.DotNet.Project/.template.config/template.json22
-rw-r--r--nuget/Qt.Bridge.DotNet.Templates/templates/Qt.Bridge.DotNet.Project/Main.qml80
-rw-r--r--nuget/Qt.Bridge.DotNet.Templates/templates/Qt.Bridge.DotNet.Project/NewProject.template.csproj20
-rw-r--r--nuget/Qt.Bridge.DotNet.Templates/templates/Qt.Bridge.DotNet.Project/Program.cs39
-rw-r--r--qtdotnet.sln15
10 files changed, 398 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index af0497d..3aa7c66 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,3 +20,5 @@ include/*
# Local nuget
nuget/local/
nuget/Qt.Bridge.DotNet.Package/README.md
+nuget/Qt.Bridge.DotNet.Templates/README.md
+nuget/Qt.Bridge.DotNet.Templates/templates/Qt.Bridge.DotNet.Project/NewProject.csproj
diff --git a/nuget/Qt.Bridge.DotNet.Templates/Qt.Bridge.DotNet.Templates.csproj b/nuget/Qt.Bridge.DotNet.Templates/Qt.Bridge.DotNet.Templates.csproj
new file mode 100644
index 0000000..a48b432
--- /dev/null
+++ b/nuget/Qt.Bridge.DotNet.Templates/Qt.Bridge.DotNet.Templates.csproj
@@ -0,0 +1,80 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <Import Project="$([MSBuild]::GetPathOfFileAbove(qt_version.props))" />
+ <Import Project="$([MSBuild]::GetPathOfFileAbove(qt_template.targets))" />
+
+ <PropertyGroup>
+ <TargetFramework>net8.0</TargetFramework>
+ <IsPackable>true</IsPackable>
+ <IncludeBuildOutput>false</IncludeBuildOutput>
+ <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
+
+ <PackageId>Qt.Bridge.DotNet.Templates</PackageId>
+ <Authors>The Qt Company</Authors>
+ <PackageReadmeFile>README.md</PackageReadmeFile>
+ <Description>
+ This official Qt Group extension, Qt.Bridge.DotNet, brings QML/Qt Quick to C#/.NET.</Description>
+
+ <NoWarn>$(NoWarn);NU5128</NoWarn>
+ <PackageType>Template</PackageType>
+ <IncludeContentInPack>true</IncludeContentInPack>
+ <PackageTags>dotnet-new;templates;Qt.Bridge.DotNet</PackageTags>
+ </PropertyGroup>
+
+ <PropertyGroup>
+ <LocalizeTemplates>false</LocalizeTemplates>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <PackageReference Include="Microsoft.TemplateEngine.Tasks"
+ Version="*" PrivateAssets="all" IsImplicitlyDefined="true" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <Content Include="templates\**\*" PackagePath="content\"
+ Exclude="templates\**\bin\**;templates\**\obj\**;templates/Qt.Bridge.DotNet.Project/NewProject.template.csproj" />
+ <Compile Remove="**\*" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <!-- Placholder - Do not remove! -->
+ <!-- Final README.md to be included in the NuGet package -->
+ <None Include="README.md" Pack="true" PackagePath="" Visible="false"/>
+
+ <!-- Templates remain in the project but are not packed into the NuGet -->
+ <None Include="README.template.md" Pack="false" />
+ <None Include="templates/Qt.Bridge.DotNet.Project/NewProject.template.csproj" Pack="false" />
+ </ItemGroup>
+
+ <Target Name="GenerateReadme" BeforeTargets="_GetPackageFiles">
+
+ <ItemGroup>
+ <!-- Tokens for README generation -->
+ <QtTemplateToken Remove="@(QtTemplateToken)" />
+ <QtTemplateToken Include="__PACKAGE_ID__">
+ <ReplacementValue>$(PackageId)</ReplacementValue>
+ </QtTemplateToken>
+ <QtTemplateToken Include="__PACKAGE_VERSION__">
+ <ReplacementValue>$(PackageVersion)</ReplacementValue>
+ </QtTemplateToken>
+ </ItemGroup>
+
+ <QtTemplateFile Template="README.template.md"
+ OutputFileName="README.md"
+ Tokens="@(QtTemplateToken)" />
+ <QtTemplateFile Template="templates/Qt.Bridge.DotNet.Project/NewProject.template.csproj"
+ OutputFileName="templates/Qt.Bridge.DotNet.Project/NewProject.csproj"
+ Tokens="@(QtTemplateToken)" />
+ </Target>
+
+ <Target Name="CopyPackage" AfterTargets="Pack">
+ <PropertyGroup>
+ <PackedNupkg>$(PackageOutputPath)$(PackageId).$(PackageVersion).nupkg</PackedNupkg>
+ </PropertyGroup>
+
+ <Message Importance="high"
+ Text="Copy $(PackedNupkg) to $(MSBuildThisFileDirectory)..\local\$(PackageId)\$(PackageVersion)" />
+ <Copy SourceFiles="$(PackedNupkg)"
+ DestinationFolder="$(MSBuildThisFileDirectory)..\local\$(PackageId)\$(PackageVersion)" />
+ </Target>
+</Project>
diff --git a/nuget/Qt.Bridge.DotNet.Templates/README.template.md b/nuget/Qt.Bridge.DotNet.Templates/README.template.md
new file mode 100644
index 0000000..2318264
--- /dev/null
+++ b/nuget/Qt.Bridge.DotNet.Templates/README.template.md
@@ -0,0 +1,103 @@
+# Qt.Bridge.DotNet Templates
+
+Official `dotnet new` templates to build Qt/QML apps with C#/.NET using **Qt.Bridge.DotNet**.
+
+## What you get
+
+- **Project template** - `Qt.Bridge.DotNet Project`
+ - Minimal C# app wired to Qt Quick / QML.
+ - Entry QML (`Main.qml`) + bootstrap `Program.cs`.
+
+- **Item template** - `Qt.Bridge.DotNet QML File (.qml)`
+ - Adds a new `.qml` file and lets the build integrate it automatically.
+
+---
+
+## How to use
+
+### Install the templates
+
+From a **NuGet feed**:
+```bash
+dotnet new install __PACKAGE_ID__
+```
+
+From a **local .nupkg**:
+```bash
+dotnet new install ./PATH/TO/__PACKAGE_ID__.__PACKAGE_VERSION__.nupkg
+```
+
+Verify installation:
+```bash
+dotnet new list
+```
+
+Update to the latest version:
+```bash
+dotnet new update
+# or force a specific package/version
+dotnet new install __PACKAGE_ID__ --force
+```
+
+### Create a project
+
+```bash
+dotnet new qtbridgedotnetproj -n MyQtApp
+cd MyQtApp
+dotnet build
+dotnet run
+```
+
+This generates:
+```
+MyQtApp/
+ Project.csproj
+ Program.cs
+ Main.qml
+```
+
+### Add a QML item to an existing project
+
+```bash
+dotnet new qtbridgedotnetqml --FileName=MainPage
+```
+
+This creates `MainPage.qml`. The build integrates QML files automatically (they'll be registered and copied alongside your app).
+
+### Uninstall the templates
+
+```bash
+dotnet new uninstall __PACKAGE_ID__
+```
+
+---
+
+## Platforms & requirements
+
+- **Runtime:** .NET 8 or newer.
+- **OS:** Windows only. Platform availability depends on the packaged Qt runtime.
+- **Tooling:** `dotnet` SDK 8+, a C++ toolchain for native build steps is required.
+
+---
+
+## Future plans
+- TODO: Fill out once we're ready to publish
+
+---
+
+## Versioning & support
+
+Issues/feedback welcome — please file an issue with your OS, .NET, and Qt details.
+
+---
+
+## Licensing
+
+This package bundles components under the open-source license. Review the included license files for details and choose the option that applies to your use case. The Qt Quick runtime subset is provided under the respective open-source terms.
+
+---
+
+## Acknowledgments
+
+© The Qt Company Ltd. and contributors.
+Qt is a trademark of The Qt Company Ltd. All other trademarks are the property of their respective owners.
diff --git a/nuget/Qt.Bridge.DotNet.Templates/templates/Qt.Bridge.DotNet.Item/.template.config/template.json b/nuget/Qt.Bridge.DotNet.Templates/templates/Qt.Bridge.DotNet.Item/.template.config/template.json
new file mode 100644
index 0000000..c76307c
--- /dev/null
+++ b/nuget/Qt.Bridge.DotNet.Templates/templates/Qt.Bridge.DotNet.Item/.template.config/template.json
@@ -0,0 +1,25 @@
+{
+ "$schema": "http://json.schemastore.org/template",
+ "author": "The Qt Company Ltd.",
+ "identity": "Qt.Bridge.DotNet.QMLFile",
+ "groupIdentity": "Qt.Bridge.DotNet.Item.Qml",
+ "name": "Qt.Bridge.DotNet QML File (.qml)",
+ "description": "Adds a new QML file to a Qt.Bridge.DotNet project.",
+ "shortName": "qtbridgedotnetqml",
+ "classifications": [ "Qt.Bridge", "QML", "Item" ],
+ "sourceName": "NewFile",
+ "tags": {
+ "language": "C#",
+ "type": "item"
+ },
+ "primaryOutputs": [ { "path": "NewFile.qml" } ],
+ "symbols": {
+ "FileName": {
+ "type": "parameter",
+ "datatype": "string",
+ "description": "Name of the QML file (without extension).",
+ "defaultValue": "NewFile",
+ "fileRename": "NewFile"
+ }
+ }
+}
diff --git a/nuget/Qt.Bridge.DotNet.Templates/templates/Qt.Bridge.DotNet.Item/NewFile.qml b/nuget/Qt.Bridge.DotNet.Templates/templates/Qt.Bridge.DotNet.Item/NewFile.qml
new file mode 100644
index 0000000..a9f269e
--- /dev/null
+++ b/nuget/Qt.Bridge.DotNet.Templates/templates/Qt.Bridge.DotNet.Item/NewFile.qml
@@ -0,0 +1,12 @@
+import QtQuick 2.3
+
+Rectangle {
+ width: 200
+ height: 100
+ color: "green"
+
+ Text {
+ anchors.centerIn: parent
+ text: "Hello, World!"
+ }
+}
diff --git a/nuget/Qt.Bridge.DotNet.Templates/templates/Qt.Bridge.DotNet.Project/.template.config/template.json b/nuget/Qt.Bridge.DotNet.Templates/templates/Qt.Bridge.DotNet.Project/.template.config/template.json
new file mode 100644
index 0000000..1d39909
--- /dev/null
+++ b/nuget/Qt.Bridge.DotNet.Templates/templates/Qt.Bridge.DotNet.Project/.template.config/template.json
@@ -0,0 +1,22 @@
+{
+ "$schema": "http://json.schemastore.org/template",
+ "author": "The Qt Company Ltd.",
+ "identity": "Qt.Bridge.DotNet.Project",
+ "groupIdentity": "Qt.Bridge.DotNet.App",
+ "name": "Qt.Bridge.DotNet App",
+ "description": "Creates a Qt.Bridge.DotNet application project.",
+ "shortName": "qtbridgedotnetproj",
+ "classifications": [ "Qt.Bridge", "Desktop", "CMake" ],
+ "sourceName": "NewProject",
+ "preferNameDirectory": true,
+ "tags": {
+ "language": "C#",
+ "type": "project"
+ },
+ "precedence": 900,
+ "primaryOutputs": [
+ {
+ "path": "NewProject.csproj"
+ }
+ ]
+}
diff --git a/nuget/Qt.Bridge.DotNet.Templates/templates/Qt.Bridge.DotNet.Project/Main.qml b/nuget/Qt.Bridge.DotNet.Templates/templates/Qt.Bridge.DotNet.Project/Main.qml
new file mode 100644
index 0000000..61fd8c3
--- /dev/null
+++ b/nuget/Qt.Bridge.DotNet.Templates/templates/Qt.Bridge.DotNet.Project/Main.qml
@@ -0,0 +1,80 @@
+import QtQuick 2.3
+import QtQuick.Layouts 1.15
+import QtQuick.Controls 2.15
+
+ApplicationWindow {
+ id: win
+ visible: true
+ width: 365
+ height: 510
+ title: "Qt.Bridge.DotNet"
+ color: "#121212"
+
+ ColumnLayout {
+ anchors.fill: parent
+ anchors.margins: 24
+ spacing: 20
+
+ Label {
+ text: "Hello, World!"
+ font.pixelSize: 40
+ color: "white"
+ horizontalAlignment: Text.AlignHCenter
+ Layout.alignment: Qt.AlignHCenter
+ }
+
+ ColumnLayout {
+ Layout.fillWidth: true
+ spacing: 6
+ Label {
+ text: "Welcome to Qt.Bridge.DotNet"
+ Layout.fillWidth: true
+ horizontalAlignment: Text.AlignHCenter
+ font.pixelSize: 24
+ color: "#cfcfcf"
+ }
+
+ Label {
+ text: "QML Multi-language App UI"
+ Layout.fillWidth: true
+ horizontalAlignment: Text.AlignHCenter
+ font.pixelSize: 24
+ color: "white"
+ wrapMode: Text.WordWrap
+ }
+ }
+
+ Button {
+ id: counterBtn
+ text: Counter.clicks === 0 ? "Click me" : "Clicked " + Counter.clicks + " times"
+
+ Layout.fillWidth: true
+ Layout.preferredHeight: 48
+ font.pixelSize: 16
+
+ hoverEnabled: false
+ focusPolicy: Qt.NoFocus
+
+ readonly property color brandGreen: "#41cd52"
+
+ background: Rectangle {
+ radius: 12
+ border.width: 0
+ color: counterBtn.down
+ ? Qt.darker(counterBtn.brandGreen, 1.2)
+ : counterBtn.brandGreen
+ }
+
+ contentItem: Text {
+ text: counterBtn.text
+ color: "white"
+ font: counterBtn.font
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ elide: Text.ElideRight
+ }
+
+ onClicked: Counter.clicks += 1
+ }
+ }
+}
diff --git a/nuget/Qt.Bridge.DotNet.Templates/templates/Qt.Bridge.DotNet.Project/NewProject.template.csproj b/nuget/Qt.Bridge.DotNet.Templates/templates/Qt.Bridge.DotNet.Project/NewProject.template.csproj
new file mode 100644
index 0000000..1ac3ef4
--- /dev/null
+++ b/nuget/Qt.Bridge.DotNet.Templates/templates/Qt.Bridge.DotNet.Project/NewProject.template.csproj
@@ -0,0 +1,20 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <OutputType>WinExe</OutputType>
+ <TargetFramework>net8.0</TargetFramework>
+ <ImplicitUsings>enable</ImplicitUsings>
+ <Nullable>enable</Nullable>
+ <EnableDefaultCompileItems>false</EnableDefaultCompileItems>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <PackageReference Include="Qt.Bridge.DotNet" Version="__PACKAGE_VERSION__" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <None Include="Main.qml"/>
+ <Compile Include="Program.cs" />
+ </ItemGroup>
+
+</Project>
diff --git a/nuget/Qt.Bridge.DotNet.Templates/templates/Qt.Bridge.DotNet.Project/Program.cs b/nuget/Qt.Bridge.DotNet.Templates/templates/Qt.Bridge.DotNet.Project/Program.cs
new file mode 100644
index 0000000..9d3a146
--- /dev/null
+++ b/nuget/Qt.Bridge.DotNet.Templates/templates/Qt.Bridge.DotNet.Project/Program.cs
@@ -0,0 +1,39 @@
+using Qt.MetaObject;
+using Qt.Quick;
+
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+
+namespace NewProject;
+
+[QObject]
+[QmlElement(Name = "Counter", Singleton = true)]
+public class Counter : INotifyPropertyChanged
+{
+ public event PropertyChangedEventHandler? PropertyChanged;
+
+ private int _clicks = 0;
+ public int Clicks
+ {
+ get => _clicks;
+ set
+ {
+ if (_clicks == value)
+ return;
+ _clicks = value;
+ OnPropertyChanged();
+ }
+ }
+
+ protected virtual void OnPropertyChanged([CallerMemberName] string? name = null)
+ => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
+}
+
+public class Program
+{
+ internal static void Main(string[] args)
+ {
+ Qml.LoadFromRootModule("Main");
+ Qml.WaitForExit();
+ }
+}
diff --git a/qtdotnet.sln b/qtdotnet.sln
index aa2aa87..97b39e5 100644
--- a/qtdotnet.sln
+++ b/qtdotnet.sln
@@ -103,6 +103,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CityTemperatures", "example
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test_Qt.DotNet.Project", "tests\Test_Qt.DotNet.Project\Test_Qt.DotNet.Project.csproj", "{691601F2-9CF6-8A1B-8F51-2B1E44DA7792}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Qt.Bridge.DotNet.Templates", "nuget\Qt.Bridge.DotNet.Templates\Qt.Bridge.DotNet.Templates.csproj", "{EB560CA6-8C96-FA9C-7862-BC449EF4469E}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -299,6 +301,18 @@ Global
{691601F2-9CF6-8A1B-8F51-2B1E44DA7792}.Release|x64.Build.0 = Release|Any CPU
{691601F2-9CF6-8A1B-8F51-2B1E44DA7792}.Release|x86.ActiveCfg = Release|Any CPU
{691601F2-9CF6-8A1B-8F51-2B1E44DA7792}.Release|x86.Build.0 = Release|Any CPU
+ {EB560CA6-8C96-FA9C-7862-BC449EF4469E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {EB560CA6-8C96-FA9C-7862-BC449EF4469E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {EB560CA6-8C96-FA9C-7862-BC449EF4469E}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {EB560CA6-8C96-FA9C-7862-BC449EF4469E}.Debug|x64.Build.0 = Debug|Any CPU
+ {EB560CA6-8C96-FA9C-7862-BC449EF4469E}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {EB560CA6-8C96-FA9C-7862-BC449EF4469E}.Debug|x86.Build.0 = Debug|Any CPU
+ {EB560CA6-8C96-FA9C-7862-BC449EF4469E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {EB560CA6-8C96-FA9C-7862-BC449EF4469E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {EB560CA6-8C96-FA9C-7862-BC449EF4469E}.Release|x64.ActiveCfg = Release|Any CPU
+ {EB560CA6-8C96-FA9C-7862-BC449EF4469E}.Release|x64.Build.0 = Release|Any CPU
+ {EB560CA6-8C96-FA9C-7862-BC449EF4469E}.Release|x86.ActiveCfg = Release|Any CPU
+ {EB560CA6-8C96-FA9C-7862-BC449EF4469E}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -326,6 +340,7 @@ Global
{6F876C80-BBF0-6F6B-C550-27F7AC799574} = {436DDD81-054F-492E-8F7A-B9445413E3B2}
{CFD4AEBB-E7B0-9B59-D04B-3A4BB8DBD576} = {436DDD81-054F-492E-8F7A-B9445413E3B2}
{691601F2-9CF6-8A1B-8F51-2B1E44DA7792} = {0828626A-BAD8-4E02-99DC-3AAA15073223}
+ {EB560CA6-8C96-FA9C-7862-BC449EF4469E} = {13659C36-5EDE-471A-9727-7D9A5254B993}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E4CDB648-EF1A-4ADE-B6EA-D4E9D668E676}