diff options
10 files changed, 398 insertions, 0 deletions
@@ -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} |
