aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiguel Costa <[email protected]>2025-06-22 19:36:56 +0200
committerMiguel Costa <[email protected]>2025-06-25 14:00:48 +0000
commit059df9cd95a82ff6febec4dcc448962393bf2e88 (patch)
tree7be1802cd4a315a08c8e33bbef1dbd94ea014957
parent251910aacf46f2c97c7d7f80adabd47d8d23b819 (diff)
RefactorHEADdev
Includes: * Implement QDotNetNativeInterface: specialization of QDotNetInterface that wraps a pointer to native data. * Implement IQVariant: QDotNetNativeInterface that wraps a QVariant. * Implement IQModelIndex interface: QDotNetNativeInterface that wraps a QModelIndex. * Implement QDotNetAbstractListModel base class: allows extending QAbstractListModel in C#. * Add class QDotNetDelegate * Remove old examples Change-Id: I2eb21df29e1cf9379e14a64d5174eb934b5bf18a Reviewed-by: Karsten Heimrich <[email protected]>
-rw-r--r--README.md10
-rw-r--r--examples/Chronometer/Chronometer/Chronometer.cs232
-rw-r--r--examples/Chronometer/Chronometer/ChronometerModel.csproj9
-rw-r--r--examples/Chronometer/QmlChronometer/OutDir.props11
-rw-r--r--examples/Chronometer/QmlChronometer/QChronometer/AdjustmentWheel.qml125
-rw-r--r--examples/Chronometer/QmlChronometer/QChronometer/InsetDial.qml65
-rw-r--r--examples/Chronometer/QmlChronometer/QChronometer/WatchButton.qml31
-rw-r--r--examples/Chronometer/QmlChronometer/QmlChronometer.vcxproj158
-rw-r--r--examples/Chronometer/QmlChronometer/QmlChronometer.vcxproj.filters156
-rw-r--r--examples/Chronometer/QmlChronometer/content/center.pngbin2773 -> 0 bytes
-rw-r--r--examples/Chronometer/QmlChronometer/content/chrono_1_center.pngbin3165 -> 0 bytes
-rw-r--r--examples/Chronometer/QmlChronometer/content/chrono_1_hand.pngbin4973 -> 0 bytes
-rw-r--r--examples/Chronometer/QmlChronometer/content/chrono_2_center.pngbin3168 -> 0 bytes
-rw-r--r--examples/Chronometer/QmlChronometer/content/chrono_2_hand.pngbin4972 -> 0 bytes
-rw-r--r--examples/Chronometer/QmlChronometer/content/chrono_3_center.pngbin3164 -> 0 bytes
-rw-r--r--examples/Chronometer/QmlChronometer/content/chrono_3_needle.pngbin7268 -> 0 bytes
-rw-r--r--examples/Chronometer/QmlChronometer/content/hour_hand.pngbin3675 -> 0 bytes
-rw-r--r--examples/Chronometer/QmlChronometer/content/minute_hand.pngbin4163 -> 0 bytes
-rw-r--r--examples/Chronometer/QmlChronometer/content/second_hand.pngbin5947 -> 0 bytes
-rw-r--r--examples/Chronometer/QmlChronometer/content/watchface.pngbin93379 -> 0 bytes
-rw-r--r--examples/Chronometer/QmlChronometer/main.cpp29
-rw-r--r--examples/Chronometer/QmlChronometer/main.qml453
-rw-r--r--examples/Chronometer/QmlChronometer/qchronometer.cpp163
-rw-r--r--examples/Chronometer/QmlChronometer/qchronometer.h84
-rw-r--r--examples/Chronometer/QmlChronometer/qlaprecorder.cpp128
-rw-r--r--examples/Chronometer/QmlChronometer/qlaprecorder.h69
-rw-r--r--examples/Chronometer/QmlChronometer/qml.qrc19
-rw-r--r--examples/EmbeddedWindow/QmlApp/QmlApp.vcxproj111
-rw-r--r--examples/EmbeddedWindow/QmlApp/QmlApp.vcxproj.filters57
-rw-r--r--examples/EmbeddedWindow/QmlApp/embeddedwindow.cpp39
-rw-r--r--examples/EmbeddedWindow/QmlApp/embeddedwindow.h31
-rw-r--r--examples/EmbeddedWindow/QmlApp/main.cpp65
-rw-r--r--examples/EmbeddedWindow/QmlApp/main.qml135
-rw-r--r--examples/EmbeddedWindow/QmlApp/mainwindow.cpp170
-rw-r--r--examples/EmbeddedWindow/QmlApp/mainwindow.h61
-rw-r--r--examples/EmbeddedWindow/QmlApp/qml.qrc6
-rw-r--r--examples/EmbeddedWindow/QmlApp/qt_logo.pngbin6208 -> 0 bytes
-rw-r--r--examples/EmbeddedWindow/WpfApp/AssemblyInfo.cs10
-rw-r--r--examples/EmbeddedWindow/WpfApp/MainWindow.xaml58
-rw-r--r--examples/EmbeddedWindow/WpfApp/MainWindow.xaml.cs109
-rw-r--r--examples/EmbeddedWindow/WpfApp/Properties/launchSettings.json11
-rw-r--r--examples/EmbeddedWindow/WpfApp/WpfApp.cs23
-rw-r--r--examples/EmbeddedWindow/WpfApp/WpfApp.csproj33
-rw-r--r--examples/QtAzureIoT/QtAzureIoT.sln88
-rw-r--r--examples/QtAzureIoT/common/PropertySet.cs27
-rw-r--r--examples/QtAzureIoT/common/Utils.csproj9
-rw-r--r--examples/QtAzureIoT/device/CardReader/CardReader.cs98
-rw-r--r--examples/QtAzureIoT/device/CardReader/CardReader.csproj17
-rw-r--r--examples/QtAzureIoT/device/DeviceToBackoffice/Backoffice.cs70
-rw-r--r--examples/QtAzureIoT/device/DeviceToBackoffice/DeviceToBackoffice.csproj17
-rw-r--r--examples/QtAzureIoT/device/DeviceToBackoffice/PnpConvention.cs423
-rw-r--r--examples/QtAzureIoT/device/DeviceToBackoffice/TemperatureControllerSample.cs483
-rw-r--r--examples/QtAzureIoT/device/DeviceToBackoffice/WritablePropertyResponse.cs57
-rw-r--r--examples/QtAzureIoT/device/SensorData/SensorData.cs107
-rw-r--r--examples/QtAzureIoT/device/SensorData/SensorData.csproj17
-rw-r--r--examples/QtAzureIoT/device/deviceapp/deviceapp.vcxproj126
-rw-r--r--examples/QtAzureIoT/device/deviceapp/deviceapp.vcxproj.filters41
-rw-r--r--examples/QtAzureIoT/device/deviceapp/main.cpp183
-rw-r--r--examples/QtAzureIoT/device/deviceapp/main.qml56
-rw-r--r--examples/QtAzureIoT/device/deviceapp/qml.qrc5
-rw-r--r--include/iqmodelindex.h67
-rw-r--r--include/iqvariant.h73
-rw-r--r--include/qdotnetabstractlistmodel.h241
-rw-r--r--include/qdotnetadapter.h39
-rw-r--r--include/qdotnetarray.h4
-rw-r--r--include/qdotnetcallback.h80
-rw-r--r--include/qdotnetdelegate.h37
-rw-r--r--include/qdotnetevent.h3
-rw-r--r--include/qdotnethost.h205
-rw-r--r--include/qdotnethostfxr.h34
-rw-r--r--include/qdotnetinterface.h111
-rw-r--r--include/qdotnetobject.h65
-rw-r--r--include/qdotnetref.h31
-rw-r--r--include/qdotnetstatic.h53
-rw-r--r--include/qdotnettype.h75
-rw-r--r--qtdotnet.sln124
-rw-r--r--src/Qt.DotNet.Adapter/Qt.DotNet.Adapter.csproj25
-rw-r--r--src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.Delegates.cs (renamed from src/Qt.DotNet.Adapter/Adapter.Delegates.cs)7
-rw-r--r--src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.Events.cs (renamed from src/Qt.DotNet.Adapter/Adapter.Events.cs)0
-rw-r--r--src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.Methods.cs (renamed from src/Qt.DotNet.Adapter/Adapter.Methods.cs)0
-rw-r--r--src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.Objects.cs (renamed from src/Qt.DotNet.Adapter/Adapter.Objects.cs)8
-rw-r--r--src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.Static.cs16
-rw-r--r--src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.Test.cs (renamed from src/Qt.DotNet.Adapter/Adapter.Test.cs)0
-rw-r--r--src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.cs (renamed from src/Qt.DotNet.Adapter/Adapter.cs)77
-rw-r--r--src/Qt.DotNet.Adapter/Qt/DotNet/CodeGenerator.cs (renamed from src/Qt.DotNet.Adapter/CodeGenerator.cs)149
-rw-r--r--src/Qt.DotNet.Adapter/Qt/DotNet/EventRelay.cs (renamed from src/Qt.DotNet.Adapter/EventRelay.cs)0
-rw-r--r--src/Qt.DotNet.Adapter/Qt/DotNet/IQModelIndex.cs29
-rw-r--r--src/Qt.DotNet.Adapter/Qt/DotNet/IQVariant.cs25
-rw-r--r--src/Qt.DotNet.Adapter/Qt/DotNet/InterfaceProxy.cs (renamed from src/Qt.DotNet.Adapter/InterfaceProxy.cs)92
-rw-r--r--src/Qt.DotNet.Adapter/Qt/DotNet/ObjectMarshaler.cs (renamed from src/Qt.DotNet.Adapter/ObjectMarshaler.cs)0
-rw-r--r--src/Qt.DotNet.Adapter/Qt/DotNet/Parameter.cs (renamed from src/Qt.DotNet.Adapter/Parameter.cs)6
-rw-r--r--src/Qt.DotNet.Adapter/Qt/DotNet/QAbstractListModel.cs153
-rw-r--r--src/Qt.DotNet.Adapter/Qt/DotNet/StringMarshaler.cs (renamed from src/Qt.DotNet.Adapter/StringMarshaler.cs)0
-rw-r--r--src/Qt.DotNet.Adapter/build/Qt.DotNet.Adapter.targets58
-rw-r--r--tests/FooConsoleApp/FooConsoleApp.csproj14
-rw-r--r--tests/FooConsoleApp/Program.cs23
-rw-r--r--tests/FooLib/FooClass.cs93
-rw-r--r--tests/FooLib/FooLib.csproj6
-rw-r--r--tests/Perf_Qt.DotNet.Adapter/Perf_Qt.DotNet.Adapter.csproj2
-rw-r--r--tests/Test_Qt.DotNet.Adapter/Test_Qt.DotNet.Adapter.csproj2
-rw-r--r--tests/tst_qtdotnet/foo.cpp36
-rw-r--r--tests/tst_qtdotnet/foo.h7
-rw-r--r--tests/tst_qtdotnet/tst_qtdotnet.cpp532
-rw-r--r--tests/tst_qtdotnet/tst_qtdotnet.vcxproj25
-rw-r--r--tests/tst_qtdotnet/tst_qtdotnet.vcxproj.filters63
105 files changed, 2137 insertions, 5038 deletions
diff --git a/README.md b/README.md
index 51484b3..60cad33 100644
--- a/README.md
+++ b/README.md
@@ -188,7 +188,7 @@ public:
...
};
-class SensorData : public QObject, public QDotNetObject, public QDotNetObject::IEventHandler
+class SensorData : public QObject, public QDotNetObject, public QDotNetEventHandler
{
Q_OBJECT
Q_PROPERTY(double temperature READ temperature NOTIFY temperatureChanged)
@@ -197,7 +197,7 @@ public:
Q_DOTNET_OBJECT_INLINE(SensorData, "QtAzureIoT.Device.SensorData, SensorData");
SensorData() : QDotNetObject(getConstructor<SensorData>().invoke(nullptr))
{
- subscribeEvent("PropertyChanged", this);
+ subscribe("PropertyChanged", this);
}
double temperature() const
{
@@ -331,14 +331,14 @@ objects in a Qt application. This includes receiving notifications of .NET event
corresponding Qt signals.
```cpp
-class Ping : public QObject, public QDotNetObject, public QDotNetObject::IEventHandler
+class Ping : public QObject, public QDotNetObject, public QDotNetEventHandler
{
Q_OBJECT
public:
Q_DOTNET_OBJECT_INLINE(Ping, "System.Net.NetworkInformation.Ping, System");
Ping() : QDotNetObject(constructor<Ping>().invoke(nullptr))
{
- subscribeEvent("PingCompleted", this);
+ subscribe("PingCompleted", this);
}
void sendAsync(const QString &hostNameOrAddress)
{
@@ -430,7 +430,7 @@ signals:
...
};
...
-struct QChronometerPrivate : public QDotNetObject::IEventHandler
+struct QChronometerPrivate : public QDotNetEventHandler
{
...
void handleEvent(const QString &eventName, QDotNetObject &sender, QDotNetObject &args) override
diff --git a/examples/Chronometer/Chronometer/Chronometer.cs b/examples/Chronometer/Chronometer/Chronometer.cs
deleted file mode 100644
index 51db4bb..0000000
--- a/examples/Chronometer/Chronometer/Chronometer.cs
+++ /dev/null
@@ -1,232 +0,0 @@
-/***************************************************************************************************
- Copyright (C) 2023 The Qt Company Ltd.
- SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-***************************************************************************************************/
-
-using System.ComponentModel;
-using System.Diagnostics;
-
-namespace WatchModels
-{
- public interface ILapRecorder
- {
- void Mark(int hours, int minutes, int seconds, int milliseconds);
- }
-
- public class Chronometer : INotifyPropertyChanged
- {
- public double Hours
- {
- get => hours;
- private set => SetProperty(ref hours, value, nameof(Hours));
- }
- public double Minutes
- {
- get => minutes;
- private set => SetProperty(ref minutes, value, nameof(Minutes));
- }
- public double Seconds
- {
- get => seconds;
- private set => SetProperty(ref seconds, value, nameof(Seconds));
- }
-
- public int Day
- {
- get => day;
- private set => SetProperty(ref day, value, nameof(Day));
- }
-
- public double ElapsedHours
- {
- get => elapsedHours;
- private set => SetProperty(ref elapsedHours, value, nameof(ElapsedHours));
- }
-
- public double ElapsedMinutes
- {
- get => elapsedMinutes;
- private set => SetProperty(ref elapsedMinutes, value, nameof(ElapsedMinutes));
- }
-
- public double ElapsedSeconds
- {
- get => elapsedSeconds;
- private set => SetProperty(ref elapsedSeconds, value, nameof(ElapsedSeconds));
- }
-
- public double ElapsedMilliseconds
- {
- get => elapsedMilliseconds;
- private set => SetProperty(ref elapsedMilliseconds, value, nameof(ElapsedMilliseconds));
- }
-
- public bool Started
- {
- get => Stopwatch.IsRunning;
- private set
- {
- if (value == Stopwatch.IsRunning)
- return;
- if (value)
- Stopwatch.Start();
- else
- Stopwatch.Stop();
- NotifyPropertyChanged(nameof(Started));
- }
- }
-
- public bool AdjustDayMode
- {
- get => adjustDayMode;
- set
- {
- if (value == adjustDayMode)
- return;
- adjustDayMode = value;
- if (adjustDayMode) {
- if (adjustTimeMode) {
- adjustTimeMode = false;
- NotifyPropertyChanged(nameof(AdjustTimeMode));
- }
- Started = false;
- Reset();
- Time.Stop();
- baseTime = baseTime.Add(Time.Elapsed);
- Time.Reset();
- } else if (!adjustTimeMode) {
- Time.Start();
- }
- NotifyPropertyChanged(nameof(AdjustDayMode));
- }
- }
-
- public bool AdjustTimeMode
- {
- get => adjustTimeMode;
- set
- {
- if (value == adjustTimeMode)
- return;
- adjustTimeMode = value;
- if (adjustTimeMode) {
- if (adjustDayMode) {
- adjustDayMode = false;
- NotifyPropertyChanged(nameof(AdjustDayMode));
- }
- Started = false;
- Reset();
- Time.Stop();
- baseTime = baseTime.Add(Time.Elapsed);
- Time.Reset();
- } else if (!adjustDayMode) {
- Time.Start();
- }
- NotifyPropertyChanged(nameof(AdjustTimeMode));
- }
- }
-
- public ILapRecorder LapRecorder { get; set; }
-
- public Chronometer()
- {
- Mechanism = new Task(async () => await MechanismLoopAsync(), MechanismLoop.Token);
- Mechanism.Start();
- baseTime = DateTime.Now;
- Time.Start();
- }
-
- public void StartStop()
- {
- if (AdjustTimeMode || AdjustDayMode)
- return;
- Started = !Started;
- }
-
- public void Reset()
- {
- ElapsedHours = ElapsedMinutes = ElapsedSeconds = ElapsedMilliseconds = 0;
- if (!Stopwatch.IsRunning) {
- Stopwatch.Reset();
- } else {
- LapRecorder?.Mark(
- Stopwatch.Elapsed.Hours,
- Stopwatch.Elapsed.Minutes,
- Stopwatch.Elapsed.Seconds,
- Stopwatch.Elapsed.Milliseconds);
- Stopwatch.Restart();
- }
- }
-
- public void Adjust(int delta)
- {
- if (AdjustDayMode)
- baseTime = baseTime.AddDays(delta);
- else if (AdjustTimeMode)
- baseTime = baseTime.AddSeconds(delta * 60);
- Refresh();
- }
-
- public event PropertyChangedEventHandler PropertyChanged;
- private void NotifyPropertyChanged(string propertyName)
- {
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
- }
-
- private void Refresh()
- {
- DateTime now = baseTime.Add(Time.Elapsed);
- Day = now.Day;
-
- TimeSpan time = now.TimeOfDay;
- Hours = time.TotalHours;
- Minutes = time.TotalMinutes
- - (time.Hours * 60);
- Seconds = time.TotalSeconds
- - (time.Hours * 3600) - (time.Minutes * 60);
-
- TimeSpan elapsed = Stopwatch.Elapsed;
- ElapsedHours = elapsed.TotalHours;
- ElapsedMinutes = elapsed.TotalMinutes
- - (elapsed.Hours * 60);
- ElapsedSeconds = elapsed.TotalSeconds
- - (elapsed.Hours * 3600) - (elapsed.Minutes * 60);
- ElapsedMilliseconds = elapsed.TotalMilliseconds
- - (elapsed.Hours * 3600000) - (elapsed.Minutes * 60000) - (elapsed.Seconds * 1000);
- }
-
- private async Task MechanismLoopAsync()
- {
- while (!MechanismLoop.IsCancellationRequested) {
- await Task.Delay(5);
- Refresh();
- }
- }
-
- private void SetProperty<T>(ref T currentValue, T newValue, string name)
- {
- if (newValue.Equals(currentValue))
- return;
- currentValue = newValue;
- NotifyPropertyChanged(name);
- }
-
- private double hours;
- private double minutes;
- private double seconds;
- private int day;
- private double elapsedHours;
- private double elapsedMinutes;
- private double elapsedSeconds;
- private double elapsedMilliseconds;
- private bool adjustDayMode;
- private bool adjustTimeMode;
-
- private DateTime baseTime;
- private Stopwatch Time { get; } = new();
- private Stopwatch Stopwatch { get; } = new();
-
- private CancellationTokenSource MechanismLoop { get; } = new();
- private Task Mechanism { get; }
- }
-}
diff --git a/examples/Chronometer/Chronometer/ChronometerModel.csproj b/examples/Chronometer/Chronometer/ChronometerModel.csproj
deleted file mode 100644
index 141e38f..0000000
--- a/examples/Chronometer/Chronometer/ChronometerModel.csproj
+++ /dev/null
@@ -1,9 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-
- <PropertyGroup>
- <TargetFramework>net6.0</TargetFramework>
- <ImplicitUsings>enable</ImplicitUsings>
- <Nullable>disable</Nullable>
- </PropertyGroup>
-
-</Project>
diff --git a/examples/Chronometer/QmlChronometer/OutDir.props b/examples/Chronometer/QmlChronometer/OutDir.props
deleted file mode 100644
index c7a9463..0000000
--- a/examples/Chronometer/QmlChronometer/OutDir.props
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <ImportGroup Label="PropertySheets" />
- <PropertyGroup Label="UserMacros" />
- <PropertyGroup>
- <OutDir>bin\$(VisualStudioVersion)\$(Platform)\$(Configuration)\</OutDir>
- <IntDir>obj\$(VisualStudioVersion)\$(Platform)\$(Configuration)\</IntDir>
- </PropertyGroup>
- <ItemDefinitionGroup />
- <ItemGroup />
-</Project> \ No newline at end of file
diff --git a/examples/Chronometer/QmlChronometer/QChronometer/AdjustmentWheel.qml b/examples/Chronometer/QmlChronometer/QChronometer/AdjustmentWheel.qml
deleted file mode 100644
index 0c5d904..0000000
--- a/examples/Chronometer/QmlChronometer/QChronometer/AdjustmentWheel.qml
+++ /dev/null
@@ -1,125 +0,0 @@
-/***************************************************************************************************
- Copyright (C) 2023 The Qt Company Ltd.
- SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-***************************************************************************************************/
-
-import QtQml
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import QtQuick.Shapes
-
-//////////////////////////////////////////////////////////////////////
-// Adjustment wheel
-Tumbler {
- id: adjustmentWheel
- model: 100
- property int startX
- property int startY
-
- x: startX + ((chrono.adjustDayMode || chrono.adjustTimeMode) ? 8 : 0)
- Behavior on x {
- SpringAnimation { spring: 3; damping: 0.5 }
- }
- y: startY; width: 25; height: 70
-
- enabled: chrono.adjustDayMode || chrono.adjustTimeMode
- onEnabledChanged: {
- if (enabled) {
- chrono.reset();
- if (!chrono.started) {
- laps.reset();
- showLap = currentLap;
- }
- }
- }
-
- property int lastIndex: 0
- property var lastTime: Date.UTC(0)
- property double turnSpeed: 0.0
- onCurrentIndexChanged: {
- if (currentIndex != lastIndex) {
- var i1 = currentIndex;
- var i0 = lastIndex;
- if (Math.abs(i1 - i0) > 50) {
- if (i1 < i0)
- i1 += 100;
- else
- i0 += 100;
- }
- var deltaX = i1 - i0;
- chrono.adjust(deltaX);
- lastIndex = currentIndex;
-
- var deltaT = Date.now() - lastTime;
- lastTime += deltaT;
- turnSpeed = Math.abs((deltaX * 1000) / deltaT);
- }
- }
-
- MouseArea {
- anchors.fill: parent
- onWheel: function(wheel) {
- turn(wheel.angleDelta.y > 0 ? 1 : -1);
- }
- }
-
- function turn(delta) {
- if (enabled) {
- adjustmentWheel.currentIndex = (100 + adjustmentWheel.currentIndex + (delta)) % 100;
- }
- }
-
- //////////////////////////////////////////////////////////////////////
- // Wheel surface
- background: Rectangle {
- anchors.fill: adjustmentWheel
- color: gray6
- border.color: gray8
- border.width: 2
- radius: 2
- }
-
- //////////////////////////////////////////////////////////////////////
- // Notches
- delegate: Component {
- Item {
- Rectangle {
- x: 4; y: 0; width: Tumbler.tumbler.width - 8; height: 2
- color: gray3
- }
- }
- }
-
- //////////////////////////////////////////////////////////////////////
- // Wheel shadow
- Rectangle {
- anchors.centerIn: parent
- width: parent.width; height: parent.height
- gradient: Gradient {
- GradientStop { position: 0.0; color: gray3 }
- GradientStop { position: 0.3; color: "transparent" }
- GradientStop { position: 0.5; color: "transparent" }
- GradientStop { position: 0.7; color: "transparent" }
- GradientStop { position: 1.0; color: gray3 }
- }
- }
-
- //////////////////////////////////////////////////////////////////////
- // Wheel axis
- Rectangle {
- anchors.verticalCenter: parent.verticalCenter
- anchors.left: parent.left
- anchors.leftMargin: parent.startX - parent.x + 2
- width: 20; height: 20
- z: -1
- gradient: Gradient {
- GradientStop { position: 0.0; color: gray3 }
- GradientStop { position: 0.3; color: gray6 }
- GradientStop { position: 0.5; color: gray6 }
- GradientStop { position: 0.7; color: gray6 }
- GradientStop { position: 1.0; color: gray3 }
- }
- border.color: "transparent"
- }
-}
diff --git a/examples/Chronometer/QmlChronometer/QChronometer/InsetDial.qml b/examples/Chronometer/QmlChronometer/QChronometer/InsetDial.qml
deleted file mode 100644
index 7c07106..0000000
--- a/examples/Chronometer/QmlChronometer/QChronometer/InsetDial.qml
+++ /dev/null
@@ -1,65 +0,0 @@
-/***************************************************************************************************
- Copyright (C) 2023 The Qt Company Ltd.
- SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-***************************************************************************************************/
-
-import QtQml
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import QtQuick.Shapes
-
-//////////////////////////////////////////////////////////////////
-// Inset dial
-Item {
- id: insetDial
- property string handSource
- property string pinSource
- property int centerX
- property int centerY
- property double rotationAngle
- //////////////////////////////////////////////////////////////////
- // Hand
- Image {
- source: insetDial.handSource
- transform: Rotation {
- origin.x: insetDial.centerX; origin.y: insetDial.centerY
- Behavior on angle {
- SpringAnimation { spring: 3; damping: 0.5; modulus: 360 }
- }
- angle: insetDial.rotationAngle
- }
- }
- //////////////////////////////////////////////////////////////////
- // Highlight
- Shape {
- id: insetDialHighlight
- anchors.fill: insetDial
- opacity: 0.5
- property var centerX: insetDial.centerX
- property var centerY: insetDial.centerY
- property var color:
- showLap == lastLap ? blue86 : showLap == bestLap ? green86 : "transparent"
- ShapePath {
- startX: insetDialHighlight.centerX; startY: insetDialHighlight.centerY
- strokeColor: "transparent"
- PathAngleArc {
- centerX: insetDialHighlight.centerX; centerY: insetDialHighlight.centerY
- radiusX: 55; radiusY: 55; startAngle: 0; sweepAngle: 360
- }
- fillGradient: RadialGradient {
- centerX: insetDialHighlight.centerX; centerY: insetDialHighlight.centerY
- centerRadius: 55;
- focalX: centerX; focalY: centerY
- GradientStop { position: 0; color: "transparent" }
- GradientStop { position: 0.6; color: "transparent" }
- GradientStop { position: 1; color: insetDialHighlight.color }
- }
- }
- }
- //////////////////////////////////////////////////////////////////
- // Center pin
- Image {
- source: insetDial.pinSource
- }
-}
diff --git a/examples/Chronometer/QmlChronometer/QChronometer/WatchButton.qml b/examples/Chronometer/QmlChronometer/QChronometer/WatchButton.qml
deleted file mode 100644
index 6894c69..0000000
--- a/examples/Chronometer/QmlChronometer/QChronometer/WatchButton.qml
+++ /dev/null
@@ -1,31 +0,0 @@
-/***************************************************************************************************
- Copyright (C) 2023 The Qt Company Ltd.
- SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-***************************************************************************************************/
-
-import QtQml
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import QtQuick.Shapes
-
-RoundButton {
- id: watchButton
- property string buttonText
- property bool split
- property string color
- width: 70; height: 70; radius: 25
- palette.button: color
- Text {
- anchors.centerIn: parent
- horizontalAlignment: Text.AlignHCenter
- font.bold: true
- text: watchButton.buttonText
- }
- Rectangle {
- visible: watchButton.split
- color: "black"
- width: 55; height: 1
- anchors.centerIn: parent
- }
-}
diff --git a/examples/Chronometer/QmlChronometer/QmlChronometer.vcxproj b/examples/Chronometer/QmlChronometer/QmlChronometer.vcxproj
deleted file mode 100644
index 8fb3def..0000000
--- a/examples/Chronometer/QmlChronometer/QmlChronometer.vcxproj
+++ /dev/null
@@ -1,158 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="17.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <ItemGroup Label="ProjectConfigurations">
- <ProjectConfiguration Include="Debug|x64">
- <Configuration>Debug</Configuration>
- <Platform>x64</Platform>
- </ProjectConfiguration>
- <ProjectConfiguration Include="Release|x64">
- <Configuration>Release</Configuration>
- <Platform>x64</Platform>
- </ProjectConfiguration>
- </ItemGroup>
- <PropertyGroup Label="Globals">
- <ProjectGuid>{5A1E5424-CDB1-4776-A9CC-01B2CD57F516}</ProjectGuid>
- <Keyword>QtVS_v304</Keyword>
- <WindowsTargetPlatformVersion Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">10.0.19041.0</WindowsTargetPlatformVersion>
- <WindowsTargetPlatformVersion Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">10.0.19041.0</WindowsTargetPlatformVersion>
- <QtMsBuild Condition="'$(QtMsBuild)'=='' OR !Exists('$(QtMsBuild)\qt.targets')">$(MSBuildProjectDirectory)\QtMsBuild</QtMsBuild>
- </PropertyGroup>
- <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
- <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'" Label="Configuration">
- <ConfigurationType>Application</ConfigurationType>
- <PlatformToolset>v143</PlatformToolset>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'" Label="Configuration">
- <ConfigurationType>Application</ConfigurationType>
- <PlatformToolset>v143</PlatformToolset>
- </PropertyGroup>
- <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
- <ImportGroup Condition="Exists('$(QtMsBuild)\qt_defaults.props')">
- <Import Project="$(QtMsBuild)\qt_defaults.props" />
- </ImportGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'" Label="QtSettings">
- <QtInstall>$(DefaultQtVersion)</QtInstall>
- <QtModules>quick;core</QtModules>
- <QtBuildConfig>debug</QtBuildConfig>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'" Label="QtSettings">
- <QtInstall>$(DefaultQtVersion)</QtInstall>
- <QtModules>quick;core</QtModules>
- <QtBuildConfig>release</QtBuildConfig>
- </PropertyGroup>
- <Target Name="QtMsBuildNotFound" BeforeTargets="CustomBuild;ClCompile" Condition="!Exists('$(QtMsBuild)\qt.targets') or !Exists('$(QtMsBuild)\qt.props')">
- <Message Importance="High" Text="QtMsBuild: could not locate qt.targets, qt.props; project may not build correctly." />
- </Target>
- <ImportGroup Label="ExtensionSettings" />
- <ImportGroup Label="Shared" />
- <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
- <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
- <Import Project="OutDir.props" />
- <Import Project="$(QtMsBuild)\Qt.props" />
- </ImportGroup>
- <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
- <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
- <Import Project="OutDir.props" />
- <Import Project="$(QtMsBuild)\Qt.props" />
- </ImportGroup>
- <PropertyGroup Label="UserMacros" />
- <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
- <IncludePath>..\..\..\include;$(IncludePath)</IncludePath>
- <OutDir>bin\$(VisualStudioVersion)\$(Platform)\$(Configuration)\</OutDir>
- <IntDir>obj\$(VisualStudioVersion)\$(Platform)\$(Configuration)\</IntDir>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
- <IncludePath>..\..\..\include;$(IncludePath)</IncludePath>
- <OutDir>bin\$(VisualStudioVersion)\$(Platform)\$(Configuration)\</OutDir>
- <IntDir>obj\$(VisualStudioVersion)\$(Platform)\$(Configuration)\</IntDir>
- </PropertyGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'" Label="Configuration">
- <ClCompile>
- <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
- <MultiProcessorCompilation>true</MultiProcessorCompilation>
- <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
- <Optimization>Disabled</Optimization>
- <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
- </ClCompile>
- <Link>
- <SubSystem>Windows</SubSystem>
- <GenerateDebugInformation>true</GenerateDebugInformation>
- </Link>
- </ItemDefinitionGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'" Label="Configuration">
- <ClCompile>
- <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
- <MultiProcessorCompilation>true</MultiProcessorCompilation>
- <DebugInformationFormat>None</DebugInformationFormat>
- <Optimization>MaxSpeed</Optimization>
- <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
- </ClCompile>
- <Link>
- <SubSystem>Windows</SubSystem>
- <GenerateDebugInformation>false</GenerateDebugInformation>
- </Link>
- </ItemDefinitionGroup>
- <ItemGroup>
- <ClCompile Include="main.cpp" />
- <ClCompile Include="qchronometer.cpp" />
- <ClCompile Include="qlaprecorder.cpp" />
- <None Include="QChronometer\AdjustmentWheel.qml" />
- <None Include="QChronometer\InsetDial.qml" />
- <None Include="QChronometer\WatchButton.qml" />
- <QtRcc Include="qml.qrc" />
- <CopyFileToFolders Include="..\..\..\bin\Qt.DotNet.Adapter.dll">
- <DeploymentContent>true</DeploymentContent>
- <FileType>Document</FileType>
- <TreatOutputAsContent Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</TreatOutputAsContent>
- </CopyFileToFolders>
- <None Include="main.qml" />
- </ItemGroup>
- <ItemGroup>
- <QtMoc Include="qchronometer.h" />
- </ItemGroup>
- <ItemGroup>
- <ProjectReference Include="..\Chronometer\ChronometerModel.csproj">
- <Project>{d3d5ed0e-fd65-4f54-b2d8-d3a380f247a2}</Project>
- </ProjectReference>
- <ProjectReference
- Condition="Exists('..\..\..\src\Qt.DotNet.Adapter\Qt.DotNet.Adapter.csproj')"
- Include="..\..\..\src\Qt.DotNet.Adapter\Qt.DotNet.Adapter.csproj" />
- </ItemGroup>
- <ItemGroup>
- <ClInclude Include="..\..\..\include\qdotnetadapter.h" />
- <ClInclude Include="..\..\..\include\qdotnetarray.h" />
- <ClInclude Include="..\..\..\include\qdotnetcallback.h" />
- <ClInclude Include="..\..\..\include\qdotnetevent.h" />
- <ClInclude Include="..\..\..\include\qdotnetexception.h" />
- <ClInclude Include="..\..\..\include\qdotnetfunction.h" />
- <ClInclude Include="..\..\..\include\qdotnethost.h" />
- <ClInclude Include="..\..\..\include\qdotnethostfxr.h" />
- <ClInclude Include="..\..\..\include\qdotnetinterface.h" />
- <ClInclude Include="..\..\..\include\qdotnetmarshal.h" />
- <ClInclude Include="..\..\..\include\qdotnetobject.h" />
- <ClInclude Include="..\..\..\include\qdotnetparameter.h" />
- <ClInclude Include="..\..\..\include\qdotnetref.h" />
- <ClInclude Include="..\..\..\include\qdotnetsafemethod.h" />
- <ClInclude Include="..\..\..\include\qdotnettype.h" />
- <QtMoc Include="qlaprecorder.h" />
- </ItemGroup>
- <ItemGroup>
- <Image Include="content\center.png" />
- <Image Include="content\chrono_1_center.png" />
- <Image Include="content\chrono_1_hand.png" />
- <Image Include="content\chrono_2_center.png" />
- <Image Include="content\chrono_2_hand.png" />
- <Image Include="content\chrono_3_center.png" />
- <Image Include="content\chrono_3_needle.png" />
- <Image Include="content\hour_hand.png" />
- <Image Include="content\minute_hand.png" />
- <Image Include="content\second_hand.png" />
- <Image Include="content\watchface.png" />
- </ItemGroup>
- <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
- <ImportGroup Condition="Exists('$(QtMsBuild)\qt.targets')">
- <Import Project="$(QtMsBuild)\qt.targets" />
- </ImportGroup>
- <ImportGroup Label="ExtensionTargets">
- </ImportGroup>
-</Project> \ No newline at end of file
diff --git a/examples/Chronometer/QmlChronometer/QmlChronometer.vcxproj.filters b/examples/Chronometer/QmlChronometer/QmlChronometer.vcxproj.filters
deleted file mode 100644
index 30ca20d..0000000
--- a/examples/Chronometer/QmlChronometer/QmlChronometer.vcxproj.filters
+++ /dev/null
@@ -1,156 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <ItemGroup>
- <Filter Include="Source Files">
- <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
- <Extensions>qml;cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
- </Filter>
- <Filter Include="Header Files">
- <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
- <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
- </Filter>
- <Filter Include="Resource Files">
- <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
- <Extensions>qrc;rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
- </Filter>
- <Filter Include="Form Files">
- <UniqueIdentifier>{99349809-55BA-4b9d-BF79-8FDBB0286EB3}</UniqueIdentifier>
- <Extensions>ui</Extensions>
- </Filter>
- <Filter Include="Translation Files">
- <UniqueIdentifier>{639EADAA-A684-42e4-A9AD-28FC9BCB8F7C}</UniqueIdentifier>
- <Extensions>ts</Extensions>
- </Filter>
- <Filter Include="Header Files\qtdotnet">
- <UniqueIdentifier>{a17873af-389e-48bb-9b3d-0d6fcd150a7f}</UniqueIdentifier>
- </Filter>
- <Filter Include="Resource Files\png">
- <UniqueIdentifier>{1f892826-c7e1-49be-b4eb-0007d1308298}</UniqueIdentifier>
- </Filter>
- <Filter Include="Source Files\qml">
- <UniqueIdentifier>{9f5c583a-a421-4bcf-9f22-59ef59da1b60}</UniqueIdentifier>
- <Extensions>.qml</Extensions>
- </Filter>
- </ItemGroup>
- <ItemGroup>
- <ClCompile Include="main.cpp">
- <Filter>Source Files</Filter>
- </ClCompile>
- <QtRcc Include="qml.qrc">
- <Filter>Resource Files</Filter>
- </QtRcc>
- <ClCompile Include="qchronometer.cpp">
- <Filter>Source Files</Filter>
- </ClCompile>
- <ClCompile Include="qlaprecorder.cpp">
- <Filter>Source Files</Filter>
- </ClCompile>
- </ItemGroup>
- <ItemGroup>
- <QtMoc Include="qchronometer.h">
- <Filter>Header Files</Filter>
- </QtMoc>
- <QtMoc Include="qlaprecorder.h">
- <Filter>Header Files</Filter>
- </QtMoc>
- </ItemGroup>
- <ItemGroup>
- <CopyFileToFolders Include="..\..\..\bin\Qt.DotNet.Adapter.dll" />
- </ItemGroup>
- <ItemGroup>
- <ClInclude Include="..\..\..\include\qdotnetadapter.h">
- <Filter>Header Files\qtdotnet</Filter>
- </ClInclude>
- <ClInclude Include="..\..\..\include\qdotnetcallback.h">
- <Filter>Header Files\qtdotnet</Filter>
- </ClInclude>
- <ClInclude Include="..\..\..\include\qdotnetevent.h">
- <Filter>Header Files\qtdotnet</Filter>
- </ClInclude>
- <ClInclude Include="..\..\..\include\qdotnetfunction.h">
- <Filter>Header Files\qtdotnet</Filter>
- </ClInclude>
- <ClInclude Include="..\..\..\include\qdotnethost.h">
- <Filter>Header Files\qtdotnet</Filter>
- </ClInclude>
- <ClInclude Include="..\..\..\include\qdotnethostfxr.h">
- <Filter>Header Files\qtdotnet</Filter>
- </ClInclude>
- <ClInclude Include="..\..\..\include\qdotnetinterface.h">
- <Filter>Header Files\qtdotnet</Filter>
- </ClInclude>
- <ClInclude Include="..\..\..\include\qdotnetmarshal.h">
- <Filter>Header Files\qtdotnet</Filter>
- </ClInclude>
- <ClInclude Include="..\..\..\include\qdotnetobject.h">
- <Filter>Header Files\qtdotnet</Filter>
- </ClInclude>
- <ClInclude Include="..\..\..\include\qdotnetparameter.h">
- <Filter>Header Files\qtdotnet</Filter>
- </ClInclude>
- <ClInclude Include="..\..\..\include\qdotnetref.h">
- <Filter>Header Files\qtdotnet</Filter>
- </ClInclude>
- <ClInclude Include="..\..\..\include\qdotnettype.h">
- <Filter>Header Files\qtdotnet</Filter>
- </ClInclude>
- <ClInclude Include="..\..\..\include\qdotnetarray.h">
- <Filter>Header Files\qtdotnet</Filter>
- </ClInclude>
- <ClInclude Include="..\..\..\include\qdotnetexception.h">
- <Filter>Header Files\qtdotnet</Filter>
- </ClInclude>
- <ClInclude Include="..\..\..\include\qdotnetsafemethod.h">
- <Filter>Header Files\qtdotnet</Filter>
- </ClInclude>
- </ItemGroup>
- <ItemGroup>
- <Image Include="content\center.png">
- <Filter>Resource Files\png</Filter>
- </Image>
- <Image Include="content\chrono_1_center.png">
- <Filter>Resource Files\png</Filter>
- </Image>
- <Image Include="content\chrono_1_hand.png">
- <Filter>Resource Files\png</Filter>
- </Image>
- <Image Include="content\chrono_2_center.png">
- <Filter>Resource Files\png</Filter>
- </Image>
- <Image Include="content\chrono_2_hand.png">
- <Filter>Resource Files\png</Filter>
- </Image>
- <Image Include="content\chrono_3_center.png">
- <Filter>Resource Files\png</Filter>
- </Image>
- <Image Include="content\chrono_3_needle.png">
- <Filter>Resource Files\png</Filter>
- </Image>
- <Image Include="content\hour_hand.png">
- <Filter>Resource Files\png</Filter>
- </Image>
- <Image Include="content\minute_hand.png">
- <Filter>Resource Files\png</Filter>
- </Image>
- <Image Include="content\second_hand.png">
- <Filter>Resource Files\png</Filter>
- </Image>
- <Image Include="content\watchface.png">
- <Filter>Resource Files\png</Filter>
- </Image>
- </ItemGroup>
- <ItemGroup>
- <None Include="QChronometer\AdjustmentWheel.qml">
- <Filter>Source Files\qml</Filter>
- </None>
- <None Include="QChronometer\InsetDial.qml">
- <Filter>Source Files\qml</Filter>
- </None>
- <None Include="QChronometer\WatchButton.qml">
- <Filter>Source Files\qml</Filter>
- </None>
- <None Include="main.qml">
- <Filter>Source Files</Filter>
- </None>
- </ItemGroup>
-</Project> \ No newline at end of file
diff --git a/examples/Chronometer/QmlChronometer/content/center.png b/examples/Chronometer/QmlChronometer/content/center.png
deleted file mode 100644
index a6c610f..0000000
--- a/examples/Chronometer/QmlChronometer/content/center.png
+++ /dev/null
Binary files differ
diff --git a/examples/Chronometer/QmlChronometer/content/chrono_1_center.png b/examples/Chronometer/QmlChronometer/content/chrono_1_center.png
deleted file mode 100644
index eb57973..0000000
--- a/examples/Chronometer/QmlChronometer/content/chrono_1_center.png
+++ /dev/null
Binary files differ
diff --git a/examples/Chronometer/QmlChronometer/content/chrono_1_hand.png b/examples/Chronometer/QmlChronometer/content/chrono_1_hand.png
deleted file mode 100644
index d507b5e..0000000
--- a/examples/Chronometer/QmlChronometer/content/chrono_1_hand.png
+++ /dev/null
Binary files differ
diff --git a/examples/Chronometer/QmlChronometer/content/chrono_2_center.png b/examples/Chronometer/QmlChronometer/content/chrono_2_center.png
deleted file mode 100644
index 1552403..0000000
--- a/examples/Chronometer/QmlChronometer/content/chrono_2_center.png
+++ /dev/null
Binary files differ
diff --git a/examples/Chronometer/QmlChronometer/content/chrono_2_hand.png b/examples/Chronometer/QmlChronometer/content/chrono_2_hand.png
deleted file mode 100644
index 390b0e4..0000000
--- a/examples/Chronometer/QmlChronometer/content/chrono_2_hand.png
+++ /dev/null
Binary files differ
diff --git a/examples/Chronometer/QmlChronometer/content/chrono_3_center.png b/examples/Chronometer/QmlChronometer/content/chrono_3_center.png
deleted file mode 100644
index 6dbfb64..0000000
--- a/examples/Chronometer/QmlChronometer/content/chrono_3_center.png
+++ /dev/null
Binary files differ
diff --git a/examples/Chronometer/QmlChronometer/content/chrono_3_needle.png b/examples/Chronometer/QmlChronometer/content/chrono_3_needle.png
deleted file mode 100644
index 6499d0a..0000000
--- a/examples/Chronometer/QmlChronometer/content/chrono_3_needle.png
+++ /dev/null
Binary files differ
diff --git a/examples/Chronometer/QmlChronometer/content/hour_hand.png b/examples/Chronometer/QmlChronometer/content/hour_hand.png
deleted file mode 100644
index 2bd7ad7..0000000
--- a/examples/Chronometer/QmlChronometer/content/hour_hand.png
+++ /dev/null
Binary files differ
diff --git a/examples/Chronometer/QmlChronometer/content/minute_hand.png b/examples/Chronometer/QmlChronometer/content/minute_hand.png
deleted file mode 100644
index ee352fe..0000000
--- a/examples/Chronometer/QmlChronometer/content/minute_hand.png
+++ /dev/null
Binary files differ
diff --git a/examples/Chronometer/QmlChronometer/content/second_hand.png b/examples/Chronometer/QmlChronometer/content/second_hand.png
deleted file mode 100644
index 33f4504..0000000
--- a/examples/Chronometer/QmlChronometer/content/second_hand.png
+++ /dev/null
Binary files differ
diff --git a/examples/Chronometer/QmlChronometer/content/watchface.png b/examples/Chronometer/QmlChronometer/content/watchface.png
deleted file mode 100644
index e336d5e..0000000
--- a/examples/Chronometer/QmlChronometer/content/watchface.png
+++ /dev/null
Binary files differ
diff --git a/examples/Chronometer/QmlChronometer/main.cpp b/examples/Chronometer/QmlChronometer/main.cpp
deleted file mode 100644
index 94f8f45..0000000
--- a/examples/Chronometer/QmlChronometer/main.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-/***************************************************************************************************
- Copyright (C) 2023 The Qt Company Ltd.
- SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-***************************************************************************************************/
-
-#include "qchronometer.h"
-#include "qlaprecorder.h"
-
-#include <QGuiApplication>
-#include <QQmlApplicationEngine>
-#include <QQmlContext>
-
-int main(int argc, char *argv[])
-{
- QGuiApplication app(argc, argv);
- QQmlApplicationEngine engine;
-
- QLapRecorder lapRecorder;
- engine.rootContext()->setContextProperty("laps", &lapRecorder);
-
- QChronometer chrono(lapRecorder);
- engine.rootContext()->setContextProperty("chrono", &chrono);
-
- engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
- if (engine.rootObjects().isEmpty())
- return -1;
-
- return app.exec();
-}
diff --git a/examples/Chronometer/QmlChronometer/main.qml b/examples/Chronometer/QmlChronometer/main.qml
deleted file mode 100644
index 059c8ae..0000000
--- a/examples/Chronometer/QmlChronometer/main.qml
+++ /dev/null
@@ -1,453 +0,0 @@
-/***************************************************************************************************
- Copyright (C) 2023 The Qt Company Ltd.
- SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-***************************************************************************************************/
-
-import QtQml
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import QtQuick.Shapes
-
-import "QChronometer"
-
-Window {
- visible: true
- property int windowWidth: 544 + (showHelp.checked ? 265 : 0)
- Behavior on windowWidth { SmoothedAnimation { duration: 750 } }
- width: windowWidth; minimumWidth: windowWidth; maximumWidth: windowWidth
- height: 500; minimumHeight: height; maximumHeight: height
- title: "QML Chronometer"
-
- //////////////////////////////////////////////////////////////////
- // Colors
- readonly property var gray1: "#111111"
- readonly property var gray3: "#333333"
- readonly property var gray4: "#444444"
- readonly property var gray6: "#666666"
- readonly property var redF6: "#FF6666"
- readonly property var green86: "#668866"
- readonly property var blue86: "#666688"
- readonly property var gray8: "#888888"
-
- //////////////////////////////////////////////////////////////////
- // Window background color
- color: gray4
-
- //////////////////////////////////////////////////////////////////
- // Stopwatch mode
- property int showLap: currentLap
- // 0: showing current lap, or stopwatch not running
- readonly property int currentLap: 0
- // 1: showing last recorded lap
- readonly property int lastLap: 1
- // 2: showing best recorded lap
- readonly property int bestLap: 2
-
- //////////////////////////////////////////////////////////////////
- // Watch
- Image {
- id: watch
- source: "watchface.png"
- }
-
- //////////////////////////////////////////////////////////////////
- // Rim
- Rectangle {
- color: "transparent"
- border { color: gray8; width: 3 }
- anchors.centerIn: watch
- width: watch.width; height: watch.height; radius: watch.width / 2
- }
-
- //////////////////////////////////////////////////////////////////
- // Calendar
- Text {
- enabled: false
- x: 345; y: 295; width: 32; height: 22
- transform: Rotation { origin.x: 0; origin.y: 0; angle: 30 }
- horizontalAlignment: Text.AlignHCenter
- verticalAlignment: Text.AlignVCenter
- font { bold: true; pointSize: 11 }
- text: chrono.day
- }
-
- //////////////////////////////////////////////////////////////////
- // Inset dial #1 (above-left of center; 30x minutes)
- InsetDial {
- id: insetDial1
- handSource: "/chrono_1_hand.png"
- pinSource: "/chrono_1_center.png"
- centerX: 176; centerY: 208
- rotationAngle:
- (showLap == lastLap) ? (
- /////////////////////////////////////////////////////////////
- // Show minutes of previous lap
- (laps.lastMinutes % 30) * 12
- ) : (showLap == bestLap) ? (
- /////////////////////////////////////////////////////////////
- // Show minutes of best lap
- (laps.bestMinutes % 30) * 12
- ) : (
- /////////////////////////////////////////////////////////////
- // Show minutes of current lap
- (chrono.elapsedMinutes % 30) * 12
- )
- }
-
- ////////////////////////////////////////////////////////////////////////////////////
- // Inset chrono counter #2 (above-right of center; 10x 1/10 second or 10x hours)
- InsetDial {
- id: insetDial2
- handSource: "/chrono_2_hand.png"
- pinSource: "/chrono_2_center.png"
- centerX: 325; centerY: 208
- rotationAngle:
- (showLap == lastLap) ? (
- /////////////////////////////////////////////////////////////
- // Show previous lap
- (laps.lastHours == 0 && laps.lastMinutes < 30) ? (
- /////////////////////////////////////////////////////////////
- // 1/10 seconds
- laps.lastMilliseconds * 360 / 1000
- ) : (
- /////////////////////////////////////////////////////////////
- // hours
- ((laps.lastHours % 10) + (laps.lastMinutes / 60)) * 360 / 10
- )
- ) : (showLap == bestLap) ? (
- /////////////////////////////////////////////////////////////
- // Show best lap
- (laps.bestHours == 0 && laps.bestMinutes < 30) ? (
- /////////////////////////////////////////////////////////////
- // 1/10 seconds
- laps.bestMilliseconds * 360 / 1000
- ) : (
- /////////////////////////////////////////////////////////////
- // hours
- ((laps.bestHours % 10) + (laps.bestMinutes / 60)) * 360 / 10
- )
- ) : (
- /////////////////////////////////////////////////////////////
- // Show current lap
- (chrono.elapsedHours < 1 && chrono.elapsedMinutes < 30) ? (
- /////////////////////////////////////////////////////////////
- // 1/10 seconds
- chrono.elapsedMilliseconds * 360 / 1000
- ) : (
- /////////////////////////////////////////////////////////////
- // hours
- (chrono.elapsedHours % 10) * 360 / 10
- )
- )
- }
-
- //////////////////////////////////////////////////////////////////////
- // Inset chrono counter #3 (below center; 60x seconds)
- InsetDial {
- id: insetDial3
- handSource: "/chrono_3_needle.png"
- pinSource: "/chrono_3_center.png"
- centerX: 250; centerY: 336
- rotationAngle: 150 + (
- (showLap == lastLap) ? (
- /////////////////////////////////////////////////////////////
- // Show seconds of previous lap
- laps.lastSeconds * 6
- ) : (showLap == bestLap) ? (
- /////////////////////////////////////////////////////////////
- // Show seconds of best lap
- laps.bestSeconds * 6
- ) : (
- /////////////////////////////////////////////////////////////
- // Show seconds of current (wall-clock) time
- chrono.seconds * 6
- ))
- }
-
- //////////////////////////////////////////////////////////////////////
- // Hours hand for current (wall-clock) time
- Image {
- id: hoursHand;
- source: "hour_hand.png"
- transform: Rotation {
- origin.x: 249; origin.y: 251
- angle: 110 + (chrono.hours % 12) * 30
- Behavior on angle {
- enabled: adjustmentWheel.turnSpeed < 75
- SpringAnimation { spring: 3; damping: 0.5; modulus: 360 }
- }
- }
- }
-
- //////////////////////////////////////////////////////////////////////
- // Minutes hand for current (wall-clock) time
- Image {
- id: minutesHand;
- source: "minute_hand.png"
- transform: Rotation {
- origin.x: 249; origin.y: 251
- angle: -108 + chrono.minutes * 6
- Behavior on angle {
- enabled: adjustmentWheel.turnSpeed < 75
- SpringAnimation { spring: 3; damping: 0.5; modulus: 360 }
- }
- }
- }
- Image {
- source: "center.png"
- }
-
- //////////////////////////////////////////////////////////////////////
- // Stopwatch seconds hand
- Image {
- id: secondsHand;
- source: "second_hand.png"
- transform: Rotation {
- origin.x: 250; origin.y: 250
- angle: chrono.elapsedSeconds * 6
- Behavior on angle {
- SpringAnimation { spring: 3; damping: 0.5; modulus: 360 }
- }
- }
- }
-
- //////////////////////////////////////////////////////////////////////
- // Adjustment wheel
- AdjustmentWheel {
- id: adjustmentWheel
- startX: 498; startY: 215
- }
-
- //////////////////////////////////////////////////////////////////////
- // Adjust date
- Switch {
- id: adjustDay
- x: 500; y: 290; padding: 0; spacing: -35
- palette.button: gray8; font { bold: true; pointSize: 9 }
- text: "Date"
- checked: chrono.adjustDayMode
- onToggled: chrono.adjustDayMode = (position == 1)
- }
-
- //////////////////////////////////////////////////////////////////////
- // Adjust time
- Switch {
- id: adjustTime
- x: 500; y: 310; padding: 0; spacing: -35
- palette.button: gray8; font { bold: true; pointSize: 9 }
- text: "Time"
- checked: chrono.adjustTimeMode
- onToggled: chrono.adjustTimeMode = (position == 1)
- }
-
- //////////////////////////////////////////////////////////////////////
- // Stopwatch start/stop button
- WatchButton {
- id: buttonStartStop
- x: 425; y: 5
- buttonText: "Start\n\nStop"; split: true
- color: chrono.started ? redF6 : !enabled ? gray3 : gray6
- enabled: !chrono.adjustDayMode && !chrono.adjustTimeMode
- onClicked: chrono.startStop()
- }
-
- //////////////////////////////////////////////////////////////////////
- // Stopwatch lap/reset button
- WatchButton {
- id: buttonLapReset
- x: 425; y: 425
- buttonText: "Lap\n\nReset"; split: true
- color: !enabled ? gray3 : gray6
- enabled: chrono.started
- || chrono.elapsedHours > 0
- || chrono.elapsedMinutes > 0
- || chrono.elapsedSeconds > 0
- || chrono.elapsedMilliseconds > 0
- || laps.lapCount > 0
- onClicked: {
- chrono.reset();
- if (!chrono.started) {
- laps.reset();
- showLap = currentLap;
- }
- }
- }
-
- //////////////////////////////////////////////////////////////////////
- // Stopwatch last lap button
- WatchButton {
- id: buttonLastLap
- x: 5; y: 425
- buttonText: "Last\nLap"; split: false
- color: (showLap == lastLap) ? blue86 : !enabled ? gray3 : gray6
- enabled: laps.lapCount > 0
- onClicked: {
- showLapTimer.stop();
- if (laps.lapCount > 0) {
- showLap = (showLap != lastLap) ? lastLap : currentLap;
- }
- }
- }
-
- //////////////////////////////////////////////////////////////////////
- // Stopwatch best lap button
- WatchButton {
- id: buttonBestLap
- x: 5; y: 5
- buttonText: "Best\nLap"; split: false
- color: (showLap == bestLap) ? green86 : !enabled ? gray3 : gray6
- enabled: laps.lapCount > 1
- onClicked: {
- showLapTimer.stop();
- if (laps.lapCount > 1) {
- showLap = (showLap != bestLap) ? bestLap : currentLap;
- }
- }
- }
-
- //////////////////////////////////////////////////////////////////////
- // Timer to show last/best lap for 5 secs. after mark
- Timer {
- id: showLapTimer
- interval: 5000
- running: false
- repeat: false
- onTriggered: showLap = currentLap
- }
-
- //////////////////////////////////////////////////////////////////////
- // Lap events
- Connections {
- target: laps
-
- //////////////////////////////////////////////////////////////////////
- // Lap counter changed: new lap recorded, or lap counter reset
- function onLapCountChanged() {
- if (laps.lapCount > 0) {
- showLap = lastLap;
- showLapTimer.restart()
- }
- }
-
- //////////////////////////////////////////////////////////////////////
- // New best lap recorded
- function onNewBestLap() {
- if (laps.lapCount > 1) {
- showLap = bestLap;
- showLapTimer.restart()
- }
- }
- }
-
- //////////////////////////////////////////////////////////////////////
- // Keyboard events
- Shortcut {
- sequence: " "
- onActivated: buttonStartStop.clicked()
- }
- Shortcut {
- sequences: [ "Return", "Enter" ]
- onActivated: {
- if (chrono.started)
- buttonLapReset.clicked();
- }
- }
- Shortcut {
- sequence: "Escape"
- onActivated: {
- if (chrono.adjustDayMode || chrono.adjustTimeMode)
- chrono.adjustDayMode = chrono.adjustTimeMode = false;
- else if (!chrono.started)
- buttonLapReset.clicked()
- else
- showLap = currentLap;
- }
- }
- Shortcut {
- sequence: "Tab"
- onActivated: buttonLastLap.clicked()
- }
- Shortcut {
- sequence: "Shift+Tab"
- onActivated: buttonBestLap.clicked()
- }
- Shortcut {
- sequence: "Ctrl+D"
- onActivated: {
- adjustDay.toggle();
- adjustDay.onToggled();
- }
- }
- Shortcut {
- sequence: "Ctrl+T"
- onActivated: {
- adjustTime.toggle();
- adjustTime.onToggled();
- }
- }
- Shortcut {
- sequence: "Up"
- onActivated: adjustmentWheel.turn(1)
- }
- Shortcut {
- sequence: "Down"
- onActivated: adjustmentWheel.turn(-1)
- }
- Shortcut {
- sequence: "F1"
- onActivated: showHelp.toggle()
- }
-
- //////////////////////////////////////////////////////////////////////
- // Usage instructions
- RoundButton {
- id: showHelp
- checkable: true
- x: 524; y: 0; width: 20; height: 40
- palette.button: gray6
- radius: 0
- contentItem: Text {
- font.bold: true
- font.pointSize: 11
- horizontalAlignment: Text.AlignHCenter
- verticalAlignment: Text.AlignVCenter
- text: parent.checked ? "<" : "?"
- }
- }
- Rectangle {
- x: 544; y:0
- width: 265
- height: 500
- color: gray6
- border.width: 0
- Text {
- anchors.fill: parent
- anchors.topMargin: 10
- anchors.leftMargin: 5
- anchors.rightMargin: 5
- textFormat: Text.MarkdownText
- wrapMode: Text.WordWrap
- color: gray1
- text: "### Usage instructions
-
-The **hours and minutes hands** show the current (wall-clock) time. The **seconds hand** shows the
-stopwatch elapsed seconds. **Inset dial #1** (above-left of center) shows elapsed minutes of
-current/last/best lap. **Inset dial #2** (above-right of center) shows a 1/10th-second counter of
-current/last/best lap. **Inset dial #3** (below center) shows seconds of current time, or elapsed
-seconds of last/best lap.
-
-Press **Start|Stop** (shortcut: **[Space]**) to begin timing. While the stopwatch is running, press
-**Lap|Reset** (shortcut: **[Enter]**) to record a lap. The stopwatch can memorize the last and the
-best lap. Press **Last**&nbsp;**Lap** (shortcut: **[Tab]**) or **Best**&nbsp;**Lap** (shortcut:
-**[Shift+Tab]**) to view the recorded time of the last or best lap. Press **Start|Stop** (shortcut:
-**[Space]**) again to stop timing. Press **Lap|Reset** (shortcut: **[Esc]**) to reset stopwatch
-counters and clear lap memory.
-
-Press the **Date** switch (shortcut: **[Ctrl+D]**) or **Time** switch (shortcut: **[Ctrl+T]**) to
-enter adjustment mode. Turn the **adjustment wheel** (shortcut: **mouse wheel** or **[Up]** /
-**[Down]**) to set the desired date or time. Press the active adjustment switch (or **[Esc]**) to
-leave adjustment mode. Note: entering adjustment mode resets the stopwatch."
- }
- }
-}
diff --git a/examples/Chronometer/QmlChronometer/qchronometer.cpp b/examples/Chronometer/QmlChronometer/qchronometer.cpp
deleted file mode 100644
index 16951a0..0000000
--- a/examples/Chronometer/QmlChronometer/qchronometer.cpp
+++ /dev/null
@@ -1,163 +0,0 @@
-/***************************************************************************************************
- Copyright (C) 2023 The Qt Company Ltd.
- SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-***************************************************************************************************/
-
-#include "qchronometer.h"
-#include "qlaprecorder.h"
-
-#include <qdotnetevent.h>
-
-struct QChronometerPrivate : QDotNetObject::IEventHandler
-{
- QChronometerPrivate(QChronometer *q)
- :q(q)
- {}
-
- void handleEvent(const QString &eventName, QDotNetObject &sender, QDotNetObject &args) override
- {
- if (eventName != "PropertyChanged")
- return;
-
- if (args.type().fullName() != QDotNetPropertyEvent::FullyQualifiedTypeName)
- return;
-
- const auto propertyChangedEvent = args.cast<QDotNetPropertyEvent>();
- if (propertyChangedEvent.propertyName() == "Hours")
- emit q->hoursChanged();
- else if (propertyChangedEvent.propertyName() == "Minutes")
- emit q->minutesChanged();
- else if (propertyChangedEvent.propertyName() == "Seconds")
- emit q->secondsChanged();
- else if (propertyChangedEvent.propertyName() == "Day")
- emit q->dayChanged();
- else if (propertyChangedEvent.propertyName() == "Started")
- emit q->startedChanged();
- else if (propertyChangedEvent.propertyName() == "ElapsedHours")
- emit q->elapsedHoursChanged();
- else if (propertyChangedEvent.propertyName() == "ElapsedMinutes")
- emit q->elapsedMinutesChanged();
- else if (propertyChangedEvent.propertyName() == "ElapsedSeconds")
- emit q->elapsedSecondsChanged();
- else if (propertyChangedEvent.propertyName() == "ElapsedMilliseconds")
- emit q->elapsedMillisecondsChanged();
- else if (propertyChangedEvent.propertyName() == "AdjustDayMode")
- emit q->adjustDayModeChanged();
- else if (propertyChangedEvent.propertyName() == "AdjustTimeMode")
- emit q->adjustTimeModeChanged();
- }
-
- QChronometer *q = nullptr;
-
- QDotNetFunction<double> hours= nullptr;
- QDotNetFunction<double> minutes = nullptr;
- QDotNetFunction<double> seconds = nullptr;
- QDotNetFunction<int> day = nullptr;
- QDotNetFunction<double> elapsedHours = nullptr;
- QDotNetFunction<double> elapsedMinutes = nullptr;
- QDotNetFunction<double> elapsedSeconds = nullptr;
- QDotNetFunction<double> elapsedMilliseconds = nullptr;
- QDotNetFunction<bool> started, adjustDayMode, adjustTimeMode = nullptr;
- QDotNetFunction<void> startStop, reset, breakWatch = nullptr;
- QDotNetFunction<void, bool> setAdjustDayMode, setAdjustTimeMode = nullptr;
- QDotNetFunction<void, int> adjust = nullptr;
-};
-
-
-Q_DOTNET_OBJECT_IMPL(QChronometer, Q_DOTNET_OBJECT_INIT(d(new QChronometerPrivate(this))));
-
-
-QChronometer::QChronometer(const ILapRecorder &lapRecorder)
- : d(new QChronometerPrivate(this))
-{
- *this = constructor<QChronometer>().invoke(nullptr);
- method<void, ILapRecorder>("set_LapRecorder").invoke(*this, lapRecorder);
- subscribeEvent("PropertyChanged", d);
-}
-
-QChronometer::~QChronometer()
-{
- if (isValid())
- unsubscribeEvent("PropertyChanged", d);
- delete d;
-}
-
-double QChronometer::hours() const
-{
- return method("get_Hours", d->hours).invoke(*this);
-}
-
-double QChronometer::minutes() const
-{
- return method("get_Minutes", d->minutes).invoke(*this);
-}
-
-double QChronometer::seconds() const
-{
- return method("get_Seconds", d->seconds).invoke(*this);
-}
-
-int QChronometer::day() const
-{
- return method("get_Day", d->day).invoke(*this);
-}
-
-bool QChronometer::started() const
-{
- return method("get_Started", d->started).invoke(*this);
-}
-
-double QChronometer::elapsedHours() const
-{
- return method("get_ElapsedHours", d->elapsedHours).invoke(*this);
-}
-
-double QChronometer::elapsedMinutes() const
-{
- return method("get_ElapsedMinutes", d->elapsedMinutes).invoke(*this);
-}
-
-double QChronometer::elapsedSeconds() const
-{
- return method("get_ElapsedSeconds", d->elapsedSeconds).invoke(*this);
-}
-
-double QChronometer::elapsedMilliseconds() const
-{
- return method("get_ElapsedMilliseconds", d->elapsedMilliseconds).invoke(*this);
-}
-
-bool QChronometer::adjustDayMode() const
-{
- return method("get_AdjustDayMode", d->adjustDayMode).invoke(*this);
-}
-
-void QChronometer::setAdjustDayMode(bool value)
-{
- method("set_AdjustDayMode", d->setAdjustDayMode).invoke(*this, value);
-}
-
-bool QChronometer::adjustTimeMode() const
-{
- return method("get_AdjustTimeMode", d->adjustTimeMode).invoke(*this);
-}
-
-void QChronometer::setAdjustTimeMode(bool value)
-{
- method("set_AdjustTimeMode", d->setAdjustTimeMode).invoke(*this, value);
-}
-
-void QChronometer::adjust(int delta)
-{
- method("Adjust", d->adjust).invoke(*this, delta);
-}
-
-void QChronometer::startStop()
-{
- method("StartStop", d->startStop).invoke(*this);
-}
-
-void QChronometer::reset()
-{
- method("Reset", d->reset).invoke(*this);
-}
diff --git a/examples/Chronometer/QmlChronometer/qchronometer.h b/examples/Chronometer/QmlChronometer/qchronometer.h
deleted file mode 100644
index b90c0e5..0000000
--- a/examples/Chronometer/QmlChronometer/qchronometer.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/***************************************************************************************************
- Copyright (C) 2023 The Qt Company Ltd.
- SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-***************************************************************************************************/
-
-#pragma once
-
-#include <qdotnetinterface.h>
-#include <qdotnetobject.h>
-
-#ifdef __GNUC__
-# pragma GCC diagnostic push
-# pragma GCC diagnostic ignored "-Wconversion"
-#endif
-#include <QObject>
-#include <QString>
-#ifdef __GNUC__
-# pragma GCC diagnostic pop
-#endif
-
-struct ILapRecorder;
-struct QChronometerPrivate;
-
-class QChronometer : public QObject, public QDotNetObject
-{
- Q_OBJECT
- Q_PROPERTY(double hours READ hours NOTIFY hoursChanged)
- Q_PROPERTY(double minutes READ minutes NOTIFY minutesChanged)
- Q_PROPERTY(double seconds READ seconds NOTIFY secondsChanged)
- Q_PROPERTY(int day READ day NOTIFY dayChanged)
- Q_PROPERTY(bool started READ started NOTIFY startedChanged)
- Q_PROPERTY(double elapsedHours READ elapsedHours NOTIFY elapsedHoursChanged)
- Q_PROPERTY(double elapsedMinutes READ elapsedMinutes NOTIFY elapsedMinutesChanged)
- Q_PROPERTY(double elapsedSeconds READ elapsedSeconds NOTIFY elapsedSecondsChanged)
- Q_PROPERTY(double elapsedMilliseconds
- READ elapsedMilliseconds NOTIFY elapsedMillisecondsChanged)
- Q_PROPERTY(bool adjustDayMode
- READ adjustDayMode WRITE setAdjustDayMode NOTIFY adjustDayModeChanged)
- Q_PROPERTY(bool adjustTimeMode
- READ adjustTimeMode WRITE setAdjustTimeMode NOTIFY adjustTimeModeChanged)
-
-public:
- Q_DOTNET_OBJECT(QChronometer, "WatchModels.Chronometer, ChronometerModel");
-
- QChronometer(const ILapRecorder &lapRecorder);
- ~QChronometer() override;
-
- double hours() const;
- double minutes() const;
- double seconds() const;
- int day() const;
- bool started() const;
- double elapsedHours() const;
- double elapsedMinutes() const;
- double elapsedSeconds() const;
- double elapsedMilliseconds() const;
- bool adjustDayMode() const;
- bool adjustTimeMode() const;
-
-public slots:
- void startStop();
- void reset();
- void setAdjustDayMode(bool value);
- void setAdjustTimeMode(bool value);
- void adjust(int delta);
-
-
-signals:
- void hoursChanged();
- void minutesChanged();
- void secondsChanged();
- void dayChanged();
- void startedChanged();
- void elapsedHoursChanged();
- void elapsedMinutesChanged();
- void elapsedSecondsChanged();
- void elapsedMillisecondsChanged();
- void adjustDayModeChanged();
- void adjustTimeModeChanged();
- void lap(int hours, int minutes, int seconds, int milliseconds);
-
-private:
- QChronometerPrivate *d = nullptr;
-};
diff --git a/examples/Chronometer/QmlChronometer/qlaprecorder.cpp b/examples/Chronometer/QmlChronometer/qlaprecorder.cpp
deleted file mode 100644
index 20956c0..0000000
--- a/examples/Chronometer/QmlChronometer/qlaprecorder.cpp
+++ /dev/null
@@ -1,128 +0,0 @@
-/***************************************************************************************************
- Copyright (C) 2023 The Qt Company Ltd.
- SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-***************************************************************************************************/
-
-#include "qlaprecorder.h"
-
-struct QLapRecorderPrivate
-{
- QLapRecorderPrivate() = default;
-
- int lapCount = 0;
- int lastHours = 0;
- int lastMinutes = 0;
- int lastSeconds = 0;
- int lastMilliseconds = 0;
- int bestHours = 0;
- int bestMinutes = 0;
- int bestSeconds = 0;
- int bestMilliseconds = 0;
-};
-
-
-ILapRecorder::ILapRecorder() : QDotNetInterface(FullyQualifiedTypeName)
-{
- setCallback<void, int, int, int, int>(
- "Mark", [this](int hours, int minutes, int seconds, int milliseconds)
- {
- mark(hours, minutes, seconds, milliseconds);
- });
-}
-
-
-QLapRecorder::QLapRecorder(QObject *parent)
- : QObject(parent), d(new QLapRecorderPrivate())
-{}
-
-QLapRecorder::~QLapRecorder()
-{
- delete d;
-}
-
-int QLapRecorder::lapCount() const { return d->lapCount; }
-int QLapRecorder::lastHours() const { return d->lastHours; }
-int QLapRecorder::lastMinutes() const { return d->lastMinutes; }
-int QLapRecorder::lastSeconds() const { return d->lastSeconds; }
-int QLapRecorder::lastMilliseconds() const { return d->lastMilliseconds; }
-int QLapRecorder::bestHours() const { return d->bestHours; }
-int QLapRecorder::bestMinutes() const { return d->bestMinutes; }
-int QLapRecorder::bestSeconds() const { return d->bestSeconds; }
-int QLapRecorder::bestMilliseconds() const { return d->bestMilliseconds; }
-
-void QLapRecorder::mark(int hours, int minutes, int seconds, int milliseconds)
-{
- d->lastHours = hours;
- emit lastHoursChanged();
-
- d->lastMinutes = minutes;
- emit lastMinutesChanged();
-
- d->lastSeconds = seconds;
- emit lastSecondsChanged();
-
- d->lastMilliseconds = milliseconds;
- emit lastMillisecondsChanged();
-
- d->lapCount++;
- emit lapCountChanged();
-
- if (d->lapCount > 1
- && (d->lastHours > d->bestHours
- || (d->lastHours == d->bestHours
- && d->lastMinutes > d->bestMinutes)
- || (d->lastHours == d->bestHours
- && d->lastMinutes == d->bestMinutes
- && d->lastSeconds > d->bestSeconds)
- || (d->lastHours == d->bestHours
- && d->lastMinutes == d->bestMinutes
- && d->lastSeconds == d->bestSeconds
- && d->lastMilliseconds > d->bestMilliseconds))) {
- return;
- }
-
- d->bestHours = hours;
- emit bestHoursChanged();
-
- d->bestMinutes = minutes;
- emit bestMinutesChanged();
-
- d->bestSeconds = seconds;
- emit bestSecondsChanged();
-
- d->bestMilliseconds = milliseconds;
- emit bestMillisecondsChanged();
-
- if (d->lapCount > 1)
- emit newBestLap();
-}
-
-void QLapRecorder::reset()
-{
- d->lastHours = 0;
- emit lastHoursChanged();
-
- d->lastMinutes = 0;
- emit lastMinutesChanged();
-
- d->lastSeconds = 0;
- emit lastSecondsChanged();
-
- d->lastMilliseconds = 0;
- emit lastMillisecondsChanged();
-
- d->lapCount = 0;
- emit lapCountChanged();
-
- d->bestHours = 0;
- emit bestHoursChanged();
-
- d->bestMinutes = 0;
- emit bestMinutesChanged();
-
- d->bestSeconds = 0;
- emit bestSecondsChanged();
-
- d->bestMilliseconds = 0;
- emit bestMillisecondsChanged();
-}
diff --git a/examples/Chronometer/QmlChronometer/qlaprecorder.h b/examples/Chronometer/QmlChronometer/qlaprecorder.h
deleted file mode 100644
index 0456288..0000000
--- a/examples/Chronometer/QmlChronometer/qlaprecorder.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/***************************************************************************************************
- Copyright (C) 2023 The Qt Company Ltd.
- SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-***************************************************************************************************/
-
-#pragma once
-
-#include "qchronometer.h"
-
-#include <QObject>
-
-struct QLapRecorderPrivate;
-
-struct ILapRecorder : QDotNetInterface
-{
- static inline const QString& FullyQualifiedTypeName =
- QStringLiteral("WatchModels.ILapRecorder, ChronometerModel");
- ILapRecorder();
-
- virtual void mark(int hours, int minutes, int seconds, int milliseconds) = 0;
- virtual void reset() = 0;
-};
-
-class QLapRecorder : public QObject, public ILapRecorder
-{
- Q_OBJECT
- Q_PROPERTY(int lastHours READ lastHours NOTIFY lastHoursChanged)
- Q_PROPERTY(int lastMinutes READ lastMinutes NOTIFY lastMinutesChanged)
- Q_PROPERTY(int lastSeconds READ lastSeconds NOTIFY lastSecondsChanged)
- Q_PROPERTY(int lastMilliseconds READ lastMilliseconds NOTIFY lastMillisecondsChanged)
- Q_PROPERTY(int bestHours READ bestHours NOTIFY bestHoursChanged)
- Q_PROPERTY(int bestMinutes READ bestMinutes NOTIFY bestMinutesChanged)
- Q_PROPERTY(int bestSeconds READ bestSeconds NOTIFY bestSecondsChanged)
- Q_PROPERTY(int bestMilliseconds READ bestMilliseconds NOTIFY bestMillisecondsChanged)
- Q_PROPERTY(int lapCount READ lapCount NOTIFY lapCountChanged)
-
-public:
- QLapRecorder(QObject *parent = nullptr);
- ~QLapRecorder() override;
-
- [[nodiscard]] int lapCount() const;
- [[nodiscard]] int lastHours() const;
- [[nodiscard]] int lastMinutes() const;
- [[nodiscard]] int lastSeconds() const;
- [[nodiscard]] int lastMilliseconds() const;
- [[nodiscard]] int bestHours() const;
- [[nodiscard]] int bestMinutes() const;
- [[nodiscard]] int bestSeconds() const;
- [[nodiscard]] int bestMilliseconds() const;
-
-public slots:
- void mark(int hours, int minutes, int seconds, int milliseconds) override;
- void reset() override;
-
-signals:
- void lapCountChanged();
- void lastHoursChanged();
- void lastMinutesChanged();
- void lastSecondsChanged();
- void lastMillisecondsChanged();
- void bestHoursChanged();
- void bestMinutesChanged();
- void bestSecondsChanged();
- void bestMillisecondsChanged();
- void newBestLap();
-
-private:
- QLapRecorderPrivate *d = nullptr;
-};
diff --git a/examples/Chronometer/QmlChronometer/qml.qrc b/examples/Chronometer/QmlChronometer/qml.qrc
deleted file mode 100644
index 15d835d..0000000
--- a/examples/Chronometer/QmlChronometer/qml.qrc
+++ /dev/null
@@ -1,19 +0,0 @@
-<RCC>
- <qresource prefix="/">
- <file>main.qml</file>
- <file alias="watchface.png">content/watchface.png</file>
- <file alias="chrono_1_hand.png">content/chrono_1_hand.png</file>
- <file alias="chrono_2_hand.png">content/chrono_2_hand.png</file>
- <file alias="chrono_3_needle.png">content/chrono_3_needle.png</file>
- <file alias="hour_hand.png">content/hour_hand.png</file>
- <file alias="minute_hand.png">content/minute_hand.png</file>
- <file alias="second_hand.png">content/second_hand.png</file>
- <file alias="center.png">content/center.png</file>
- <file alias="chrono_1_center.png">content/chrono_1_center.png</file>
- <file alias="chrono_2_center.png">content/chrono_2_center.png</file>
- <file alias="chrono_3_center.png">content/chrono_3_center.png</file>
- <file>QChronometer/AdjustmentWheel.qml</file>
- <file>QChronometer/InsetDial.qml</file>
- <file>QChronometer/WatchButton.qml</file>
- </qresource>
-</RCC>
diff --git a/examples/EmbeddedWindow/QmlApp/QmlApp.vcxproj b/examples/EmbeddedWindow/QmlApp/QmlApp.vcxproj
deleted file mode 100644
index 2544420..0000000
--- a/examples/EmbeddedWindow/QmlApp/QmlApp.vcxproj
+++ /dev/null
@@ -1,111 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="17.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <ItemGroup Label="ProjectConfigurations">
- <ProjectConfiguration Include="Debug|x64">
- <Configuration>Debug</Configuration>
- <Platform>x64</Platform>
- </ProjectConfiguration>
- <ProjectConfiguration Include="Release|x64">
- <Configuration>Release</Configuration>
- <Platform>x64</Platform>
- </ProjectConfiguration>
- </ItemGroup>
- <PropertyGroup Label="Globals">
- <ProjectGuid>{8C7C4962-6AAA-4A90-927A-BC88C782CAAA}</ProjectGuid>
- <Keyword>QtVS_v304</Keyword>
- <WindowsTargetPlatformVersion Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">10.0.19041.0</WindowsTargetPlatformVersion>
- <WindowsTargetPlatformVersion Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">10.0.19041.0</WindowsTargetPlatformVersion>
- <QtMsBuild Condition="'$(QtMsBuild)'=='' OR !Exists('$(QtMsBuild)\qt.targets')">$(MSBuildProjectDirectory)\QtMsBuild</QtMsBuild>
- </PropertyGroup>
- <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
- <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'" Label="Configuration">
- <ConfigurationType>Application</ConfigurationType>
- <PlatformToolset>v143</PlatformToolset>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'" Label="Configuration">
- <ConfigurationType>Application</ConfigurationType>
- <PlatformToolset>v143</PlatformToolset>
- </PropertyGroup>
- <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
- <ImportGroup Condition="Exists('$(QtMsBuild)\qt_defaults.props')">
- <Import Project="$(QtMsBuild)\qt_defaults.props" />
- </ImportGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'" Label="QtSettings">
- <QtInstall>$(DefaultQtVersion)</QtInstall>
- <QtModules>quick;core</QtModules>
- <QtBuildConfig>debug</QtBuildConfig>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'" Label="QtSettings">
- <QtInstall>$(DefaultQtVersion)</QtInstall>
- <QtModules>quick;core</QtModules>
- <QtBuildConfig>release</QtBuildConfig>
- </PropertyGroup>
- <Target Name="QtMsBuildNotFound" BeforeTargets="CustomBuild;ClCompile" Condition="!Exists('$(QtMsBuild)\qt.targets') or !Exists('$(QtMsBuild)\qt.props')">
- <Message Importance="High" Text="QtMsBuild: could not locate qt.targets, qt.props; project may not build correctly." />
- </Target>
- <ImportGroup Label="ExtensionSettings" />
- <ImportGroup Label="Shared" />
- <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
- <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
- <Import Project="$(QtMsBuild)\Qt.props" />
- </ImportGroup>
- <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
- <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
- <Import Project="$(QtMsBuild)\Qt.props" />
- </ImportGroup>
- <PropertyGroup Label="UserMacros" />
- <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
- <IncludePath>../../../include/;$(IncludePath)</IncludePath>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
- <IncludePath>../../../include/;$(IncludePath)</IncludePath>
- </PropertyGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'" Label="Configuration">
- <ClCompile>
- <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
- <MultiProcessorCompilation>true</MultiProcessorCompilation>
- <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
- <Optimization>Disabled</Optimization>
- </ClCompile>
- <Link>
- <SubSystem>Windows</SubSystem>
- <GenerateDebugInformation>true</GenerateDebugInformation>
- </Link>
- </ItemDefinitionGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'" Label="Configuration">
- <ClCompile>
- <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
- <MultiProcessorCompilation>true</MultiProcessorCompilation>
- <DebugInformationFormat>None</DebugInformationFormat>
- <Optimization>MaxSpeed</Optimization>
- </ClCompile>
- <Link>
- <SubSystem>Windows</SubSystem>
- <GenerateDebugInformation>false</GenerateDebugInformation>
- </Link>
- </ItemDefinitionGroup>
- <ItemGroup>
- <QtMoc Include="mainwindow.h" />
- <QtMoc Include="embeddedwindow.h" />
- <ClCompile Include="main.cpp" />
- <QtRcc Include="qml.qrc" />
- <ClCompile Include="mainwindow.cpp" />
- <ClCompile Include="embeddedwindow.cpp" />
- <None Include="main.qml" />
- </ItemGroup>
- <ItemGroup>
- <Reference Include="Qt.DotNet.Adapter">
- <HintPath>..\..\..\bin\Qt.DotNet.Adapter.dll</HintPath>
- </Reference>
- <ProjectReference Include="..\WpfApp\WpfApp.csproj" />
- </ItemGroup>
- <ItemGroup>
- <Image Include="qt_logo.png" />
- </ItemGroup>
- <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
- <ImportGroup Condition="Exists('$(QtMsBuild)\qt.targets')">
- <Import Project="$(QtMsBuild)\qt.targets" />
- </ImportGroup>
- <ImportGroup Label="ExtensionTargets">
- </ImportGroup>
-</Project> \ No newline at end of file
diff --git a/examples/EmbeddedWindow/QmlApp/QmlApp.vcxproj.filters b/examples/EmbeddedWindow/QmlApp/QmlApp.vcxproj.filters
deleted file mode 100644
index d958351..0000000
--- a/examples/EmbeddedWindow/QmlApp/QmlApp.vcxproj.filters
+++ /dev/null
@@ -1,57 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <ItemGroup>
- <Filter Include="Source Files">
- <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
- <Extensions>qml;cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
- </Filter>
- <Filter Include="Header Files">
- <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
- <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
- </Filter>
- <Filter Include="Resource Files">
- <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
- <Extensions>qrc;rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
- </Filter>
- <Filter Include="Form Files">
- <UniqueIdentifier>{99349809-55BA-4b9d-BF79-8FDBB0286EB3}</UniqueIdentifier>
- <Extensions>ui</Extensions>
- </Filter>
- <Filter Include="Translation Files">
- <UniqueIdentifier>{639EADAA-A684-42e4-A9AD-28FC9BCB8F7C}</UniqueIdentifier>
- <Extensions>ts</Extensions>
- </Filter>
- </ItemGroup>
- <ItemGroup>
- <QtRcc Include="qml.qrc">
- <Filter>Resource Files</Filter>
- </QtRcc>
- <None Include="main.qml">
- <Filter>Source Files</Filter>
- </None>
- <ClCompile Include="mainwindow.cpp">
- <Filter>Source Files</Filter>
- </ClCompile>
- </ItemGroup>
- <ItemGroup>
- <QtMoc Include="mainwindow.h">
- <Filter>Header Files</Filter>
- </QtMoc>
- <QtMoc Include="embeddedwindow.h">
- <Filter>Header Files</Filter>
- </QtMoc>
- </ItemGroup>
- <ItemGroup>
- <ClCompile Include="embeddedwindow.cpp">
- <Filter>Source Files</Filter>
- </ClCompile>
- <ClCompile Include="main.cpp">
- <Filter>Source Files</Filter>
- </ClCompile>
- </ItemGroup>
- <ItemGroup>
- <Image Include="qt_logo.png">
- <Filter>Resource Files</Filter>
- </Image>
- </ItemGroup>
-</Project> \ No newline at end of file
diff --git a/examples/EmbeddedWindow/QmlApp/embeddedwindow.cpp b/examples/EmbeddedWindow/QmlApp/embeddedwindow.cpp
deleted file mode 100644
index 3b7b17d..0000000
--- a/examples/EmbeddedWindow/QmlApp/embeddedwindow.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-/***************************************************************************************************
- Copyright (C) 2023 The Qt Company Ltd.
- SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-***************************************************************************************************/
-
-#include "embeddedwindow.h"
-
-#include <QQmlContext>
-#include <QQmlEngine>
-#include <QQuickView>
-#include <QWindow>
-
-#include "mainwindow.h"
-
-EmbeddedWindow::EmbeddedWindow(QQmlEngine *qmlEngine, MainWindow *mainWindow)
- : qmlEngine(qmlEngine), mainWindow(mainWindow)
-{
- connect(mainWindow, &MainWindow::contentRendered, this, &EmbeddedWindow::show);
- connect(mainWindow, &MainWindow::closed, this, &EmbeddedWindow::close);
-}
-
-EmbeddedWindow::~EmbeddedWindow()
-{
- delete quickView;
-}
-
-void EmbeddedWindow::show()
-{
- embeddedWindow = QWindow::fromWinId((WId)mainWindow->hostHandle());
- quickView = new QQuickView(qmlEngine, embeddedWindow);
- qmlEngine->rootContext()->setContextProperty("window", quickView);
- quickView->setSource(QUrl(QStringLiteral("qrc:/main.qml")));
- quickView->show();
-}
-
-void EmbeddedWindow::close()
-{
- embeddedWindow->close();
-}
diff --git a/examples/EmbeddedWindow/QmlApp/embeddedwindow.h b/examples/EmbeddedWindow/QmlApp/embeddedwindow.h
deleted file mode 100644
index c84a370..0000000
--- a/examples/EmbeddedWindow/QmlApp/embeddedwindow.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/***************************************************************************************************
- Copyright (C) 2023 The Qt Company Ltd.
- SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-***************************************************************************************************/
-
-#pragma once
-
-#include <QObject>
-
-class QQmlEngine;
-class QQuickView;
-class QWindow;
-class MainWindow;
-
-class EmbeddedWindow : public QObject
-{
- Q_OBJECT
-public:
- EmbeddedWindow(QQmlEngine *qmlEngine, MainWindow *mainWindow);
- ~EmbeddedWindow();
-
-public slots:
- void show();
- void close();
-
-private:
- QQmlEngine *qmlEngine = nullptr;
- QQuickView *quickView = nullptr;
- MainWindow *mainWindow = nullptr;
- QWindow *embeddedWindow = nullptr;
-};
diff --git a/examples/EmbeddedWindow/QmlApp/main.cpp b/examples/EmbeddedWindow/QmlApp/main.cpp
deleted file mode 100644
index 957c749..0000000
--- a/examples/EmbeddedWindow/QmlApp/main.cpp
+++ /dev/null
@@ -1,65 +0,0 @@
-/***************************************************************************************************
- Copyright (C) 2023 The Qt Company Ltd.
- SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-***************************************************************************************************/
-
-#include <QGuiApplication>
-#include <QQmlApplicationEngine>
-#include <QQmlContext>
-#include <QThread>
-#include <QFile>
-
-#include <objbase.h>
-
-#include "mainwindow.h"
-#include "embeddedwindow.h"
-
-int main(int argc, char *argv[])
-{
-#if defined(Q_OS_WIN)
- QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
-#endif
- QGuiApplication app(argc, argv);
-
- MainWindow mainWindow;
- QObject::connect(&mainWindow, &MainWindow::closed, &app, &QCoreApplication::quit);
-
- QQmlApplicationEngine engine;
- engine.rootContext()->setContextProperty("mainWindow", &mainWindow);
-
- EmbeddedWindow embeddedWindow(&engine, &mainWindow);
-
- QThread *wpfThread = QThread::create([&app, &mainWindow] {
-
- if (FAILED(CoInitialize(nullptr))) {
- app.quit();
- return;
- }
-
- QString runtimeConfig = R"[json](
-{
- "runtimeOptions": {
- "tfm": "net6.0-windows",
- "rollForward": "LatestMinor",
- "framework": {
- "name": "Microsoft.WindowsDesktop.App",
- "version": "6.0.0"
- }
- }
-}
-)[json]";
- QFile wpfAppRuntimeConfig(QGuiApplication::applicationDirPath() + "/WpfApp.runtimeconfig.json");
- if (wpfAppRuntimeConfig.open(QFile::ReadOnly | QFile::Text))
- runtimeConfig = QString(wpfAppRuntimeConfig.readAll());
-
- QDotNetHost host;
- if (!host.load(runtimeConfig)) {
- app.quit();
- return;
- }
- QDotNetAdapter::init(&host);
- mainWindow.init();
- });
- wpfThread->start();
- return app.exec();
-}
diff --git a/examples/EmbeddedWindow/QmlApp/main.qml b/examples/EmbeddedWindow/QmlApp/main.qml
deleted file mode 100644
index 04b018d..0000000
--- a/examples/EmbeddedWindow/QmlApp/main.qml
+++ /dev/null
@@ -1,135 +0,0 @@
-/***************************************************************************************************
- Copyright (C) 2023 The Qt Company Ltd.
- SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-***************************************************************************************************/
-
-import QtQuick
-import QtQuick3D
-
-Rectangle {
- width: mainWindow.hostWidth
- height: mainWindow.hostHeight
- gradient: Gradient {
- GradientStop { position: 0.0; color: mainWindow.backgroundColor }
- GradientStop { position: 0.40; color: "#E6ECED" }
- GradientStop { position: 0.50; color: "#CCD9DB" }
- GradientStop { position: 0.60; color: "#B3C6C9" }
- GradientStop { position: 0.70; color: "#99B3B7" }
- GradientStop { position: 0.75; color: "#80A0A5" }
- GradientStop { position: 0.80; color: "#668D92" }
- GradientStop { position: 0.85; color: "#4D7A80" }
- GradientStop { position: 0.90; color: "#33676E" }
- GradientStop { position: 0.95; color: "#19545C" }
- GradientStop { position: 1.0; color: "#00414A" }
- }
- Text {
- anchors.bottom: parent.bottom
- anchors.right: parent.right
- anchors.bottomMargin: 5
- anchors.rightMargin: 10
- font.pointSize: 20
- font.weight: Font.Bold
- color: "#001012"
- text: "QML"
- }
-
- View3D {
- id: view
- anchors.fill: parent
-
- PerspectiveCamera {
- position: Qt.vector3d(
- mainWindow.cameraPositionX,
- mainWindow.cameraPositionY + 200,
- mainWindow.cameraPositionZ + 300)
- eulerRotation.x: (mainWindow.cameraRotationX - 30) % 360
- eulerRotation.y: mainWindow.cameraRotationY
- eulerRotation.z: mainWindow.cameraRotationZ
- }
-
- DirectionalLight {
- eulerRotation.x: (mainWindow.cameraRotationX - 30) % 360
- eulerRotation.y: mainWindow.cameraRotationY
- eulerRotation.z: mainWindow.cameraRotationZ
- }
-
- Model {
- id: cube
- source: "#Cube"
- materials: DefaultMaterial {
- diffuseMap: Texture {
- sourceItem: Item {
- id: qt_logo
- width: 230
- height: 230
- visible: false
- layer.enabled: true
- Rectangle {
- anchors.fill: parent
- color: "black"
- Image {
- anchors.fill: parent
- source: "qt_logo.png"
- }
- Text {
- anchors.top: parent.top
- anchors.horizontalCenter: parent.horizontalCenter
- color: "white"
- font.pixelSize: 17
- text: "The Future is Written with Qt"
- }
- Text {
- anchors.bottom: parent.bottom
- anchors.horizontalCenter: parent.horizontalCenter
- color: "white"
- font.pixelSize: 17
- text: "The Future is Written with Qt"
- }
- }
- }
- }
- }
- property var rotation: Qt.vector3d(0, 90, 0)
-
- eulerRotation.x: rotation.x % 360
- eulerRotation.y: rotation.y % 360
- eulerRotation.z: rotation.z % 360
-
- Vector3dAnimation on rotation {
- property var delta: Qt.vector3d(0, 0, 0)
- id: cubeAnimation
- loops: Animation.Infinite
- duration: mainWindow.animationDuration
- from: Qt.vector3d(0, 0, 0).plus(delta)
- to: Qt.vector3d(360, 0, 360).plus(delta)
- onDurationChanged: {
- delta = cube.eulerRotation;
- restart();
- }
- }
- }
- }
-
- property var t0: 0
- property var n: 0
-
- Component.onCompleted: {
- window.afterFrameEnd.connect(
- function() {
- var t = Date.now();
- if (t0 == 0) {
- t0 = t;
- n = 1;
- } else {
- var dt = t - t0;
- if (dt >= 1000) {
- mainWindow.framesPerSecond = (1000 * n) / dt;
- n = 0;
- t0 = t;
- } else {
- n++;
- }
- }
- });
- }
-}
diff --git a/examples/EmbeddedWindow/QmlApp/mainwindow.cpp b/examples/EmbeddedWindow/QmlApp/mainwindow.cpp
deleted file mode 100644
index 2829f89..0000000
--- a/examples/EmbeddedWindow/QmlApp/mainwindow.cpp
+++ /dev/null
@@ -1,170 +0,0 @@
-/***************************************************************************************************
- Copyright (C) 2023 The Qt Company Ltd.
- SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-***************************************************************************************************/
-
-#include "mainwindow.h"
-
-class HwndHost : public QDotNetObject
-{
-public:
- Q_DOTNET_OBJECT_INLINE(HwndHost, "System.Windows.Interop.HwndHost, PresentationFramework");
- void *handle() { return method("get_Handle", fnGetHandle).invoke(*this); }
- double width() { return method("get_Width", fnGetWidth).invoke(*this); }
- double height() { return method("get_Height", fnGetHeight).invoke(*this); }
-private:
- QDotNetFunction<void *> fnGetHandle = nullptr;
- QDotNetFunction<double> fnGetWidth = nullptr;
- QDotNetFunction<double> fnGetHeight = nullptr;
-};
-
-class MouseEventArgs : public QDotNetObject
-{
-public:
- Q_DOTNET_OBJECT_INLINE(MouseEventArgs, "System.Windows.Input.MouseEventArgs, PresentationCore");
-};
-
-class MainWindowPrivate : public QDotNetObject::IEventHandler
-{
-public:
- MainWindowPrivate(MainWindow *q) : q(q)
- {}
- void handleEvent(const QString &evName, QDotNetObject &evSource, QDotNetObject &evArgs) override
- {
- if (evName == "ContentRendered") {
- emit q->contentRendered();
- } else if (evName == "SizeChanged") {
- double width = evArgs.object("NewSize").call<double>("get_Width");
- double height = evArgs.object("NewSize").call<double>("get_Height");
- if (width != hostWidth) {
- hostWidth = width;
- emit q->hostWidthChanged();
- }
- if (height != hostHeight) {
- hostHeight = height;
- emit q->hostHeightChanged();
- }
- } else if (evName == "Closed") {
- emit q->closed();
- } else if (evName == "PropertyChanged") {
- QString propertyName = evArgs.call<QString>("get_PropertyName");
- if (propertyName == "CameraPositionX")
- emit q->cameraPositionXChanged();
- else if (propertyName == "CameraPositionY")
- emit q->cameraPositionYChanged();
- else if (propertyName == "CameraPositionZ")
- emit q->cameraPositionZChanged();
- else if (propertyName == "CameraRotationX")
- emit q->cameraRotationXChanged();
- else if (propertyName == "CameraRotationY")
- emit q->cameraRotationYChanged();
- else if (propertyName == "CameraRotationZ")
- emit q->cameraRotationZChanged();
- else if (propertyName == "AnimationDuration")
- emit q->animationDurationChanged();
- }
- };
-
- HwndHost hwndHost = nullptr;
- double hostWidth = 0.0, hostHeight = 0.0;
- QDotNetFunction<void, double> fnSetEmbeddedFps = nullptr;
- QDotNetFunction<double> fnGetCameraPositionX = nullptr;
- QDotNetFunction<double> fnGetCameraPositionY = nullptr;
- QDotNetFunction<double> fnGetCameraPositionZ = nullptr;
- QDotNetFunction<double> fnGetCameraRotationX = nullptr;
- QDotNetFunction<double> fnGetCameraRotationY = nullptr;
- QDotNetFunction<double> fnGetCameraRotationZ = nullptr;
- QDotNetFunction<double> fnGetAnimationDuration = nullptr;
- QDotNetFunction<double> fnGetFramesPerSecond = nullptr;
- QDotNetFunction<QString> fnGetBackgroundColor = nullptr;
-
-private:
- MainWindow *q;
-};
-
-Q_DOTNET_OBJECT_IMPL(MainWindow, Q_DOTNET_OBJECT_INIT(d(new MainWindowPrivate(this))));
-
-MainWindow::MainWindow() : QDotNetObject(nullptr), d(new MainWindowPrivate(this))
-{}
-
-MainWindow::~MainWindow()
-{}
-
-void MainWindow::init()
-{
- *this = constructor<MainWindow>().invoke(nullptr);
- d->hwndHost = method<HwndHost>("get_HwndHost").invoke(*this);
- subscribeEvent("ContentRendered", d);
- subscribeEvent("Closed", d);
- subscribeEvent("PropertyChanged", d);
- d->hwndHost.subscribeEvent("SizeChanged", d);
-
- QtDotNet::call<void, MainWindow>("WpfApp.Program, WpfApp", "set_MainWindow", *this);
- QtDotNet::call<int>("WpfApp.Program, WpfApp", "Main");
-}
-
-void *MainWindow::hostHandle()
-{
- return d->hwndHost.handle();
-}
-
-int MainWindow::hostWidth()
-{
- return d->hostWidth;
-}
-
-int MainWindow::hostHeight()
-{
- return d->hostHeight;
-}
-
-
-double MainWindow::cameraPositionX()
-{
- return method("get_CameraPositionX", d->fnGetCameraPositionX).invoke(*this);
-}
-
-double MainWindow::cameraPositionY()
-{
- return method("get_CameraPositionY", d->fnGetCameraPositionY).invoke(*this);
-}
-
-double MainWindow::cameraPositionZ()
-{
- return method("get_CameraPositionZ", d->fnGetCameraPositionZ).invoke(*this);
-}
-
-double MainWindow::cameraRotationX()
-{
- return method("get_CameraRotationX", d->fnGetCameraRotationX).invoke(*this);
-}
-
-double MainWindow::cameraRotationY()
-{
- return method("get_CameraRotationY", d->fnGetCameraRotationY).invoke(*this);
-}
-
-double MainWindow::cameraRotationZ()
-{
- return method("get_CameraRotationZ", d->fnGetCameraRotationZ).invoke(*this);
-}
-
-double MainWindow::animationDuration()
-{
- return method("get_AnimationDuration", d->fnGetAnimationDuration).invoke(*this);
-}
-
-double MainWindow::framesPerSecond()
-{
- return method("get_FramesPerSecond", d->fnGetFramesPerSecond).invoke(*this);
-}
-
-QString MainWindow::backgroundColor()
-{
- return method("get_BackgroundColor", d->fnGetBackgroundColor).invoke(*this);
-}
-
-void MainWindow::setFramesPerSecond(double fps)
-{
- method("set_FramesPerSecond", d->fnSetEmbeddedFps).invoke(*this, fps);
-}
diff --git a/examples/EmbeddedWindow/QmlApp/mainwindow.h b/examples/EmbeddedWindow/QmlApp/mainwindow.h
deleted file mode 100644
index 4d81ed0..0000000
--- a/examples/EmbeddedWindow/QmlApp/mainwindow.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/***************************************************************************************************
- Copyright (C) 2023 The Qt Company Ltd.
- SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-***************************************************************************************************/
-
-#pragma once
-
-#include <QObject>
-#include <QDotNetObject>
-
-class MainWindowPrivate;
-
-class MainWindow : public QObject, public QDotNetObject
-{
- Q_OBJECT
- Q_PROPERTY(double hostWidth READ hostWidth NOTIFY hostWidthChanged)
- Q_PROPERTY(double hostHeight READ hostHeight NOTIFY hostHeightChanged)
- Q_PROPERTY(double cameraPositionX READ cameraPositionX NOTIFY cameraPositionXChanged)
- Q_PROPERTY(double cameraPositionY READ cameraPositionY NOTIFY cameraPositionYChanged)
- Q_PROPERTY(double cameraPositionZ READ cameraPositionZ NOTIFY cameraPositionZChanged)
- Q_PROPERTY(double cameraRotationX READ cameraRotationX NOTIFY cameraRotationXChanged)
- Q_PROPERTY(double cameraRotationY READ cameraRotationY NOTIFY cameraRotationYChanged)
- Q_PROPERTY(double cameraRotationZ READ cameraRotationZ NOTIFY cameraRotationZChanged)
- Q_PROPERTY(double animationDuration READ animationDuration NOTIFY animationDurationChanged)
- Q_PROPERTY(QString backgroundColor READ backgroundColor NOTIFY backgroundColorChanged)
- Q_PROPERTY(double framesPerSecond READ framesPerSecond WRITE setFramesPerSecond)
-public:
- Q_DOTNET_OBJECT(MainWindow, "WpfApp.MainWindow, WpfApp");
- MainWindow();
- ~MainWindow();
- void init();
- void *hostHandle();
- int hostWidth();
- int hostHeight();
- double cameraPositionX();
- double cameraPositionY();
- double cameraPositionZ();
- double cameraRotationX();
- double cameraRotationY();
- double cameraRotationZ();
- double animationDuration();
- double framesPerSecond();
- QString backgroundColor();
-signals:
- void contentRendered();
- void hostWidthChanged();
- void hostHeightChanged();
- void cameraPositionXChanged();
- void cameraPositionYChanged();
- void cameraPositionZChanged();
- void cameraRotationXChanged();
- void cameraRotationYChanged();
- void cameraRotationZChanged();
- void animationDurationChanged();
- void backgroundColorChanged();
- void closed();
-public slots:
- void setFramesPerSecond(double fps);
-private:
- MainWindowPrivate *d;
-};
diff --git a/examples/EmbeddedWindow/QmlApp/qml.qrc b/examples/EmbeddedWindow/QmlApp/qml.qrc
deleted file mode 100644
index 040dc48..0000000
--- a/examples/EmbeddedWindow/QmlApp/qml.qrc
+++ /dev/null
@@ -1,6 +0,0 @@
-<RCC>
- <qresource prefix="/">
- <file>main.qml</file>
- <file>qt_logo.png</file>
- </qresource>
-</RCC>
diff --git a/examples/EmbeddedWindow/QmlApp/qt_logo.png b/examples/EmbeddedWindow/QmlApp/qt_logo.png
deleted file mode 100644
index 30c621c..0000000
--- a/examples/EmbeddedWindow/QmlApp/qt_logo.png
+++ /dev/null
Binary files differ
diff --git a/examples/EmbeddedWindow/WpfApp/AssemblyInfo.cs b/examples/EmbeddedWindow/WpfApp/AssemblyInfo.cs
deleted file mode 100644
index 8b5504e..0000000
--- a/examples/EmbeddedWindow/WpfApp/AssemblyInfo.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using System.Windows;
-
-[assembly: ThemeInfo(
- ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
- //(used if a resource is not found in the page,
- // or application resource dictionaries)
- ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
- //(used if a resource is not found in the page,
- // app, or any theme specific resource dictionaries)
-)]
diff --git a/examples/EmbeddedWindow/WpfApp/MainWindow.xaml b/examples/EmbeddedWindow/WpfApp/MainWindow.xaml
deleted file mode 100644
index 2695ba9..0000000
--- a/examples/EmbeddedWindow/WpfApp/MainWindow.xaml
+++ /dev/null
@@ -1,58 +0,0 @@
-<Window x:Class="WpfApp.MainWindow"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
- xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
- xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
- xmlns:local="clr-namespace:WpfApp" mc:Ignorable="d" Name="this"
- Title="WPF + QML Embedded Window" Width="640" Height="480" MinWidth="640" MinHeight="480">
- <Grid>
- <Grid.ColumnDefinitions>
- <ColumnDefinition Width="50*" />
- <ColumnDefinition Width="100*" />
- </Grid.ColumnDefinitions>
- <StackPanel Name="StackPanel" Grid.Column="0" Orientation="Vertical" Background="#FFF0F0F0">
- <Label Content="WPF" FontSize="20" FontWeight="Bold" HorizontalAlignment="Left"
- VerticalAlignment="Top" Height="37" Margin="10,0,10,0" />
- <Label Content="Camera Position X" Margin="10,0,10,0" />
- <Slider Name="SliderCameraPositionX" Minimum="-500" Maximum="500" SmallChange="1"
- ValueChanged="Slider_ValueChanged" MouseDoubleClick="Slider_MouseDoubleClick"
- Margin="10,0,10,0" />
- <Label Content="Camera Position Y" Margin="10,0,10,0" />
- <Slider Name="SliderCameraPositionY" Minimum="-500" Maximum="500" SmallChange="1"
- ValueChanged="Slider_ValueChanged" MouseDoubleClick="Slider_MouseDoubleClick"
- Margin="10,0,10,0" />
- <Label Content="Camera Position Z" Margin="10,0,10,0" />
- <Slider Name="SliderCameraPositionZ" Minimum="-500" Maximum="500" SmallChange="1"
- IsDirectionReversed="True" ValueChanged="Slider_ValueChanged"
- MouseDoubleClick="Slider_MouseDoubleClick" Margin="10,0,10,0" />
- <Label Content="Camera Rotation X" Margin="10,0,10,0" />
- <Slider Name="SliderCameraRotationX" Minimum="-180" Maximum="180" SmallChange="1"
- ValueChanged="Slider_ValueChanged" MouseDoubleClick="Slider_MouseDoubleClick"
- Margin="10,0,10,0" />
- <Label Content="Camera Rotation Y" Margin="10,0,10,0" />
- <Slider Name="SliderCameraRotationY" Minimum="-180" Maximum="180" SmallChange="1"
- ValueChanged="Slider_ValueChanged" MouseDoubleClick="Slider_MouseDoubleClick"
- Margin="10,0,10,0" />
- <Label Content="Camera Rotation Z" Margin="10,0,10,0" />
- <Slider Name="SliderCameraRotationZ" Minimum="-180" Maximum="180" SmallChange="1"
- ValueChanged="Slider_ValueChanged" MouseDoubleClick="Slider_MouseDoubleClick"
- Margin="10,0,10,0" />
- <Label Content="Animation Speed" Margin="10,0,10,0" />
- <Slider Name="SliderAnimationDuration" Minimum="0" Value="0.1" Maximum="1" SmallChange="0.001"
- TickPlacement="BottomRight" TickFrequency="0.05" ValueChanged="Slider_ValueChanged"
- MouseDoubleClick="Slider_MouseDoubleClick" Margin="10,0,10,0" />
- <Label Content="{Binding Rpm, ElementName=this}" ContentStringFormat="{}{0:F0} rpm"
- HorizontalContentAlignment="Center" Margin="10,-5,10,0" />
- <Label Content="Frame Rate" Margin="10,0,10,0" />
- <Grid Margin="10,0,10,0">
- <ProgressBar Name="FpsValue" Value="0" Maximum="60" Margin="10,0,10,0" />
- <TextBlock Name="FpsLabel" HorizontalAlignment="Center" VerticalAlignment="Center"
- Text="--.- fps" />
- </Grid>
- </StackPanel>
- <WindowsFormsHost Name="EmbeddedAppHost" Grid.Column="1">
- <wf:Panel Name="EmbeddedAppPanel" BackColor="#AAAAAA" />
- </WindowsFormsHost>
- </Grid>
-</Window>
diff --git a/examples/EmbeddedWindow/WpfApp/MainWindow.xaml.cs b/examples/EmbeddedWindow/WpfApp/MainWindow.xaml.cs
deleted file mode 100644
index 77f1bf8..0000000
--- a/examples/EmbeddedWindow/WpfApp/MainWindow.xaml.cs
+++ /dev/null
@@ -1,109 +0,0 @@
-/***************************************************************************************************
- Copyright (C) 2023 The Qt Company Ltd.
- SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-***************************************************************************************************/
-
-using System;
-using System.ComponentModel;
-using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Input;
-using System.Windows.Interop;
-using System.Windows.Media;
-
-namespace WpfApp
-{
- public partial class MainWindow : Window, INotifyPropertyChanged
- {
- public MainWindow()
- {
- InitializeComponent();
- }
-
- public HwndHost HwndHost => EmbeddedAppHost;
-
- public double CameraPositionX => WpfThread(() => SliderCameraPositionX.Value);
- public double CameraPositionY => WpfThread(() => SliderCameraPositionY.Value);
- public double CameraPositionZ => WpfThread(() => SliderCameraPositionZ.Value);
- public double CameraRotationX => WpfThread(() => SliderCameraRotationX.Value);
- public double CameraRotationY => WpfThread(() => SliderCameraRotationY.Value);
- public double CameraRotationZ => WpfThread(() => SliderCameraRotationZ.Value);
-
- public static double MaxRpm => 100;
- public double Rpm => MaxRpm * Math.Max(1 / MaxRpm, SliderAnimationDuration.Value);
- public double AnimationDuration => WpfThread(() => 60000 / Rpm);
-
- public double FramesPerSecond
- {
- get => WpfThread(() => FpsValue.Value);
- set
- {
- WpfThread(() =>
- {
- if (value <= FpsValue.Maximum)
- FpsValue.Value = value;
- FpsLabel.Text = $"{value:0.0} fps";
- });
- }
- }
-
- public string BackgroundColor { get; set; } = "#FFFFFF";
-
- public event PropertyChangedEventHandler PropertyChanged;
- private void NotifyPropertyChanged(string propertyName)
- {
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
- }
-
- private void Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
- {
- if (sender is not Slider slider)
- return;
- NotifyPropertyChanged(slider.Name switch
- {
- nameof(SliderCameraPositionX) => nameof(CameraPositionX),
- nameof(SliderCameraPositionY) => nameof(CameraPositionY),
- nameof(SliderCameraPositionZ) => nameof(CameraPositionZ),
- nameof(SliderCameraRotationX) => nameof(CameraRotationX),
- nameof(SliderCameraRotationY) => nameof(CameraRotationY),
- nameof(SliderCameraRotationZ) => nameof(CameraRotationZ),
- nameof(SliderAnimationDuration) => nameof(AnimationDuration),
- _ => throw new NotSupportedException()
- });
- if (slider.Name is nameof(SliderAnimationDuration))
- NotifyPropertyChanged(nameof(Rpm));
- }
-
- private void Slider_MouseDoubleClick(object sender, MouseButtonEventArgs e)
- {
- if (sender is not Slider slider)
- return;
- if (slider.Name is nameof(SliderAnimationDuration))
- slider.Value = (slider.Maximum + slider.Minimum) / 10;
- else
- slider.Value = (slider.Maximum + slider.Minimum) / 2;
- }
-
- protected override void OnRender(DrawingContext drawingContext)
- {
- base.OnRender(drawingContext);
- if (StackPanel.Background is not SolidColorBrush panelBrush)
- return;
- if (BackgroundColor != (BackgroundColor = panelBrush.Color.ToString()))
- NotifyPropertyChanged(nameof(BackgroundColor));
- }
-
- private static void WpfThread(Action action)
- {
- if (Application.Current?.Dispatcher is { } dispatcher)
- dispatcher.Invoke(action);
- }
-
- private static T WpfThread<T>(Func<T> func)
- {
- if (Application.Current?.Dispatcher is not { } dispatcher)
- return default;
- return dispatcher.Invoke(func);
- }
- }
-}
diff --git a/examples/EmbeddedWindow/WpfApp/Properties/launchSettings.json b/examples/EmbeddedWindow/WpfApp/Properties/launchSettings.json
deleted file mode 100644
index 70d4330..0000000
--- a/examples/EmbeddedWindow/WpfApp/Properties/launchSettings.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "profiles": {
- "WpfApp": {
- "commandName": "Project",
- "environmentVariables": {
- "PATH": "C:/lib/Qt/6.2.4/msvc2019_64/bin;%PATH%"
- },
- "nativeDebugging": false
- }
- }
-} \ No newline at end of file
diff --git a/examples/EmbeddedWindow/WpfApp/WpfApp.cs b/examples/EmbeddedWindow/WpfApp/WpfApp.cs
deleted file mode 100644
index b80d9df..0000000
--- a/examples/EmbeddedWindow/WpfApp/WpfApp.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-/***************************************************************************************************
- Copyright (C) 2023 The Qt Company Ltd.
- SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-***************************************************************************************************/
-
-using System;
-using System.Windows;
-
-namespace WpfApp
-{
- public static class Program
- {
- [STAThread]
- public static int Main()
- {
- var application = new Application();
- (MainWindow ??= new MainWindow()).InitializeComponent();
- MainWindow.Show();
- return application.Run();
- }
- public static MainWindow MainWindow { get; set; }
- }
-}
diff --git a/examples/EmbeddedWindow/WpfApp/WpfApp.csproj b/examples/EmbeddedWindow/WpfApp/WpfApp.csproj
deleted file mode 100644
index 8046823..0000000
--- a/examples/EmbeddedWindow/WpfApp/WpfApp.csproj
+++ /dev/null
@@ -1,33 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-
- <PropertyGroup>
- <OutputType>WinExe</OutputType>
- <TargetFramework>net$(BundledNETCoreAppTargetFrameworkVersion)-windows</TargetFramework>
- <Nullable>disable</Nullable>
- <UseWPF>true</UseWPF>
- <StartupObject>WpfApp.Program</StartupObject>
- </PropertyGroup>
-
- <ItemGroup>
- <ProjectReference Include="..\..\..\src\Qt.DotNet.Adapter\Qt.DotNet.Adapter.csproj" />
- </ItemGroup>
-
- <ItemGroup>
- <Reference Include="Qt.DotNet.Adapter" Condition="Exists('..\..\..\bin\Qt.DotNet.Adapter.dll')">
- <HintPath>..\..\..\bin\Qt.DotNet.Adapter.dll</HintPath>
- </Reference>
- <Reference Include="System.Drawing.Common">
- <HintPath>$(ProgramFiles)\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\$(BundledNETCoreAppPackageVersion)\ref\net$(BundledNETCoreAppTargetFrameworkVersion)\System.Drawing.Common.dll</HintPath>
- </Reference>
- <Reference Include="System.Windows.Forms">
- <HintPath>$(ProgramFiles)\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\$(BundledNETCoreAppPackageVersion)\ref\net$(BundledNETCoreAppTargetFrameworkVersion)\System.Windows.Forms.dll</HintPath>
- </Reference>
- <Reference Include="System.Windows.Forms.Primitives">
- <HintPath>$(ProgramFiles)\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\$(BundledNETCoreAppPackageVersion)\ref\net$(BundledNETCoreAppTargetFrameworkVersion)\System.Windows.Forms.Primitives.dll</HintPath>
- </Reference>
- <Reference Include="WindowsFormsIntegration">
- <HintPath>$(ProgramFiles)\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\$(BundledNETCoreAppPackageVersion)\ref\net$(BundledNETCoreAppTargetFrameworkVersion)\WindowsFormsIntegration.dll</HintPath>
- </Reference>
- </ItemGroup>
-
-</Project>
diff --git a/examples/QtAzureIoT/QtAzureIoT.sln b/examples/QtAzureIoT/QtAzureIoT.sln
deleted file mode 100644
index 4f5a653..0000000
--- a/examples/QtAzureIoT/QtAzureIoT.sln
+++ /dev/null
@@ -1,88 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 17
-VisualStudioVersion = 17.0.33130.402
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "device", "device", "{BD2258EF-0E47-4DAA-94DF-D08CA1B09A93}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SensorData", "device\SensorData\SensorData.csproj", "{D8CD7E8D-7ECA-46A7-AA39-E471F99C94F0}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DeviceToBackoffice", "device\DeviceToBackoffice\DeviceToBackoffice.csproj", "{4F2A52A9-9DAE-4250-A881-F0A013A589F4}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "deviceapp", "device\deviceapp\deviceapp.vcxproj", "{2B3D1059-93F6-42F8-9709-AE06BEDE1EEF}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "modules", "modules", "{AC26C5B9-CA58-434C-B607-DC94BFE2A665}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "app", "app", "{60F1722D-12B9-4671-B9E3-EDE5C41F1086}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CardReader", "device\CardReader\CardReader.csproj", "{66A69341-3B00-4812-AA77-EC5C2E9EA23A}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "common", "common", "{34513197-4269-4943-9F6D-CE4D89CB4DD2}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Utils", "common\Utils.csproj", "{DEF7470A-3D27-4D71-9E48-A96C9129FA42}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Debug|x64 = Debug|x64
- Release|Any CPU = Release|Any CPU
- Release|x64 = Release|x64
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {D8CD7E8D-7ECA-46A7-AA39-E471F99C94F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {D8CD7E8D-7ECA-46A7-AA39-E471F99C94F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {D8CD7E8D-7ECA-46A7-AA39-E471F99C94F0}.Debug|x64.ActiveCfg = Debug|Any CPU
- {D8CD7E8D-7ECA-46A7-AA39-E471F99C94F0}.Debug|x64.Build.0 = Debug|Any CPU
- {D8CD7E8D-7ECA-46A7-AA39-E471F99C94F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {D8CD7E8D-7ECA-46A7-AA39-E471F99C94F0}.Release|Any CPU.Build.0 = Release|Any CPU
- {D8CD7E8D-7ECA-46A7-AA39-E471F99C94F0}.Release|x64.ActiveCfg = Release|Any CPU
- {D8CD7E8D-7ECA-46A7-AA39-E471F99C94F0}.Release|x64.Build.0 = Release|Any CPU
- {4F2A52A9-9DAE-4250-A881-F0A013A589F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {4F2A52A9-9DAE-4250-A881-F0A013A589F4}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {4F2A52A9-9DAE-4250-A881-F0A013A589F4}.Debug|x64.ActiveCfg = Debug|Any CPU
- {4F2A52A9-9DAE-4250-A881-F0A013A589F4}.Debug|x64.Build.0 = Debug|Any CPU
- {4F2A52A9-9DAE-4250-A881-F0A013A589F4}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {4F2A52A9-9DAE-4250-A881-F0A013A589F4}.Release|Any CPU.Build.0 = Release|Any CPU
- {4F2A52A9-9DAE-4250-A881-F0A013A589F4}.Release|x64.ActiveCfg = Release|Any CPU
- {4F2A52A9-9DAE-4250-A881-F0A013A589F4}.Release|x64.Build.0 = Release|Any CPU
- {2B3D1059-93F6-42F8-9709-AE06BEDE1EEF}.Debug|Any CPU.ActiveCfg = Debug|x64
- {2B3D1059-93F6-42F8-9709-AE06BEDE1EEF}.Debug|Any CPU.Build.0 = Debug|x64
- {2B3D1059-93F6-42F8-9709-AE06BEDE1EEF}.Debug|x64.ActiveCfg = Debug|x64
- {2B3D1059-93F6-42F8-9709-AE06BEDE1EEF}.Debug|x64.Build.0 = Debug|x64
- {2B3D1059-93F6-42F8-9709-AE06BEDE1EEF}.Release|Any CPU.ActiveCfg = Release|x64
- {2B3D1059-93F6-42F8-9709-AE06BEDE1EEF}.Release|Any CPU.Build.0 = Release|x64
- {2B3D1059-93F6-42F8-9709-AE06BEDE1EEF}.Release|x64.ActiveCfg = Release|x64
- {2B3D1059-93F6-42F8-9709-AE06BEDE1EEF}.Release|x64.Build.0 = Release|x64
- {66A69341-3B00-4812-AA77-EC5C2E9EA23A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {66A69341-3B00-4812-AA77-EC5C2E9EA23A}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {66A69341-3B00-4812-AA77-EC5C2E9EA23A}.Debug|x64.ActiveCfg = Debug|Any CPU
- {66A69341-3B00-4812-AA77-EC5C2E9EA23A}.Debug|x64.Build.0 = Debug|Any CPU
- {66A69341-3B00-4812-AA77-EC5C2E9EA23A}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {66A69341-3B00-4812-AA77-EC5C2E9EA23A}.Release|Any CPU.Build.0 = Release|Any CPU
- {66A69341-3B00-4812-AA77-EC5C2E9EA23A}.Release|x64.ActiveCfg = Release|Any CPU
- {66A69341-3B00-4812-AA77-EC5C2E9EA23A}.Release|x64.Build.0 = Release|Any CPU
- {DEF7470A-3D27-4D71-9E48-A96C9129FA42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {DEF7470A-3D27-4D71-9E48-A96C9129FA42}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {DEF7470A-3D27-4D71-9E48-A96C9129FA42}.Debug|x64.ActiveCfg = Debug|Any CPU
- {DEF7470A-3D27-4D71-9E48-A96C9129FA42}.Debug|x64.Build.0 = Debug|Any CPU
- {DEF7470A-3D27-4D71-9E48-A96C9129FA42}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {DEF7470A-3D27-4D71-9E48-A96C9129FA42}.Release|Any CPU.Build.0 = Release|Any CPU
- {DEF7470A-3D27-4D71-9E48-A96C9129FA42}.Release|x64.ActiveCfg = Release|Any CPU
- {DEF7470A-3D27-4D71-9E48-A96C9129FA42}.Release|x64.Build.0 = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
- GlobalSection(NestedProjects) = preSolution
- {D8CD7E8D-7ECA-46A7-AA39-E471F99C94F0} = {AC26C5B9-CA58-434C-B607-DC94BFE2A665}
- {4F2A52A9-9DAE-4250-A881-F0A013A589F4} = {AC26C5B9-CA58-434C-B607-DC94BFE2A665}
- {2B3D1059-93F6-42F8-9709-AE06BEDE1EEF} = {60F1722D-12B9-4671-B9E3-EDE5C41F1086}
- {AC26C5B9-CA58-434C-B607-DC94BFE2A665} = {BD2258EF-0E47-4DAA-94DF-D08CA1B09A93}
- {60F1722D-12B9-4671-B9E3-EDE5C41F1086} = {BD2258EF-0E47-4DAA-94DF-D08CA1B09A93}
- {66A69341-3B00-4812-AA77-EC5C2E9EA23A} = {AC26C5B9-CA58-434C-B607-DC94BFE2A665}
- {DEF7470A-3D27-4D71-9E48-A96C9129FA42} = {34513197-4269-4943-9F6D-CE4D89CB4DD2}
- EndGlobalSection
- GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {9EC3D98D-2164-4E07-A5D2-334E9C93C570}
- EndGlobalSection
-EndGlobal
diff --git a/examples/QtAzureIoT/common/PropertySet.cs b/examples/QtAzureIoT/common/PropertySet.cs
deleted file mode 100644
index 2744a5c..0000000
--- a/examples/QtAzureIoT/common/PropertySet.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-/***************************************************************************************************
- Copyright (C) 2023 The Qt Company Ltd.
- SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-***************************************************************************************************/
-
-using System.ComponentModel;
-
-namespace QtAzureIoT.Utils
-{
- public class PropertySet : INotifyPropertyChanged
- {
- public event PropertyChangedEventHandler PropertyChanged;
-
- protected void NotifyPropertyChanged(string propertyName)
- {
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
- }
-
- protected void SetProperty<T>(ref T currentValue, T newValue, string name)
- {
- if (newValue.Equals(currentValue))
- return;
- currentValue = newValue;
- NotifyPropertyChanged(name);
- }
- }
-}
diff --git a/examples/QtAzureIoT/common/Utils.csproj b/examples/QtAzureIoT/common/Utils.csproj
deleted file mode 100644
index 141e38f..0000000
--- a/examples/QtAzureIoT/common/Utils.csproj
+++ /dev/null
@@ -1,9 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-
- <PropertyGroup>
- <TargetFramework>net6.0</TargetFramework>
- <ImplicitUsings>enable</ImplicitUsings>
- <Nullable>disable</Nullable>
- </PropertyGroup>
-
-</Project>
diff --git a/examples/QtAzureIoT/device/CardReader/CardReader.cs b/examples/QtAzureIoT/device/CardReader/CardReader.cs
deleted file mode 100644
index e97de9e..0000000
--- a/examples/QtAzureIoT/device/CardReader/CardReader.cs
+++ /dev/null
@@ -1,98 +0,0 @@
-/***************************************************************************************************
- Copyright (C) 2023 The Qt Company Ltd.
- SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-***************************************************************************************************/
-
-using System.Device.I2c;
-using System.Diagnostics;
-using Iot.Device.Pn532;
-using Iot.Device.Pn532.ListPassive;
-using Iot.Device.Pn532.RfConfiguration;
-using QtAzureIoT.Utils;
-
-namespace QtAzureIoT.Device
-{
- public class CardReader : PropertySet, IDisposable
- {
- public CardReader()
- { }
-
- public bool CardInReader
- {
- get => propertyCardInReader;
- private set => SetProperty(ref propertyCardInReader, value, nameof(CardInReader));
- }
-
- public void StartPolling()
- {
- if (Nfc != null)
- return;
- BusConnectionSettings = new I2cConnectionSettings(1, 0x24);
- BusDevice = I2cDevice.Create(BusConnectionSettings);
- Nfc = new Pn532(BusDevice);
-
- PollingLoop = new CancellationTokenSource();
- Polling = new Task(async () => await PollingLoopAsync(), PollingLoop.Token);
- Polling.Start();
- }
-
- public void StopPolling()
- {
- if (Nfc == null)
- return;
- PollingLoop.Cancel();
- Polling.Wait();
- Nfc.Dispose();
- Nfc = null;
- BusDevice.Dispose();
- BusDevice = null;
- BusConnectionSettings = null;
- PollingLoop.Dispose();
- PollingLoop = null;
- Polling.Dispose();
- Polling = null;
- }
-
- #region private
- private I2cConnectionSettings BusConnectionSettings { get; set; }
- private I2cDevice BusDevice { get; set; }
- private Pn532 Nfc { get; set; }
- private CancellationTokenSource PollingLoop { get; set; }
- private Task Polling { get; set; }
-
-
- private async Task PollingLoopAsync()
- {
- TargetBaudRate cardType = TargetBaudRate.B106kbpsTypeA;
- while (!PollingLoop.IsCancellationRequested) {
- try {
- if (Nfc.ListPassiveTarget(MaxTarget.One, cardType) is object) {
- CardInReader = true;
- var timeSinceDetected = Stopwatch.StartNew();
- while (timeSinceDetected.ElapsedMilliseconds < 3000) {
- if (Nfc.ListPassiveTarget(MaxTarget.One, cardType) is object)
- timeSinceDetected.Restart();
- await Task.Delay(200);
- }
- CardInReader = false;
- } else {
- Nfc.SetRfField(RfFieldMode.None);
- await Task.Delay(1000);
- Nfc.SetRfField(RfFieldMode.RF);
- }
- } catch (Exception e) {
- Debug.WriteLine($"Exception: {e.GetType().Name}: {e.Message}");
- Nfc.SetRfField(RfFieldMode.None);
- }
- }
- }
-
- public void Dispose()
- {
- StopPolling();
- }
-
- private bool propertyCardInReader = false;
- #endregion
- }
-}
diff --git a/examples/QtAzureIoT/device/CardReader/CardReader.csproj b/examples/QtAzureIoT/device/CardReader/CardReader.csproj
deleted file mode 100644
index c694f5b..0000000
--- a/examples/QtAzureIoT/device/CardReader/CardReader.csproj
+++ /dev/null
@@ -1,17 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-
- <PropertyGroup>
- <TargetFramework>net6.0</TargetFramework>
- <ImplicitUsings>enable</ImplicitUsings>
- <Nullable>disable</Nullable>
- </PropertyGroup>
-
- <ItemGroup>
- <PackageReference Include="Iot.Device.Bindings" Version="2.2.0" />
- </ItemGroup>
-
- <ItemGroup>
- <ProjectReference Include="..\..\common\Utils.csproj" />
- </ItemGroup>
-
-</Project>
diff --git a/examples/QtAzureIoT/device/DeviceToBackoffice/Backoffice.cs b/examples/QtAzureIoT/device/DeviceToBackoffice/Backoffice.cs
deleted file mode 100644
index 5fe3cb9..0000000
--- a/examples/QtAzureIoT/device/DeviceToBackoffice/Backoffice.cs
+++ /dev/null
@@ -1,70 +0,0 @@
-/***************************************************************************************************
- Copyright (C) 2023 The Qt Company Ltd.
- SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-***************************************************************************************************/
-
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.Azure.Devices.Client;
-using Microsoft.Azure.Devices.Client.Samples;
-using Microsoft.Azure.Devices.Provisioning.Client;
-using Microsoft.Azure.Devices.Provisioning.Client.PlugAndPlay;
-using Microsoft.Azure.Devices.Provisioning.Client.Transport;
-using Microsoft.Azure.Devices.Shared;
-using Microsoft.Extensions.Logging;
-
-namespace QtAzureIoT.Device
-{
- public class Backoffice
- {
- public Backoffice()
- {
- DeviceClient = DeviceClient.CreateFromConnectionString(
- "HostName=QtDotNetDemo-Hub.azure-devices.net;DeviceId=QtDotNetDemoDevice;SharedAccessKey=YkZmsSOZf8lvQb5HDthosRHP4XV1hYSuDEoExe/2Fj8=",
- TransportType.Mqtt,
- new ClientOptions
- {
- ModelId = "dtmi:com:example:TemperatureController;2"
- });
- BackofficeInterface = new TemperatureControllerSample(DeviceClient);
- }
-
- public void SetTelemetry(string name, double value)
- {
- BackofficeInterface.SetTelemetry(name, value);
- }
-
- public void SetTelemetry(string name, bool value)
- {
- BackofficeInterface.SetTelemetry(name, value);
- }
-
- public void StartPolling()
- {
- PollingLoop = new CancellationTokenSource();
- Polling = new Task(async () => await PollingLoopAsync(), PollingLoop.Token);
- Polling.Start();
- }
-
- public void StopPolling()
- {
- PollingLoop.Cancel();
- }
-
- #region private
- private CancellationTokenSource PollingLoop { get; set; }
- private Task Polling { get; set; }
- private DeviceClient DeviceClient { get; }
- private TemperatureControllerSample BackofficeInterface { get; }
-
- private async Task PollingLoopAsync()
- {
- await BackofficeInterface.InitOperationsAsync(PollingLoop.Token);
- while (!PollingLoop.IsCancellationRequested) {
- await BackofficeInterface.PerformOperationsAsync(PollingLoop.Token);
- }
- }
- #endregion
- }
-}
diff --git a/examples/QtAzureIoT/device/DeviceToBackoffice/DeviceToBackoffice.csproj b/examples/QtAzureIoT/device/DeviceToBackoffice/DeviceToBackoffice.csproj
deleted file mode 100644
index a53efc9..0000000
--- a/examples/QtAzureIoT/device/DeviceToBackoffice/DeviceToBackoffice.csproj
+++ /dev/null
@@ -1,17 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-
- <PropertyGroup>
- <TargetFramework>net6.0</TargetFramework>
- <ImplicitUsings>enable</ImplicitUsings>
- <Nullable>disable</Nullable>
- </PropertyGroup>
-
- <ItemGroup>
- <PackageReference Include="Microsoft.Azure.Devices" Version="1.38.2" />
- <PackageReference Include="Microsoft.Azure.Devices.Client" Version="1.41.3" />
- <PackageReference Include="Microsoft.Azure.Devices.Provisioning.Client" Version="1.19.2" />
- <PackageReference Include="Microsoft.Azure.Devices.Provisioning.Transport.Mqtt" Version="1.17.2" />
- <PackageReference Include="Microsoft.Azure.Devices.Shared" Version="1.30.2" />
- </ItemGroup>
-
-</Project>
diff --git a/examples/QtAzureIoT/device/DeviceToBackoffice/PnpConvention.cs b/examples/QtAzureIoT/device/DeviceToBackoffice/PnpConvention.cs
deleted file mode 100644
index 16bef2b..0000000
--- a/examples/QtAzureIoT/device/DeviceToBackoffice/PnpConvention.cs
+++ /dev/null
@@ -1,423 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-
-using Microsoft.Azure.Devices.Client;
-using Microsoft.Azure.Devices.Shared;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace PnpHelpers
-{
- public class PnpConvention
- {
- /// <summary>
- /// The content type for a plug and play compatible telemetry message.
- /// </summary>
- public const string ContentApplicationJson = "application/json";
-
- /// <summary>
- /// The key for a component identifier within a property update patch. Corresponding value is <see cref="PropertyComponentIdentifierValue"/>.
- /// </summary>
- public const string PropertyComponentIdentifierKey = "__t";
-
- /// <summary>
- /// The value for a component identifier within a property update patch. Corresponding key is <see cref="PropertyComponentIdentifierKey"/>.
- /// </summary>
- public const string PropertyComponentIdentifierValue = "c";
-
- /// <summary>
- /// Create a plug and play compatible telemetry message.
- /// </summary>
- /// <param name="telemetryName">The name of the telemetry, as defined in the DTDL interface. Must be 64 characters or less. For more details see
- /// <see href="https://github.com/Azure/opendigitaltwins-dtdl/blob/master/DTDL/v2/dtdlv2.md#telemetry"/>.</param>
- /// <param name="telemetryValue">The unserialized telemetry payload, in the format defined in the DTDL interface.</param>
- /// <param name="componentName">The name of the component in which the telemetry is defined. Can be null for telemetry defined under the root interface.</param>
- /// <param name="encoding">The character encoding to be used when encoding the message body to bytes. This defaults to utf-8.</param>
- /// <returns>A plug and play compatible telemetry message, which can be sent to IoT Hub. The caller must dispose this object when finished.</returns>
- public static Message CreateMessage(string telemetryName, object telemetryValue, string componentName = default, Encoding encoding = default)
- {
- if (string.IsNullOrWhiteSpace(telemetryName))
- {
- throw new ArgumentNullException(nameof(telemetryName));
- }
- if (telemetryValue == null)
- {
- throw new ArgumentNullException(nameof(telemetryValue));
- }
-
- return CreateMessage(new Dictionary<string, object> { { telemetryName, telemetryValue } }, componentName, encoding);
- }
-
- /// <summary>
- /// Create a plug and play compatible telemetry message.
- /// </summary>
- /// <param name="componentName">The name of the component in which the telemetry is defined. Can be null for telemetry defined under the root interface.</param>
- /// <param name="telemetryPairs">The unserialized name and value telemetry pairs, as defined in the DTDL interface. Names must be 64 characters or less. For more details see
- /// <see href="https://github.com/Azure/opendigitaltwins-dtdl/blob/master/DTDL/v2/dtdlv2.md#telemetry"/>.</param>
- /// <param name="encoding">The character encoding to be used when encoding the message body to bytes. This defaults to utf-8.</param>
- /// <returns>A plug and play compatible telemetry message, which can be sent to IoT Hub. The caller must dispose this object when finished.</returns>
- public static Message CreateMessage(IDictionary<string, object> telemetryPairs, string componentName = default, Encoding encoding = default)
- {
- if (telemetryPairs == null)
- {
- throw new ArgumentNullException(nameof(telemetryPairs));
- }
-
- Encoding messageEncoding = encoding ?? Encoding.UTF8;
- string payload = JsonConvert.SerializeObject(telemetryPairs);
- var message = new Message(messageEncoding.GetBytes(payload))
- {
- ContentEncoding = messageEncoding.WebName,
- ContentType = ContentApplicationJson,
- };
-
- if (!string.IsNullOrWhiteSpace(componentName))
- {
- message.ComponentName = componentName;
- }
-
- return message;
- }
-
- /// <summary>
- /// Creates a batch property update payload for the specified property key/value pairs.
- /// </summary>
- /// <param name="propertyName">The name of the twin property.</param>
- /// <param name="propertyValue">The unserialized value of the twin property.</param>
- /// <returns>A compact payload of the properties to update.</returns>
- /// <remarks>
- /// This creates a property patch for both read-only and read-write properties, both of which are named from a service perspective.
- /// All properties are read-write from a device's perspective.
- /// For a root-level property update, the patch is in the format: <c>{ "samplePropertyName": 20 }</c>
- /// </remarks>
- public static TwinCollection CreatePropertyPatch(string propertyName, object propertyValue)
- {
- return CreatePropertyPatch(new Dictionary<string, object> { { propertyName, propertyValue } });
- }
-
- /// <summary>
- /// Creates a batch property update payload for the specified property key/value pairs
- /// </summary>
- /// <remarks>
- /// This creates a property patch for both read-only and read-write properties, both of which are named from a service perspective.
- /// All properties are read-write from a device's perspective.
- /// For a root-level property update, the patch is in the format: <c>{ "samplePropertyName": 20 }</c>
- /// </remarks>
- /// <param name="propertyPairs">The twin properties and values to update.</param>
- /// <returns>A compact payload of the properties to update.</returns>
- public static TwinCollection CreatePropertyPatch(IDictionary<string, object> propertyPairs)
- {
- return new TwinCollection(JsonConvert.SerializeObject(propertyPairs));
- }
-
- /// <summary>
- /// Create a key/value property patch for updating digital twin properties.
- /// </summary>
- /// <remarks>
- /// This creates a property patch for both read-only and read-write properties, both of which are named from a service perspective.
- /// All properties are read-write from a device's perspective.
- /// For a component-level property update, the patch is in the format:
- /// <code>
- /// {
- /// "sampleComponentName": {
- /// "__t": "c",
- /// "samplePropertyName"": 20
- /// }
- /// }
- /// </code>
- /// </remarks>
- /// <param name="componentName">The name of the component in which the property is defined. Can be null for property defined under the root interface.</param>
- /// <param name="propertyName">The name of the twin property.</param>
- /// <param name="propertyValue">The unserialized value of the twin property.</param>
- /// <returns>The property patch for read-only and read-write property updates.</returns>
- public static TwinCollection CreateComponentPropertyPatch(string componentName, string propertyName, object propertyValue)
- {
- if (string.IsNullOrWhiteSpace(propertyName))
- {
- throw new ArgumentNullException(nameof(propertyName));
- }
- if (propertyValue == null)
- {
- throw new ArgumentNullException(nameof(propertyValue));
- }
-
- return CreateComponentPropertyPatch(componentName, new Dictionary<string, object> { { propertyName, propertyValue } });
- }
-
- /// <summary>
- /// Create a key/value property patch for updating digital twin properties.
- /// </summary>
- /// <remarks>
- /// This creates a property patch for both read-only and read-write properties, both of which are named from a service perspective.
- /// All properties are read-write from a device's perspective.
- /// For a component-level property update, the patch is in the format:
- /// <code>
- /// {
- /// "sampleComponentName": {
- /// "__t": "c",
- /// "samplePropertyName": 20
- /// }
- /// }
- /// </code>
- /// </remarks>
- /// <param name="componentName">The name of the component in which the property is defined. Can be null for property defined under the root interface.</param>
- /// <param name="propertyPairs">The property name and an unserialized value, as defined in the DTDL interface.</param>
- /// <returns>The property patch for read-only and read-write property updates.</returns>
- public static TwinCollection CreateComponentPropertyPatch(string componentName, IDictionary<string, object> propertyPairs)
- {
- if (string.IsNullOrWhiteSpace(componentName))
- {
- throw new ArgumentNullException(nameof(componentName));
- }
- if (propertyPairs == null)
- {
- throw new ArgumentNullException(nameof(propertyPairs));
- }
-
- var propertyPatch = new StringBuilder();
- propertyPatch.Append('{');
- propertyPatch.Append($"\"{componentName}\":");
- propertyPatch.Append('{');
- propertyPatch.Append($"\"{PropertyComponentIdentifierKey}\":\"{PropertyComponentIdentifierValue}\",");
- foreach (var kvp in propertyPairs)
- {
- propertyPatch.Append($"\"{kvp.Key}\":{JsonConvert.SerializeObject(kvp.Value)},");
- }
-
- // remove the extra comma
- propertyPatch.Remove(propertyPatch.Length - 1, 1);
-
- propertyPatch.Append("}}");
-
- return new TwinCollection(propertyPatch.ToString());
- }
-
- /// <summary>
- /// Creates a response to a write request on a device property.
- /// </summary>
- /// <remarks>
- /// This creates a property patch for both read-only and read-write properties, both of which are named from a service perspective.
- /// All properties are read-write from a device's perspective.
- /// For a component-level property update, the patch is in the format:
- /// <code>
- /// {
- /// "sampleComponentName": {
- /// "__t": "c",
- /// "samplePropertyName": 20
- /// }
- /// }
- /// </code>
- /// </remarks>
- /// <param name="propertyName">The name of the property to report.</param>
- /// <param name="propertyValue">The unserialized property value.</param>
- /// <param name="ackCode">The acknowledgment code, usually an HTTP Status Code e.g. 200, 400.</param>
- /// <param name="ackVersion">The acknowledgment version, as supplied in the property update request.</param>
- /// <param name="ackDescription">The acknowledgment description, an optional, human-readable message about the result of the property update.</param>
- /// <returns>A serialized json string response.</returns>
- public static TwinCollection CreateWritablePropertyResponse(
- string propertyName,
- object propertyValue,
- int ackCode,
- long ackVersion,
- string ackDescription = null)
- {
- if (string.IsNullOrWhiteSpace(propertyName))
- {
- throw new ArgumentNullException(nameof(propertyName));
- }
-
- return CreateWritablePropertyResponse(
- new Dictionary<string, object> { { propertyName, propertyValue } },
- ackCode,
- ackVersion,
- ackDescription);
- }
-
- /// <summary>
- /// Creates a response to a write request on a device property.
- /// </summary>
- /// <param name="propertyPairs">The name and unserialized value of the property to report.</param>
- /// <param name="ackCode">The acknowledgment code, usually an HTTP Status Code e.g. 200, 400.</param>
- /// <param name="ackVersion">The acknowledgment version, as supplied in the property update request.</param>
- /// <param name="ackDescription">The acknowledgment description, an optional, human-readable message about the result of the property update.</param>
- /// <returns>A serialized json string response.</returns>
- public static TwinCollection CreateWritablePropertyResponse(
- IDictionary<string, object> propertyPairs,
- int ackCode,
- long ackVersion,
- string ackDescription = null)
- {
- if (propertyPairs == null)
- {
- throw new ArgumentNullException(nameof(propertyPairs));
- }
-
- var response = new Dictionary<string, WritablePropertyResponse>(propertyPairs.Count);
- foreach (var kvp in propertyPairs)
- {
- if (string.IsNullOrWhiteSpace(kvp.Key))
- {
- throw new ArgumentNullException(nameof(kvp.Key), $"One of the propertyPairs keys was null, empty, or white space.");
- }
- response.Add(kvp.Key, new WritablePropertyResponse(kvp.Value, ackCode, ackVersion, ackDescription));
- }
-
- return new TwinCollection(JsonConvert.SerializeObject(response));
- }
-
- /// <summary>
- /// Creates a response to a write request on a device property.
- /// </summary>
- /// <remarks>
- /// For a component-level property update, the patch is in the format:
- /// <code>
- /// "sampleComponentName": {
- /// "__t": "c",
- /// "samplePropertyName": {
- /// "value": 20,
- /// "ac": 200,
- /// "av": 5,
- /// "ad": "The update was successful."
- /// }
- /// }
- /// }
- /// </code>
- /// </remarks>
- /// <param name="componentName">The component to which the property belongs.</param>
- /// <param name="propertyName">The name of the property to report.</param>
- /// <param name="propertyValue">The unserialized property value.</param>
- /// <param name="ackCode">The acknowledgment code, usually an HTTP Status Code e.g. 200, 400.</param>
- /// <param name="ackVersion">The acknowledgment version, as supplied in the property update request.</param>
- /// <param name="ackDescription">The acknowledgment description, an optional, human-readable message about the result of the property update.</param>
- /// <returns>A serialized json string response.</returns>
- public static TwinCollection CreateComponentWritablePropertyResponse(
- string componentName,
- string propertyName,
- object propertyValue,
- int ackCode,
- long ackVersion,
- string ackDescription = null)
- {
- if (string.IsNullOrWhiteSpace(componentName))
- {
- throw new ArgumentNullException(nameof(componentName));
- }
- if (string.IsNullOrWhiteSpace(propertyName))
- {
- throw new ArgumentNullException(nameof(propertyName));
- }
-
- return CreateComponentWritablePropertyResponse(
- componentName,
- new Dictionary<string, object> { { propertyName, propertyValue } },
- ackCode,
- ackVersion,
- ackDescription);
- }
-
- /// <summary>
- /// Creates a response to a write request on a device property.
- /// </summary>
- /// <remarks>
- /// For a component-level property update, the patch is in the format:
- /// <code>
- /// "sampleComponentName": {
- /// "__t": "c",
- /// "samplePropertyName": {
- /// "value": 20,
- /// "ac": 200,
- /// "av": 5,
- /// "ad": "The update was successful."
- /// }
- /// }
- /// }
- /// </code>
- /// </remarks>
- /// <param name="componentName">The component to which the property belongs.</param>
- /// <param name="propertyPairs">The name and unserialized value of the property to report.</param>
- /// <param name="ackCode">The acknowledgment code, usually an HTTP Status Code e.g. 200, 400.</param>
- /// <param name="ackVersion">The acknowledgment version, as supplied in the property update request.</param>
- /// <param name="ackDescription">The acknowledgment description, an optional, human-readable message about the result of the property update.</param>
- /// <returns>A serialized json string response.</returns>
- public static TwinCollection CreateComponentWritablePropertyResponse(
- string componentName,
- IDictionary<string, object> propertyPairs,
- int ackCode,
- long ackVersion,
- string ackDescription = null)
- {
- if (string.IsNullOrWhiteSpace(componentName))
- {
- throw new ArgumentNullException(nameof(componentName));
- }
- if (propertyPairs == null)
- {
- throw new ArgumentNullException(nameof(propertyPairs));
- }
-
- var propertyPatch = new Dictionary<string, object>
- {
- { PropertyComponentIdentifierKey, PropertyComponentIdentifierValue },
- };
- foreach (var kvp in propertyPairs)
- {
- if (string.IsNullOrWhiteSpace(kvp.Key))
- {
- throw new ArgumentNullException(nameof(kvp.Key), $"One of the propertyPairs keys was null, empty, or white space.");
- }
- propertyPatch.Add(kvp.Key, new WritablePropertyResponse(kvp.Value, ackCode, ackVersion, ackDescription));
- }
-
- var response = new Dictionary<string, object>
- {
- { componentName, propertyPatch },
- };
-
- return new TwinCollection(JsonConvert.SerializeObject(response));
- }
-
- /// <summary>
- /// Helper to retrieve the property value from the <see cref="TwinCollection"/> property update patch which was received as a result of service-initiated update.
- /// </summary>
- /// <typeparam name="T">The data type of the property, as defined in the DTDL interface.</typeparam>
- /// <param name="collection">The <see cref="TwinCollection"/> property update patch received as a result of service-initiated update.</param>
- /// <param name="propertyName">The property name, as defined in the DTDL interface.</param>
- /// <param name="propertyValue">The corresponding property value.</param>
- /// <param name="componentName">The name of the component in which the property is defined. Can be null for property defined under the root interface.</param>
- /// <returns>A boolean indicating if the <see cref="TwinCollection"/> property update patch received contains the property update.</returns>
- public static bool TryGetPropertyFromTwin<T>(TwinCollection collection, string propertyName, out T propertyValue, string componentName = null)
- {
- if (collection == null)
- {
- throw new ArgumentNullException(nameof(collection));
- }
-
- // If the desired property update is for a root component or nested component, verify that property patch received contains the desired property update.
- propertyValue = default;
-
- if (string.IsNullOrWhiteSpace(componentName))
- {
- if (collection.Contains(propertyName))
- {
- propertyValue = (T)collection[propertyName];
- return true;
- }
- }
-
- if (collection.Contains(componentName))
- {
- JObject componentProperty = collection[componentName];
- if (componentProperty.ContainsKey(propertyName))
- {
- propertyValue = componentProperty.Value<T>(propertyName);
- return true;
- }
- }
-
- return false;
- }
- }
-}
diff --git a/examples/QtAzureIoT/device/DeviceToBackoffice/TemperatureControllerSample.cs b/examples/QtAzureIoT/device/DeviceToBackoffice/TemperatureControllerSample.cs
deleted file mode 100644
index 58e7885..0000000
--- a/examples/QtAzureIoT/device/DeviceToBackoffice/TemperatureControllerSample.cs
+++ /dev/null
@@ -1,483 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Linq;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.Azure.Devices.Shared;
-using Microsoft.Extensions.Logging;
-using Newtonsoft.Json;
-using PnpHelpers;
-
-namespace Microsoft.Azure.Devices.Client.Samples
-{
- internal enum StatusCode
- {
- Completed = 200,
- InProgress = 202,
- ReportDeviceInitialProperty = 203,
- BadRequest = 400,
- NotFound = 404
- }
-
-
- public class TemperatureControllerSample
- {
- public ConcurrentDictionary<string, object> Telemetry = new();
- public void SetTelemetry(string name, double value)
- {
- Telemetry.AddOrUpdate(name, value, (k, v) => value);
- }
- public void SetTelemetry(string name, bool value)
- {
- Telemetry.AddOrUpdate(name, value, (k, v) => value);
- }
-
- // The default reported "value" and "av" for each "Thermostat" component on the client initial startup.
- // See https://docs.microsoft.com/azure/iot-develop/concepts-convention#writable-properties for more details in acknowledgment responses.
- private const double DefaultPropertyValue = 0d;
-
- private const long DefaultAckVersion = 0L;
-
- private const string TargetTemperatureProperty = "targetTemperature";
-
- private const string Thermostat1 = "thermostat1";
- private const string Thermostat2 = "thermostat2";
- private const string SerialNumber = "SR-123456";
-
- private static readonly Random s_random = new Random();
-
- private readonly DeviceClient _deviceClient;
- //private readonly ILogger _logger;
-
- // Dictionary to hold the temperature updates sent over each "Thermostat" component.
- // NOTE: Memory constrained devices should leverage storage capabilities of an external service to store this
- // information and perform computation.
- // See https://docs.microsoft.com/en-us/azure/event-grid/compare-messaging-services for more details.
- private readonly Dictionary<string, Dictionary<DateTimeOffset, double>> _temperatureReadingsDateTimeOffset =
- new Dictionary<string, Dictionary<DateTimeOffset, double>>();
-
- // A dictionary to hold all desired property change callbacks that this pnp device should be able to handle.
- // The key for this dictionary is the componentName.
- private readonly IDictionary<string, DesiredPropertyUpdateCallback> _desiredPropertyUpdateCallbacks =
- new Dictionary<string, DesiredPropertyUpdateCallback>();
-
- // Dictionary to hold the current temperature for each "Thermostat" component.
- private readonly Dictionary<string, double> _temperature = new Dictionary<string, double>();
-
- // Dictionary to hold the max temperature since last reboot, for each "Thermostat" component.
- private readonly Dictionary<string, double> _maxTemp = new Dictionary<string, double>();
-
- // A safe initial value for caching the writable properties version is 1, so the client
- // will process all previous property change requests and initialize the device application
- // after which this version will be updated to that, so we have a high water mark of which version number
- // has been processed.
- private static long s_localWritablePropertiesVersion = 1;
-
- public TemperatureControllerSample(DeviceClient deviceClient)
- {
- _deviceClient = deviceClient ?? throw new ArgumentNullException(nameof(deviceClient));
- //_logger = logger ?? throw new ArgumentNullException(nameof(logger));
- }
-
- public async Task InitOperationsAsync(CancellationToken cancellationToken)
- {
- _deviceClient.SetConnectionStatusChangesHandler(async (status, reason) =>
- {
- //_logger.LogDebug($"Connection status change registered - status={status}, reason={reason}.");
-
- // Call GetWritablePropertiesAndHandleChangesAsync() to get writable properties from the server once the connection status changes into Connected.
- // This can get back "lost" property updates in a device reconnection from status Disconnected_Retrying or Disconnected.
- if (status == ConnectionStatus.Connected) {
- await GetWritablePropertiesAndHandleChangesAsync();
- }
- });
-
- //_logger.LogDebug("Set handler for 'reboot' command.");
- await _deviceClient.SetMethodHandlerAsync("reboot", HandleRebootCommandAsync, _deviceClient, cancellationToken);
-
- // For a component-level command, the command name is in the format "<component-name>*<command-name>".
- //_logger.LogDebug($"Set handler for \"getMaxMinReport\" command.");
- await _deviceClient.SetMethodHandlerAsync("thermostat1*getMaxMinReport", HandleMaxMinReportCommand, Thermostat1, cancellationToken);
- await _deviceClient.SetMethodHandlerAsync("thermostat2*getMaxMinReport", HandleMaxMinReportCommand, Thermostat2, cancellationToken);
-
- //_logger.LogDebug("Set handler to receive 'targetTemperature' updates.");
- await _deviceClient.SetDesiredPropertyUpdateCallbackAsync(SetDesiredPropertyUpdateCallback, null, cancellationToken);
- _desiredPropertyUpdateCallbacks.Add(Thermostat1, TargetTemperatureUpdateCallbackAsync);
- _desiredPropertyUpdateCallbacks.Add(Thermostat2, TargetTemperatureUpdateCallbackAsync);
-
- //_logger.LogDebug("For each component, check if the device properties are empty on the initial startup.");
- await CheckEmptyPropertiesAsync(Thermostat1, cancellationToken);
- await CheckEmptyPropertiesAsync(Thermostat2, cancellationToken);
-
- await UpdateDeviceInformationAsync(cancellationToken);
- await SendDeviceSerialNumberAsync(cancellationToken);
-
- _maxTemp[Thermostat1] = 0d;
- _maxTemp[Thermostat2] = 0d;
- }
-
- public async Task PerformOperationsAsync(CancellationToken cancellationToken)
- {
- // This sample follows the following workflow:
- // -> Set handler to receive and respond to connection status changes.
- // -> Set handler to receive "reboot" command - root interface.
- // -> Set handler to receive "getMaxMinReport" command - on "Thermostat" components.
- // -> Set handler to receive "targetTemperature" property updates from service - on "Thermostat" components.
- // -> Check if the properties are empty on the initial startup - for each "Thermostat" component. If so, report the default values with ACK to the hub.
- // -> Update device information on "deviceInformation" component.
- // -> Send initial device info - "workingSet" over telemetry, "serialNumber" over reported property update - root interface.
- // -> Periodically send "temperature" over telemetry - on "Thermostat" components.
- // -> Send "maxTempSinceLastReboot" over property update, when a new max temperature is set - on "Thermostat" components.
-
-
-
- //while (!cancellationToken.IsCancellationRequested)
- {
- //if (temperatureReset)
- //{
- // // Generate a random value between 5.0°C and 45.0°C for the current temperature reading for each "Thermostat" component.
- // _temperature[Thermostat1] = Math.Round(s_random.NextDouble() * 40.0 + 5.0, 1);
- // _temperature[Thermostat2] = Math.Round(s_random.NextDouble() * 40.0 + 5.0, 1);
- //}
-
- await SendTemperatureAsync(Thermostat1, cancellationToken);
- //await SendTemperatureAsync(Thermostat2, cancellationToken);
- //await SendDeviceMemoryAsync(cancellationToken);
-
- //temperatureReset = _temperature[Thermostat1] == 0 && _temperature[Thermostat2] == 0;
- await Task.Delay(5 * 1000, cancellationToken);
- }
- }
-
- private async Task GetWritablePropertiesAndHandleChangesAsync()
- {
- Twin twin = await _deviceClient.GetTwinAsync();
- //_logger.LogInformation($"Device retrieving twin values on CONNECT: {twin.ToJson()}");
-
- TwinCollection twinCollection = twin.Properties.Desired;
- long serverWritablePropertiesVersion = twinCollection.Version;
-
- // Check if the writable property version is outdated on the local side.
- // For the purpose of this sample, we'll only check the writable property versions between local and server
- // side without comparing the property values.
- if (serverWritablePropertiesVersion > s_localWritablePropertiesVersion)
- {
- //_logger.LogInformation($"The writable property version cached on local is changing " +
- //$"from {s_localWritablePropertiesVersion} to {serverWritablePropertiesVersion}.");
-
- foreach (KeyValuePair<string, object> propertyUpdate in twinCollection)
- {
- string componentName = propertyUpdate.Key;
- switch (componentName)
- {
- case Thermostat1:
- case Thermostat2:
- // This will be called when a device client gets initialized and the _temperature dictionary is still empty.
- if (!_temperature.TryGetValue(componentName, out double value))
- {
- _temperature[componentName] = 21d; // The default temperature value is 21°C.
- }
- await TargetTemperatureUpdateCallbackAsync(twinCollection, componentName);
- break;
-
- default:
- //_logger.LogWarning($"Property: Received an unrecognized property update from service:" +
- //$"\n[ {propertyUpdate.Key}: {propertyUpdate.Value} ].");
- break;
- }
- }
-
- //_logger.LogInformation($"The writable property version on local is currently {s_localWritablePropertiesVersion}.");
- }
- }
-
- // The callback to handle "reboot" command. This method will send a temperature update (of 0°C) over telemetry for both associated components.
- private async Task<MethodResponse> HandleRebootCommandAsync(MethodRequest request, object userContext)
- {
- try
- {
- int delay = JsonConvert.DeserializeObject<int>(request.DataAsJson);
-
- //_logger.LogDebug($"Command: Received - Rebooting thermostat (resetting temperature reading to 0°C after {delay} seconds).");
- await Task.Delay(delay * 1000);
-
- //_logger.LogDebug("\tRebooting...");
-
- _temperature[Thermostat1] = _maxTemp[Thermostat1] = 0;
- _temperature[Thermostat2] = _maxTemp[Thermostat2] = 0;
-
- _temperatureReadingsDateTimeOffset.Clear();
-
- //_logger.LogDebug("\tRestored.");
- } catch (JsonReaderException /*ex*/)
- {
- //_logger.LogDebug($"Command input is invalid: {ex.Message}.");
- return new MethodResponse((int)StatusCode.BadRequest);
- }
-
- return new MethodResponse((int)StatusCode.Completed);
- }
-
- // The callback to handle "getMaxMinReport" command. This method will returns the max, min and average temperature from the
- // specified time to the current time.
- private Task<MethodResponse> HandleMaxMinReportCommand(MethodRequest request, object userContext)
- {
- try
- {
- string componentName = (string)userContext;
- DateTime sinceInUtc = JsonConvert.DeserializeObject<DateTime>(request.DataAsJson);
- var sinceInDateTimeOffset = new DateTimeOffset(sinceInUtc);
-
- if (_temperatureReadingsDateTimeOffset.ContainsKey(componentName))
- {
- //_logger.LogDebug($"Command: Received - component=\"{componentName}\", generating max, min and avg temperature " +
- //$"report since {sinceInDateTimeOffset.LocalDateTime}.");
-
- Dictionary<DateTimeOffset, double> allReadings = _temperatureReadingsDateTimeOffset[componentName];
- Dictionary<DateTimeOffset, double> filteredReadings = allReadings.Where(i => i.Key > sinceInDateTimeOffset)
- .ToDictionary(i => i.Key, i => i.Value);
-
- if (filteredReadings != null && filteredReadings.Any())
- {
- var report = new
- {
- maxTemp = filteredReadings.Values.Max<double>(),
- minTemp = filteredReadings.Values.Min<double>(),
- avgTemp = filteredReadings.Values.Average(),
- startTime = filteredReadings.Keys.Min(),
- endTime = filteredReadings.Keys.Max(),
- };
-
- //_logger.LogDebug($"Command: component=\"{componentName}\", MaxMinReport since {sinceInDateTimeOffset.LocalDateTime}:" +
- // $" maxTemp={report.maxTemp}, minTemp={report.minTemp}, avgTemp={report.avgTemp}, startTime={report.startTime.LocalDateTime}, " +
- // $"endTime={report.endTime.LocalDateTime}");
-
- byte[] responsePayload = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(report));
- return Task.FromResult(new MethodResponse(responsePayload, (int)StatusCode.Completed));
- }
-
- //_logger.LogDebug($"Command: component=\"{componentName}\", no relevant readings found since {sinceInDateTimeOffset.LocalDateTime}, " +
- // $"cannot generate any report.");
- return Task.FromResult(new MethodResponse((int)StatusCode.NotFound));
- }
-
- //_logger.LogDebug($"Command: component=\"{componentName}\", no temperature readings sent yet, cannot generate any report.");
- return Task.FromResult(new MethodResponse((int)StatusCode.NotFound));
- }
- catch (JsonReaderException /*ex*/)
- {
- //_logger.LogDebug($"Command input is invalid: {ex.Message}.");
- return Task.FromResult(new MethodResponse((int)StatusCode.BadRequest));
- }
- }
-
- private Task SetDesiredPropertyUpdateCallback(TwinCollection desiredProperties, object userContext)
- {
- bool callbackNotInvoked = true;
-
- foreach (KeyValuePair<string, object> propertyUpdate in desiredProperties)
- {
- string componentName = propertyUpdate.Key;
- if (_desiredPropertyUpdateCallbacks.ContainsKey(componentName))
- {
- _desiredPropertyUpdateCallbacks[componentName]?.Invoke(desiredProperties, componentName);
- callbackNotInvoked = false;
- }
- }
-
- if (callbackNotInvoked)
- {
- //_logger.LogDebug($"Property: Received a property update that is not implemented by any associated component.");
- }
-
- return Task.CompletedTask;
- }
-
- // The desired property update callback, which receives the target temperature as a desired property update,
- // and updates the current temperature value over telemetry and property update.
- private async Task TargetTemperatureUpdateCallbackAsync(TwinCollection desiredProperties, object userContext)
- {
- string componentName = (string)userContext;
-
- bool targetTempUpdateReceived = PnpConvention.TryGetPropertyFromTwin(
- desiredProperties,
- TargetTemperatureProperty,
- out double targetTemperature,
- componentName);
- if (!targetTempUpdateReceived)
- {
- //_logger.LogDebug($"Property: Update - component=\"{componentName}\", received an update which is not associated with a valid property.\n{desiredProperties.ToJson()}");
- return;
- }
-
- //_logger.LogDebug($"Property: Received - component=\"{componentName}\", {{ \"{TargetTemperatureProperty}\": {targetTemperature}°C }}.");
-
- s_localWritablePropertiesVersion = desiredProperties.Version;
-
- TwinCollection pendingReportedProperty = PnpConvention.CreateComponentWritablePropertyResponse(
- componentName,
- TargetTemperatureProperty,
- targetTemperature,
- (int)StatusCode.InProgress,
- desiredProperties.Version,
- "In progress - reporting current temperature");
-
- await _deviceClient.UpdateReportedPropertiesAsync(pendingReportedProperty);
- //_logger.LogDebug($"Property: Update - component=\"{componentName}\", {{\"{TargetTemperatureProperty}\": {targetTemperature} }} in °C is {StatusCode.InProgress}.");
-
- // Update Temperature in 2 steps
- double step = (targetTemperature - _temperature[componentName]) / 2d;
- for (int i = 1; i <= 2; i++)
- {
- _temperature[componentName] = Math.Round(_temperature[componentName] + step, 1);
- await Task.Delay(6 * 1000);
- }
-
- TwinCollection completedReportedProperty = PnpConvention.CreateComponentWritablePropertyResponse(
- componentName,
- TargetTemperatureProperty,
- _temperature[componentName],
- (int)StatusCode.Completed,
- desiredProperties.Version,
- "Successfully updated target temperature");
-
- await _deviceClient.UpdateReportedPropertiesAsync(completedReportedProperty);
- //_logger.LogDebug($"Property: Update - component=\"{componentName}\", {{\"{TargetTemperatureProperty}\": {_temperature[componentName]} }} in °C is {StatusCode.Completed}");
- }
-
- // Report the property updates on "deviceInformation" component.
- private async Task UpdateDeviceInformationAsync(CancellationToken cancellationToken)
- {
- const string componentName = "deviceInformation";
-
- TwinCollection deviceInfoTc = PnpConvention.CreateComponentPropertyPatch(
- componentName,
- new Dictionary<string, object>
- {
- { "manufacturer", "element15" },
- { "model", "ModelIDxcdvmk" },
- { "swVersion", "1.0.0" },
- { "osName", "Windows 10" },
- { "processorArchitecture", "64-bit" },
- { "processorManufacturer", "Intel" },
- { "totalStorage", 256 },
- { "totalMemory", 1024 },
- });
-
- await _deviceClient.UpdateReportedPropertiesAsync(deviceInfoTc, cancellationToken);
- //_logger.LogDebug($"Property: Update - component = '{componentName}', properties update is complete.");
- }
-
- // Send working set of device memory over telemetry.
- private async Task SendDeviceMemoryAsync(CancellationToken cancellationToken)
- {
- const string workingSetName = "workingSet";
-
- long workingSet = Process.GetCurrentProcess().PrivateMemorySize64 / 1024;
-
- var telemetry = new Dictionary<string, object>
- {
- { workingSetName, workingSet },
- };
-
- using Message msg = PnpConvention.CreateMessage(telemetry);
-
- await _deviceClient.SendEventAsync(msg, cancellationToken);
- //_logger.LogDebug($"Telemetry: Sent - {JsonConvert.SerializeObject(telemetry)} in KB.");
- }
-
- // Send device serial number over property update.
- private async Task SendDeviceSerialNumberAsync(CancellationToken cancellationToken)
- {
- const string propertyName = "serialNumber";
- TwinCollection reportedProperties = PnpConvention.CreatePropertyPatch(propertyName, SerialNumber);
-
- await _deviceClient.UpdateReportedPropertiesAsync(reportedProperties, cancellationToken);
- //var oBrace = '{';
- //var cBrace = '}';
- //_logger.LogDebug($"Property: Update - {oBrace} \"{propertyName}\": \"{SerialNumber}\" {cBrace} is complete.");
- }
-
- private async Task SendTemperatureAsync(string componentName, CancellationToken cancellationToken)
- {
- await SendTemperatureTelemetryAsync(componentName, cancellationToken);
-
- double maxTemp = _temperatureReadingsDateTimeOffset[componentName].Values.Max<double>();
- if (maxTemp > _maxTemp[componentName])
- {
- _maxTemp[componentName] = maxTemp;
- await UpdateMaxTemperatureSinceLastRebootAsync(componentName, cancellationToken);
- }
- }
-
- private async Task SendTemperatureTelemetryAsync(string componentName, CancellationToken cancellationToken)
- {
- //const string telemetryName = "temperature";
- double currentTemperature = _temperature[componentName];
- using Message msg = PnpConvention.CreateMessage(Telemetry, /*telemetryName, currentTemperature, */componentName);
- await _deviceClient.SendEventAsync(msg, cancellationToken);
-
- //_logger.LogDebug($"Telemetry: Sent - component=\"{componentName}\", {{ \"{telemetryName}\": {currentTemperature} }} in °C.");
-
- if (_temperatureReadingsDateTimeOffset.ContainsKey(componentName))
- {
- _temperatureReadingsDateTimeOffset[componentName].TryAdd(DateTimeOffset.UtcNow, currentTemperature);
- }
- else
- {
- _temperatureReadingsDateTimeOffset.TryAdd(
- componentName,
- new Dictionary<DateTimeOffset, double>
- {
- { DateTimeOffset.UtcNow, currentTemperature },
- });
- }
- }
-
- private async Task UpdateMaxTemperatureSinceLastRebootAsync(string componentName, CancellationToken cancellationToken)
- {
- const string propertyName = "maxTempSinceLastReboot";
- double maxTemp = _maxTemp[componentName];
- TwinCollection reportedProperties = PnpConvention.CreateComponentPropertyPatch(componentName, propertyName, maxTemp);
-
- await _deviceClient.UpdateReportedPropertiesAsync(reportedProperties, cancellationToken);
- //_logger.LogDebug($"Property: Update - component=\"{componentName}\", {{ \"{propertyName}\": {maxTemp} }} in °C is complete.");
- }
-
- private async Task CheckEmptyPropertiesAsync(string componentName, CancellationToken cancellationToken)
- {
- Twin twin = await _deviceClient.GetTwinAsync(cancellationToken);
- TwinCollection writableProperty = twin.Properties.Desired;
- TwinCollection reportedProperty = twin.Properties.Reported;
-
- // Check if the device properties (both writable and reported) for the current component are empty.
- if (!writableProperty.Contains(componentName) && !reportedProperty.Contains(componentName))
- {
- await ReportInitialPropertyAsync(componentName, TargetTemperatureProperty, cancellationToken);
- }
- }
-
- private async Task ReportInitialPropertyAsync(string componentName, string propertyName, CancellationToken cancellationToken)
- {
- // If the device properties are empty, report the default value with ACK(ac=203, av=0) as part of the PnP convention.
- // "DefaultPropertyValue" is set from the device when the desired property is not set via the hub.
- TwinCollection reportedProperties = PnpConvention.CreateComponentWritablePropertyResponse(
- componentName,
- propertyName,
- DefaultPropertyValue,
- (int)StatusCode.ReportDeviceInitialProperty,
- DefaultAckVersion,
- "Initialized with default value");
-
- await _deviceClient.UpdateReportedPropertiesAsync(reportedProperties, cancellationToken);
-
- //_logger.LogDebug($"Report the default values for \"{componentName}\".\nProperty: Update - {reportedProperties.ToJson()} is complete.");
- }
- }
-}
diff --git a/examples/QtAzureIoT/device/DeviceToBackoffice/WritablePropertyResponse.cs b/examples/QtAzureIoT/device/DeviceToBackoffice/WritablePropertyResponse.cs
deleted file mode 100644
index 3cbfb64..0000000
--- a/examples/QtAzureIoT/device/DeviceToBackoffice/WritablePropertyResponse.cs
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-
-using Newtonsoft.Json;
-
-namespace PnpHelpers
-{
- /// <summary>
- /// The payload for a property update response.
- /// </summary>
- public class WritablePropertyResponse
- {
- /// <summary>
- /// Empty constructor.
- /// </summary>
- public WritablePropertyResponse() { }
-
- /// <summary>
- /// Convenience constructor for specifying the properties.
- /// </summary>
- /// <param name="propertyValue">The unserialized property value.</param>
- /// <param name="ackCode">The acknowledgment code, usually an HTTP Status Code e.g. 200, 400.</param>
- /// <param name="ackVersion">The acknowledgment version, as supplied in the property update request.</param>
- /// <param name="ackDescription">The acknowledgment description, an optional, human-readable message about the result of the property update.</param>
- public WritablePropertyResponse(object propertyValue, int ackCode, long ackVersion, string ackDescription = null)
- {
- PropertyValue = propertyValue;
- AckCode = ackCode;
- AckVersion = ackVersion;
- AckDescription = ackDescription;
- }
-
- /// <summary>
- /// The unserialized property value.
- /// </summary>
- [JsonProperty("value")]
- public object PropertyValue { get; set; }
-
- /// <summary>
- /// The acknowledgment code, usually an HTTP Status Code e.g. 200, 400.
- /// </summary>
- [JsonProperty("ac")]
- public int AckCode { get; set; }
-
- /// <summary>
- /// The acknowledgment version, as supplied in the property update request.
- /// </summary>
- [JsonProperty("av")]
- public long AckVersion { get; set; }
-
- /// <summary>
- /// The acknowledgment description, an optional, human-readable message about the result of the property update.
- /// </summary>
- [JsonProperty("ad", DefaultValueHandling = DefaultValueHandling.Ignore)]
- public string AckDescription { get; set; }
- }
-}
diff --git a/examples/QtAzureIoT/device/SensorData/SensorData.cs b/examples/QtAzureIoT/device/SensorData/SensorData.cs
deleted file mode 100644
index c58c5c1..0000000
--- a/examples/QtAzureIoT/device/SensorData/SensorData.cs
+++ /dev/null
@@ -1,107 +0,0 @@
-/***************************************************************************************************
- Copyright (C) 2023 The Qt Company Ltd.
- SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-***************************************************************************************************/
-
-using System.Diagnostics;
-using System.Device.I2c;
-using Iot.Device.Bmxx80;
-using Iot.Device.Bmxx80.PowerMode;
-using QtAzureIoT.Utils;
-
-namespace QtAzureIoT.Device
-{
- public class SensorData : PropertySet, IDisposable
- {
- public SensorData()
- { }
-
- public double Temperature
- {
- get => propertyTemperature;
- private set => SetProperty(ref propertyTemperature, value, nameof(Temperature));
- }
-
- public double Pressure
- {
- get => propertyPressure;
- private set => SetProperty(ref propertyPressure, value, nameof(Pressure));
- }
-
- public double Humidity
- {
- get => propertyHumidity;
- private set => SetProperty(ref propertyHumidity, value, nameof(Humidity));
- }
-
- public void StartPolling()
- {
- if (Sensor != null)
- return;
- BusConnectionSettings = new I2cConnectionSettings(1, Bme280.DefaultI2cAddress);
- BusDevice = I2cDevice.Create(BusConnectionSettings);
- Sensor = new Bme280(BusDevice);
- MesasuramentDelay = Sensor.GetMeasurementDuration();
-
- PollingLoop = new CancellationTokenSource();
- Polling = new Task(async () => await PollingLoopAsync(), PollingLoop.Token);
- Polling.Start();
- }
-
- public void StopPolling()
- {
- if (Sensor == null)
- return;
- PollingLoop.Cancel();
- Polling.Wait();
- Sensor.Dispose();
- Sensor = null;
- BusDevice.Dispose();
- BusDevice = null;
- BusConnectionSettings = null;
- PollingLoop.Dispose();
- PollingLoop = null;
- Polling.Dispose();
- Polling = null;
- }
-
- #region private
- private I2cConnectionSettings BusConnectionSettings { get; set; }
- private I2cDevice BusDevice { get; set; }
- private Bme280 Sensor { get; set; }
- int MesasuramentDelay { get; set; }
- private CancellationTokenSource PollingLoop { get; set; }
- private Task Polling { get; set; }
-
-
- private async Task PollingLoopAsync()
- {
- while (!PollingLoop.IsCancellationRequested) {
- try {
- Sensor.SetPowerMode(Bmx280PowerMode.Forced);
- await Task.Delay(MesasuramentDelay);
-
- if (Sensor.TryReadTemperature(out var tempValue))
- Temperature = tempValue.DegreesCelsius;
- if (Sensor.TryReadPressure(out var preValue))
- Pressure = preValue.Hectopascals;
- if (Sensor.TryReadHumidity(out var humValue))
- Humidity = humValue.Percent;
- } catch (Exception e) {
- Debug.WriteLine($"Exception: {e.GetType().Name}: {e.Message}");
- }
- await Task.Delay(1000);
- }
- }
-
- public void Dispose()
- {
- StopPolling();
- }
-
- double propertyTemperature;
- double propertyPressure;
- double propertyHumidity;
- #endregion
- }
-}
diff --git a/examples/QtAzureIoT/device/SensorData/SensorData.csproj b/examples/QtAzureIoT/device/SensorData/SensorData.csproj
deleted file mode 100644
index c694f5b..0000000
--- a/examples/QtAzureIoT/device/SensorData/SensorData.csproj
+++ /dev/null
@@ -1,17 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-
- <PropertyGroup>
- <TargetFramework>net6.0</TargetFramework>
- <ImplicitUsings>enable</ImplicitUsings>
- <Nullable>disable</Nullable>
- </PropertyGroup>
-
- <ItemGroup>
- <PackageReference Include="Iot.Device.Bindings" Version="2.2.0" />
- </ItemGroup>
-
- <ItemGroup>
- <ProjectReference Include="..\..\common\Utils.csproj" />
- </ItemGroup>
-
-</Project>
diff --git a/examples/QtAzureIoT/device/deviceapp/deviceapp.vcxproj b/examples/QtAzureIoT/device/deviceapp/deviceapp.vcxproj
deleted file mode 100644
index fb25e64..0000000
--- a/examples/QtAzureIoT/device/deviceapp/deviceapp.vcxproj
+++ /dev/null
@@ -1,126 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="17.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <ItemGroup Label="ProjectConfigurations">
- <ProjectConfiguration Include="Debug|x64">
- <Configuration>Debug</Configuration>
- <Platform>x64</Platform>
- </ProjectConfiguration>
- <ProjectConfiguration Include="Release|x64">
- <Configuration>Release</Configuration>
- <Platform>x64</Platform>
- </ProjectConfiguration>
- </ItemGroup>
- <PropertyGroup Label="Globals">
- <ProjectGuid>{2B3D1059-93F6-42F8-9709-AE06BEDE1EEF}</ProjectGuid>
- <Keyword>QtVS_v304</Keyword>
- <WindowsTargetPlatformVersion Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">10.0.19041.0</WindowsTargetPlatformVersion>
- <WindowsTargetPlatformVersion Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">10.0.19041.0</WindowsTargetPlatformVersion>
- <QtMsBuild Condition="'$(QtMsBuild)'=='' OR !Exists('$(QtMsBuild)\qt.targets')">$(MSBuildProjectDirectory)\QtMsBuild</QtMsBuild>
- </PropertyGroup>
- <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
- <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'" Label="Configuration">
- <ConfigurationType>Application</ConfigurationType>
- <PlatformToolset>v143</PlatformToolset>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'" Label="Configuration">
- <ConfigurationType>Application</ConfigurationType>
- <PlatformToolset>v143</PlatformToolset>
- </PropertyGroup>
- <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
- <ImportGroup Condition="Exists('$(QtMsBuild)\qt_defaults.props')">
- <Import Project="$(QtMsBuild)\qt_defaults.props" />
- </ImportGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'" Label="QtSettings">
- <QtInstall>6.2.7_msvc2019_64</QtInstall>
- <QtModules>core;quick</QtModules>
- <QtBuildConfig>debug</QtBuildConfig>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'" Label="QtSettings">
- <QtInstall>6.2.7_msvc2019_64</QtInstall>
- <QtModules>core;quick</QtModules>
- <QtBuildConfig>release</QtBuildConfig>
- </PropertyGroup>
- <Target Name="QtMsBuildNotFound" BeforeTargets="CustomBuild;ClCompile" Condition="!Exists('$(QtMsBuild)\qt.targets') or !Exists('$(QtMsBuild)\qt.props')">
- <Message Importance="High" Text="QtMsBuild: could not locate qt.targets, qt.props; project may not build correctly." />
- </Target>
- <ImportGroup Label="ExtensionSettings" />
- <ImportGroup Label="Shared" />
- <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
- <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
- <Import Project="$(QtMsBuild)\Qt.props" />
- </ImportGroup>
- <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
- <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
- <Import Project="$(QtMsBuild)\Qt.props" />
- </ImportGroup>
- <PropertyGroup Label="UserMacros" />
- <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
- <IncludePath>C:\dev\source\qt-labs\qtdotnet\include;$(IncludePath)</IncludePath>
- <OutDir>bin\$(Platform)\$(Configuration)\</OutDir>
- <IntDir>obj\$(Platform)\$(Configuration)\</IntDir>
- <CopyLocalProjectReference>true</CopyLocalProjectReference>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
- <IncludePath>C:\dev\source\qt-labs\qtdotnet\include;$(IncludePath)</IncludePath>
- <OutDir>bin\$(Platform)\$(Configuration)\</OutDir>
- <IntDir>obj\$(Platform)\$(Configuration)\</IntDir>
- <CopyLocalProjectReference>true</CopyLocalProjectReference>
- </PropertyGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'" Label="Configuration">
- <ClCompile>
- <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
- <MultiProcessorCompilation>true</MultiProcessorCompilation>
- <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
- <Optimization>Disabled</Optimization>
- </ClCompile>
- <Link>
- <SubSystem>Windows</SubSystem>
- <GenerateDebugInformation>true</GenerateDebugInformation>
- </Link>
- </ItemDefinitionGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'" Label="Configuration">
- <ClCompile>
- <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
- <MultiProcessorCompilation>true</MultiProcessorCompilation>
- <DebugInformationFormat>None</DebugInformationFormat>
- <Optimization>MaxSpeed</Optimization>
- </ClCompile>
- <Link>
- <SubSystem>Windows</SubSystem>
- <GenerateDebugInformation>false</GenerateDebugInformation>
- </Link>
- </ItemDefinitionGroup>
- <ItemGroup>
- <QtMoc Include="main.cpp">
- <DynamicSource Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">input</DynamicSource>
- <QtMocFileName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(Filename).moc</QtMocFileName>
- <DynamicSource Condition="'$(Configuration)|$(Platform)'=='Release|x64'">input</DynamicSource>
- <QtMocFileName Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(Filename).moc</QtMocFileName>
- </QtMoc>
- <QtRcc Include="qml.qrc" />
- <CopyFileToFolders Include="..\..\..\qtdotnet\bin\QtVsTools.QtDotNet.Adapter.dll">
- <FileType>Document</FileType>
- <TreatOutputAsContent Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</TreatOutputAsContent>
- <TreatOutputAsContent Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</TreatOutputAsContent>
- </CopyFileToFolders>
- <None Include="main.qml" />
- </ItemGroup>
- <ItemGroup>
- <ProjectReference Include="..\CardReader\CardReader.csproj">
- <Project>{66a69341-3b00-4812-aa77-ec5c2e9ea23a}</Project>
- <ReferenceOutputAssembly>true</ReferenceOutputAssembly>
- <LinkLibraryDependencies>false</LinkLibraryDependencies>
- </ProjectReference>
- <ProjectReference Include="..\SensorData\SensorData.csproj">
- <Project>{d8cd7e8d-7eca-46a7-aa39-e471f99c94f0}</Project>
- <ReferenceOutputAssembly>true</ReferenceOutputAssembly>
- <LinkLibraryDependencies>false</LinkLibraryDependencies>
- </ProjectReference>
- </ItemGroup>
- <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
- <ImportGroup Condition="Exists('$(QtMsBuild)\qt.targets')">
- <Import Project="$(QtMsBuild)\qt.targets" />
- </ImportGroup>
- <ImportGroup Label="ExtensionTargets">
- </ImportGroup>
-</Project> \ No newline at end of file
diff --git a/examples/QtAzureIoT/device/deviceapp/deviceapp.vcxproj.filters b/examples/QtAzureIoT/device/deviceapp/deviceapp.vcxproj.filters
deleted file mode 100644
index 4965748..0000000
--- a/examples/QtAzureIoT/device/deviceapp/deviceapp.vcxproj.filters
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <ItemGroup>
- <Filter Include="Source Files">
- <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
- <Extensions>qml;cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
- </Filter>
- <Filter Include="Header Files">
- <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
- <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
- </Filter>
- <Filter Include="Resource Files">
- <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
- <Extensions>qrc;rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
- </Filter>
- <Filter Include="Form Files">
- <UniqueIdentifier>{99349809-55BA-4b9d-BF79-8FDBB0286EB3}</UniqueIdentifier>
- <Extensions>ui</Extensions>
- </Filter>
- <Filter Include="Translation Files">
- <UniqueIdentifier>{639EADAA-A684-42e4-A9AD-28FC9BCB8F7C}</UniqueIdentifier>
- <Extensions>ts</Extensions>
- </Filter>
- </ItemGroup>
- <ItemGroup>
- <QtRcc Include="qml.qrc">
- <Filter>Resource Files</Filter>
- </QtRcc>
- <None Include="main.qml">
- <Filter>Source Files</Filter>
- </None>
- </ItemGroup>
- <ItemGroup>
- <CopyFileToFolders Include="..\..\..\qtdotnet\bin\QtVsTools.QtDotNet.Adapter.dll" />
- </ItemGroup>
- <ItemGroup>
- <QtMoc Include="main.cpp">
- <Filter>Source Files</Filter>
- </QtMoc>
- </ItemGroup>
-</Project> \ No newline at end of file
diff --git a/examples/QtAzureIoT/device/deviceapp/main.cpp b/examples/QtAzureIoT/device/deviceapp/main.cpp
deleted file mode 100644
index ba049d1..0000000
--- a/examples/QtAzureIoT/device/deviceapp/main.cpp
+++ /dev/null
@@ -1,183 +0,0 @@
-/***************************************************************************************************
- Copyright (C) 2023 The Qt Company Ltd.
- SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-***************************************************************************************************/
-
-#include <QtGui/QGuiApplication>
-#include <QtQml/QQmlApplicationEngine>
-#include <QtQml/QQmlContext>
-
-#include <qdotnetobject.h>
-#include <qdotnetevent.h>
-
-class Backoffice : public QDotNetObject
-{
-public:
- Q_DOTNET_OBJECT_INLINE(Backoffice, "QtAzureIoT.Device.Backoffice, DeviceToBackoffice");
- Backoffice() : QDotNetObject(getConstructor<Backoffice>().invoke(nullptr))
- {}
- void setTelemetry(QString name, double value)
- {
- getMethod("SetTelemetry", fnSetTelemetryDouble).invoke(*this, name, value);
- }
- void setTelemetry(QString name, bool value)
- {
- getMethod("SetTelemetry", fnSetTelemetryBool).invoke(*this, name, value);
- }
-public:
- void startPolling() {
- getMethod("StartPolling", fnStartPolling).invoke(*this);
- }
- void stopPolling() {
- getMethod("StopPolling", fnStopPolling).invoke(*this);
- }
-private:
- mutable QDotNetFunction<void, QString, double> fnSetTelemetryDouble;
- mutable QDotNetFunction<void, QString, bool> fnSetTelemetryBool;
- mutable QDotNetFunction<void> fnStartPolling;
- mutable QDotNetFunction<void> fnStopPolling;
-};
-
-class SensorData : public QObject, public QDotNetObject, public QDotNetObject::IEventHandler
-{
- Q_OBJECT
- Q_PROPERTY(double temperature READ temperature NOTIFY temperatureChanged)
- Q_PROPERTY(double pressure READ pressure NOTIFY pressureChanged)
- Q_PROPERTY(double humidity READ humidity NOTIFY humidityChanged)
-public:
- Q_DOTNET_OBJECT_INLINE(SensorData, "QtAzureIoT.Device.SensorData, SensorData");
- SensorData() : QDotNetObject(getConstructor<SensorData>().invoke(nullptr))
- {
- subscribeEvent("PropertyChanged", this);
- }
- double temperature() const
- {
- return getMethod("get_Temperature", fnGet_Temperature).invoke(*this);
- }
- double pressure() const
- {
- return getMethod("get_Pressure", fnGet_Pressure).invoke(*this);
- }
- double humidity() const
- {
- return getMethod("get_Humidity", fnGet_Humidity).invoke(*this);
- }
-public slots:
- void startPolling() {
- getMethod("StartPolling", fnStartPolling).invoke(*this);
- }
- void stopPolling() {
- getMethod("StopPolling", fnStopPolling).invoke(*this);
- }
-signals:
- void temperatureChanged();
- void pressureChanged();
- void humidityChanged();
-private:
- void handleEvent(const QString& evName, QDotNetObject& evSrc, QDotNetObject& evArgs) override
- {
- if (evName == "PropertyChanged") {
- if (evArgs.type().fullName() == QDotNetPropertyEvent::FullyQualifiedTypeName) {
- auto propertyChangedEvent = evArgs.cast<QDotNetPropertyEvent>();
- if (propertyChangedEvent.propertyName() == "Temperature")
- emit temperatureChanged();
- else if (propertyChangedEvent.propertyName() == "Pressure")
- emit pressureChanged();
- else if (propertyChangedEvent.propertyName() == "Humidity")
- emit humidityChanged();
- }
- }
- }
- mutable QDotNetFunction<double> fnGet_Temperature;
- mutable QDotNetFunction<double> fnGet_Pressure;
- mutable QDotNetFunction<double> fnGet_Humidity;
- mutable QDotNetFunction<void> fnStartPolling;
- mutable QDotNetFunction<void> fnStopPolling;
-};
-
-class CardReader : public QObject, public QDotNetObject, public QDotNetObject::IEventHandler
-{
- Q_OBJECT
- Q_PROPERTY(bool cardInReader READ cardInReader NOTIFY cardInReaderChanged)
-public:
- Q_DOTNET_OBJECT_INLINE(CardReader, "QtAzureIoT.Device.CardReader, CardReader");
- CardReader() : QDotNetObject(getConstructor<CardReader>().invoke(nullptr))
- {
- subscribeEvent("PropertyChanged", this);
- }
- bool cardInReader() const
- {
- return getMethod("get_CardInReader", fnGet_CardInReader).invoke(*this);
- }
-public slots:
- void startPolling() {
- getMethod("StartPolling", fnStartPolling).invoke(*this);
- }
- void stopPolling() {
- getMethod("StopPolling", fnStopPolling).invoke(*this);
- }
-signals:
- void cardInReaderChanged();
-private:
- void handleEvent(const QString& evName, QDotNetObject& evSrc, QDotNetObject& evArgs) override
- {
- if (evName == "PropertyChanged") {
- if (evArgs.type().fullName() == QDotNetPropertyEvent::FullyQualifiedTypeName) {
- auto propertyChangedEvent = evArgs.cast<QDotNetPropertyEvent>();
- if (propertyChangedEvent.propertyName() == "CardInReader")
- emit cardInReaderChanged();
- }
- }
- }
- mutable QDotNetFunction<bool> fnGet_CardInReader;
- mutable QDotNetFunction<void> fnStartPolling;
- mutable QDotNetFunction<void> fnStopPolling;
-};
-
-int main(int argc, char* argv[])
-{
- QGuiApplication app(argc, argv);
- QQmlApplicationEngine engine;
-
- CardReader card;
- card.startPolling();
- engine.rootContext()->setContextProperty("card", &card);
-
- SensorData sensor;
- sensor.startPolling();
- engine.rootContext()->setContextProperty("sensor", &sensor);
-
- Backoffice backoffice;
- QObject::connect(&card, &CardReader::cardInReaderChanged,
- [&backoffice, &card]()
- {
- backoffice.setTelemetry("card", card.cardInReader());
- });
-
- QObject::connect(&sensor, &SensorData::temperatureChanged,
- [&backoffice, &sensor]()
- {
- backoffice.setTelemetry("temperature", sensor.temperature());
- });
-
- QObject::connect(&sensor, &SensorData::pressureChanged,
- [&backoffice, &sensor]()
- {
- backoffice.setTelemetry("pressure", sensor.pressure());
- });
-
- QObject::connect(&sensor, &SensorData::humidityChanged,
- [&backoffice, &sensor]()
- {
- backoffice.setTelemetry("humidity", sensor.humidity());
- });
- backoffice.startPolling();
-
- engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
- if (engine.rootObjects().isEmpty())
- return -1;
-
- return app.exec();
-}
-
-#include "main.moc"
diff --git a/examples/QtAzureIoT/device/deviceapp/main.qml b/examples/QtAzureIoT/device/deviceapp/main.qml
deleted file mode 100644
index 368ce0c..0000000
--- a/examples/QtAzureIoT/device/deviceapp/main.qml
+++ /dev/null
@@ -1,56 +0,0 @@
-/***************************************************************************************************
- Copyright (C) 2023 The Qt Company Ltd.
- SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-***************************************************************************************************/
-
-import QtQml
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import QtQuick.Shapes
-
-Window {
- visible: true
- width: 800
- height: 480
- flags: Qt.FramelessWindowHint
- color: "black"
- GridLayout {
- anchors.fill: parent
- columns: 2
- Text {
- Layout.alignment: Qt.AlignCenter
- horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignVCenter
- font { bold: true; pointSize: 32 }
- color: "white"
- text: sensor.temperature.toFixed(2) + " deg.C."
- }
- Text {
- Layout.alignment: Qt.AlignCenter
- horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignVCenter
- font { bold: true; pointSize: 32 }
- color: "white"
- text: card.cardInReader ? "CARD DETECTED" : "No card";
- }
- Text {
- Layout.alignment: Qt.AlignCenter
- horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignVCenter
- font { bold: true; pointSize: 32 }
- color: "white"
- text: sensor.pressure.toFixed(2) + " hPa."
- }
- Text {
- Layout.alignment: Qt.AlignCenter
- horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignVCenter
- font { bold: true; pointSize: 32 }
- color: "white"
- text: sensor.humidity.toFixed(2) + " %"
- }
- }
- Button {
- text: "EXIT"
- onClicked: Qt.exit(0)
- anchors.right: parent.right
- anchors.bottom: parent.bottom
- }
-}
diff --git a/examples/QtAzureIoT/device/deviceapp/qml.qrc b/examples/QtAzureIoT/device/deviceapp/qml.qrc
deleted file mode 100644
index 5f6483a..0000000
--- a/examples/QtAzureIoT/device/deviceapp/qml.qrc
+++ /dev/null
@@ -1,5 +0,0 @@
-<RCC>
- <qresource prefix="/">
- <file>main.qml</file>
- </qresource>
-</RCC>
diff --git a/include/iqmodelindex.h b/include/iqmodelindex.h
new file mode 100644
index 0000000..84dd309
--- /dev/null
+++ b/include/iqmodelindex.h
@@ -0,0 +1,67 @@
+/***************************************************************************************************
+ Copyright (C) 2024 The Qt Company Ltd.
+ SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+***************************************************************************************************/
+
+#pragma once
+
+#include "qdotnetinterface.h"
+
+#ifdef __GNUC__
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wconversion"
+#endif
+#include <QModelIndex>
+#ifdef __GNUC__
+# pragma GCC diagnostic pop
+#endif
+
+#include <functional>
+
+struct IQModelIndex : public QDotNetNativeInterface<QModelIndex>
+{
+ static inline const QString &AssemblyQualifiedName =
+ QStringLiteral("Qt.DotNet.IQModelIndex, Qt.DotNet.Adapter");
+
+ IQModelIndex(const void *objectRef = nullptr)
+ : QDotNetNativeInterface<QModelIndex>(objectRef)
+ {
+ }
+
+ IQModelIndex(const QModelIndex &idx)
+ : QDotNetNativeInterface<QModelIndex>(AssemblyQualifiedName, new QModelIndex(idx), true)
+ {
+ init();
+ }
+
+ IQModelIndex(bool doCleanUp)
+ : QDotNetNativeInterface<QModelIndex>(AssemblyQualifiedName, new QModelIndex(), doCleanUp)
+ {
+ init();
+ }
+
+ void init() {
+ setCallback<bool>("IsValid", [this](void *data)
+ {
+ return reinterpret_cast<QModelIndex *>(data)->isValid();
+ });
+ setCallback<int>("Column", [this](void *data)
+ {
+ return reinterpret_cast<QModelIndex *>(data)->column();
+ });
+ setCallback<int>("Row", [this](void *data)
+ {
+ return reinterpret_cast<QModelIndex *>(data)->row();
+ });
+ setCallback<void *>("InternalPointer", [this](void *data)
+ {
+ return reinterpret_cast<QModelIndex *>(data)->internalPointer();
+ });
+ }
+
+ static void staticInit(QDotNetInterface *sta)
+ {
+ sta->setCallback<IQModelIndex>("QModelIndex_Create",
+ [](void *) { return IQModelIndex(true); });
+ }
+};
diff --git a/include/iqvariant.h b/include/iqvariant.h
new file mode 100644
index 0000000..255b082
--- /dev/null
+++ b/include/iqvariant.h
@@ -0,0 +1,73 @@
+/***************************************************************************************************
+ Copyright (C) 2024 The Qt Company Ltd.
+ SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+***************************************************************************************************/
+
+#pragma once
+
+#include "qdotnetinterface.h"
+
+#ifdef __GNUC__
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wconversion"
+#endif
+#include <QVariant>
+#ifdef __GNUC__
+# pragma GCC diagnostic pop
+#endif
+
+#include <functional>
+
+struct IQVariant : public QDotNetNativeInterface<QVariant>
+{
+ static inline const QString &AssemblyQualifiedName =
+ QStringLiteral("Qt.DotNet.IQVariant, Qt.DotNet.Adapter");
+
+ IQVariant(const void *objectRef = nullptr)
+ : QDotNetNativeInterface<QVariant>(objectRef)
+ {
+ }
+
+ IQVariant(QVariant &value, bool doCleanUp = false)
+ : QDotNetNativeInterface<QVariant>(AssemblyQualifiedName, &value, doCleanUp)
+ {
+ init();
+ }
+
+ IQVariant(const QString &value, bool doCleanUp = true)
+ : QDotNetNativeInterface<QVariant>(AssemblyQualifiedName, new QVariant(value), doCleanUp)
+ {
+ init();
+ }
+
+ IQVariant(bool doCleanUp)
+ : QDotNetNativeInterface<QVariant>(AssemblyQualifiedName, new QVariant(), doCleanUp)
+ {
+ init();
+ }
+
+ void init() {
+ setCallback<QString>("ToStringValue", [this](void *data)
+ {
+ QVariant *v = reinterpret_cast<QVariant *>(data);
+ if (!v)
+ return QString();
+ return v->toString();
+ });
+ setCallback<void, QString>("SetValue", [this](void *data, const auto &newValue)
+ {
+ QVariant *v = reinterpret_cast<QVariant *>(data);
+ if (!v)
+ return;
+ v->setValue(newValue);
+ });
+ }
+
+ static void staticInit(QDotNetInterface *sta)
+ {
+ sta->setCallback<IQVariant, QString>("QVariant_Create",
+ [](void *, QString value) { return IQVariant(value, true); });
+ sta->setCallback<IQVariant>("QVariant_Create",
+ [](void *) { return IQVariant(true); });
+ }
+};
diff --git a/include/qdotnetabstractlistmodel.h b/include/qdotnetabstractlistmodel.h
new file mode 100644
index 0000000..d917d37
--- /dev/null
+++ b/include/qdotnetabstractlistmodel.h
@@ -0,0 +1,241 @@
+/***************************************************************************************************
+ Copyright (C) 2024 The Qt Company Ltd.
+ SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+***************************************************************************************************/
+
+#pragma once
+
+#include "qdotnetinterface.h"
+#include "qdotnetobject.h"
+#include "qdotnetarray.h"
+#include "iqvariant.h"
+#include "iqmodelindex.h"
+
+#ifdef __GNUC__
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wconversion"
+#endif
+#include <QAbstractListModel>
+#ifdef __GNUC__
+# pragma GCC diagnostic pop
+#endif
+
+#include <functional>
+
+template<typename Base>
+struct IQAbstractListModel : public QDotNetNativeInterface<QAbstractListModel>
+{
+ static inline const QString &AssemblyQualifiedName =
+ QStringLiteral("Qt.DotNet.IQAbstractListModel, Qt.DotNet.Adapter");
+
+ IQAbstractListModel(Base *self)
+ : QDotNetNativeInterface<QAbstractListModel>(AssemblyQualifiedName, self, false)
+ {
+ setCallback<int, IQModelIndex>(
+ "Flags", [this](void *selfPtr, IQModelIndex index)
+ {
+ Base &self = *reinterpret_cast<Base *>(selfPtr);
+ return (int)self.base_flags(index);
+ });
+ setCallback<bool, IQModelIndex, IQVariant, int>(
+ "SetData", [this](void *selfPtr, IQModelIndex index, IQVariant value, int role)
+ {
+ auto *self = reinterpret_cast<Base *>(selfPtr);
+ return self->base_setData(index, value, role);
+ });
+ setCallback<bool, int, int, IQModelIndex>(
+ "InsertRows", [this](void *selfPtr, int row, int count, IQModelIndex parent)
+ {
+ auto *self = reinterpret_cast<Base *>(selfPtr);
+ return self->base_insertRows(row, count, parent);
+ });
+ setCallback<bool, int, int, IQModelIndex>(
+ "RemoveRows", [this](void *selfPtr, int row, int count, IQModelIndex parent)
+ {
+ auto *self = reinterpret_cast<Base *>(selfPtr);
+ return self->base_removeRows(row, count, parent);
+ });
+ setCallback<void, IQModelIndex, int, int>(
+ "BeginInsertRows", [this](void *selfPtr, IQModelIndex parent, int first, int last)
+ {
+ auto *self = reinterpret_cast<Base *>(selfPtr);
+ self->base_beginInsertRows(parent, first, last);
+ });
+ setCallback<void>(
+ "EndInsertRows", [this](void *selfPtr)
+ {
+ auto *self = reinterpret_cast<Base *>(selfPtr);
+ self->base_endInsertRows();
+ });
+ setCallback<void, IQModelIndex, int, int>(
+ "BeginRemoveRows", [this](void *selfPtr, IQModelIndex parent, int first, int last)
+ {
+ auto *self = reinterpret_cast<Base *>(selfPtr);
+ self->base_beginRemoveRows(parent, first, last);
+ });
+ setCallback<void>(
+ "EndRemoveRows", [this](void *selfPtr)
+ {
+ auto *self = reinterpret_cast<Base *>(selfPtr);
+ self->base_endRemoveRows();
+ });
+ setCallback<IQModelIndex, int, int, void *>(
+ "CreateIndex", [this](void *selfPtr, int row, int col, void *ptr)
+ {
+ auto *self = reinterpret_cast<Base *>(selfPtr);
+ return IQModelIndex(self->base_createIndex(row, col, ptr));
+ });
+ setCallback<void, IQModelIndex, IQModelIndex, QDotNetArray<int>>(
+ "EmitDataChanged", [this](void *selfPtr,
+ IQModelIndex topLeft, IQModelIndex bottomRight, QDotNetArray<int> roles)
+ {
+ auto *self = reinterpret_cast<Base *>(selfPtr);
+ if (roles.isValid()) {
+ QList<int> listRoles(roles.length());
+ for (int i = 0; i < roles.length(); ++i)
+ listRoles[i] = roles[i];
+ self->emit_base_dataChanged(topLeft, bottomRight, listRoles);
+ }
+ else {
+ self->emit_base_dataChanged(topLeft, bottomRight);
+ }
+ });
+ }
+};
+
+class QDotNetAbstractListModel : public QAbstractListModel, public QDotNetObject
+{
+
+public:
+ using IBase = IQAbstractListModel<QDotNetAbstractListModel>;
+ Q_DOTNET_OBJECT_REF_INLINE(QDotNetAbstractListModel, Q_DOTNET_OBJECT_INIT(base(this)))
+ Q_DOTNET_OBJECT_COPY_INLINE(QDotNetAbstractListModel, Q_DOTNET_OBJECT_INIT(base(this)))
+
+ QDotNetAbstractListModel(QDotNetObject &&movSrc) noexcept
+ : QDotNetObject(std::move(movSrc)), base(this)
+ {}
+
+ QDotNetAbstractListModel &operator=(QDotNetObject &&movSrc) noexcept
+ {
+ QDotNetObject::operator=(std::move(movSrc));
+ return *this;
+ }
+
+ static void staticInit(QDotNetInterface *sta)
+ {
+ sta->setCallback<IBase, QDotNetObject>("QAbstractListModel_Create",
+ [](void *, QDotNetObject self)
+ {
+ auto *obj = new QDotNetAbstractListModel(std::move(self));
+ return obj->base;
+ });
+ }
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override
+ {
+ return method("RowCount", fnRowCount).invoke(*this, parent);
+ }
+ int base_rowCount(const QModelIndex &parent = QModelIndex()) const
+ {
+ return QAbstractListModel::rowCount(parent);
+ }
+
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
+ {
+ return method("Data", fnData).invoke(*this, index, role);
+ }
+ QVariant base_data(const QModelIndex &index, int role = Qt::DisplayRole) const
+ {
+ return QAbstractListModel::data(index, role);
+ }
+
+ Qt::ItemFlags flags(const QModelIndex &index) const override
+ {
+ return Qt::ItemFlags::fromInt(method("Flags", fnFlags).invoke(*this, index));
+ }
+ Qt::ItemFlags base_flags(const QModelIndex &index) const
+ {
+ return QAbstractListModel::flags(index);
+ }
+
+ bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override
+ {
+ return method("SetData", fnSetData)
+ .invoke(*this, index, const_cast<std::remove_const_t<QVariant &>>(value), role);
+ }
+ bool base_setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole)
+ {
+ return QAbstractListModel::setData(index, value, role);
+ }
+
+ bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override
+ {
+ return method("InsertRows", fnInsertRows).invoke(*this, row, count, parent);
+ }
+ bool base_insertRows(int row, int count, const QModelIndex &parent = QModelIndex())
+ {
+ return QAbstractListModel::insertRows(row, count, parent);
+ }
+
+ bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override
+ {
+ return method("RemoveRows", fnRemoveRows).invoke(*this, row, count, parent);
+ }
+ bool base_removeRows(int row, int count, const QModelIndex &parent = QModelIndex())
+ {
+ return QAbstractListModel::removeRows(row, count, parent);
+ }
+
+ QHash<int, QByteArray> roleNames() const override
+ {
+ auto names = method("RoleNames", fnRoleNames).invoke(*this);
+ if (names.isEmpty())
+ return QAbstractListModel::roleNames();
+ auto nameList = names.split(u',', Qt::SkipEmptyParts);
+ QHash<int, QByteArray> roles;
+ for (int i = 0; i < nameList.size(); ++i)
+ roles[Qt::UserRole + i + 1] = nameList[i].toUtf8();
+ return roles;
+ }
+
+ void base_beginInsertRows(const QModelIndex &parent, int first, int last)
+ {
+ QAbstractListModel::beginInsertRows(parent, first, last);
+ }
+
+ void base_endInsertRows()
+ {
+ QAbstractListModel::endInsertRows();
+ }
+
+ void base_beginRemoveRows(const QModelIndex &parent, int first, int last)
+ {
+ QAbstractListModel::beginRemoveRows(parent, first, last);
+ }
+
+ void base_endRemoveRows()
+ {
+ QAbstractListModel::endRemoveRows();
+ }
+
+ QModelIndex base_createIndex(int arow, int acolumn, const void *adata) const
+ {
+ return QAbstractListModel::createIndex(arow, acolumn, adata);
+ }
+
+ void emit_base_dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight,
+ const QList<int> &roles = QList<int>())
+ {
+ emit QAbstractListModel::dataChanged(topLeft, bottomRight, roles);
+ }
+
+protected:
+ IBase base;
+ mutable QDotNetFunction<int, IQModelIndex> fnFlags = nullptr;
+ mutable QDotNetFunction<int, IQModelIndex> fnRowCount = nullptr;
+ mutable QDotNetFunction<IQVariant, IQModelIndex, int> fnData = nullptr;
+ mutable QDotNetFunction<bool, IQModelIndex, IQVariant, int> fnSetData = nullptr;
+ mutable QDotNetFunction<bool, int, int, IQModelIndex> fnInsertRows = nullptr;
+ mutable QDotNetFunction<bool, int, int, IQModelIndex> fnRemoveRows = nullptr;
+ mutable QDotNetFunction<QString> fnRoleNames = nullptr;
+};
diff --git a/include/qdotnetadapter.h b/include/qdotnetadapter.h
index fc9b74e..d9b5b95 100644
--- a/include/qdotnetadapter.h
+++ b/include/qdotnetadapter.h
@@ -33,6 +33,14 @@ private:
~QDotNetAdapter()
{
+ if (staticInterface && dtor_staticInterface) {
+ dtor_staticInterface(staticInterface);
+ staticInterface = nullptr;
+ }
+ fnReset();
+ //gcCollect();
+ //gcWaitForPendingFinalizers();
+
defaultHost.unload();
}
@@ -53,6 +61,12 @@ public:
.filePath(defaultDllName), defaultAssemblyName, defaultTypeName, externalHost);
}
+ static void init(const QString &assemblyPath, const QString &typeAndAssemblyName,
+ QDotNetHost *externalHost = nullptr)
+ {
+ init(assemblyPath, typeAndAssemblyName, typeAndAssemblyName, externalHost);
+ }
+
static void init(const QString &assemblyPath, const QString &assemblyName,
const QString &typeName, QDotNetHost *externalHost = nullptr)
{
@@ -92,10 +106,18 @@ public:
host->resolveFunction(QDOTNETADAPTER_DELEGATE(SetInterfaceMethod));
host->resolveFunction(QDOTNETADAPTER_DELEGATE(Stats));
host->resolveFunction(QDOTNETADAPTER_DELEGATE(GetObject));
+ host->resolveFunction(QDOTNETADAPTER_DELEGATE(Reset));
#undef QDOTNETADAPTER_DELEGATE
instance().host = host;
+
+ if (ctor_staticInterface)
+ instance().staticInterface = ctor_staticInterface();
+ gcCollect = instance().resolveStaticMethod("System.GC, System.Runtime",
+ "Collect", { QDotNetInbound<void>::Parameter });
+ gcWaitForPendingFinalizers = instance().resolveStaticMethod("System.GC, System.Runtime",
+ "WaitForPendingFinalizers", { QDotNetInbound<void>::Parameter });
}
static QDotNetAdapter &instance()
@@ -225,12 +247,12 @@ public:
fnFreeTypeRef(typeName);
}
- void *addInterfaceProxy(const QString &interfaceName) const
+ void *addInterfaceProxy(const QString &interfaceName, void *data, void *cleanUp) const
{
init();
if (interfaceName.isEmpty())
return nullptr;
- return fnAddInterfaceProxy(interfaceName);
+ return fnAddInterfaceProxy(interfaceName, data, cleanUp);
}
void setInterfaceMethod(const QDotNetRef &obj, const QString &methodName,
@@ -261,6 +283,8 @@ public:
Stats s{ };
init();
fnStats(&s.refCount, &s.staticCount, &s.eventCount);
+ if (staticInterface && s.refCount > 0)
+ --s.refCount;
return s;
}
@@ -287,13 +311,22 @@ private:
mutable QDotNetFunction<void, void *> fnFreeDelegateRef;
mutable QDotNetFunction<void, QDotNetRef> fnFreeObjectRef;
mutable QDotNetFunction<void, QString> fnFreeTypeRef;
- mutable QDotNetFunction<void *, QString> fnAddInterfaceProxy;
+ mutable QDotNetFunction<void *, QString, void *, void *> fnAddInterfaceProxy;
mutable QDotNetFunction<void, QDotNetRef, QString, qint32, QList<QDotNetParameter>,
void *, void *, void *> fnSetInterfaceMethod;
mutable QDotNetFunction<void, qint32 *, qint32 *, qint32 *> fnStats;
mutable QDotNetFunction<void *, QDotNetRef, QString> fnGetObject;
+ mutable QDotNetFunction<void> fnReset;
static inline const QString defaultDllName = QLatin1String("Qt.DotNet.Adapter.dll");
static inline const QString defaultAssemblyName = QLatin1String("Qt.DotNet.Adapter");
static inline const QString defaultTypeName = QLatin1String("Qt.DotNet.Adapter");
+
+ mutable void *staticInterface = nullptr;
+
+public:
+ inline static std::function<void *()> ctor_staticInterface = nullptr;
+ inline static std::function<void(void *)> dtor_staticInterface = nullptr;
+ inline static QDotNetFunction<void> gcCollect = nullptr;
+ inline static QDotNetFunction<void> gcWaitForPendingFinalizers = nullptr;
};
diff --git a/include/qdotnetarray.h b/include/qdotnetarray.h
index 3a0289b..9f97b5f 100644
--- a/include/qdotnetarray.h
+++ b/include/qdotnetarray.h
@@ -36,9 +36,9 @@ public:
QDotNetArray(qint32 length)
{
const QString elementTypeName = QDotNetTypeOf<T>::TypeName;
- const QDotNetType elementType = QDotNetType::find(elementTypeName);
+ const QDotNetType elementType = QDotNetType::typeOf(elementTypeName);
- QDotNetType arrayType = QDotNetType::find(QDotNetArray::FullyQualifiedTypeName);
+ QDotNetType arrayType = QDotNetType::typeOf(QDotNetArray::AssemblyQualifiedName);
auto ctor = constructor<QDotNetArray, qint32>();
*this = ctor(length);
}
diff --git a/include/qdotnetcallback.h b/include/qdotnetcallback.h
index 6efea1e..9f8c150 100644
--- a/include/qdotnetcallback.h
+++ b/include/qdotnetcallback.h
@@ -18,6 +18,19 @@
#include <functional>
+template<typename T>
+struct QDotNetCallbackArg : public QDotNetInbound<T> {};
+
+template<typename T>
+struct QDotNetCallbackReturn : public QDotNetOutbound<T> {};
+
+template<>
+struct QDotNetCallbackReturn<QString> : public QDotNetOutbound<QString>
+{
+ using SourceType = QString;
+ static inline const QDotNetParameter Parameter = QDotNetParameter::String;
+};
+
class QDotNetCallbackBase
{
protected:
@@ -30,18 +43,18 @@ template<typename TResult, typename... TArg>
class QDotNetCallback : public QDotNetCallbackBase
{
public:
- using ReturnType = typename QDotNetInbound<TResult>::TargetType;
- using FunctionType = std::function<ReturnType(
- typename QDotNetInbound<TArg>::TargetType... arg)>;
+ using FunctionType = std::function<TResult(void *, TArg... arg)>;
+ using CleanUpType = std::function<void(TResult *)>;
- using OutboundType = typename QDotNetOutbound<TResult>::OutboundType;
+ using OutboundType = typename QDotNetCallbackReturn<TResult>::OutboundType;
using Delegate = OutboundType(QDOTNETFUNCTION_CALLTYPE *)(
- QDotNetCallback *callback, quint64 key, typename QDotNetInbound<TArg>::InboundType...);
+ QDotNetCallback *callback, quint64 key,
+ void *data, typename QDotNetCallbackArg<TArg>::InboundType...);
using CleanUp = void(QDOTNETFUNCTION_CALLTYPE *)(QDotNetCallback *callback, quint64 key);
- QDotNetCallback(FunctionType function)
- : function(function)
+ QDotNetCallback(FunctionType fnCallback, CleanUpType fnCleanUp = nullptr)
+ : fnCallback(fnCallback), fnCleanUp(fnCleanUp)
{}
~QDotNetCallback() override = default;
@@ -59,64 +72,69 @@ public:
private:
struct Box
{
- ReturnType returnValue;
+ TResult returnValue;
+ Box(TResult &&ret) : returnValue(std::move(ret)) {}
};
QMap<quint64, Box *> boxes;
static OutboundType QDOTNETFUNCTION_CALLTYPE callbackDelegate(
- QDotNetCallback *callback, quint64 key, typename QDotNetInbound<TArg>::InboundType... arg)
+ QDotNetCallback *callback, quint64 key,
+ void *data, typename QDotNetCallbackArg<TArg>::InboundType... arg)
{
- Box *box = callback->boxes[key] = new Box
- {
- callback->function(QDotNetInbound<TArg>::convert(arg)...)
- };
- const auto result = QDotNetOutbound<TResult>::convert(box->returnValue);
- return result;
+ Box *box = callback->boxes[key] = new Box(
+ callback->fnCallback(data, QDotNetCallbackArg<TArg>::convert(arg)...));
+ return QDotNetCallbackReturn<TResult>::convert(box->returnValue);
}
static void QDOTNETFUNCTION_CALLTYPE callbackCleanUp(QDotNetCallback *callback, quint64 key)
{
- if (const Box *box = callback->boxes.take(key))
+ if (const Box *box = callback->boxes.take(key)) {
+ if (callback->fnCleanUp)
+ callback->fnCleanUp(const_cast<std::remove_const_t<TResult*>>(&(box->returnValue)));
delete box;
+ }
}
- FunctionType function = nullptr;
+ FunctionType fnCallback = nullptr;
+ CleanUpType fnCleanUp = nullptr;
};
template<typename... TArg>
class QDotNetCallback<void, TArg...> : public QDotNetCallbackBase
{
public:
- using FunctionType = std::function<void(typename QDotNetOutbound<TArg>::SourceType... arg)>;
+ using FunctionType = std::function<void(void *, TArg... arg)>;
+ using CleanUpType = nullptr_t;
+
+ using Delegate = void(QDOTNETFUNCTION_CALLTYPE *)(
+ QDotNetCallback *callback, quint64 key,
+ void *data, typename QDotNetCallbackArg<TArg>::InboundType...);
- QDotNetCallback(FunctionType function)
- : function(function)
+ using CleanUp = nullptr_t;
+
+ QDotNetCallback(FunctionType fnCallback, CleanUpType fnCleanUp = nullptr)
+ : fnCallback(fnCallback)
{}
~QDotNetCallback() override = default;
- using Delegate = void(QDOTNETFUNCTION_CALLTYPE *)(
- QDotNetCallback *callback, quint64 key, typename QDotNetInbound<TArg>::InboundType...);
static Delegate delegate()
{
return callbackDelegate;
}
- using CleanUp = void(QDOTNETFUNCTION_CALLTYPE *)(QDotNetCallback *callback, quint64 key);
static CleanUp cleanUp()
{
- return callbackCleanUp;
+ return nullptr;
}
private:
- static void QDOTNETFUNCTION_CALLTYPE callbackDelegate(QDotNetCallback *callback, quint64 key,
- typename QDotNetInbound<TArg>::InboundType... arg)
+ static void QDOTNETFUNCTION_CALLTYPE callbackDelegate(
+ QDotNetCallback *callback, quint64 key,
+ void *data, typename QDotNetCallbackArg<TArg>::InboundType... arg)
{
- callback->function(QDotNetInbound<TArg>::convert(arg)...);
+ callback->fnCallback(data, QDotNetCallbackArg<TArg>::convert(arg)...);
}
- static void QDOTNETFUNCTION_CALLTYPE callbackCleanUp(QDotNetCallback *callback, quint64 key)
- {}
-
- FunctionType function = nullptr;
+ FunctionType fnCallback = nullptr;
};
diff --git a/include/qdotnetdelegate.h b/include/qdotnetdelegate.h
new file mode 100644
index 0000000..25fc3a8
--- /dev/null
+++ b/include/qdotnetdelegate.h
@@ -0,0 +1,37 @@
+/***************************************************************************************************
+ Copyright (C) 2025 The Qt Company Ltd.
+ SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+***************************************************************************************************/
+
+#pragma once
+
+#include "qdotnetobject.h"
+
+#ifdef __GNUC__
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wconversion"
+#endif
+#include <QString>
+#ifdef __GNUC__
+# pragma GCC diagnostic pop
+#endif
+
+template<typename T, typename... TArg>
+class QDotNetDelegate : public QDotNetObject
+{
+public:
+ Q_DOTNET_OBJECT_INLINE(QDotNetDelegate, "System.Delegate");
+
+ T invoke(TArg... arg) const
+ {
+ return method("Invoke", fnInvoke).invoke(*this, arg...);
+ }
+
+ T operator()(TArg... arg) const
+ {
+ return invoke(arg...);
+ }
+
+private:
+ mutable QDotNetFunction<T, TArg...> fnInvoke;
+};
diff --git a/include/qdotnetevent.h b/include/qdotnetevent.h
index 11df0a1..00ea036 100644
--- a/include/qdotnetevent.h
+++ b/include/qdotnetevent.h
@@ -20,7 +20,8 @@
class QDotNetPropertyEvent : public QDotNetObject
{
public:
- Q_DOTNET_OBJECT_INLINE(QDotNetPropertyEvent, "System.ComponentModel.PropertyChangedEventArgs");
+ Q_DOTNET_OBJECT_INLINE(QDotNetPropertyEvent,
+ "System.ComponentModel.PropertyChangedEventArgs, System.ObjectModel");
QString propertyName() const
{
diff --git a/include/qdotnethost.h b/include/qdotnethost.h
index c2fe298..05b0922 100644
--- a/include/qdotnethost.h
+++ b/include/qdotnethost.h
@@ -35,7 +35,7 @@ public:
unload();
}
- bool load(const QString& runtimeConfig = defaultRuntimeConfig, const QString &runtimePath = {})
+ bool load(const QString &runtimeConfig = defaultRuntimeConfig, const QString &runtimePath = {})
{
if (isLoaded())
return true;
@@ -48,6 +48,57 @@ public:
return true;
}
+ bool appMain(const QString &appHostPath, const QString &appLibPath)
+ {
+ if (isLoaded())
+ return false;
+
+ if (!loadRuntime({}))
+ return false;
+ const char_t *host_path = STR(appHostPath);
+ const char_t *app_path = STR(appLibPath);
+ const char_t *dotnet_root = (char_t *)L"C:\\Program Files\\dotnet\\";
+
+ int argc = 1;
+ const char_t *argv[] = { host_path, nullptr };
+
+ return fnMainStartup(argc, argv, host_path, dotnet_root, app_path) == 0;
+ }
+
+ bool loadApp(const QString &appPath, const QStringList &args = {}, const QString &runtimePath = {})
+ {
+ if (isLoaded())
+ return false;
+
+ if (!loadRuntime(runtimePath))
+ return false;
+
+ int argc = 1;
+ const char_t *argv[] = { STR(appPath), nullptr };
+
+ auto result = fnInitApp(argc, argv, nullptr, &hostContext);
+ if (HOSTFN_FAILED(result) || hostContext == nullptr) {
+ qCritical() << "Error calling function: hostfxr_initialize_for_dotnet_command_line";
+ unloadRuntime();
+ return false;
+ }
+
+ setRuntimeProperty("STARTUP_HOOKS",
+ QDir(QCoreApplication::applicationDirPath()).filePath("Qt.DotNet.Adapter.dll"));
+
+ setRuntimeProperty("QT_DOTNET_RESOLVE_FN", QString("%1")
+ .arg((qulonglong)(&fnLoadAssemblyAndGetFunctionPointer), 16, 16, QChar('0')));
+
+ return true;
+ }
+
+ int runApp()
+ {
+ if (!isLoaded() || fnLoadAssemblyAndGetFunctionPointer != nullptr)
+ return false;
+ return fnRunApp(hostContext);
+ }
+
void unload()
{
if (!isLoaded())
@@ -61,6 +112,11 @@ public:
return (hostContext != nullptr);
}
+ bool isReady() const
+ {
+ return (fnLoadAssemblyAndGetFunctionPointer != nullptr);
+ }
+
bool resolveFunction(QDotNetFunction<quint32, void *, qint32> &outFunc,
const QString &assemblyPath, const QString &typeName, const QString &methodName)
{
@@ -172,51 +228,49 @@ private:
}
const QString dotNetInfo(procDotNetInfo.readAllStandardOutput());
- QString runtimeDirPath = {};
- QString hostVersion = {};
- QVersionNumber maxVersion;
+ QVersionNumber selectedVersion = {};
+ QString selectedRuntimePath = {};
const QRegularExpression dotNetInfoParser(regexParseDotNetInfo,
QRegularExpression::MultilineOption | QRegularExpression::DotMatchesEverythingOption);
for (const auto &match : dotNetInfoParser.globalMatch(dotNetInfo)) {
- const auto version = QVersionNumber::fromString(match.captured("version"));
- if (version > maxVersion) {
- maxVersion = version;
- hostVersion = match.captured("version");
- runtimeDirPath = match.captured("path");
- }
- }
-
- if (runtimeDirPath.isEmpty()) {
- qCritical() << "Error parsing dotnet info";
- return {};
- }
- QDir runtimeDir(runtimeDirPath);
- if (!runtimeDir.exists()) {
- qCritical() << "Error dotnet runtime directory not found";
- return {};
- }
-
- runtimeDir.cd(QString("../../host/fxr/%1").arg(hostVersion));
- if (!runtimeDir.exists()) {
- qCritical() << "Error dotnet host fxr directory not found";
- return {};
- }
+ const auto hostVersion = match.captured("version");
+ const auto version = QVersionNumber::fromString(hostVersion);
+ if (version <= selectedVersion)
+ continue;
+
+ const auto runtimeDirPath = match.captured("path");
+ if (runtimeDirPath.isEmpty())
+ continue;
+ QDir runtimeDir(runtimeDirPath);
+ if (!runtimeDir.exists())
+ continue;
+ runtimeDir.cd(QString("../../host/fxr/%1").arg(hostVersion));
+ if (!runtimeDir.exists())
+ continue;
#ifdef Q_OS_WINDOWS
- QString runtimePath = runtimeDir.absoluteFilePath("hostfxr.dll");
+ const auto runtimePath = runtimeDir.absoluteFilePath("hostfxr.dll");
#else
- QString runtimePath = runtimeDir.absoluteFilePath("libhostfxr.so");
+ const auto runtimePath = runtimeDir.absoluteFilePath("libhostfxr.so");
#endif
- if (!QFile::exists(runtimePath)) {
- qCritical() << "Error dotnet host fxr dll not found";
+ if (!QFile::exists(runtimePath))
+ continue;
+
+ selectedVersion = version;
+ selectedRuntimePath = runtimePath;
+ }
+
+ if (selectedVersion.isNull()) {
+ qCritical() << "Error locating runtime host library.";
return {};
}
- return runtimePath;
+
+ return selectedRuntimePath;
}
bool loadRuntime(const QString & runtimePath)
{
- if (fnInitHost != nullptr)
+ if (fnCloseHost != nullptr)
return true;
if (!runtimePath.isEmpty()) {
@@ -235,45 +289,53 @@ private:
return false;
}
- fnInitHost = GET_FN(runtime, hostfxr_initialize_for_runtime_config_fn);
- if (!fnInitHost) {
- qCritical() << "Error loading function: hostfxr_initialize_for_runtime_config";
+ if (!(fnMainStartup = GET_FN(runtime, hostfxr_main_startupinfo_fn))) {
+ qCritical() << "Error loading function: hostfxr_main_startupinfo";
return false;
}
- fnGetRuntimeDelegate = GET_FN(runtime, hostfxr_get_runtime_delegate_fn);
- if (!fnGetRuntimeDelegate) {
- qCritical() << "Error loading function: hostfxr_get_runtime_delegate";
+ if (!(fnSetErrorWriter = GET_FN(runtime, hostfxr_set_error_writer_fn))) {
+ qCritical() << "Error loading function: hostfxr_set_error_writer";
return false;
}
- fnCloseHost = GET_FN(runtime, hostfxr_close_fn);
- if (!fnCloseHost) {
- qCritical() << "Error loading function: hostfxr_close";
+ if (!(fnInitApp = GET_FN(runtime, hostfxr_initialize_for_dotnet_command_line_fn))) {
+ qCritical() << "Error loading function: hostfxr_initialize_for_dotnet_command_line";
return false;
}
- fnSetErrorWriter = GET_FN(runtime, hostfxr_set_error_writer_fn);
- if (!fnSetErrorWriter) {
- qCritical() << "Error loading function: hostfxr_set_error_writer";
+ if (!(fnInitHost = GET_FN(runtime, hostfxr_initialize_for_runtime_config_fn))) {
+ qCritical() << "Error loading function: hostfxr_initialize_for_runtime_config";
+ return false;
+ }
+
+ if (!(fnRuntimeProperty = GET_FN(runtime, hostfxr_get_runtime_property_value_fn))) {
+ qCritical() << "Error loading function: hostfxr_get_runtime_property_value_fn";
+ return false;
+ }
+
+ if (!(fnSetRuntimeProperty = GET_FN(runtime, hostfxr_set_runtime_property_value_fn))) {
+ qCritical() << "Error loading function: hostfxr_set_runtime_property_value_fn";
return false;
}
- fnAllRuntimeProperties = GET_FN(runtime, hostfxr_get_runtime_properties_fn);
- if (!fnAllRuntimeProperties) {
+ if (!(fnAllRuntimeProperties = GET_FN(runtime, hostfxr_get_runtime_properties_fn))) {
qCritical() << "Error loading function: hostfxr_get_runtime_properties_fn";
return false;
}
- fnRuntimeProperty = GET_FN(runtime, hostfxr_get_runtime_property_value_fn);
- if (!fnRuntimeProperty) {
- qCritical() << "Error loading function: hostfxr_get_runtime_property_value_fn";
+ if (!(fnRunApp = GET_FN(runtime, hostfxr_run_app_fn))) {
+ qCritical() << "Error loading function: hostfxr_run_app";
return false;
}
- fnSetRuntimeProperty = GET_FN(runtime, hostfxr_set_runtime_property_value_fn);
- if (!fnSetRuntimeProperty) {
- qCritical() << "Error loading function: hostfxr_set_runtime_property_value_fn";
+ if (!(fnGetRuntimeDelegate = GET_FN(runtime, hostfxr_get_runtime_delegate_fn))) {
+ qCritical() << "Error loading function: hostfxr_get_runtime_delegate";
+ return false;
+ }
+
+ if (!(fnCloseHost = GET_FN(runtime, hostfxr_close_fn))) {
+ qCritical() << "Error loading function: hostfxr_close";
return false;
}
@@ -283,17 +345,26 @@ private:
void unloadRuntime()
{
- runtime.unload();
+ fnSetErrorWriter = nullptr;
+ fnInitApp = nullptr;
fnInitHost = nullptr;
- fnGetRuntimeDelegate = nullptr;
- fnCloseHost = nullptr;
- fnAllRuntimeProperties = nullptr;
fnRuntimeProperty = nullptr;
fnSetRuntimeProperty = nullptr;
+ fnAllRuntimeProperties = nullptr;
+ fnRunApp = nullptr;
+ fnGetRuntimeDelegate = nullptr;
+ fnCloseHost = nullptr;
+ fnLoadAssemblyAndGetFunctionPointer = nullptr;
+ fnLoadAssembly = nullptr;
+ fnGetFunctionPointer = nullptr;
+ runtime.unload();
}
bool init(const QString &runtimeConfig)
{
+ if (fnLoadAssemblyAndGetFunctionPointer)
+ return true;
+
if (fnInitHost == nullptr)
return false;
@@ -365,11 +436,11 @@ private:
static inline const QString defaultRuntimeConfig = QStringLiteral(R"[json](
{
"runtimeOptions": {
- "tfm": "net6.0",
+ "tfm": "net8.0",
"rollForward": "LatestMinor",
"framework": {
"name": "Microsoft.NETCore.App",
- "version": "6.0.0"
+ "version": "8.0.0"
}
}
}
@@ -379,16 +450,24 @@ private:
\bMicrosoft\.NETCore\.App[^0-9]*(?<version>[0-9\.]+)[^\[]*\[(?<path>[^\]]+)\]
)[regex]").remove('\r').remove('\n');
+ static inline const QString regexParseDotNetRoot = QStringLiteral(R"[regex](
+^(?:(?![\\\/]host[\\\/]).)*[\\\/]
+)[regex]").remove('\r').remove('\n');
QLibrary runtime;
- hostfxr_initialize_for_runtime_config_fn fnInitHost = nullptr;
- hostfxr_get_runtime_delegate_fn fnGetRuntimeDelegate = nullptr;
- hostfxr_close_fn fnCloseHost = nullptr;
+ hostfxr_main_startupinfo_fn fnMainStartup = nullptr;
hostfxr_set_error_writer_fn fnSetErrorWriter = nullptr;
- hostfxr_get_runtime_properties_fn fnAllRuntimeProperties = nullptr;
+ hostfxr_initialize_for_dotnet_command_line_fn fnInitApp = nullptr;
+ hostfxr_initialize_for_runtime_config_fn fnInitHost = nullptr;
hostfxr_get_runtime_property_value_fn fnRuntimeProperty = nullptr;
hostfxr_set_runtime_property_value_fn fnSetRuntimeProperty = nullptr;
+ hostfxr_get_runtime_properties_fn fnAllRuntimeProperties = nullptr;
+ hostfxr_run_app_fn fnRunApp = nullptr;
+ hostfxr_get_runtime_delegate_fn fnGetRuntimeDelegate = nullptr;
+ hostfxr_close_fn fnCloseHost = nullptr;
hostfxr_handle hostContext = nullptr;
load_assembly_and_get_function_pointer_fn fnLoadAssemblyAndGetFunctionPointer = nullptr;
+ load_assembly_fn fnLoadAssembly = nullptr;
+ get_function_pointer_fn fnGetFunctionPointer = nullptr;
};
diff --git a/include/qdotnethostfxr.h b/include/qdotnethostfxr.h
index e5e1d0c..dc57ce0 100644
--- a/include/qdotnethostfxr.h
+++ b/include/qdotnethostfxr.h
@@ -116,6 +116,13 @@ using get_hostfxr_path_fn = quint32(NETHOST_CALLTYPE *)(
# define HOSTFXR_CALLTYPE
#endif
+using hostfxr_main_startupinfo_fn = int(HOSTFXR_CALLTYPE *)(
+ const int argc,
+ const char_t **argv,
+ const char_t *host_path,
+ const char_t *dotnet_root,
+ const char_t *app_path);
+
enum hostfxr_delegate_type
{
hdt_com_activation,
@@ -124,7 +131,9 @@ enum hostfxr_delegate_type
hdt_com_register,
hdt_com_unregister,
hdt_load_assembly_and_get_function_pointer,
- hdt_get_function_pointer
+ hdt_get_function_pointer,
+ hdt_load_assembly,
+ hdt_load_assembly_bytes
};
using hostfxr_error_writer_fn = void(HOSTFXR_CALLTYPE *)(const char_t *message);
@@ -140,6 +149,13 @@ struct hostfxr_initialize_parameters
const char_t *dotnet_root;
};
+using hostfxr_initialize_for_dotnet_command_line_fn = int(HOSTFXR_CALLTYPE *)(
+ int argc,
+ const char_t *argv[],
+ const hostfxr_initialize_parameters *parameters,
+ /*out*/ hostfxr_handle *host_context_handle
+);
+
using hostfxr_initialize_for_runtime_config_fn = quint32(HOSTFXR_CALLTYPE *)(
const char_t *runtime_config_path,
const hostfxr_initialize_parameters *parameters,
@@ -161,6 +177,8 @@ using hostfxr_get_runtime_properties_fn = quint32(HOSTFXR_CALLTYPE *)(
/*out*/ const char_t **keys,
/*out*/ const char_t **values);
+using hostfxr_run_app_fn = int (HOSTFXR_CALLTYPE *)(const hostfxr_handle host_context_handle);
+
using hostfxr_get_runtime_delegate_fn = quint32(HOSTFXR_CALLTYPE *)(
hostfxr_handle host_context_handle,
hostfxr_delegate_type type,
@@ -168,7 +186,6 @@ using hostfxr_get_runtime_delegate_fn = quint32(HOSTFXR_CALLTYPE *)(
using hostfxr_close_fn = quint32(HOSTFXR_CALLTYPE *)(hostfxr_handle host_context_handle);
-
/*
adapted from:
https://github.com/dotnet/runtime/blob/main/src/native/corehost/coreclr_delegates.h
@@ -179,25 +196,26 @@ using hostfxr_close_fn = quint32(HOSTFXR_CALLTYPE *)(hostfxr_handle host_context
# define CORECLR_DELEGATE_CALLTYPE
#endif
-using component_entry_point_fn = quint32(CORECLR_DELEGATE_CALLTYPE *)(
- void *arg, qint32 arg_size_in_bytes);
-
using load_assembly_and_get_function_pointer_fn = quint32(CORECLR_DELEGATE_CALLTYPE *)(
const char_t *assembly_path ,
const char_t *type_name ,
const char_t *method_name ,
const char_t *delegate_type_name,
- void *reserved,
+ nullptr_t reserved,
/*out*/ void **delegate );
using get_function_pointer_fn = quint32(CORECLR_DELEGATE_CALLTYPE *)(
const char_t *type_name,
const char_t *method_name,
const char_t *delegate_type_name,
- void *load_context,
- void *reserved,
+ nullptr_t load_context,
+ nullptr_t reserved,
/*out*/ void **delegate);
+using load_assembly_fn = int (CORECLR_DELEGATE_CALLTYPE *)(
+ const char_t *assembly_path,
+ nullptr_t load_context,
+ nullptr_t reserved);
/*
adapted from:
diff --git a/include/qdotnetinterface.h b/include/qdotnetinterface.h
index 4dae61c..c6850e3 100644
--- a/include/qdotnetinterface.h
+++ b/include/qdotnetinterface.h
@@ -21,44 +21,71 @@
class QDotNetInterface : public QDotNetRef
{
public:
- QDotNetInterface(const QString &interfaceName)
- : QDotNetRef(adapter().addInterfaceProxy(interfaceName))
+ QDotNetInterface(const QString &interfaceName, void *data = nullptr, void *cleanUp = nullptr)
+ : QDotNetRef(adapter().addInterfaceProxy(interfaceName, data, cleanUp))
{}
- template<typename TResult, typename... TArg>
- void setCallback(const QString &methodName, const QList<QDotNetParameter> &params,
- typename QDotNetCallback<TResult, TArg...>::FunctionType function)
+ QDotNetInterface(const void *objectRef = nullptr)
+ : QDotNetRef(objectRef)
+ {}
+
+ QDotNetInterface(const QDotNetInterface &cpySrc)
+ : QDotNetRef(cpySrc)
+ {}
+
+ QDotNetInterface &operator =(const QDotNetInterface &cpySrc)
{
- auto *callback = new QDotNetCallback<TResult, TArg...>(function);
- callbacks.append(callback);
+ QDotNetRef::operator=(cpySrc);
+ return *this;
+ }
- QList<QDotNetParameter> modifiedParams
- {
- params[0],
- UnmanagedType::SysInt,
- UnmanagedType::U8
- };
- for (qsizetype i = 1; i < params.size(); ++i)
- modifiedParams.append(params[i]);
+ QDotNetInterface(QDotNetInterface &&movSrc) noexcept
+ : QDotNetRef(std::move(movSrc))
+ {}
- adapter().setInterfaceMethod(*this, methodName, modifiedParams,
- reinterpret_cast<void *>(callback->delegate()),
- reinterpret_cast<void *>(callback->cleanUp()), callback);
+ QDotNetInterface &operator=(QDotNetInterface &&movSrc) noexcept
+ {
+ QDotNetRef::operator=(std::move(movSrc));
+ return *this;
+ }
+
+ template<typename T>
+ T *dataAs()
+ {
+ if (!fnDataPtr.isValid()) {
+ const QList<QDotNetParameter> parameters
+ {
+ QDotNetInbound<void *>::Parameter
+ };
+ fnDataPtr = adapter().resolveInstanceMethod(*this, "get_Data", parameters);
+ }
+ return reinterpret_cast<T *>(fnDataPtr());
+ }
+
+ virtual ~QDotNetInterface() override
+ {
+ if (!isValid())
+ return;
+ for (const QDotNetCallbackBase *callback : callbacks)
+ delete callback;
+ callbacks.clear();
}
template<typename TResult, typename... TArg>
void setCallback(const QString &methodName,
- typename QDotNetCallback<TResult, TArg...>::FunctionType function)
+ typename QDotNetCallback<TResult, TArg...>::FunctionType function,
+ typename QDotNetCallback<TResult, TArg...>::CleanUpType cleanUp = nullptr)
{
- auto *callback = new QDotNetCallback<TResult, TArg...>(function);
+ auto *callback = new QDotNetCallback<TResult, TArg...>(function, cleanUp);
callbacks.append(callback);
const QList<QDotNetParameter> parameters
{
- QDotNetInbound<TResult>::Parameter,
+ QDotNetCallbackReturn<TResult>::Parameter,
UnmanagedType::SysInt,
UnmanagedType::U8,
- QDotNetInbound<TArg>::Parameter...
+ UnmanagedType::SysInt,
+ QDotNetCallbackArg<TArg>::Parameter...
};
adapter().setInterfaceMethod(
@@ -66,20 +93,44 @@ public:
callback->delegate(), callback->cleanUp(), callback);
}
- ~QDotNetInterface() override
- {
- for (const QDotNetCallbackBase *callback : callbacks)
- delete callback;
- callbacks.clear();
- }
-
private:
QList<QDotNetCallbackBase *> callbacks;
+ QDotNetFunction<void *> fnDataPtr = nullptr;
};
template<typename T>
struct QDotNetTypeOf<T, std::enable_if_t<std::is_base_of_v<QDotNetInterface, T>>>
{
- static inline const QString TypeName = T::FullyQualifiedTypeName;
+ static inline const QString TypeName = T::AssemblyQualifiedName;
static inline UnmanagedType MarshalAs = UnmanagedType::ObjectRef;
};
+
+template<typename T>
+struct QDotNetNativeInterface : public QDotNetInterface
+{
+ QDotNetNativeInterface(const void *objectRef = nullptr)
+ : QDotNetInterface(objectRef)
+ {
+ }
+
+ QDotNetNativeInterface(const QString &interfaceName, T *data, bool doCleanUp = true)
+ : QDotNetInterface(interfaceName, data, doCleanUp ? cleanUp : nullptr)
+ {
+ }
+
+ T *data()
+ {
+ return dataAs<T>();
+ }
+
+ operator T&()
+ {
+ return *data();
+ }
+
+ static void QDOTNETFUNCTION_CALLTYPE cleanUp(void *data)
+ {
+ if (data)
+ delete reinterpret_cast<T *>(data);
+ }
+};
diff --git a/include/qdotnetobject.h b/include/qdotnetobject.h
index 34dbc3c..5657c6f 100644
--- a/include/qdotnetobject.h
+++ b/include/qdotnetobject.h
@@ -17,6 +17,8 @@
# pragma GCC diagnostic pop
#endif
+struct QDotNetEventHandler;
+
class QDotNetObject : public QDotNetRef
{
private:
@@ -37,7 +39,7 @@ private:
// Fully qualified .NET class name
#define Q_DOTNET_OBJECT_TYPE(T,type_name)\
- static inline const QString &FullyQualifiedTypeName = QString(type_name)
+ static inline const QString &AssemblyQualifiedName = QString(type_name)
// All required declarations
#define Q_DOTNET_OBJECT(T,type_name)\
@@ -125,7 +127,7 @@ private:
#define Q_DOTNET_OBJECT_INIT(...) , __VA_ARGS__
public:
- static inline const QString &FullyQualifiedTypeName = QStringLiteral("System.Object");
+ static inline const QString &AssemblyQualifiedName = QStringLiteral("System.Object");
QDotNetObject(const void *objectRef = nullptr)
: QDotNetRef(objectRef)
@@ -151,6 +153,8 @@ public:
return *this;
}
+ virtual ~QDotNetObject() override = default;
+
const QDotNetType &type() const
{
if (!fnGetType.isValid()) {
@@ -160,16 +164,6 @@ public:
return objType;
}
- QString toString() const
- {
- return method("ToString", fnToString).invoke(*this);
- }
-
- bool equals(const QDotNetRef &obj) const
- {
- return method("Equals", fnEquals).invoke(*this, obj);
- }
-
template<typename TResult, typename ...TArg>
QDotNetFunction<TResult, TArg...> method(const QString &methodName) const
{
@@ -243,19 +237,12 @@ public:
return QDotNetType::constructor(typeName, ctor);
}
- struct IEventHandler
- {
- virtual ~IEventHandler() = default;
- virtual void handleEvent(const QString &eventName, QDotNetObject &eventSource,
- QDotNetObject &eventArgs) = 0;
- };
-
- void subscribeEvent(const QString &eventName, IEventHandler *eventHandler)
+ void subscribe(const QString &eventName, QDotNetEventHandler *eventHandler)
{
adapter().addEventHandler(*this, eventName, eventHandler, eventCallback);
}
- void unsubscribeEvent(const QString &eventName, IEventHandler *eventHandler)
+ void unsubscribe(const QString &eventName, QDotNetEventHandler *eventHandler)
{
adapter().removeEventHandler(*this, eventName, eventHandler);
}
@@ -281,31 +268,24 @@ protected:
template<typename T, typename ...TArg>
static QDotNetFunction<T, TArg...> constructor()
{
- return QDotNetType::constructor<T, TArg...>(T::FullyQualifiedTypeName);
+ return QDotNetType::constructor<T, TArg...>(T::AssemblyQualifiedName);
}
template<typename T, typename ...TArg>
static QDotNetFunction<T, TArg...> &constructor(QDotNetFunction<T, TArg...> &ctor)
{
- return QDotNetType::constructor(T::FullyQualifiedTypeName, ctor);
+ return QDotNetType::constructor(T::AssemblyQualifiedName, ctor);
}
template<typename T, typename ...TArg>
static QDotNetSafeMethod<T, TArg...> &constructor(QDotNetSafeMethod<T, TArg...> &ctor)
{
- return QDotNetType::constructor(T::FullyQualifiedTypeName, ctor);
+ return QDotNetType::constructor(T::AssemblyQualifiedName, ctor);
}
private:
- static void QDOTNETFUNCTION_CALLTYPE eventCallback(void *context, void *eventNameChars,
- void *eventSourceRef, void *eventArgsRef)
- {
- auto *receiver = static_cast<IEventHandler *>(context);
- const QString eventName(static_cast<const QChar *>(eventNameChars));
- QDotNetObject eventSource(eventSourceRef);
- QDotNetObject eventArgs(eventArgsRef);
- receiver->handleEvent(eventName, eventSource, eventArgs);
- }
+ static inline void QDOTNETFUNCTION_CALLTYPE eventCallback(void *context, void *eventNameChars,
+ void *eventSourceRef, void *eventArgsRef);
mutable QDotNetFunction<QDotNetType> fnGetType;
mutable QDotNetType objType = nullptr;
@@ -313,9 +293,26 @@ private:
mutable QDotNetFunction<bool, QDotNetRef> fnEquals;
};
+struct QDotNetEventHandler
+{
+ virtual ~QDotNetEventHandler() = default;
+ virtual void handleEvent(const QString &eventName, QDotNetObject &eventSource,
+ QDotNetObject &eventArgs) = 0;
+};
+
+inline void QDOTNETFUNCTION_CALLTYPE QDotNetObject::eventCallback(void *context, void *eventNameChars,
+ void *eventSourceRef, void *eventArgsRef)
+{
+ auto *receiver = static_cast<QDotNetEventHandler *>(context);
+ const QString eventName(static_cast<const QChar *>(eventNameChars));
+ QDotNetObject eventSource(eventSourceRef);
+ QDotNetObject eventArgs(eventArgsRef);
+ receiver->handleEvent(eventName, eventSource, eventArgs);
+}
+
template<typename T>
struct QDotNetTypeOf<T, std::enable_if_t<std::is_base_of_v<QDotNetObject, T>>>
{
- static inline const QString TypeName = T::FullyQualifiedTypeName;
+ static inline const QString TypeName = T::AssemblyQualifiedName;
static inline UnmanagedType MarshalAs = UnmanagedType::ObjectRef;
};
diff --git a/include/qdotnetref.h b/include/qdotnetref.h
index fff2e06..06ce02a 100644
--- a/include/qdotnetref.h
+++ b/include/qdotnetref.h
@@ -10,7 +10,7 @@
class QDotNetRef
{
public:
- static inline const QString &FullyQualifiedTypeName = QStringLiteral("System.Object");
+ static inline const QString &AssemblyQualifiedName = QStringLiteral("System.Object");
const void *gcHandle() const { return objectRef; }
bool isValid() const { return gcHandle() != nullptr; }
@@ -59,6 +59,32 @@ public:
class Null
{};
+ QString toString() const
+ {
+ if (!fnToString.isValid()) {
+ const QList<QDotNetParameter> parameters
+ {
+ QDotNetInbound<QString>::Parameter
+ };
+ fnToString = adapter().resolveInstanceMethod(*this, "ToString", parameters);
+ }
+ return fnToString();
+ }
+
+ bool equals(const QDotNetRef &obj) const
+ {
+ if (!fnEquals.isValid()) {
+ const QList<QDotNetParameter> parameters
+ {
+ QDotNetInbound<bool>::Parameter,
+ QDotNetOutbound<QDotNetRef>::Parameter
+ };
+ fnEquals = adapter().resolveInstanceMethod(*this, "Equals", parameters);
+ }
+ return fnEquals(obj);
+ }
+
+
protected:
static QDotNetAdapter &adapter() { return QDotNetAdapter::instance(); }
@@ -93,6 +119,9 @@ private:
}
const void *objectRef = nullptr;
+
+ mutable QDotNetFunction<QString> fnToString = nullptr;
+ mutable QDotNetFunction<bool, QDotNetRef> fnEquals = nullptr;
};
template<typename T>
diff --git a/include/qdotnetstatic.h b/include/qdotnetstatic.h
new file mode 100644
index 0000000..7b07147
--- /dev/null
+++ b/include/qdotnetstatic.h
@@ -0,0 +1,53 @@
+/***************************************************************************************************
+ Copyright (C) 2024 The Qt Company Ltd.
+ SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+***************************************************************************************************/
+
+#pragma once
+
+#include "qdotnetinterface.h"
+#include "iqmodelindex.h"
+#include "iqvariant.h"
+#include "qdotnetabstractlistmodel.h"
+
+#include <functional>
+
+class QDotNetStatic : public QDotNetInterface
+{
+public:
+ static inline const QString &AssemblyQualifiedName =
+ QStringLiteral("Qt.DotNet.Adapter+IStatic, Qt.DotNet.Adapter");
+
+ QDotNetStatic(const void *objectRef) : QDotNetInterface(objectRef) {}
+
+ QDotNetStatic() : QDotNetInterface(AssemblyQualifiedName, nullptr)
+ {
+ IQVariant::staticInit(this);
+ IQModelIndex::staticInit(this);
+ QDotNetAbstractListModel::staticInit(this);
+ }
+};
+
+inline static bool ctor_static = std::invoke([]()
+ {
+ QDotNetAdapter::ctor_staticInterface = []()
+ {
+ auto *staticQVariant = new QDotNetStatic();
+ auto setStatic = QDotNetType::staticMethod<void, QDotNetStatic>(
+ "Qt.DotNet.Adapter, Qt.DotNet.Adapter", "set_Static");
+ setStatic(*staticQVariant);
+ return staticQVariant;
+ };
+ return true;
+ });
+
+inline static bool dtor_static = std::invoke([]()
+ {
+ QDotNetAdapter::dtor_staticInterface = [](void *that)
+ {
+ QtDotNet::call<void, QDotNetStatic>(
+ "Qt.DotNet.Adapter, Qt.DotNet.Adapter", "set_Static", nullptr);
+ delete reinterpret_cast<QDotNetStatic *>(that);
+ };
+ return true;
+ });
diff --git a/include/qdotnettype.h b/include/qdotnettype.h
index 4ab99bf..dc3bb90 100644
--- a/include/qdotnettype.h
+++ b/include/qdotnettype.h
@@ -19,7 +19,7 @@
class QDotNetType : public QDotNetRef
{
public:
- static inline const QString &FullyQualifiedTypeName = QStringLiteral("System.Type");
+ static inline const QString &AssemblyQualifiedName = QStringLiteral("System.Type");
QDotNetType(const void *typeRef = nullptr)
: QDotNetRef(typeRef)
@@ -45,6 +45,18 @@ public:
return *this;
}
+ QString assemblyQualifiedName() const
+ {
+ if (!isValid())
+ return QStringLiteral("");
+ if (!fnAssemblyQualifiedName.isValid()) {
+ fnAssemblyQualifiedName = adapter().resolveInstanceMethod(*this,
+ "get_AssemblyQualifiedName", { UnmanagedType::LPWStr });
+ strFullName = fnAssemblyQualifiedName();
+ }
+ return strFullName;
+ }
+
QString fullName() const
{
if (!isValid())
@@ -57,16 +69,16 @@ public:
return strFullName;
}
- static QDotNetType find(const QString &typeName)
+ static QDotNetType typeOf(const QString &typeName)
{
QDotNetFunction<QDotNetType, QString> fnGetType;
- return staticMethod(FullyQualifiedTypeName, "GetType", fnGetType).invoke(nullptr, typeName);
+ return staticMethod(AssemblyQualifiedName, "GetType", fnGetType).invoke(nullptr, typeName);
}
template<typename T>
- static QDotNetType find()
+ static QDotNetType typeOf()
{
- return find(T::FullyQualifiedTypeName);
+ return typeOf(T::AssemblyQualifiedName);
}
template<typename TResult, typename ...TArg>
@@ -102,7 +114,7 @@ public:
template<typename TResult, typename ...TArg>
QDotNetFunction<TResult, TArg...> staticMethod(const QString &methodName) const
{
- return staticMethod<TResult, TArg...>(fullName(), methodName);
+ return staticMethod<TResult, TArg...>(assemblyQualifiedName(), methodName);
}
template<typename TResult, typename ...TArg>
@@ -155,24 +167,24 @@ public:
template<typename T, typename ...TArg>
QDotNetFunction<T, TArg...> constructor() const
{
- return constructor<T, TArg...>(fullName());
+ return constructor<T, TArg...>(assemblyQualifiedName());
}
template<typename T, typename ...TArg>
QDotNetFunction<T, TArg...> &constructor(QDotNetFunction<T, TArg...> &ctor) const
{
- return constructor(fullName(), ctor);
+ return constructor(assemblyQualifiedName(), ctor);
}
template<typename T, typename ...TArg>
QDotNetFunction<T, TArg...> &constructor(QDotNetSafeMethod<T, TArg...> &ctor) const
{
- return constructor(fullName(), ctor);
+ return constructor(assemblyQualifiedName(), ctor);
}
void freeTypeRef()
{
- freeTypeRef(fullName());
+ freeTypeRef(assemblyQualifiedName());
}
static void freeTypeRef(const QString &typeName)
@@ -183,12 +195,53 @@ public:
template<typename T>
static void freeTypeRef()
{
- freeTypeRef(T::FullyQualifiedTypeName);
+ freeTypeRef(T::AssemblyQualifiedName);
+ }
+
+ bool isAssignableFrom(QDotNetType c) const
+ {
+ if (!c.isValid())
+ return false;
+ if (!fnIsAssignableFrom.isValid()) {
+ fnIsAssignableFrom = adapter().resolveInstanceMethod(*this, "IsAssignableFrom",
+ { UnmanagedType::Bool, QStringLiteral("System.Type") });
+ if (!fnIsAssignableFrom.isValid())
+ return false;
+ }
+ return fnIsAssignableFrom(c);
+ }
+
+ template<typename T>
+ bool isAssignableFrom() const
+ {
+ return isAssignableFrom(typeOf<T>());
+ }
+
+ bool isAssignableTo(QDotNetType c) const
+ {
+ if (!c.isValid())
+ return false;
+ if (!fnIsAssignableTo.isValid()) {
+ fnIsAssignableTo = adapter().resolveInstanceMethod(*this, "IsAssignableTo",
+ { UnmanagedType::Bool, QStringLiteral("System.Type") });
+ if (!fnIsAssignableTo.isValid())
+ return false;
+ }
+ return fnIsAssignableTo(c);
+ }
+
+ template<typename T>
+ bool isAssignableTo() const
+ {
+ return isAssignableTo(typeOf<T>());
}
private:
+ mutable QDotNetFunction<QString> fnAssemblyQualifiedName;
mutable QDotNetFunction<QString> fnFullName;
mutable QString strFullName;
+ mutable QDotNetFunction<bool, QDotNetRef> fnIsAssignableFrom;
+ mutable QDotNetFunction<bool, QDotNetRef> fnIsAssignableTo;
};
namespace QtDotNet
diff --git a/qtdotnet.sln b/qtdotnet.sln
index e3bfd88..005b70c 100644
--- a/qtdotnet.sln
+++ b/qtdotnet.sln
@@ -1,18 +1,16 @@
-
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.4.33403.182
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "QmlApp", "examples\EmbeddedWindow\QmlApp\QmlApp.vcxproj", "{8C7C4962-6AAA-4A90-927A-BC88C782CAAA}"
- ProjectSection(ProjectDependencies) = postProject
- {B003D2B0-BCAE-4CEE-8A73-413C914716D0} = {B003D2B0-BCAE-4CEE-8A73-413C914716D0}
- EndProjectSection
-EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "include", "include", "{63F4FEA7-5B8B-4496-988E-B84410C50D42}"
ProjectSection(SolutionItems) = preProject
+ include\iqmodelindex.h = include\iqmodelindex.h
+ include\iqvariant.h = include\iqvariant.h
+ include\qdotnetabstractlistmodel.h = include\qdotnetabstractlistmodel.h
include\qdotnetadapter.h = include\qdotnetadapter.h
include\qdotnetarray.h = include\qdotnetarray.h
include\qdotnetcallback.h = include\qdotnetcallback.h
+ include\qdotnetdelegate.h = include\qdotnetdelegate.h
include\qdotnetevent.h = include\qdotnetevent.h
include\qdotnetexception.h = include\qdotnetexception.h
include\qdotnetfunction.h = include\qdotnetfunction.h
@@ -24,6 +22,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "include", "include", "{63F4
include\qdotnetparameter.h = include\qdotnetparameter.h
include\qdotnetref.h = include\qdotnetref.h
include\qdotnetsafemethod.h = include\qdotnetsafemethod.h
+ include\qdotnetstatic.h = include\qdotnetstatic.h
include\qdotnettype.h = include\qdotnettype.h
EndProjectSection
EndProject
@@ -35,23 +34,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "solution", "solution", "{0F
README.md = README.md
EndProjectSection
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{D6E014F1-7EC9-4005-A8BC-80E0E3491F0B}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Chronometer", "Chronometer", "{0A2A51C5-E759-420C-9EFA-9AF6A9A4BB83}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "EmbeddedWindow", "EmbeddedWindow", "{D63F2ACE-DE7B-4208-B602-5C48F53052B6}"
-EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{0828626A-BAD8-4E02-99DC-3AAA15073223}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ChronometerModel", "examples\Chronometer\Chronometer\ChronometerModel.csproj", "{F56CB23A-BE44-4639-A498-D6882E373AB4}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "QmlChronometer", "examples\Chronometer\QmlChronometer\QmlChronometer.vcxproj", "{5A1E5424-CDB1-4776-A9CC-01B2CD57F516}"
- ProjectSection(ProjectDependencies) = postProject
- {3863807C-2F87-4E27-A9C9-8675645A8DA5} = {3863807C-2F87-4E27-A9C9-8675645A8DA5}
- {F56CB23A-BE44-4639-A498-D6882E373AB4} = {F56CB23A-BE44-4639-A498-D6882E373AB4}
- EndProjectSection
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WpfApp", "examples\EmbeddedWindow\WpfApp\WpfApp.csproj", "{B003D2B0-BCAE-4CEE-8A73-413C914716D0}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tst_qtdotnet", "tests\tst_qtdotnet\tst_qtdotnet.vcxproj", "{4E317E0F-0565-4B8A-84F3-56ADF18C65AD}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Qt.DotNet.Adapter", "src\Qt.DotNet.Adapter\Qt.DotNet.Adapter.csproj", "{3863807C-2F87-4E27-A9C9-8675645A8DA5}"
EndProject
@@ -59,12 +44,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "includegen", "src\includege
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FooLib", "tests\FooLib\FooLib.csproj", "{45D3DDF3-135B-46CA-B3EE-3537FCFFFBEB}"
EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tst_qtdotnet", "tests\tst_qtdotnet\tst_qtdotnet.vcxproj", "{4E317E0F-0565-4B8A-84F3-56ADF18C65AD}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Test_Qt.DotNet.Adapter", "tests\Test_Qt.DotNet.Adapter\Test_Qt.DotNet.Adapter.csproj", "{F7D48930-4705-4775-91DA-A948F30D224F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Perf_Qt.DotNet.Adapter", "tests\Perf_Qt.DotNet.Adapter\Perf_Qt.DotNet.Adapter.csproj", "{9E29FE05-FAA4-4440-AE1D-7AF736D6676D}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FooConsoleApp", "tests\FooConsoleApp\FooConsoleApp.csproj", "{72CA8EDA-C9CF-40C2-B7FE-4A529204EEC2}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -78,74 +63,6 @@ Global
Tests|x86 = Tests|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {8C7C4962-6AAA-4A90-927A-BC88C782CAAA}.Debug|Any CPU.ActiveCfg = Debug|x64
- {8C7C4962-6AAA-4A90-927A-BC88C782CAAA}.Debug|Any CPU.Build.0 = Debug|x64
- {8C7C4962-6AAA-4A90-927A-BC88C782CAAA}.Debug|x64.ActiveCfg = Debug|x64
- {8C7C4962-6AAA-4A90-927A-BC88C782CAAA}.Debug|x64.Build.0 = Debug|x64
- {8C7C4962-6AAA-4A90-927A-BC88C782CAAA}.Debug|x86.ActiveCfg = Debug|x64
- {8C7C4962-6AAA-4A90-927A-BC88C782CAAA}.Debug|x86.Build.0 = Debug|x64
- {8C7C4962-6AAA-4A90-927A-BC88C782CAAA}.Release|Any CPU.ActiveCfg = Release|x64
- {8C7C4962-6AAA-4A90-927A-BC88C782CAAA}.Release|x64.ActiveCfg = Release|x64
- {8C7C4962-6AAA-4A90-927A-BC88C782CAAA}.Release|x64.Build.0 = Release|x64
- {8C7C4962-6AAA-4A90-927A-BC88C782CAAA}.Release|x86.ActiveCfg = Release|x64
- {8C7C4962-6AAA-4A90-927A-BC88C782CAAA}.Release|x86.Build.0 = Release|x64
- {8C7C4962-6AAA-4A90-927A-BC88C782CAAA}.Tests|Any CPU.ActiveCfg = Release|x64
- {8C7C4962-6AAA-4A90-927A-BC88C782CAAA}.Tests|Any CPU.Build.0 = Release|x64
- {8C7C4962-6AAA-4A90-927A-BC88C782CAAA}.Tests|x64.ActiveCfg = Debug|x64
- {8C7C4962-6AAA-4A90-927A-BC88C782CAAA}.Tests|x64.Build.0 = Debug|x64
- {8C7C4962-6AAA-4A90-927A-BC88C782CAAA}.Tests|x86.ActiveCfg = Release|x64
- {8C7C4962-6AAA-4A90-927A-BC88C782CAAA}.Tests|x86.Build.0 = Release|x64
- {F56CB23A-BE44-4639-A498-D6882E373AB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {F56CB23A-BE44-4639-A498-D6882E373AB4}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {F56CB23A-BE44-4639-A498-D6882E373AB4}.Debug|x64.ActiveCfg = Debug|Any CPU
- {F56CB23A-BE44-4639-A498-D6882E373AB4}.Debug|x64.Build.0 = Debug|Any CPU
- {F56CB23A-BE44-4639-A498-D6882E373AB4}.Debug|x86.ActiveCfg = Debug|Any CPU
- {F56CB23A-BE44-4639-A498-D6882E373AB4}.Debug|x86.Build.0 = Debug|Any CPU
- {F56CB23A-BE44-4639-A498-D6882E373AB4}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {F56CB23A-BE44-4639-A498-D6882E373AB4}.Release|x64.ActiveCfg = Release|Any CPU
- {F56CB23A-BE44-4639-A498-D6882E373AB4}.Release|x64.Build.0 = Release|Any CPU
- {F56CB23A-BE44-4639-A498-D6882E373AB4}.Release|x86.ActiveCfg = Release|Any CPU
- {F56CB23A-BE44-4639-A498-D6882E373AB4}.Release|x86.Build.0 = Release|Any CPU
- {F56CB23A-BE44-4639-A498-D6882E373AB4}.Tests|Any CPU.ActiveCfg = Release|Any CPU
- {F56CB23A-BE44-4639-A498-D6882E373AB4}.Tests|Any CPU.Build.0 = Release|Any CPU
- {F56CB23A-BE44-4639-A498-D6882E373AB4}.Tests|x64.ActiveCfg = Debug|Any CPU
- {F56CB23A-BE44-4639-A498-D6882E373AB4}.Tests|x64.Build.0 = Debug|Any CPU
- {F56CB23A-BE44-4639-A498-D6882E373AB4}.Tests|x86.ActiveCfg = Release|Any CPU
- {F56CB23A-BE44-4639-A498-D6882E373AB4}.Tests|x86.Build.0 = Release|Any CPU
- {5A1E5424-CDB1-4776-A9CC-01B2CD57F516}.Debug|Any CPU.ActiveCfg = Debug|x64
- {5A1E5424-CDB1-4776-A9CC-01B2CD57F516}.Debug|Any CPU.Build.0 = Debug|x64
- {5A1E5424-CDB1-4776-A9CC-01B2CD57F516}.Debug|x64.ActiveCfg = Debug|x64
- {5A1E5424-CDB1-4776-A9CC-01B2CD57F516}.Debug|x64.Build.0 = Debug|x64
- {5A1E5424-CDB1-4776-A9CC-01B2CD57F516}.Debug|x86.ActiveCfg = Debug|x64
- {5A1E5424-CDB1-4776-A9CC-01B2CD57F516}.Debug|x86.Build.0 = Debug|x64
- {5A1E5424-CDB1-4776-A9CC-01B2CD57F516}.Release|Any CPU.ActiveCfg = Release|x64
- {5A1E5424-CDB1-4776-A9CC-01B2CD57F516}.Release|x64.ActiveCfg = Release|x64
- {5A1E5424-CDB1-4776-A9CC-01B2CD57F516}.Release|x64.Build.0 = Release|x64
- {5A1E5424-CDB1-4776-A9CC-01B2CD57F516}.Release|x86.ActiveCfg = Release|x64
- {5A1E5424-CDB1-4776-A9CC-01B2CD57F516}.Release|x86.Build.0 = Release|x64
- {5A1E5424-CDB1-4776-A9CC-01B2CD57F516}.Tests|Any CPU.ActiveCfg = Release|x64
- {5A1E5424-CDB1-4776-A9CC-01B2CD57F516}.Tests|Any CPU.Build.0 = Release|x64
- {5A1E5424-CDB1-4776-A9CC-01B2CD57F516}.Tests|x64.ActiveCfg = Debug|x64
- {5A1E5424-CDB1-4776-A9CC-01B2CD57F516}.Tests|x64.Build.0 = Debug|x64
- {5A1E5424-CDB1-4776-A9CC-01B2CD57F516}.Tests|x86.ActiveCfg = Release|x64
- {5A1E5424-CDB1-4776-A9CC-01B2CD57F516}.Tests|x86.Build.0 = Release|x64
- {B003D2B0-BCAE-4CEE-8A73-413C914716D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {B003D2B0-BCAE-4CEE-8A73-413C914716D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {B003D2B0-BCAE-4CEE-8A73-413C914716D0}.Debug|x64.ActiveCfg = Debug|Any CPU
- {B003D2B0-BCAE-4CEE-8A73-413C914716D0}.Debug|x64.Build.0 = Debug|Any CPU
- {B003D2B0-BCAE-4CEE-8A73-413C914716D0}.Debug|x86.ActiveCfg = Debug|Any CPU
- {B003D2B0-BCAE-4CEE-8A73-413C914716D0}.Debug|x86.Build.0 = Debug|Any CPU
- {B003D2B0-BCAE-4CEE-8A73-413C914716D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {B003D2B0-BCAE-4CEE-8A73-413C914716D0}.Release|x64.ActiveCfg = Release|Any CPU
- {B003D2B0-BCAE-4CEE-8A73-413C914716D0}.Release|x64.Build.0 = Release|Any CPU
- {B003D2B0-BCAE-4CEE-8A73-413C914716D0}.Release|x86.ActiveCfg = Release|Any CPU
- {B003D2B0-BCAE-4CEE-8A73-413C914716D0}.Release|x86.Build.0 = Release|Any CPU
- {B003D2B0-BCAE-4CEE-8A73-413C914716D0}.Tests|Any CPU.ActiveCfg = Release|Any CPU
- {B003D2B0-BCAE-4CEE-8A73-413C914716D0}.Tests|Any CPU.Build.0 = Release|Any CPU
- {B003D2B0-BCAE-4CEE-8A73-413C914716D0}.Tests|x64.ActiveCfg = Debug|Any CPU
- {B003D2B0-BCAE-4CEE-8A73-413C914716D0}.Tests|x64.Build.0 = Debug|Any CPU
- {B003D2B0-BCAE-4CEE-8A73-413C914716D0}.Tests|x86.ActiveCfg = Release|Any CPU
- {B003D2B0-BCAE-4CEE-8A73-413C914716D0}.Tests|x86.Build.0 = Release|Any CPU
{3863807C-2F87-4E27-A9C9-8675645A8DA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3863807C-2F87-4E27-A9C9-8675645A8DA5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3863807C-2F87-4E27-A9C9-8675645A8DA5}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -250,23 +167,36 @@ Global
{9E29FE05-FAA4-4440-AE1D-7AF736D6676D}.Tests|x64.Build.0 = Tests|Any CPU
{9E29FE05-FAA4-4440-AE1D-7AF736D6676D}.Tests|x86.ActiveCfg = Tests|Any CPU
{9E29FE05-FAA4-4440-AE1D-7AF736D6676D}.Tests|x86.Build.0 = Tests|Any CPU
+ {72CA8EDA-C9CF-40C2-B7FE-4A529204EEC2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {72CA8EDA-C9CF-40C2-B7FE-4A529204EEC2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {72CA8EDA-C9CF-40C2-B7FE-4A529204EEC2}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {72CA8EDA-C9CF-40C2-B7FE-4A529204EEC2}.Debug|x64.Build.0 = Debug|Any CPU
+ {72CA8EDA-C9CF-40C2-B7FE-4A529204EEC2}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {72CA8EDA-C9CF-40C2-B7FE-4A529204EEC2}.Debug|x86.Build.0 = Debug|Any CPU
+ {72CA8EDA-C9CF-40C2-B7FE-4A529204EEC2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {72CA8EDA-C9CF-40C2-B7FE-4A529204EEC2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {72CA8EDA-C9CF-40C2-B7FE-4A529204EEC2}.Release|x64.ActiveCfg = Release|Any CPU
+ {72CA8EDA-C9CF-40C2-B7FE-4A529204EEC2}.Release|x64.Build.0 = Release|Any CPU
+ {72CA8EDA-C9CF-40C2-B7FE-4A529204EEC2}.Release|x86.ActiveCfg = Release|Any CPU
+ {72CA8EDA-C9CF-40C2-B7FE-4A529204EEC2}.Release|x86.Build.0 = Release|Any CPU
+ {72CA8EDA-C9CF-40C2-B7FE-4A529204EEC2}.Tests|Any CPU.ActiveCfg = Debug|Any CPU
+ {72CA8EDA-C9CF-40C2-B7FE-4A529204EEC2}.Tests|Any CPU.Build.0 = Debug|Any CPU
+ {72CA8EDA-C9CF-40C2-B7FE-4A529204EEC2}.Tests|x64.ActiveCfg = Debug|Any CPU
+ {72CA8EDA-C9CF-40C2-B7FE-4A529204EEC2}.Tests|x64.Build.0 = Debug|Any CPU
+ {72CA8EDA-C9CF-40C2-B7FE-4A529204EEC2}.Tests|x86.ActiveCfg = Debug|Any CPU
+ {72CA8EDA-C9CF-40C2-B7FE-4A529204EEC2}.Tests|x86.Build.0 = Debug|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
- {8C7C4962-6AAA-4A90-927A-BC88C782CAAA} = {D63F2ACE-DE7B-4208-B602-5C48F53052B6}
- {0A2A51C5-E759-420C-9EFA-9AF6A9A4BB83} = {D6E014F1-7EC9-4005-A8BC-80E0E3491F0B}
- {D63F2ACE-DE7B-4208-B602-5C48F53052B6} = {D6E014F1-7EC9-4005-A8BC-80E0E3491F0B}
- {F56CB23A-BE44-4639-A498-D6882E373AB4} = {0A2A51C5-E759-420C-9EFA-9AF6A9A4BB83}
- {5A1E5424-CDB1-4776-A9CC-01B2CD57F516} = {0A2A51C5-E759-420C-9EFA-9AF6A9A4BB83}
- {B003D2B0-BCAE-4CEE-8A73-413C914716D0} = {D63F2ACE-DE7B-4208-B602-5C48F53052B6}
{3863807C-2F87-4E27-A9C9-8675645A8DA5} = {41193496-02AE-44FA-9A63-28E7A168A3AC}
{2F31204A-2084-4C9D-977E-B1673A7EE6DD} = {63F4FEA7-5B8B-4496-988E-B84410C50D42}
{45D3DDF3-135B-46CA-B3EE-3537FCFFFBEB} = {0828626A-BAD8-4E02-99DC-3AAA15073223}
{4E317E0F-0565-4B8A-84F3-56ADF18C65AD} = {0828626A-BAD8-4E02-99DC-3AAA15073223}
{F7D48930-4705-4775-91DA-A948F30D224F} = {0828626A-BAD8-4E02-99DC-3AAA15073223}
{9E29FE05-FAA4-4440-AE1D-7AF736D6676D} = {0828626A-BAD8-4E02-99DC-3AAA15073223}
+ {72CA8EDA-C9CF-40C2-B7FE-4A529204EEC2} = {0828626A-BAD8-4E02-99DC-3AAA15073223}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E4CDB648-EF1A-4ADE-B6EA-D4E9D668E676}
diff --git a/src/Qt.DotNet.Adapter/Qt.DotNet.Adapter.csproj b/src/Qt.DotNet.Adapter/Qt.DotNet.Adapter.csproj
index db731eb..5c0114f 100644
--- a/src/Qt.DotNet.Adapter/Qt.DotNet.Adapter.csproj
+++ b/src/Qt.DotNet.Adapter/Qt.DotNet.Adapter.csproj
@@ -1,4 +1,4 @@
-<!--
+<!--
/***************************************************************************************************
Copyright (C) 2023 The Qt Company Ltd.
SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
@@ -8,21 +8,40 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>net6.0</TargetFramework>
+ <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>disable</Nullable>
<Configurations>Debug;Release;Tests</Configurations>
+ <RootNamespace></RootNamespace>
</PropertyGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="MKDIR $(SolutionDir)bin 2&gt; NUL&#xD;&#xA;COPY $(OutputPath)$(AssemblyName).dll $(SolutionDir)bin\$(AssemblyName).dll&#xD;&#xA;" />
</Target>
<Target Name="CleanPostBuild" AfterTargets="Clean">
- <Delete Files="$(SolutionDir)bin\$(AssemblyName).dll"/>
+ <Delete Files="$(SolutionDir)bin\$(AssemblyName).dll" />
</Target>
+ <ItemGroup>
+ <None Remove="build\Qt.DotNet.Adapter.targets" />
+ </ItemGroup>
+ <ItemGroup>
+ <Content Include="build\Qt.DotNet.Adapter.targets">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ </ItemGroup>
<ItemGroup>
<ProjectReference Include="..\includegen\includegen.csproj" />
</ItemGroup>
+ <ItemGroup>
+ <Folder Include="build\include\" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <Content Include="..\..\include\*" Link="build\include\%(FileName)%(Extension)">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ </ItemGroup>
+
</Project>
diff --git a/src/Qt.DotNet.Adapter/Adapter.Delegates.cs b/src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.Delegates.cs
index c3f2b9d..bb5e716 100644
--- a/src/Qt.DotNet.Adapter/Adapter.Delegates.cs
+++ b/src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.Delegates.cs
@@ -105,7 +105,9 @@ namespace Qt.DotNet
MarshalTypeRef = typeof(ObjectMarshaler))]
public delegate InterfaceProxy AddInterfaceProxy(
[MarshalAs(UnmanagedType.LPWStr)]
- [In] string interfaceName);
+ [In] string interfaceName,
+ [In] IntPtr data,
+ [In] IntPtr cleanUp);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate void SetInterfaceMethod(
@@ -125,6 +127,9 @@ namespace Qt.DotNet
public delegate IntPtr GetObject
([In] IntPtr objRefPtr, [MarshalAs(UnmanagedType.LPWStr)][In] string path);
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+ public delegate void Reset();
+
#if DEBUG || TESTS
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate void Stats(
diff --git a/src/Qt.DotNet.Adapter/Adapter.Events.cs b/src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.Events.cs
index 777121d..777121d 100644
--- a/src/Qt.DotNet.Adapter/Adapter.Events.cs
+++ b/src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.Events.cs
diff --git a/src/Qt.DotNet.Adapter/Adapter.Methods.cs b/src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.Methods.cs
index 47db8a6..47db8a6 100644
--- a/src/Qt.DotNet.Adapter/Adapter.Methods.cs
+++ b/src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.Methods.cs
diff --git a/src/Qt.DotNet.Adapter/Adapter.Objects.cs b/src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.Objects.cs
index 8692ea3..5377d55 100644
--- a/src/Qt.DotNet.Adapter/Adapter.Objects.cs
+++ b/src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.Objects.cs
@@ -3,6 +3,7 @@
SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
***************************************************************************************************/
+using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;
@@ -65,8 +66,11 @@ namespace Qt.DotNet
// Compile-time signature check of delegate vs. method
_ = new Delegates.FreeObjectRef(FreeObjectRef);
#endif
- if (!ObjectRefs.TryRemove(objRefPtr, out var objRef))
- throw new ArgumentException("Invalid object reference", nameof(objRefPtr));
+ if (!ObjectRefs.TryRemove(objRefPtr, out var objRef)) {
+ Debug.WriteLine(@$"
+ADAPTER::FreeObjectRef: WARNING Invalid object reference: 0x{objRefPtr:x16}");
+ return;
+ }
RemoveAllEventHandlers(objRef);
var liveObjects = ObjectRefs.Values.Select(x => x.Target).ToList();
diff --git a/src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.Static.cs b/src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.Static.cs
new file mode 100644
index 0000000..cfc2c96
--- /dev/null
+++ b/src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.Static.cs
@@ -0,0 +1,16 @@
+/***************************************************************************************************
+ Copyright (C) 2024 The Qt Company Ltd.
+ SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+***************************************************************************************************/
+
+namespace Qt.DotNet
+{
+ public partial class Adapter
+ {
+ public partial interface IStatic
+ {
+ }
+
+ public static IStatic Static { get; set; }
+ }
+}
diff --git a/src/Qt.DotNet.Adapter/Adapter.Test.cs b/src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.Test.cs
index 983e582..983e582 100644
--- a/src/Qt.DotNet.Adapter/Adapter.Test.cs
+++ b/src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.Test.cs
diff --git a/src/Qt.DotNet.Adapter/Adapter.cs b/src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.cs
index b26d89e..286c28c 100644
--- a/src/Qt.DotNet.Adapter/Adapter.cs
+++ b/src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.cs
@@ -3,9 +3,71 @@
SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
***************************************************************************************************/
+using System;
using System.Collections.Concurrent;
+using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;
+using System.Runtime.Loader;
+using static Qt.DotNet.Adapter;
+
+internal class StartupHook
+{
+ private static ConcurrentBag<DelegateRef> Callbacks { get; set; } = new();
+ public static void Initialize()
+ {
+ if (AppContext.GetData("QT_DOTNET_RESOLVE_FN") is not string refFnPtrHex)
+ return;
+ var refFnPtrInt = Convert.ToInt64(refFnPtrHex, 16);
+ var refFnPtr = new IntPtr(refFnPtrInt);
+ var fnPtr = Marshal.ReadIntPtr(refFnPtr);
+ if (fnPtr != IntPtr.Zero)
+ return;
+ var delegateType = typeof(ResolveFnDelegate);
+ var method = typeof(StartupHook).GetMethod("ResolveFn");
+ var methodDelegate = Delegate.CreateDelegate(delegateType, method, false);
+ var methodHandle = GCHandle.Alloc(methodDelegate);
+ fnPtr = Marshal.GetFunctionPointerForDelegate(methodDelegate);
+ Callbacks.Add(new DelegateRef(methodHandle, fnPtr));
+ Marshal.WriteIntPtr(refFnPtr, fnPtr);
+ }
+
+ public static int ResolveFn(
+ IntPtr assemblyPathNative,
+ IntPtr typeNameNative,
+ IntPtr methodNameNative,
+ IntPtr delegateTypeNative,
+ IntPtr reserved,
+ IntPtr functionHandle)
+ {
+ string assemblyPath = Marshal.PtrToStringUni(assemblyPathNative);
+ string typeName = Marshal.PtrToStringUni(typeNameNative);
+ string methodName = Marshal.PtrToStringUni(methodNameNative);
+ string delegateTypeName = Marshal.PtrToStringUni(delegateTypeNative);
+ var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyPath);
+ var type = Type.GetType(typeName);
+ var delegateType = string.IsNullOrEmpty(delegateTypeName) ? typeof(EntryPointDelegate) : Type.GetType(delegateTypeName);
+ var invoke = delegateType.GetMethod("Invoke");
+ var paramTypes = invoke.GetParameters().Select(x => x.ParameterType).ToArray();
+ var method = type.GetMethod(methodName, paramTypes);
+ var methodDelegate = Delegate.CreateDelegate(delegateType, method, false);
+ var methodHandle = GCHandle.Alloc(methodDelegate);
+ var fnPtr = Marshal.GetFunctionPointerForDelegate(methodDelegate);
+ Callbacks.Add(new DelegateRef(methodHandle, fnPtr));
+ Marshal.WriteIntPtr(functionHandle, fnPtr);
+ return 0;
+ }
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+ public delegate int ResolveFnDelegate(
+ IntPtr assemblyPathNative,
+ IntPtr typeNameNative,
+ IntPtr methodNameNative,
+ IntPtr delegateTypeNative,
+ IntPtr reserved,
+ IntPtr functionHandle);
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+ public delegate int EntryPointDelegate(IntPtr arg, int argLength);
+}
namespace Qt.DotNet
{
@@ -50,6 +112,21 @@ namespace Qt.DotNet
return false;
}
+ /// <summary>
+ /// Reset adapter cache
+ /// </summary>
+ public static void Reset()
+ {
+#if DEBUG
+ // Compile-time signature check of delegate vs. method
+ _ = new Delegates.Reset(Reset);
+#endif
+ ObjectRefs.Clear();
+ DelegateRefs.Clear();
+ DelegatesByMethod.Clear();
+ Events.Clear();
+ }
+
internal class DelegateRef
{
public GCHandle Handle { get; }
diff --git a/src/Qt.DotNet.Adapter/CodeGenerator.cs b/src/Qt.DotNet.Adapter/Qt/DotNet/CodeGenerator.cs
index 5922960..bb97c60 100644
--- a/src/Qt.DotNet.Adapter/CodeGenerator.cs
+++ b/src/Qt.DotNet.Adapter/Qt/DotNet/CodeGenerator.cs
@@ -8,6 +8,7 @@ using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Reflection.Emit;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Qt.DotNet
@@ -141,6 +142,7 @@ namespace Qt.DotNet
.Select(x => x.ParameterType)
.ToArray();
var callbackParamTypes = paramTypes
+ .Prepend(typeof(IntPtr))
.Prepend(typeof(ulong))
.Prepend(typeof(IntPtr))
.ToArray();
@@ -154,17 +156,27 @@ namespace Qt.DotNet
TypeAttributes.Sealed | TypeAttributes.NestedPublic,
typeof(MulticastDelegate));
var callbackInvoke = InitDelegateType(delegateGen, callbackParameters);
+#if TEST || DEBUG
+ Debug.Assert(callbackInvoke.ReturnType == method.ReturnType);
+#endif
+ var fieldData = typeof(InterfaceProxy).GetField("DataPtr");
- var callbackGen = typeGen.DefineField(
+ var prototype = typeGen.DefineField(
UniqueName(method.Name, "Callback"), delegateGen, FieldAttributes.Public);
var nativeCallbackGen = typeGen.DefineField(
- $"Native_{callbackGen.Name}", typeof(Delegate), FieldAttributes.Public);
+ $"Call_{prototype.Name}", typeof(Delegate), FieldAttributes.Public);
var cleanUpPtr = typeGen.DefineField(
- $"CleanUp_{callbackGen.Name}", typeof(IntPtr), FieldAttributes.Public);
+ $"CleanUp_{prototype.Name}", typeof(IntPtr), FieldAttributes.Public);
var contextGen = typeGen.DefineField(
- $"Context_{callbackGen.Name}", typeof(IntPtr), FieldAttributes.Public);
+ $"Context_{prototype.Name}", typeof(IntPtr), FieldAttributes.Public);
var countGen = typeGen.DefineField(
- $"Count_{callbackGen.Name}", typeof(ulong), FieldAttributes.Public);
+ $"Count_{prototype.Name}", typeof(ulong), FieldAttributes.Public);
+
+ var monitorEnter = typeof(Monitor).GetMethod("Enter", new[] { typeof(object) });
+ var monitorExit = typeof(Monitor).GetMethod("Exit", new[] { typeof(object) });
+#if TEST || DEBUG
+ Debug.Assert(monitorEnter != null && monitorExit != null);
+#endif
var methodGen = typeGen.DefineMethod(method.Name,
MethodAttributes.Public
@@ -175,53 +187,88 @@ namespace Qt.DotNet
method.ReturnType, paramTypes);
var code = methodGen.GetILGenerator();
- // ++Count;
- code.Emit(OpCodes.Ldarg_0);
- code.Emit(OpCodes.Ldarg_0);
- code.Emit(OpCodes.Ldfld, countGen);
- code.Emit(OpCodes.Ldc_I4_1);
- code.Emit(OpCodes.Conv_I8);
- code.Emit(OpCodes.Add);
- code.Emit(OpCodes.Stfld, countGen);
-
- // NativeCallback.Invoke(
- code.Emit(OpCodes.Ldarg_0);
- code.Emit(OpCodes.Ldfld, nativeCallbackGen);
- // context,
- code.Emit(OpCodes.Ldarg_0);
- code.Emit(OpCodes.Ldfld, contextGen);
- // count,
- code.Emit(OpCodes.Ldarg_0);
- code.Emit(OpCodes.Ldfld, countGen);
- // Load method call arguments into stack
- for (int paramIdx = 0; paramIdx < paramTypes.Length; ++paramIdx) {
- if (paramIdx == 0)
- code.Emit(OpCodes.Ldarg_1);
- else if (paramIdx == 1)
- code.Emit(OpCodes.Ldarg_2);
- else if (paramIdx == 2)
- code.Emit(OpCodes.Ldarg_3);
- else
- code.Emit(OpCodes.Ldarg_S, paramIdx + 1);
+ // locals
+ if (method.ReturnType != typeof(void)) {
+ // [0] = default;
+ code.DeclareLocal(method.ReturnType);
+ code.Emit(OpCodes.Ldloca_S, 0);
+ code.Emit(OpCodes.Initobj, method.ReturnType);
+ }
+
+ // try {
+ {
+ code.BeginExceptionBlock();
+
+ // Monitor.Enter(<Callback>);
+ code.Emit(OpCodes.Ldarg_0);
+ code.Emit(OpCodes.Ldfld, nativeCallbackGen);
+ code.Emit(OpCodes.Call, monitorEnter);
+
+ // ++Count;
+ code.Emit(OpCodes.Ldarg_0);
+ code.Emit(OpCodes.Ldarg_0);
+ code.Emit(OpCodes.Ldfld, countGen);
+ code.Emit(OpCodes.Ldc_I4_1);
+ code.Emit(OpCodes.Conv_I8);
+ code.Emit(OpCodes.Add);
+ code.Emit(OpCodes.Stfld, countGen);
+
+ // NativeCallback.Invoke(
+ code.Emit(OpCodes.Ldarg_0);
+ code.Emit(OpCodes.Ldfld, nativeCallbackGen);
+ // context,
+ code.Emit(OpCodes.Ldarg_0);
+ code.Emit(OpCodes.Ldfld, contextGen);
+ // count,
+ code.Emit(OpCodes.Ldarg_0);
+ code.Emit(OpCodes.Ldfld, countGen);
+ // data,
+ code.Emit(OpCodes.Ldarg_0);
+ code.Emit(OpCodes.Ldfld, fieldData);
+ // Load method call arguments into stack
+ for (int paramIdx = 0; paramIdx < paramTypes.Length; ++paramIdx) {
+ if (paramIdx == 0)
+ code.Emit(OpCodes.Ldarg_1);
+ else if (paramIdx == 1)
+ code.Emit(OpCodes.Ldarg_2);
+ else if (paramIdx == 2)
+ code.Emit(OpCodes.Ldarg_3);
+ else
+ code.Emit(OpCodes.Ldarg_S, paramIdx + 1);
+ }
+ // ); //NativeCallback.Invoke
+ code.Emit(OpCodes.Callvirt, callbackInvoke);
+
+ // [0] = ^^^
+ if (method.ReturnType != typeof(void))
+ code.Emit(OpCodes.Stloc_0);
+
+ // CleanUp.Invoke(
+ code.Emit(OpCodes.Ldarg_0);
+ // cleanUpPtr,
+ code.Emit(OpCodes.Ldarg_0);
+ code.Emit(OpCodes.Ldfld, cleanUpPtr);
+ // context,
+ code.Emit(OpCodes.Ldarg_0);
+ code.Emit(OpCodes.Ldfld, contextGen);
+ // count,
+ code.Emit(OpCodes.Ldarg_0);
+ code.Emit(OpCodes.Ldfld, countGen);
+ // ); //CleanUp.Invoke
+ code.Emit(OpCodes.Callvirt, interfaceImpl_CleanUp);
}
- // ); //NativeCallback.Invoke
- code.Emit(OpCodes.Callvirt, callbackInvoke);
-
- // CleanUp.Invoke(
- code.Emit(OpCodes.Ldarg_0);
- // cleanUpPtr,
- code.Emit(OpCodes.Ldarg_0);
- code.Emit(OpCodes.Ldfld, cleanUpPtr);
- // context,
- code.Emit(OpCodes.Ldarg_0);
- code.Emit(OpCodes.Ldfld, contextGen);
- // count,
- code.Emit(OpCodes.Ldarg_0);
- code.Emit(OpCodes.Ldfld, countGen);
- // ); //CleanUp.Invoke
- code.Emit(OpCodes.Callvirt, interfaceImpl_CleanUp);
-
- // return <ret>;
+ code.BeginFinallyBlock(); // } finally {
+ {
+ // Monitor.Exit(<Callback>);
+ code.Emit(OpCodes.Ldarg_0);
+ code.Emit(OpCodes.Ldfld, nativeCallbackGen);
+ code.Emit(OpCodes.Call, monitorExit);
+ }
+ code.EndExceptionBlock(); // } /* try */
+
+ // return [0];
+ if (method.ReturnType != typeof(void))
+ code.Emit(OpCodes.Ldloc_0);
code.Emit(OpCodes.Ret);
// Generate nested type
diff --git a/src/Qt.DotNet.Adapter/EventRelay.cs b/src/Qt.DotNet.Adapter/Qt/DotNet/EventRelay.cs
index f64b938..f64b938 100644
--- a/src/Qt.DotNet.Adapter/EventRelay.cs
+++ b/src/Qt.DotNet.Adapter/Qt/DotNet/EventRelay.cs
diff --git a/src/Qt.DotNet.Adapter/Qt/DotNet/IQModelIndex.cs b/src/Qt.DotNet.Adapter/Qt/DotNet/IQModelIndex.cs
new file mode 100644
index 0000000..9ce0a61
--- /dev/null
+++ b/src/Qt.DotNet.Adapter/Qt/DotNet/IQModelIndex.cs
@@ -0,0 +1,29 @@
+/***************************************************************************************************
+ Copyright (C) 2024 The Qt Company Ltd.
+ SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+***************************************************************************************************/
+
+namespace Qt.DotNet
+{
+ public interface IQModelIndex
+ {
+ bool IsValid();
+ int Row();
+ int Column();
+ IntPtr InternalPointer();
+ }
+
+ public partial class Adapter
+ {
+ public partial interface IStatic
+ {
+ IQModelIndex QModelIndex_Create();
+ }
+ public static IQModelIndex QModelIndex() => Static.QModelIndex_Create();
+ }
+
+ public static class QModelIndex
+ {
+ public static IQModelIndex Create() => Adapter.Static?.QModelIndex_Create();
+ }
+}
diff --git a/src/Qt.DotNet.Adapter/Qt/DotNet/IQVariant.cs b/src/Qt.DotNet.Adapter/Qt/DotNet/IQVariant.cs
new file mode 100644
index 0000000..df5c7fd
--- /dev/null
+++ b/src/Qt.DotNet.Adapter/Qt/DotNet/IQVariant.cs
@@ -0,0 +1,25 @@
+/***************************************************************************************************
+ Copyright (C) 2024 The Qt Company Ltd.
+ SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+***************************************************************************************************/
+
+namespace Qt.DotNet
+{
+ public interface IQVariant
+ {
+ string ToStringValue();
+ void SetValue(string value);
+ }
+
+ public partial class Adapter
+ {
+ public partial interface IStatic
+ {
+ IQVariant QVariant_Create();
+ IQVariant QVariant_Create(string value);
+ }
+ public static IQVariant QVariant() => Static.QVariant_Create();
+ public static IQVariant QVariant(string value) => Static.QVariant_Create(value);
+ }
+
+}
diff --git a/src/Qt.DotNet.Adapter/InterfaceProxy.cs b/src/Qt.DotNet.Adapter/Qt/DotNet/InterfaceProxy.cs
index ec40e22..f378e0d 100644
--- a/src/Qt.DotNet.Adapter/InterfaceProxy.cs
+++ b/src/Qt.DotNet.Adapter/Qt/DotNet/InterfaceProxy.cs
@@ -6,23 +6,40 @@
using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;
+using System.Text.RegularExpressions;
namespace Qt.DotNet
{
public class InterfaceProxy
{
public delegate void CleanUpDelegate(IntPtr context, ulong count);
- public CleanUpDelegate CleanUpCallback;
public void CleanUp(IntPtr callback, IntPtr context, ulong count)
{
- CleanUpCallback(context, count);
+ if (callback == IntPtr.Zero)
+ return;
+ var CleanUp = Marshal.GetDelegateForFunctionPointer<CleanUpDelegate>(callback);
+ CleanUp?.Invoke(context, count);
+ }
+
+ public IntPtr DataPtr = IntPtr.Zero;
+ public IntPtr Data => DataPtr;
+ public IntPtr CleanUpData { get; set; } = IntPtr.Zero;
+
+ public delegate void DeleteDelegate(IntPtr data);
+
+ ~InterfaceProxy()
+ {
+ if (Data == IntPtr.Zero || CleanUpData == IntPtr.Zero)
+ return;
+ Marshal.GetDelegateForFunctionPointer<DeleteDelegate>(CleanUpData)?.Invoke(Data);
}
}
public partial class Adapter
{
- public static InterfaceProxy AddInterfaceProxy(string interfaceName)
+ public static InterfaceProxy AddInterfaceProxy(string interfaceName,
+ IntPtr data, IntPtr cleanUp)
{
#if DEBUG
// Compile-time signature check of delegate vs. method
@@ -36,8 +53,12 @@ namespace Qt.DotNet
#if DEBUG
Debug.Assert(ctor != null, nameof(ctor) + " is null");
#endif
- var obj = ctor.Invoke(null);
- return obj as InterfaceProxy;
+ if (ctor.Invoke(null) is not InterfaceProxy proxy)
+ throw new InvalidOperationException($"Error creating proxy for {interfaceName}");
+
+ proxy.DataPtr = data;
+ proxy.CleanUpData = cleanUp;
+ return proxy;
}
public static void SetInterfaceMethod(
@@ -55,7 +76,7 @@ namespace Qt.DotNet
#endif
var type = proxy.GetType();
var parameterTypes = parameters
- .Skip(3)
+ .Skip(4) // (return, context, key, data)
.Select((x, i) => x.GetParameterType()
?? throw new ArgumentException($"Type not found [{i}]", nameof(parameters)))
.ToArray();
@@ -70,33 +91,54 @@ namespace Qt.DotNet
Debug.Assert(delegateTypeInvoke != null, nameof(delegateTypeInvoke) + " != null");
#endif
var paramTypes = delegateTypeInvoke.GetParameters()
- .Select(p => p.ParameterType);
-
- var fieldDelegate = proxy.GetType().GetFields()
- .FirstOrDefault(f => f.FieldType.IsAssignableTo(typeof(Delegate))
- && f.FieldType.GetMethod("Invoke") is MethodInfo invoke
- && invoke.GetParameters()
- .Zip(paramTypes)
- .All(x => x.First.ParameterType == x.Second)
- && invoke.ReturnType.IsAssignableTo(delegateTypeInvoke.ReturnType))
- ?? throw new ArgumentException("Signature mismatch", nameof(parameters));
-
- var fieldNative = proxy.GetType().GetField($"Native_{fieldDelegate.Name}");
+ .Select(p => p.ParameterType)
+ .ToArray();
+
+ var callbacks = proxy.GetType().GetFields()
+ .Where(f => Regex.IsMatch(f.Name, $@"^{methodName}_Callback_\w{{11}}$"))
+ .ToArray();
+ if (!callbacks.Any())
+ throw new ArgumentException("Method not found", methodName);
+
+ FieldInfo prototype = null;
+ foreach (var callback in callbacks) {
+ bool sigOk = callback.FieldType.IsAssignableTo(typeof(Delegate));
+ var callbackInvoke = callback.FieldType.GetMethod("Invoke");
+ sigOk = sigOk && callbackInvoke is not null;
+ if (!sigOk)
+ continue;
+ var callbackParams = callbackInvoke.GetParameters();
+ sigOk = sigOk && callbackParams is not null;
+ sigOk = sigOk && callbackParams.Length == paramTypes.Length;
+ sigOk = sigOk && callbackParams.Zip(paramTypes)
+ .All(x => x.First.ParameterType == x.Second);
+ if (!sigOk)
+ continue;
+
+ prototype = callback;
+ break;
+ }
+ if (prototype is null)
+ throw new ArgumentException("Signature mismatch", nameof(parameters));
+
+ var fieldCallback = proxy.GetType().GetField($"Call_{prototype.Name}");
var callbackDelegate = Marshal.GetDelegateForFunctionPointer(callbackPtr, delegateType);
#if DEBUG
- Debug.Assert(fieldNative != null, nameof(fieldNative) + " is null");
+ Debug.Assert(fieldCallback != null, nameof(fieldCallback) + " is null");
#endif
- fieldNative.SetValue(proxy, callbackDelegate);
+ fieldCallback.SetValue(proxy, callbackDelegate);
- var fieldContext = proxy.GetType().GetField($"Context_{fieldDelegate.Name}");
+ var fieldCleanup = proxy.GetType().GetField($"CleanUp_{prototype.Name}");
+#if DEBUG
+ Debug.Assert(fieldCleanup != null, nameof(fieldCleanup) + " is null");
+#endif
+ fieldCleanup.SetValue(proxy, cleanUpPtr);
+
+ var fieldContext = proxy.GetType().GetField($"Context_{prototype.Name}");
#if DEBUG
Debug.Assert(fieldContext != null, nameof(fieldContext) + " is null");
#endif
fieldContext.SetValue(proxy, context);
-
- proxy.CleanUpCallback = Marshal.GetDelegateForFunctionPointer(
- cleanUpPtr, typeof(InterfaceProxy.CleanUpDelegate))
- as InterfaceProxy.CleanUpDelegate;
}
}
}
diff --git a/src/Qt.DotNet.Adapter/ObjectMarshaler.cs b/src/Qt.DotNet.Adapter/Qt/DotNet/ObjectMarshaler.cs
index 49c011f..49c011f 100644
--- a/src/Qt.DotNet.Adapter/ObjectMarshaler.cs
+++ b/src/Qt.DotNet.Adapter/Qt/DotNet/ObjectMarshaler.cs
diff --git a/src/Qt.DotNet.Adapter/Parameter.cs b/src/Qt.DotNet.Adapter/Qt/DotNet/Parameter.cs
index 4caa9ad..7c1f4f3 100644
--- a/src/Qt.DotNet.Adapter/Parameter.cs
+++ b/src/Qt.DotNet.Adapter/Qt/DotNet/Parameter.cs
@@ -96,10 +96,10 @@ namespace Qt.DotNet
return type;
}
- public Parameter(Type type, ulong paramInfo = 0)
+ public Parameter(Type type)
{
- TypeName = type.FullName;
- ParamInfo = paramInfo;
+ TypeName = type.AssemblyQualifiedName;
+ ParamInfo = 0;
}
public Parameter(string typeName, ulong paramInfo = 0)
diff --git a/src/Qt.DotNet.Adapter/Qt/DotNet/QAbstractListModel.cs b/src/Qt.DotNet.Adapter/Qt/DotNet/QAbstractListModel.cs
new file mode 100644
index 0000000..98f1a07
--- /dev/null
+++ b/src/Qt.DotNet.Adapter/Qt/DotNet/QAbstractListModel.cs
@@ -0,0 +1,153 @@
+/***************************************************************************************************
+ Copyright (C) 2024 The Qt Company Ltd.
+ SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+***************************************************************************************************/
+
+namespace Qt.DotNet
+{
+ public enum ItemDataRole
+ {
+ DisplayRole = 0,
+ DecorationRole = 1,
+ EditRole = 2,
+ ToolTipRole = 3,
+ StatusTipRole = 4,
+ WhatsThisRole = 5,
+ FontRole = 6,
+ TextAlignmentRole = 7,
+ BackgroundRole = 8,
+ ForegroundRole = 9,
+ CheckStateRole = 10,
+ // Accessibility
+ AccessibleTextRole = 11,
+ AccessibleDescriptionRole = 12,
+ // More general purpose
+ SizeHintRole = 13,
+ InitialSortOrderRole = 14,
+ // Internal UiLib roles. Start worrying when public roles go that high.
+ DisplayPropertyRole = 27,
+ DecorationPropertyRole = 28,
+ ToolTipPropertyRole = 29,
+ StatusTipPropertyRole = 30,
+ WhatsThisPropertyRole = 31,
+ // Reserved
+ UserRole = 0x0100
+ };
+
+ [Flags]
+ public enum ItemFlag
+ {
+ NoItemFlags = 0,
+ ItemIsSelectable = 1,
+ ItemIsEditable = 2,
+ ItemIsDragEnabled = 4,
+ ItemIsDropEnabled = 8,
+ ItemIsUserCheckable = 16,
+ ItemIsEnabled = 32,
+ ItemIsAutoTristate = 64,
+ ItemNeverHasChildren = 128,
+ ItemIsUserTristate = 256
+ };
+
+ public interface IQAbstractListModel
+ {
+ int Flags(IQModelIndex index);
+ bool SetData(IQModelIndex index, IQVariant value, int role);
+
+ bool InsertRows(int row, int count, IQModelIndex parent);
+ bool RemoveRows(int row, int count, IQModelIndex parent);
+
+ void BeginInsertRows(IQModelIndex parent, int first, int last);
+ void EndInsertRows();
+
+ void BeginRemoveRows(IQModelIndex parent, int first, int last);
+ void EndRemoveRows();
+
+ IQModelIndex CreateIndex(int arow, int acolumn, IntPtr adata);
+
+ void EmitDataChanged(IQModelIndex topLeft, IQModelIndex bottomRight, int[] roles);
+ }
+
+ public abstract class QAbstractListModel
+ {
+ public IQAbstractListModel Base { get; private set; }
+
+ public QAbstractListModel()
+ {
+ Base = Adapter.Static.QAbstractListModel_Create(this);
+ }
+
+ public abstract int RowCount(IQModelIndex parent = null);
+
+ public abstract IQVariant Data(IQModelIndex index,
+ int role = (int)ItemDataRole.DisplayRole);
+
+ public virtual int Flags(IQModelIndex index)
+ {
+ if (Base == null)
+ return 0;
+ var flags = (ItemFlag)Base.Flags(index);
+ return (int)flags;
+ }
+
+ public virtual bool SetData(IQModelIndex index, IQVariant value,
+ int role = (int)ItemDataRole.EditRole)
+ {
+ return Base?.SetData(index, value, role) ?? false;
+ }
+
+ public virtual bool InsertRows(int row, int count, IQModelIndex parent)
+ {
+ return Base?.InsertRows(row, count, parent) ?? false;
+ }
+
+ public virtual bool RemoveRows(int row, int count, IQModelIndex parent)
+ {
+ return Base?.RemoveRows(row, count, parent) ?? false;
+ }
+
+ public virtual string RoleNames()
+ {
+ return string.Empty;
+ }
+
+ protected void BeginInsertRows(IQModelIndex parent, int first, int last)
+ {
+ Base?.BeginInsertRows(parent, first, last);
+ }
+
+ protected void EndInsertRows()
+ {
+ Base?.EndInsertRows();
+ }
+
+ protected void BeginRemoveRows(IQModelIndex parent, int first, int last)
+ {
+ Base?.BeginRemoveRows(parent, first, last);
+ }
+
+ protected void EndRemoveRows()
+ {
+ Base?.EndRemoveRows();
+ }
+
+ protected IQModelIndex CreateIndex(int arow, int acolumn, IntPtr adata)
+ {
+ return Base?.CreateIndex(arow, acolumn, adata);
+ }
+
+ protected void EmitDataChanged(IQModelIndex topLeft, IQModelIndex bottomRight, int[] roles)
+ {
+ Base?.EmitDataChanged(topLeft, bottomRight, roles);
+ }
+ }
+
+
+ public partial class Adapter
+ {
+ public partial interface IStatic
+ {
+ IQAbstractListModel QAbstractListModel_Create(object self);
+ }
+ }
+}
diff --git a/src/Qt.DotNet.Adapter/StringMarshaler.cs b/src/Qt.DotNet.Adapter/Qt/DotNet/StringMarshaler.cs
index c7b1869..c7b1869 100644
--- a/src/Qt.DotNet.Adapter/StringMarshaler.cs
+++ b/src/Qt.DotNet.Adapter/Qt/DotNet/StringMarshaler.cs
diff --git a/src/Qt.DotNet.Adapter/build/Qt.DotNet.Adapter.targets b/src/Qt.DotNet.Adapter/build/Qt.DotNet.Adapter.targets
new file mode 100644
index 0000000..805deb1
--- /dev/null
+++ b/src/Qt.DotNet.Adapter/build/Qt.DotNet.Adapter.targets
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+
+ <Target Name="QtDotNetBuildNative" AfterTargets="CoreCompile" BeforeTargets="_CreateAppHost">
+ <PropertyGroup>
+ <QtDir Condition="'$(QtDir)' == ''">C:\lib\qt\6.8.0\msvc2022_64</QtDir>
+ <QtDotNetDir Condition="'$(QtDotNetDir)' == ''">$(ProjectDir)\qtdotnet</QtDotNetDir>
+ <QtNativeSource>$(TargetDir)\native_source</QtNativeSource>
+ <QtNativeBuild>$(TargetDir)\native_build</QtNativeBuild>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <QtDotNetBin Include="$(QtDotNetDir)\adapter\*" />
+ <QtDotNetGenerated Include="$(QtDotNetDir)\generated_files\*" />
+ <Qml Include="$(ProjectDir)\*.qml" />
+ </ItemGroup>
+
+ <MakeDir Directories="$(QtNativeSource);$(QtNativeBuild)" />
+ <Copy SkipUnchangedFiles="true" SourceFiles="@(QtDotNetGenerated);@(Qml)" DestinationFolder="$(QtNativeSource)" />
+ <Copy SkipUnchangedFiles="true" SourceFiles="@(QtDotNetBin)" DestinationFolder="$(TargetDir)" />
+
+ <Message Importance="high" Text="Qt/.NET: building native code..." />
+ <PropertyGroup>
+ <BuildNative>
+ CALL "$(VsInstallRoot)\VC\Auxiliary\Build\vcvars64.bat" &gt; NUL
+ SET QtDir=$(QtDir)
+ SET QtDotNetDir=$(QtDotNetDir)
+ CD "$(QtNativeBuild)"
+ cmake "-DCMAKE_PREFIX_PATH=$(QtDir)" -G Ninja -S ..\native_source
+ cmake --build .
+ </BuildNative>
+ </PropertyGroup>
+ <Exec Command="$(BuildNative)" />
+ <Copy SkipUnchangedFiles="true" SourceFiles="$(QtNativeBuild)\qmlapp.exe" DestinationFolder="$(TargetDir)" />
+
+ <Message Importance="high" Text="Deploying QML app..." />
+ <PropertyGroup>
+ <RunWinDeployQt>
+ CD "$(TargetDir)"
+ "$(QtDir)\bin\windeployqt.exe" --no-translations --no-compiler-runtime qmlapp.exe &gt; NUL
+ </RunWinDeployQt>
+ </PropertyGroup>
+ <Exec Command="$(RunWinDeployQt)" />
+
+ <PropertyGroup>
+ <AppHostSourcePath>$(TargetDir)qmlapp.exe</AppHostSourcePath>
+ </PropertyGroup>
+ </Target>
+
+ <Target Name="CleanNative" AfterTargets="Clean">
+ <PropertyGroup>
+ <CleanNative>
+ CD "$(TargetDir)" &amp;&amp; RD /S /Q "$(TargetDir)" &gt; NUL 2&gt;&amp;1
+ </CleanNative>
+ </PropertyGroup>
+ <Exec Command="$(CleanNative)" />
+ </Target>
+</Project>
diff --git a/tests/FooConsoleApp/FooConsoleApp.csproj b/tests/FooConsoleApp/FooConsoleApp.csproj
new file mode 100644
index 0000000..77f8870
--- /dev/null
+++ b/tests/FooConsoleApp/FooConsoleApp.csproj
@@ -0,0 +1,14 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <OutputType>Exe</OutputType>
+ <TargetFramework>net8.0</TargetFramework>
+ <ImplicitUsings>enable</ImplicitUsings>
+ <Nullable>disable</Nullable>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\..\src\Qt.DotNet.Adapter\Qt.DotNet.Adapter.csproj" />
+ </ItemGroup>
+
+</Project>
diff --git a/tests/FooConsoleApp/Program.cs b/tests/FooConsoleApp/Program.cs
new file mode 100644
index 0000000..944f95f
--- /dev/null
+++ b/tests/FooConsoleApp/Program.cs
@@ -0,0 +1,23 @@
+/***************************************************************************************************
+ Copyright (C) 2024 The Qt Company Ltd.
+ SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+***************************************************************************************************/
+
+namespace FooConsoleApp
+{
+ public static class Program
+ {
+ public static bool KeepRunning { get; set; } = true;
+ public static int Main(string[] args)
+ {
+ Console.WriteLine("FooConsoleApp: started");
+ Thread.Sleep(1000);
+ while (KeepRunning) {
+ Console.WriteLine("FooConsoleApp: running");
+ Thread.Sleep(1000);
+ }
+ Console.WriteLine("FooConsoleApp: stopped");
+ return 0;
+ }
+ }
+}
diff --git a/tests/FooLib/FooClass.cs b/tests/FooLib/FooClass.cs
index 5182192..f4bd64f 100644
--- a/tests/FooLib/FooClass.cs
+++ b/tests/FooLib/FooClass.cs
@@ -4,26 +4,35 @@
***************************************************************************************************/
using System.ComponentModel;
+using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+using Qt.DotNet;
+
namespace FooLib
{
public interface IBarTransformation
{
string Transform(string bar);
+ Uri GetUri(int n);
+ void SetUri(Uri uri);
+ int GetNumber();
}
public class BarIdentity : IBarTransformation
{
public string Transform(string bar) => bar;
+ public Uri GetUri(int n) => null;
+ public void SetUri(Uri uri) { }
+ public int GetNumber() => 0;
}
public class Foo : INotifyPropertyChanged
{
public Foo(IBarTransformation barTransformation)
{
- BarTransformation = barTransformation ?? new BarIdentity();
+ BarTransformation = barTransformation;
}
public Foo() : this(null)
@@ -44,7 +53,14 @@ namespace FooLib
get => bar;
set
{
- bar = BarTransformation?.Transform(value) ?? value;
+ bar = value;
+ if (BarTransformation is IBarTransformation) {
+ bar = BarTransformation.Transform(value) ?? bar;
+ BarTransformation.SetUri(new("https://qt.io/developers"));
+ var n = BarTransformation.GetNumber();
+ if (BarTransformation.GetUri(n) is { } uri)
+ bar += $" ({uri})";
+ }
NotifyPropertyChanged();
}
}
@@ -83,5 +99,78 @@ namespace FooLib
{
return string.Format(format, date.Year, date.Month, date.Day);
}
+
+ public static void VariantStringToUpper(IQVariant v)
+ {
+ v.SetValue(v.ToStringValue().ToUpper());
+ }
+
+ public static IQVariant GetVariant()
+ {
+ return Adapter.Static.QVariant_Create();
+ }
+
+ public static IQVariant GetVariant(string value)
+ {
+ return Adapter.Static.QVariant_Create(value);
+ }
+
+ public static IQModelIndex GetModelIndex()
+ {
+ return Adapter.Static.QModelIndex_Create();
+ }
+
+ public static int ModelIndexRowColPtr(IQModelIndex idx)
+ {
+ return idx.Row() * idx.Column() * (int)idx.InternalPointer();
+ }
+
+ public static void InitModel()
+ {
+ if (Model is null)
+ Model = new TestListModel();
+ }
+
+ public static void CleanupModel()
+ {
+ if (Model is null)
+ return;
+ Model = null;
+ GC.Collect();
+ GC.WaitForPendingFinalizers();
+ }
+
+ public static QAbstractListModel Model { get; private set; }
+
+ public class TestListModel : QAbstractListModel
+ {
+ public override int Flags(IQModelIndex index = null)
+ {
+ if (index == null)
+ return base.Flags(index);
+ return (int)( ItemFlag.ItemIsSelectable
+ | ItemFlag.ItemIsEnabled
+ | ItemFlag.ItemNeverHasChildren);
+ }
+
+ public override int RowCount(IQModelIndex parent = null)
+ {
+ return 2;
+ }
+
+ public override IQVariant Data(IQModelIndex index, int role = 0)
+ {
+ switch (index.Row()) {
+ case 0:
+ return Adapter.Static.QVariant_Create("FOO");
+ default:
+ return Adapter.Static.QVariant_Create("BAR");
+ }
+ }
+ }
+
+ public delegate int FooFunc(int x);
+
+ public static FooFunc Plus42 { get; } = new FooFunc(x => x + 42);
}
}
diff --git a/tests/FooLib/FooLib.csproj b/tests/FooLib/FooLib.csproj
index 2b8cb94..1759ee7 100644
--- a/tests/FooLib/FooLib.csproj
+++ b/tests/FooLib/FooLib.csproj
@@ -8,9 +8,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>net6.0</TargetFramework>
+ <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>disable</Nullable>
</PropertyGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\..\src\Qt.DotNet.Adapter\Qt.DotNet.Adapter.csproj" />
+ </ItemGroup>
+
</Project>
diff --git a/tests/Perf_Qt.DotNet.Adapter/Perf_Qt.DotNet.Adapter.csproj b/tests/Perf_Qt.DotNet.Adapter/Perf_Qt.DotNet.Adapter.csproj
index f49a9b8..187520b 100644
--- a/tests/Perf_Qt.DotNet.Adapter/Perf_Qt.DotNet.Adapter.csproj
+++ b/tests/Perf_Qt.DotNet.Adapter/Perf_Qt.DotNet.Adapter.csproj
@@ -8,7 +8,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
- <TargetFramework>net6.0</TargetFramework>
+ <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Configurations>Debug;Release;Tests</Configurations>
diff --git a/tests/Test_Qt.DotNet.Adapter/Test_Qt.DotNet.Adapter.csproj b/tests/Test_Qt.DotNet.Adapter/Test_Qt.DotNet.Adapter.csproj
index 37742b4..3fc80bc 100644
--- a/tests/Test_Qt.DotNet.Adapter/Test_Qt.DotNet.Adapter.csproj
+++ b/tests/Test_Qt.DotNet.Adapter/Test_Qt.DotNet.Adapter.csproj
@@ -7,7 +7,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>net6.0</TargetFramework>
+ <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>disable</Nullable>
<IsPackable>false</IsPackable>
diff --git a/tests/tst_qtdotnet/foo.cpp b/tests/tst_qtdotnet/foo.cpp
index 8c8c3d3..789ec1b 100644
--- a/tests/tst_qtdotnet/foo.cpp
+++ b/tests/tst_qtdotnet/foo.cpp
@@ -7,7 +7,7 @@
#include <qdotnetevent.h>
-struct FooPrivate final : QDotNetObject::IEventHandler
+struct FooPrivate final : QDotNetEventHandler
{
Foo *q;
FooPrivate(Foo *q) : q(q) {}
@@ -17,12 +17,16 @@ struct FooPrivate final : QDotNetObject::IEventHandler
QDotNetFunction<QString> bar;
QDotNetFunction<void, QString> setBar;
+ QDotNetType typePropertyEvent = nullptr;
+
void handleEvent(const QString &eventName, QDotNetObject &sender, QDotNetObject &args) override
{
if (eventName != "PropertyChanged")
return;
- if (args.type().fullName() != QDotNetPropertyEvent::FullyQualifiedTypeName)
+ if (!typePropertyEvent.isValid())
+ typePropertyEvent = QDotNetType::typeOf<QDotNetPropertyEvent>();
+ if (!args.type().equals(typePropertyEvent))
return;
const auto propertyChangedEvent = args.cast<QDotNetPropertyEvent>();
@@ -37,13 +41,13 @@ Foo::Foo() : d(new FooPrivate(this))
{
const auto ctor = constructor<Foo, Null<IBarTransformation>>();
*this = ctor(nullptr);
- subscribeEvent("PropertyChanged", d);
+ subscribe("PropertyChanged", d);
}
Foo::Foo(const IBarTransformation &transformation) : d(new FooPrivate(this))
{
*this = constructor(d->ctor).invoke(*this, transformation);
- subscribeEvent("PropertyChanged", d);
+ subscribe("PropertyChanged", d);
}
Foo::~Foo()
@@ -61,10 +65,28 @@ void Foo::setBar(const QString &value)
method("set_Bar", d->setBar).invoke(*this, value);
}
-IBarTransformation::IBarTransformation() : QDotNetInterface(FullyQualifiedTypeName)
+IBarTransformation::IBarTransformation() : QDotNetInterface(AssemblyQualifiedName, nullptr)
{
- setCallback<QString, QString>("Transform", { QDotNetParameter::String, UnmanagedType::LPWStr },
- [this](const QString &bar) {
+ setCallback<QString, QString>("Transform",
+ [this](void *, const QString &bar) {
return transform(bar);
});
+
+ setCallback<Uri, int>("GetUri",
+ [this](void *, int n)
+ {
+ return getUri(n);
+ });
+
+ setCallback<void, Uri>("SetUri",
+ [this](void *, Uri uri)
+ {
+ setUri(uri);
+ });
+
+ setCallback<int>("GetNumber",
+ [this](void *)
+ {
+ return getNumber();
+ });
}
diff --git a/tests/tst_qtdotnet/foo.h b/tests/tst_qtdotnet/foo.h
index 1c8f9fc..6510c31 100644
--- a/tests/tst_qtdotnet/foo.h
+++ b/tests/tst_qtdotnet/foo.h
@@ -8,6 +8,8 @@
#include <qdotnetinterface.h>
#include <qdotnetobject.h>
+#include "uri.h"
+
#ifdef __GNUC__
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wconversion"
@@ -23,10 +25,13 @@ struct FooPrivate;
class IBarTransformation : public QDotNetInterface
{
public:
- static inline const QString &FullyQualifiedTypeName =
+ static inline const QString &AssemblyQualifiedName =
QStringLiteral("FooLib.IBarTransformation, FooLib");
virtual QString transform(const QString &) = 0;
+ virtual Uri getUri(int) = 0;
+ virtual void setUri(const Uri &uri) = 0;
+ virtual int getNumber() = 0;
protected:
IBarTransformation();
diff --git a/tests/tst_qtdotnet/tst_qtdotnet.cpp b/tests/tst_qtdotnet/tst_qtdotnet.cpp
index 92dbe85..65fdb30 100644
--- a/tests/tst_qtdotnet/tst_qtdotnet.cpp
+++ b/tests/tst_qtdotnet/tst_qtdotnet.cpp
@@ -15,6 +15,13 @@
#include <qdotnetobject.h>
#include <qdotnetsafemethod.h>
#include <qdotnettype.h>
+#include <qdotnetstatic.h>
+#include <qdotnetdelegate.h>
+
+#include <iqvariant.h>
+#include <iqmodelindex.h>
+
+#include <qdotnetabstractlistmodel.h>
#ifdef __GNUC__
# pragma GCC diagnostic push
@@ -29,12 +36,30 @@
#include <QObject>
#include <QSignalSpy>
#include <QString>
+#include <QStringListModel>
+#include <QThread>
#include <QtTest>
#ifdef __GNUC__
# pragma GCC diagnostic pop
#endif
+#define TESTCASE_DOTNET_MAIN
+//#define TESTCASE_QT_MAIN
+
+#if defined(TESTCASE_DOTNET_MAIN)
+ #define TEST_APP_STARTUP
+ #define TEST_FUNCTION_CALLS
+ #define TEST_APP_SHUTDOWN
+ #define TEST_HOST_UNLOAD
+#elif defined(TESTCASE_QT_MAIN)
+ #define TEST_HOST_LOAD
+ #define TEST_FUNCTION_CALLS
+ #define TEST_HOST_UNLOAD
+#endif
+
+//#define COREHOST_TRACE
+
class tst_qtdotnet : public QObject
{
Q_OBJECT
@@ -42,31 +67,123 @@ class tst_qtdotnet : public QObject
public:
tst_qtdotnet() = default;
+private:
+ int refCount = 0;
+ bool skipCleanup = false;
+
private slots:
+
+ void initTestCase()
+ {
+#ifdef COREHOST_TRACE
+ qputenv("COREHOST_TRACE", "1");
+#endif
+ }
+
+ void init()
+ {
+ if (!QDotNetAdapter::instance().isValid())
+ return;
+ refCount = QDotNetAdapter::instance().stats().refCount;
+ }
+
+ void cleanup()
+ {
+ if (skipCleanup) {
+ skipCleanup = false;
+ QSKIP("cleanup skipped");
+ }
+ if (!QDotNetAdapter::instance().isValid())
+ return;
+ QVERIFY(QDotNetAdapter::instance().stats().refCount == refCount);
+ }
+
+#ifdef TEST_APP_STARTUP
+ void appStartup();
+#endif //TEST_APP_STARTUP
+#ifdef TEST_HOST_LOAD
void loadHost();
void runtimeProperties();
+#endif //TEST_HOST_LOAD
+#ifdef TEST_FUNCTION_CALLS
void resolveFunction();
void callFunction();
void callFunctionWithCustomMarshaling();
void callDefaultEntryPoint();
void callWithComplexArg();
+#endif //TEST_FUNCTION_CALLS
+#ifdef TEST_HOST_LOAD
void adapterInit();
+#endif //TEST_HOST_LOAD
+#ifdef TEST_FUNCTION_CALLS
void callStaticMethod();
void handleException();
void createObject();
void callInstanceMethod();
- void useWrapperClass();
+ void useWrapperClassForStringBuilder();
+ void useWrapperClassForUri();
void emitSignalFromEvent();
void propertyBinding();
void implementInterface();
void arrayOfInts();
void arrayOfStrings();
void arrayOfObjects();
+ void variantNull();
+ void variantGet();
+ void variantSet();
+ void modelIndexNull();
+ void modelIndexGet();
+ void models();
+ void delegates();
+#endif //TEST_FUNCTION_CALLS
+#ifdef TEST_APP_SHUTDOWN
+ void appShutdown();
+#endif //TEST_APP_SHUTDOWN
+#ifdef TEST_HOST_UNLOAD
void unloadHost();
+#endif //TEST_HOST_UNLOAD
};
QDotNetHost dotNetHost;
+QThread *dotnetThread = nullptr;
+
+#ifdef TEST_APP_STARTUP
+
+void tst_qtdotnet::appStartup()
+{
+ dotnetThread = QThread::create(
+ [this]()
+ {
+ dotNetHost.loadApp(
+ QDir(QCoreApplication::applicationDirPath()).filePath("FooConsoleApp.dll"));
+ dotNetHost.runApp();
+ });
+ dotnetThread->start();
+ bool block = true;
+ while (!dotNetHost.isReady())
+ QThread::sleep(1);
+ QDotNetAdapter::instance().init(
+ QDir(QCoreApplication::applicationDirPath()).filePath("Qt.DotNet.Adapter.dll"),
+ "Qt.DotNet.Adapter", "Qt.DotNet.Adapter", &dotNetHost);
+}
+#endif //TEST_APP_STARTUP
+#ifdef TEST_APP_STARTUP
+void tst_qtdotnet::appShutdown()
+{
+ if (!dotnetThread)
+ QSKIP("App thread not running");
+
+ QtDotNet::call<void, bool>("FooConsoleApp.Program, FooConsoleApp", "set_KeepRunning", false);
+ QThread::sleep(1);
+ while (dotnetThread->isRunning()) {
+ qInfo() << "App thread still running...";
+ QThread::sleep(1);
+ }
+}
+#endif //TEST_APP_STARTUP
+
+#ifdef TEST_HOST_LOAD
void tst_qtdotnet::loadHost()
{
QVERIFY(!dotNetHost.isLoaded());
@@ -85,6 +202,17 @@ void tst_qtdotnet::runtimeProperties()
}
}
+void tst_qtdotnet::adapterInit()
+{
+ QVERIFY(!QDotNetAdapter::instance().isValid());
+ QDotNetAdapter::instance().init(
+ QDir(QCoreApplication::applicationDirPath()).filePath("Qt.DotNet.Adapter.dll"),
+ "Qt.DotNet.Adapter", "Qt.DotNet.Adapter", &dotNetHost);
+ QVERIFY(QDotNetAdapter::instance().isValid());
+}
+#endif //TEST_HOST_LOAD
+
+#ifdef TEST_FUNCTION_CALLS
QDotNetFunction<QString, QString, int> formatNumber;
void tst_qtdotnet::resolveFunction()
@@ -93,7 +221,7 @@ void tst_qtdotnet::resolveFunction()
QVERIFY(!formatNumber.isValid());
QVERIFY(dotNetHost.resolveFunction(formatNumber,
QDir(QCoreApplication::applicationDirPath()).filePath("FooLib.dll"),
- Foo::FullyQualifiedTypeName, "FormatNumber", "FooLib.Foo+FormatNumberDelegate, FooLib"));
+ Foo::AssemblyQualifiedName, "FormatNumber", "FooLib.Foo+FormatNumberDelegate, FooLib"));
QVERIFY(formatNumber.isValid());
}
@@ -149,7 +277,7 @@ void tst_qtdotnet::callFunctionWithCustomMarshaling()
QDotNetFunction<QUpperCaseString, QString, DoubleAsInt> formatDouble;
QVERIFY(dotNetHost.resolveFunction(formatDouble,
QDir(QCoreApplication::applicationDirPath()).filePath("FooLib.dll"),
- Foo::FullyQualifiedTypeName, "FormatNumber", "FooLib.Foo+FormatNumberDelegate, FooLib"));
+ Foo::AssemblyQualifiedName, "FormatNumber", "FooLib.Foo+FormatNumberDelegate, FooLib"));
QVERIFY(formatDouble.isValid());
@@ -165,7 +293,7 @@ void tst_qtdotnet::callDefaultEntryPoint()
QDotNetFunction<quint32, void*, qint32> entryPoint;
QVERIFY(dotNetHost.resolveFunction(entryPoint,
QDir(QCoreApplication::applicationDirPath()).filePath("FooLib.dll"),
- Foo::FullyQualifiedTypeName, "EntryPoint"));
+ Foo::AssemblyQualifiedName, "EntryPoint"));
QVERIFY(entryPoint.isValid());
@@ -206,7 +334,7 @@ void tst_qtdotnet::callWithComplexArg()
QDotNetFunction<QString, QString, Date> formatDate;
QVERIFY(dotNetHost.resolveFunction(formatDate,
QDir(QCoreApplication::applicationDirPath()).filePath("FooLib.dll"),
- Foo::FullyQualifiedTypeName, "FormatDate", "FooLib.Foo+FormatDateDelegate, FooLib"));
+ Foo::AssemblyQualifiedName, "FormatDate", "FooLib.Foo+FormatDateDelegate, FooLib"));
QVERIFY(formatDate.isValid());
@@ -216,105 +344,77 @@ void tst_qtdotnet::callWithComplexArg()
QCOMPARE(formattedText, "Today is 2022-12-25");
}
-void tst_qtdotnet::adapterInit()
-{
- QVERIFY(!QDotNetAdapter::instance().isValid());
- QDotNetAdapter::instance().init(
- QDir(QCoreApplication::applicationDirPath()).filePath("Qt.DotNet.Adapter.dll"),
- "Qt.DotNet.Adapter", "Qt.DotNet.Adapter", &dotNetHost);
- QVERIFY(QDotNetAdapter::instance().isValid());
-}
-
void tst_qtdotnet::callStaticMethod()
{
- QVERIFY(QDotNetAdapter::instance().stats().refCount == 0);
- {
- const QDotNetType environment = QDotNetType::find("System.Environment");
- const auto getEnvironmentVariable
- = environment.staticMethod<QString, QString>("GetEnvironmentVariable");
- const QString path = getEnvironmentVariable("PATH");
- QVERIFY(path.length() > 0);
- const QString samePath = QtDotNet::call<QString, QString>(
- "System.Environment", "GetEnvironmentVariable", "PATH");
- QVERIFY(path == samePath);
- }
- QVERIFY(QDotNetAdapter::instance().stats().refCount == 0);
+ const QDotNetType environment = QDotNetType::typeOf("System.Environment");
+ const auto getEnvironmentVariable
+ = environment.staticMethod<QString, QString>("GetEnvironmentVariable");
+ const QString path = getEnvironmentVariable("PATH");
+ QVERIFY(path.length() > 0);
+ const QString samePath = QtDotNet::call<QString, QString>(
+ "System.Environment", "GetEnvironmentVariable", "PATH");
+ QVERIFY(path == samePath);
}
void tst_qtdotnet::createObject()
{
- QVERIFY(QDotNetAdapter::instance().stats().refCount == 0);
- {
- const auto newStringBuilder = QDotNetObject::constructor("System.Text.StringBuilder");
- QDotNetObject stringBuilder = newStringBuilder();
- QVERIFY(QDotNetAdapter::instance().stats().refCount == 1);
- }
- QVERIFY(QDotNetAdapter::instance().stats().refCount == 0);
+ const auto newStringBuilder = QDotNetObject::constructor("System.Text.StringBuilder");
+ QDotNetObject stringBuilder = newStringBuilder();
+ QVERIFY(QDotNetAdapter::instance().stats().refCount == 1);
}
void tst_qtdotnet::callInstanceMethod()
{
- QVERIFY(QDotNetAdapter::instance().stats().refCount == 0);
- {
- const auto newStringBuilder = QDotNetObject::constructor("System.Text.StringBuilder");
- const auto stringBuilder = newStringBuilder();
- const auto append = stringBuilder.method<QDotNetObject, QString>("Append");
- std::ignore = append("Hello");
- std::ignore = append(" World!");
- const QString helloWorld = stringBuilder.toString();
- QVERIFY(helloWorld == "Hello World!");
- }
- QVERIFY(QDotNetAdapter::instance().stats().refCount == 0);
+ const auto newStringBuilder = QDotNetObject::constructor("System.Text.StringBuilder");
+ const auto stringBuilder = newStringBuilder();
+ const auto append = stringBuilder.method<QDotNetObject, QString>("Append");
+ std::ignore = append("Hello");
+ std::ignore = append(" World!");
+ const QString helloWorld = stringBuilder.toString();
+ QVERIFY(helloWorld == "Hello World!");
}
-void tst_qtdotnet::useWrapperClass()
+void tst_qtdotnet::useWrapperClassForStringBuilder()
{
- QVERIFY(QDotNetAdapter::instance().stats().refCount == 0);
- {
- StringBuilder sb;
- QVERIFY(QDotNetAdapter::instance().stats().refCount == 1);
- QVERIFY(sb.isValid());
- sb.append("Hello").append(" ");
- StringBuilder sbCpy(sb);
- QVERIFY(QDotNetAdapter::instance().stats().refCount == 2);
- QVERIFY(sbCpy.isValid());
- sbCpy.append("World");
- sb = StringBuilder(std::move(sbCpy));
- QVERIFY(QDotNetAdapter::instance().stats().refCount == 1);
- sb.append("!");
- QCOMPARE(sb.toString(), "Hello World!");
- }
- QVERIFY(QDotNetAdapter::instance().stats().refCount == 0);
- {
- const Uri uri(QStringLiteral(
- "https://user:[email protected]:80/Home/Index.htm?q1=v1&q2=v2#FragmentName"));
- QVERIFY(uri.segments().length() == 3);
- QVERIFY(uri.segments()[0]->compare("/") == 0);
- }
- QVERIFY(QDotNetAdapter::instance().stats().refCount == 0);
+ StringBuilder sb;
+ QVERIFY(QDotNetAdapter::instance().stats().refCount == 1);
+ QVERIFY(sb.isValid());
+ sb.append("Hello").append(" ");
+ StringBuilder sbCpy(sb);
+ QVERIFY(QDotNetAdapter::instance().stats().refCount == 2);
+ QVERIFY(sbCpy.isValid());
+ sbCpy.append("World");
+ sb = StringBuilder(std::move(sbCpy));
+ QVERIFY(QDotNetAdapter::instance().stats().refCount == 1);
+ sb.append("!");
+ QCOMPARE(sb.toString(), "Hello World!");
+}
+
+void tst_qtdotnet::useWrapperClassForUri()
+{
+ const Uri uri(QStringLiteral(
+ "https://user:[email protected]:80/Home/Index.htm?q1=v1&q2=v2#FragmentName"));
+ QVERIFY(uri.segments().length() == 3);
+ QVERIFY(uri.segments()[0]->compare("/") == 0);
}
void tst_qtdotnet::handleException()
{
- QVERIFY(QDotNetAdapter::instance().stats().refCount == 0);
- {
- StringBuilder stringBuilder(5, 5);
- QString helloWorld;
- try {
- stringBuilder.append("Hello");
- QVERIFY(stringBuilder.toString() == "Hello");
- stringBuilder.append(" World!");
- helloWorld = stringBuilder.toString();
- }
- catch (QDotNetException&) {
- helloWorld = "<ERROR>";
- }
- QVERIFY(helloWorld == "<ERROR>");
+ StringBuilder stringBuilder(5, 5);
+ QString helloWorld;
+ try {
+ stringBuilder.append("Hello");
+ QVERIFY(stringBuilder.toString() == "Hello");
+ stringBuilder.append(" World!");
+ helloWorld = stringBuilder.toString();
}
- QVERIFY(QDotNetAdapter::instance().stats().refCount == 0);
+ catch (const QDotNetException &ex) {
+ helloWorld = ex.type().cast<QDotNetType>().fullName();
+ }
+ QVERIFY(helloWorld == "System.ArgumentOutOfRangeException");
}
-class Ping final : public QObject, public QDotNetObject, public QDotNetObject::IEventHandler
+class Ping final : public QObject, public QDotNetObject, public QDotNetEventHandler
{
Q_OBJECT
@@ -324,7 +424,7 @@ public:
Ping()
: QDotNetObject(QDotNetSafeMethod(constructor<Ping>()).invoke(nullptr))
{
- subscribeEvent("PingCompleted", this);
+ subscribe("PingCompleted", this);
}
~Ping() override = default;
@@ -366,135 +466,210 @@ private:
void tst_qtdotnet::emitSignalFromEvent()
{
- QVERIFY(QDotNetAdapter::instance().stats().refCount == 0);
- {
- Ping ping;
- bool waiting = true;
- int signalCount = 0;
- connect(&ping, &Ping::pingCompleted,
- [&waiting, &signalCount](const QString& address, qint64 roundtripMsecs) {
- qInfo() << "Reply from" << address << "in" << roundtripMsecs << "msecs";
- signalCount++;
- waiting = false;
- });
- connect(&ping, &Ping::pingError,
- [&waiting, &signalCount] {
- qInfo() << "Ping error";
- signalCount++;
- waiting = false;
- });
- qInfo() << "Pinging www.qt.io:";
- QElapsedTimer waitTime;
- for (int i = 0; i < 4; ++i) {
- waitTime.restart();
- waiting = true;
- ping.sendAsync("www.qt.io");
- while (waiting) {
- QCoreApplication::processEvents();
- if (waitTime.elapsed() > 3000) {
- ping.sendAsyncCancel();
- waiting = false;
- qInfo() << "Ping timeout";
- }
+ Ping ping;
+ bool waiting = true;
+ int signalCount = 0;
+ connect(&ping, &Ping::pingCompleted,
+ [&waiting, &signalCount](const QString& address, qint64 roundtripMsecs) {
+ qInfo() << "Reply from" << address << "in" << roundtripMsecs << "msecs";
+ signalCount++;
+ waiting = false;
+ });
+ connect(&ping, &Ping::pingError,
+ [&waiting, &signalCount] {
+ qInfo() << "Ping error";
+ signalCount++;
+ waiting = false;
+ });
+ qInfo() << "Pinging www.qt.io:";
+ QElapsedTimer waitTime;
+ for (int i = 0; i < 4; ++i) {
+ waitTime.restart();
+ waiting = true;
+ ping.sendAsync("www.qt.io");
+ while (waiting) {
+ QCoreApplication::processEvents();
+ if (waitTime.elapsed() > 3000) {
+ ping.sendAsyncCancel();
+ waiting = false;
+ qInfo() << "Ping timeout";
}
}
- QVERIFY(signalCount == 4);
}
- QVERIFY(QDotNetAdapter::instance().stats().refCount == 0);
+ QVERIFY(signalCount == 4);
}
void tst_qtdotnet::propertyBinding()
{
- QVERIFY(QDotNetAdapter::instance().stats().refCount == 0);
- {
- Foo foo;
- const QSignalSpy spy(&foo, &Foo::barChanged);
- for (int i = 0; i < 1000; ++i)
- foo.setBar(QString("hello x %1").arg(i + 1));
- QVERIFY(foo.bar() == "hello x 1000");
- QVERIFY(spy.count() == 1000);
- }
- QVERIFY(QDotNetAdapter::instance().stats().refCount == 0);
+ Foo foo;
+ const QSignalSpy spy(&foo, &Foo::barChanged);
+ for (int i = 0; i < 1000; ++i)
+ foo.setBar(QString("hello x %1").arg(i + 1));
+ QVERIFY(foo.bar() == "hello x 1000");
+ QVERIFY(spy.count() == 1000);
}
struct ToUpper : IBarTransformation
{
+ Uri uri = Uri("https://qt.io/");
QString transform(const QString& bar) override
{
return bar.toUpper();
}
+ Uri getUri(int n) override
+ {
+ return uri;
+ }
+ void setUri(const Uri &uri) override
+ {
+ this->uri = uri;
+ }
+ int getNumber() override
+ {
+ return 42;
+ }
};
void tst_qtdotnet::implementInterface()
{
- QVERIFY(QDotNetAdapter::instance().stats().refCount == 0);
- {
- const ToUpper transfToUpper;
- Foo foo(transfToUpper);
- const QSignalSpy spy(&foo, &Foo::barChanged);
- for (int i = 0; i < 1000; ++i)
- foo.setBar(QString("hello x %1").arg(i + 1));
- QVERIFY(foo.bar() == "HELLO X 1000");
- QVERIFY(spy.count() == 1000);
- }
- QVERIFY(QDotNetAdapter::instance().stats().refCount == 0);
+ const ToUpper transfToUpper;
+ Foo foo(transfToUpper);
+ foo.setBar("hello there");
+ QVERIFY(foo.bar() == "HELLO THERE (https://qt.io/developers)");
}
void tst_qtdotnet::arrayOfInts()
{
- QVERIFY(QDotNetAdapter::instance().stats().refCount == 0);
- {
- QDotNetArray<qint32> a(11);
- a[0] = 0;
- a[1] = 1;
- for (int i = 2; i < a.length(); ++i)
- a[i] = a[i - 1] + a[i - 2];
- QVERIFY(a[10] == 55);
- }
- QVERIFY(QDotNetAdapter::instance().stats().refCount == 0);
+ QDotNetArray<qint32> a(11);
+ a[0] = 0;
+ a[1] = 1;
+ for (int i = 2; i < a.length(); ++i)
+ a[i] = a[i - 1] + a[i - 2];
+ QVERIFY(a[10] == 55);
}
void tst_qtdotnet::arrayOfStrings()
{
- QVERIFY(QDotNetAdapter::instance().stats().refCount == 0);
+ QDotNetArray<QString> a(8);
+ a[0] = "Lorem";
+ a[1] = "ipsum";
+ a[2] = "dolor";
+ a[3] = "sit";
+ a[4] = "amet,";
+ a[5] = "consectetur";
+ a[6] = "adipiscing";
+ a[7] = "elit.";
+ const auto stringType = QDotNetType::typeOf("System.String");
+ const auto join = stringType.staticMethod<QString, QString, QDotNetArray<QString>>("Join");
+ const auto loremIpsum = join(" ", a);
+ QVERIFY(loremIpsum == "Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
+}
+
+void tst_qtdotnet::arrayOfObjects()
+{
+ QDotNetArray<StringBuilder> a(8);
+ for (int i = 0; i < a.length(); ++i)
+ a[i] = StringBuilder();
+ a[0]->append("Lorem");
+ a[1]->append(a[0]->toString()).append(" ipsum");
+ a[2]->append(a[1]->toString()).append(" dolor");
+ a[3]->append(a[2]->toString()).append(" sit");
+ a[4]->append(a[3]->toString()).append(" amet,");
+ a[5]->append(a[4]->toString()).append(" consectetur");
+ a[6]->append(a[5]->toString()).append(" adipiscing");
+ a[7]->append(a[6]->toString()).append(" elit.");
+ QVERIFY(a[7]->toString() == "Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
+}
+
+void tst_qtdotnet::variantNull()
+{
+ auto getVariant = QDotNetType::staticMethod<IQVariant>("FooLib.Foo, FooLib", "GetVariant");
+ auto iqv = getVariant();
+ auto &qv = *iqv.dataAs<QVariant>();
+ QVERIFY(!qv.isValid());
+}
+
+void tst_qtdotnet::variantGet()
+{
+ auto getVariant = QDotNetType::staticMethod<IQVariant, QString>("FooLib.Foo, FooLib", "GetVariant");
+ auto iqv = getVariant("foobar");
+ auto &qv = *iqv.dataAs<QVariant>();
+ QVERIFY(qv.toString() == "foobar");
+}
+
+void tst_qtdotnet::variantSet()
+{
+ QVariant qv = "foobar";
+ IQVariant iqv(qv);
+ auto toUpper = QDotNetType::staticMethod<void, IQVariant>("FooLib.Foo, FooLib", "VariantStringToUpper");
+ toUpper(iqv);
+ QVERIFY(qv.toString() == "FOOBAR");
+}
+
+struct TestModel : public QStringListModel
+{
+ QModelIndex getIndex(int row, int col, void *ptr)
{
- QDotNetArray<QString> a(8);
- a[0] = "Lorem";
- a[1] = "ipsum";
- a[2] = "dolor";
- a[3] = "sit";
- a[4] = "amet,";
- a[5] = "consectetur";
- a[6] = "adipiscing";
- a[7] = "elit.";
- const auto stringType = QDotNetType::find("System.String");
- const auto join = stringType.staticMethod<QString, QString, QDotNetArray<QString>>("Join");
- const auto loremIpsum = join(" ", a);
- QVERIFY(loremIpsum == "Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
+ return createIndex(row, col, ptr);
}
- QVERIFY(QDotNetAdapter::instance().stats().refCount == 0);
+};
+
+void tst_qtdotnet::modelIndexNull()
+{
+ auto getModelIndex = QDotNetType::staticMethod<IQModelIndex>("FooLib.Foo, FooLib", "GetModelIndex");
+ auto iqmi = getModelIndex();
+ auto &qmi = *iqmi.dataAs<QModelIndex>();
+ QVERIFY(!qmi.isValid());
}
-void tst_qtdotnet::arrayOfObjects()
+void tst_qtdotnet::modelIndexGet()
{
- QVERIFY(QDotNetAdapter::instance().stats().refCount == 0);
+ TestModel tm;
+ auto idx = IQModelIndex(tm.getIndex(2, 3, reinterpret_cast<void *>(7)));
+ auto idxRowColPtr = QDotNetType::staticMethod<int, IQModelIndex>("FooLib.Foo, FooLib", "ModelIndexRowColPtr");
+ auto rcp = idxRowColPtr(idx);
+ QVERIFY(rcp == 42);
+}
+
+struct TestListModel : public QDotNetObject
+{
+ Q_DOTNET_OBJECT_INLINE(TestListModel, "FooLib.Foo+TestListModel, FooLib");
+ TestListModel()
+ : QDotNetObject(constructor<TestListModel>().invoke(nullptr))
+ { }
+ QAbstractListModel *base() const
{
- QDotNetArray<StringBuilder> a(8);
- for (int i = 0; i < a.length(); ++i)
- a[i] = StringBuilder();
- a[0]->append("Lorem");
- a[1]->append(a[0]->toString()).append(" ipsum");
- a[2]->append(a[1]->toString()).append(" dolor");
- a[3]->append(a[2]->toString()).append(" sit");
- a[4]->append(a[3]->toString()).append(" amet,");
- a[5]->append(a[4]->toString()).append(" consectetur");
- a[6]->append(a[5]->toString()).append(" adipiscing");
- a[7]->append(a[6]->toString()).append(" elit.");
- QVERIFY(a[7]->toString() == "Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
+ auto baseObj = method("get_Base", fnBase).invoke(*this);
+ auto baseInterface = baseObj.cast<QDotNetInterface>();
+ return baseInterface.dataAs<QAbstractListModel>();
}
- QVERIFY(QDotNetAdapter::instance().stats().refCount == 0);
+ mutable QDotNetFunction<QDotNetRef> fnBase = nullptr;
+};
+
+void tst_qtdotnet::models()
+{
+ const auto testModel = TestListModel();
+ auto *baseModel = testModel.base();
+ auto n = baseModel->rowCount();
+ QVERIFY(n == 2);
+ auto ff = baseModel->flags(baseModel->index(0));
+ QVERIFY(ff == (Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren));
+ auto it0 = baseModel->data(baseModel->index(0));
+ QVERIFY(it0.toString() == "FOO");
+ auto it1 = baseModel->data(baseModel->index(1));
+ QVERIFY(it1.toString() == "BAR");
+ skipCleanup = true; // TODO: figure out why refs are still pending here
+}
+
+void tst_qtdotnet::delegates()
+{
+ auto plus42 = QtDotNet::call<QDotNetDelegate<int, int>>("FooLib.Foo, FooLib", "get_Plus42");
+ QVERIFY(plus42(3) == 45);
}
+#endif //TEST_FUNCTION_CALLS
+
+#ifdef TEST_HOST_UNLOAD
void tst_qtdotnet::unloadHost()
{
QVERIFY(dotNetHost.isLoaded());
@@ -503,6 +678,7 @@ void tst_qtdotnet::unloadHost()
QVERIFY(!dotNetHost.isLoaded());
}
+#endif //TEST_HOST_UNLOAD
QTEST_MAIN(tst_qtdotnet)
#include "tst_qtdotnet.moc"
diff --git a/tests/tst_qtdotnet/tst_qtdotnet.vcxproj b/tests/tst_qtdotnet/tst_qtdotnet.vcxproj
index 1d0efa1..be822fe 100644
--- a/tests/tst_qtdotnet/tst_qtdotnet.vcxproj
+++ b/tests/tst_qtdotnet/tst_qtdotnet.vcxproj
@@ -30,7 +30,7 @@
<RootNamespace>tst_qtdotnet</RootNamespace>
<WindowsTargetPlatformVersion Condition="'$(VisualStudioVersion)'=='15.0'">10.0.17763.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion Condition="'$(VisualStudioVersion)'=='16.0'">10.0</WindowsTargetPlatformVersion>
- <WindowsTargetPlatformVersion Condition="'$(VisualStudioVersion)'=='17.0'">10.0</WindowsTargetPlatformVersion>
+ <WindowsTargetPlatformVersion Condition="'$(VisualStudioVersion)'=='17.0'">10.0.19041.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup>
@@ -205,6 +205,26 @@
<QtMoc Include="foo.h" />
</ItemGroup>
<ItemGroup>
+ <ClInclude Include="..\..\include\iqmodelindex.h" />
+ <ClInclude Include="..\..\include\iqvariant.h" />
+ <ClInclude Include="..\..\include\qdotnetabstractlistmodel.h" />
+ <ClInclude Include="..\..\include\qdotnetadapter.h" />
+ <ClInclude Include="..\..\include\qdotnetarray.h" />
+ <ClInclude Include="..\..\include\qdotnetcallback.h" />
+ <ClInclude Include="..\..\include\qdotnetdelegate.h" />
+ <ClInclude Include="..\..\include\qdotnetevent.h" />
+ <ClInclude Include="..\..\include\qdotnetexception.h" />
+ <ClInclude Include="..\..\include\qdotnetfunction.h" />
+ <ClInclude Include="..\..\include\qdotnethost.h" />
+ <ClInclude Include="..\..\include\qdotnethostfxr.h" />
+ <ClInclude Include="..\..\include\qdotnetinterface.h" />
+ <ClInclude Include="..\..\include\qdotnetmarshal.h" />
+ <ClInclude Include="..\..\include\qdotnetobject.h" />
+ <ClInclude Include="..\..\include\qdotnetparameter.h" />
+ <ClInclude Include="..\..\include\qdotnetref.h" />
+ <ClInclude Include="..\..\include\qdotnetsafemethod.h" />
+ <ClInclude Include="..\..\include\qdotnetstatic.h" />
+ <ClInclude Include="..\..\include\qdotnettype.h" />
<ClInclude Include="uri.h" />
<ClInclude Include="stringbuilder.h" />
</ItemGroup>
@@ -215,6 +235,9 @@
<ProjectReference Include="..\FooLib\FooLib.csproj">
<Project>{45d3ddf3-135b-46ca-b3ee-3537fcfffbeb}</Project>
</ProjectReference>
+ <ProjectReference Include="..\FooConsoleApp\FooConsoleApp.csproj">
+ <Project>{72ca8eda-c9cf-40c2-b7fe-4a529204eec2}</Project>
+ </ProjectReference>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Condition="Exists('$(QtMsBuild)\qt.targets')">
diff --git a/tests/tst_qtdotnet/tst_qtdotnet.vcxproj.filters b/tests/tst_qtdotnet/tst_qtdotnet.vcxproj.filters
index 4c9a2f3..aff5e85 100644
--- a/tests/tst_qtdotnet/tst_qtdotnet.vcxproj.filters
+++ b/tests/tst_qtdotnet/tst_qtdotnet.vcxproj.filters
@@ -21,6 +21,9 @@
<UniqueIdentifier>{639EADAA-A684-42e4-A9AD-28FC9BCB8F7C}</UniqueIdentifier>
<Extensions>ts</Extensions>
</Filter>
+ <Filter Include="qtdotnet">
+ <UniqueIdentifier>{3c233e4d-a97c-47c6-b8bb-5f97e38b5090}</UniqueIdentifier>
+ </Filter>
</ItemGroup>
<ItemGroup>
<QtMoc Include="tst_qtdotnet.cpp">
@@ -48,5 +51,65 @@
<ClInclude Include="uri.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="..\..\include\iqvariant.h">
+ <Filter>qtdotnet</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\qdotnetadapter.h">
+ <Filter>qtdotnet</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\qdotnetarray.h">
+ <Filter>qtdotnet</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\qdotnetcallback.h">
+ <Filter>qtdotnet</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\qdotnetevent.h">
+ <Filter>qtdotnet</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\qdotnetexception.h">
+ <Filter>qtdotnet</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\qdotnetfunction.h">
+ <Filter>qtdotnet</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\qdotnethost.h">
+ <Filter>qtdotnet</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\qdotnethostfxr.h">
+ <Filter>qtdotnet</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\qdotnetinterface.h">
+ <Filter>qtdotnet</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\qdotnetmarshal.h">
+ <Filter>qtdotnet</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\qdotnetobject.h">
+ <Filter>qtdotnet</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\qdotnetparameter.h">
+ <Filter>qtdotnet</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\qdotnetref.h">
+ <Filter>qtdotnet</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\qdotnetsafemethod.h">
+ <Filter>qtdotnet</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\qdotnettype.h">
+ <Filter>qtdotnet</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\iqmodelindex.h">
+ <Filter>qtdotnet</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\qdotnetabstractlistmodel.h">
+ <Filter>qtdotnet</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\qdotnetstatic.h">
+ <Filter>qtdotnet</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\qdotnetdelegate.h">
+ <Filter>qtdotnet</Filter>
+ </ClInclude>
</ItemGroup>
</Project> \ No newline at end of file