From 79bd19d6df714bf1eff011340a3181382e231c81 Mon Sep 17 00:00:00 2001 From: Paul Knopf Date: Fri, 20 Jul 2018 04:17:29 -0400 Subject: [PATCH] Added support for associated signals with properties. --- .../QtNetCoreQml/qml/NetValueMetaObject.cpp | 75 ++++++++----------- .../QtNetCoreQml/types/NetMethodInfo.cpp | 20 +++++ .../QtNetCoreQml/types/NetMethodInfo.h | 2 + .../QtNetCoreQml/types/NetPropertyInfo.cpp | 34 +++++++-- .../QtNetCoreQml/types/NetPropertyInfo.h | 5 +- .../QtNetCoreQml/types/NetSignalInfo.cpp | 21 ++++++ .../QtNetCoreQml/types/NetSignalInfo.h | 1 + .../Types/NetTypeManagerTests.cs | 56 +++++++++++++- .../Qt.NetCore/Internal/DefaultCallbacks.cs | 55 ++++++++++---- src/net/Qt.NetCore/NotifySignalAttribute.cs | 20 +++++ src/net/Qt.NetCore/Types/NetPropertyInfo.cs | 27 +++++-- 11 files changed, 245 insertions(+), 71 deletions(-) create mode 100644 src/net/Qt.NetCore/NotifySignalAttribute.cs diff --git a/src/native/QtNetCoreQml/QtNetCoreQml/qml/NetValueMetaObject.cpp b/src/native/QtNetCoreQml/QtNetCoreQml/qml/NetValueMetaObject.cpp index 37c02c22..886a13f9 100644 --- a/src/native/QtNetCoreQml/QtNetCoreQml/qml/NetValueMetaObject.cpp +++ b/src/native/QtNetCoreQml/QtNetCoreQml/qml/NetValueMetaObject.cpp @@ -199,54 +199,13 @@ QMetaObject *metaObjectFor(QSharedPointer typeInfo) for(uint index = 0; index <= typeInfo->getSignalCount() - 1; index++) { QSharedPointer signalInfo = typeInfo->getSignal(index); - QString signature = signalInfo->getName(); - - signature.append("("); - - if(signalInfo->getParameterCount() > 0) { - for(uint parameterIndex = 0; parameterIndex <= signalInfo->getParameterCount() - 1; parameterIndex++) - { - if(parameterIndex > 0) { - signature.append(", "); - } - signature.append("QVariant"); - } - } - - signature.append(")"); - - mob.addSignal(signature.toLocal8Bit().constData()); + QString signature = signalInfo->getSignature(); + mob.addSignal(QMetaObject::normalizedSignature(signature.toLocal8Bit().constData())); } } - if(typeInfo->getMethodCount() > 0) { - for(uint index = 0; index <= typeInfo->getMethodCount() - 1; index++) - { - QSharedPointer methodInfo = typeInfo->getMethodInfo(index); - QSharedPointer returnType = methodInfo->getReturnType(); - QString signature = methodInfo->getMethodName(); - - signature.append("("); - - if(methodInfo->getParameterCount() > 0) { - for(uint parameterIndex = 0; parameterIndex <= methodInfo->getParameterCount() - 1; parameterIndex++) - { - if(parameterIndex > 0) { - signature.append(", "); - } - signature.append("QVariant"); - } - } - - signature.append(")"); - - if(returnType != NULL) { - mob.addMethod(signature.toLocal8Bit().constData(), "QVariant"); - } else { - mob.addMethod(signature.toLocal8Bit().constData()); - } - } - } + // NOTE: It is important to register properties after the signals (before methods) + // because of the assumptions we make about getting the "notifySignal" by index. if(typeInfo->getPropertyCount() > 0) { for(uint index = 0; index <= typeInfo->getPropertyCount() - 1; index++) @@ -255,11 +214,37 @@ QMetaObject *metaObjectFor(QSharedPointer typeInfo) QMetaPropertyBuilder propb = mob.addProperty(propertyInfo->getPropertyName().toLatin1(), "QVariant", index); + QSharedPointer notifySignal = propertyInfo->getNotifySignal(); + if(notifySignal != NULL) { + // The signal was previously registered, find the index. + for(uint signalIndex = 0; signalIndex <= typeInfo->getSignalCount() - 1; signalIndex++) + { + if(typeInfo->getSignal(signalIndex) == notifySignal) { + QMetaMethodBuilder notifySignalBuilder = mob.method(signalIndex); + propb.setNotifySignal(notifySignalBuilder); + break; + } + } + } propb.setWritable(propertyInfo->canWrite()); propb.setReadable(propertyInfo->canRead()); } } + if(typeInfo->getMethodCount() > 0) { + for(uint index = 0; index <= typeInfo->getMethodCount() - 1; index++) + { + QSharedPointer methodInfo = typeInfo->getMethodInfo(index); + QSharedPointer returnType = methodInfo->getReturnType(); + QString signature = methodInfo->getSignature(); + if(returnType != NULL) { + mob.addMethod(QMetaObject::normalizedSignature(signature.toLocal8Bit().constData()), "QVariant"); + } else { + mob.addMethod(QMetaObject::normalizedSignature(signature.toLocal8Bit().constData())); + } + } + } + QMetaObject *mo = mob.toMetaObject(); typeInfo->metaObject = mo; return mo; diff --git a/src/native/QtNetCoreQml/QtNetCoreQml/types/NetMethodInfo.cpp b/src/native/QtNetCoreQml/QtNetCoreQml/types/NetMethodInfo.cpp index 3bd3df83..d8fcff3e 100644 --- a/src/native/QtNetCoreQml/QtNetCoreQml/types/NetMethodInfo.cpp +++ b/src/native/QtNetCoreQml/QtNetCoreQml/types/NetMethodInfo.cpp @@ -44,6 +44,26 @@ QSharedPointer NetMethodInfo::getParameter(uint index) { return _parameters.at(index); } +QString NetMethodInfo::getSignature() { + QString signature = _methodName; + + signature.append("("); + + if(_parameters.size() > 0) { + for(int parameterIndex = 0; parameterIndex <= _parameters.size() - 1; parameterIndex++) + { + if(parameterIndex > 0) { + signature.append(","); + } + signature.append("QVariant"); + } + } + + signature.append(")"); + + return signature; +} + extern "C" { Q_DECL_EXPORT void method_info_parameter_destroy(NetMethodInfoArguementContainer* container) { diff --git a/src/native/QtNetCoreQml/QtNetCoreQml/types/NetMethodInfo.h b/src/native/QtNetCoreQml/QtNetCoreQml/types/NetMethodInfo.h index c7acd362..cf5890a7 100644 --- a/src/native/QtNetCoreQml/QtNetCoreQml/types/NetMethodInfo.h +++ b/src/native/QtNetCoreQml/QtNetCoreQml/types/NetMethodInfo.h @@ -30,6 +30,8 @@ public: uint getParameterCount(); QSharedPointer getParameter(uint index); + QString getSignature(); + private: QSharedPointer _parentTypeInfo; QString _methodName; diff --git a/src/native/QtNetCoreQml/QtNetCoreQml/types/NetPropertyInfo.cpp b/src/native/QtNetCoreQml/QtNetCoreQml/types/NetPropertyInfo.cpp index 27ee35ed..da6f5907 100644 --- a/src/native/QtNetCoreQml/QtNetCoreQml/types/NetPropertyInfo.cpp +++ b/src/native/QtNetCoreQml/QtNetCoreQml/types/NetPropertyInfo.cpp @@ -1,15 +1,18 @@ #include +#include NetPropertyInfo::NetPropertyInfo(QSharedPointer parentType, QString name, QSharedPointer returnType, bool canRead, - bool canWrite) : + bool canWrite, + QSharedPointer notifySignal) : _parentType(parentType), _name(name), _returnType(returnType), _canRead(canRead), - _canWrite(canWrite) + _canWrite(canWrite), + _notifySignal(notifySignal) { } @@ -39,19 +42,30 @@ bool NetPropertyInfo::canWrite() return _canWrite; } +QSharedPointer NetPropertyInfo::getNotifySignal() +{ + return _notifySignal; +} + extern "C" { -Q_DECL_EXPORT NetPropertyInfoContainer* property_info_create(NetTypeInfoContainer* parentType, +Q_DECL_EXPORT NetPropertyInfoContainer* property_info_create(NetTypeInfoContainer* parentTypeContainer, LPWSTR name, NetTypeInfoContainer* returnType, bool canRead, - bool canWrite) { + bool canWrite, + NetSignalInfoContainer* notifySignalContainer) { NetPropertyInfoContainer* result = new NetPropertyInfoContainer(); - NetPropertyInfo* instance = new NetPropertyInfo(parentType->netTypeInfo, + QSharedPointer notifySignal; + if(notifySignalContainer != NULL) { + notifySignal = notifySignalContainer->signal; + } + NetPropertyInfo* instance = new NetPropertyInfo(parentTypeContainer->netTypeInfo, QString::fromUtf16((const char16_t*)name), returnType->netTypeInfo, canRead, - canWrite); + canWrite, + notifySignal); result->property = QSharedPointer(instance); return result; } @@ -85,4 +99,12 @@ Q_DECL_EXPORT bool property_info_canWrite(NetPropertyInfoContainer* container) { return container->property->canWrite(); } +Q_DECL_EXPORT NetSignalInfoContainer* property_info_getNotifySignal(NetPropertyInfoContainer* container) { + QSharedPointer notifySignal = container->property->getNotifySignal(); + if(notifySignal == NULL) { + return NULL; + } + return new NetSignalInfoContainer{notifySignal}; +} + } diff --git a/src/native/QtNetCoreQml/QtNetCoreQml/types/NetPropertyInfo.h b/src/native/QtNetCoreQml/QtNetCoreQml/types/NetPropertyInfo.h index 886dc3f1..76c936c8 100644 --- a/src/native/QtNetCoreQml/QtNetCoreQml/types/NetPropertyInfo.h +++ b/src/native/QtNetCoreQml/QtNetCoreQml/types/NetPropertyInfo.h @@ -9,18 +9,21 @@ public: QString name, QSharedPointer returnType, bool canRead, - bool canWrite); + bool canWrite, + QSharedPointer notifySignal); QSharedPointer getParentType(); QString getPropertyName(); QSharedPointer getReturnType(); bool canRead(); bool canWrite(); + QSharedPointer getNotifySignal(); private: QSharedPointer _parentType; QString _name; QSharedPointer _returnType; bool _canRead; bool _canWrite; + QSharedPointer _notifySignal; }; struct NetPropertyInfoContainer { diff --git a/src/native/QtNetCoreQml/QtNetCoreQml/types/NetSignalInfo.cpp b/src/native/QtNetCoreQml/QtNetCoreQml/types/NetSignalInfo.cpp index 855b0b7a..df57eaa0 100644 --- a/src/native/QtNetCoreQml/QtNetCoreQml/types/NetSignalInfo.cpp +++ b/src/native/QtNetCoreQml/QtNetCoreQml/types/NetSignalInfo.cpp @@ -28,6 +28,27 @@ NetVariantTypeEnum NetSignalInfo::getParameter(uint index) { return _parameters.at(index); } +QString NetSignalInfo::getSignature() { + QString signature = _name; + + signature.append("("); + + if(_parameters.size() > 0) { + for(int parameterIndex = 0; parameterIndex <= _parameters.size() - 1; parameterIndex++) + { + if(parameterIndex > 0) { + signature.append(","); + } + signature.append("QVariant"); + } + } + + signature.append(")"); + + return signature; + +} + extern "C" { Q_DECL_EXPORT NetSignalInfoContainer* signal_info_create(NetTypeInfoContainer* parentTypeContainer, LPWSTR name) { diff --git a/src/native/QtNetCoreQml/QtNetCoreQml/types/NetSignalInfo.h b/src/native/QtNetCoreQml/QtNetCoreQml/types/NetSignalInfo.h index 8e6972a3..4b3a5a1d 100644 --- a/src/native/QtNetCoreQml/QtNetCoreQml/types/NetSignalInfo.h +++ b/src/native/QtNetCoreQml/QtNetCoreQml/types/NetSignalInfo.h @@ -13,6 +13,7 @@ public: void addParameter(NetVariantTypeEnum type); uint getParameterCount(); NetVariantTypeEnum getParameter(uint index); + QString getSignature(); private: QSharedPointer _parentType; QString _name; diff --git a/src/net/Qt.NetCore.Tests/Types/NetTypeManagerTests.cs b/src/net/Qt.NetCore.Tests/Types/NetTypeManagerTests.cs index 05cd919a..8a818346 100644 --- a/src/net/Qt.NetCore.Tests/Types/NetTypeManagerTests.cs +++ b/src/net/Qt.NetCore.Tests/Types/NetTypeManagerTests.cs @@ -1,4 +1,5 @@ -using FluentAssertions; +using System; +using FluentAssertions; using Qt.NetCore.Types; using Xunit; @@ -128,6 +129,7 @@ namespace Qt.NetCore.Tests.Types property.CanWrite.Should().BeTrue(); property.ReturnType.ClassName.Should().Be("String"); property.ParentType.ClassName.Should().Be("TestType6"); + property.NotifySignal.Should().BeNull(); } [Signal("testSignal", NetVariantType.DateTime, NetVariantType.Object)] @@ -149,6 +151,58 @@ namespace Qt.NetCore.Tests.Types signal.GetParameter(1).Should().Be(NetVariantType.Object); } + [Signal("signalName")] + public class TestType8 + { + [NotifySignal("signalName")] + public string Property { get; set; } + } + + [Fact] + public void Can_get_notifiy_signal_for_property() + { + var type = NetTypeManager.GetTypeInfo(); + type.SignalCount.Should().Be(1); + type.PropertyCount.Should().Be(1); + type.GetProperty(0).Name.Should().Be("Property"); + type.GetProperty(0).NotifySignal.Should().NotBeNull(); + type.GetProperty(0).NotifySignal.Name.Should().Be("signalName"); + } + + public class TestType9 + { + [NotifySignal("signalName")] + public string Property { get; set; } + } + + [Fact] + public void Can_auto_add_signal_for_property_notification() + { + var type = NetTypeManager.GetTypeInfo(); + type.SignalCount.Should().Be(1); + type.PropertyCount.Should().Be(1); + type.GetProperty(0).Name.Should().Be("Property"); + type.GetProperty(0).NotifySignal.Should().NotBeNull(); + type.GetProperty(0).NotifySignal.Name.Should().Be("signalName"); + } + + public class TestType10 + { + [NotifySignal] + public string Property { get; set; } + } + + [Fact] + public void Can_auto_create_signal_with_property_name_if_no_name_given() + { + var type = NetTypeManager.GetTypeInfo(); + type.SignalCount.Should().Be(1); + type.PropertyCount.Should().Be(1); + type.GetProperty(0).Name.Should().Be("Property"); + type.GetProperty(0).NotifySignal.Should().NotBeNull(); + type.GetProperty(0).NotifySignal.Name.Should().Be("PropertyChanged"); + } + [Fact] public void Null_type_returned_for_invalid_type() { diff --git a/src/net/Qt.NetCore/Internal/DefaultCallbacks.cs b/src/net/Qt.NetCore/Internal/DefaultCallbacks.cs index b3d62465..110d9549 100644 --- a/src/net/Qt.NetCore/Internal/DefaultCallbacks.cs +++ b/src/net/Qt.NetCore/Internal/DefaultCallbacks.cs @@ -68,32 +68,61 @@ namespace Qt.NetCore.Internal type.AddMethod(method); } + var signals = new Dictionary(); + + foreach (var signalAttribute in typeInfo.GetCustomAttributes().OfType()) + { + var signal = new NetSignalInfo(type, signalAttribute.Name); + foreach (var parameter in signalAttribute.Parameters) + { + signal.AddParameter(parameter); + } + type.AddSignal(signal); + signals.Add(signal.Name, signal); + } + foreach (var propertyInfo in typeInfo.GetProperties(BindingFlags.Public | BindingFlags.Instance)) { if (Helpers.IsPrimitive(propertyInfo.DeclaringType)) continue; + NetSignalInfo notifySignal = null; + var notifySignalAttribute = propertyInfo.GetCustomAttribute(); + if (notifySignalAttribute != null) + { + var name = notifySignalAttribute.Name; + if (string.IsNullOrEmpty(name)) + { + name = $"{propertyInfo.Name}Changed"; + } + + if (signals.ContainsKey(name)) + { + notifySignal = signals[name]; + // Make sure the signal we are referencing has no parameters. + if (notifySignal.ParameterCount != 0) + { + // TODO: They can actually of parameters, but not implemented yet. + throw new Exception("Notify signals must have no parameters."); + } + } + else + { + notifySignal = new NetSignalInfo(type, name); + type.AddSignal(notifySignal); + } + } + using (var property = new NetPropertyInfo( type, propertyInfo.Name, NetTypeManager.GetTypeInfo(propertyInfo.PropertyType.AssemblyQualifiedName), propertyInfo.CanRead, - propertyInfo.CanWrite)) + propertyInfo.CanWrite, + notifySignal)) { type.AddProperty(property); } } - - foreach (var signalAttribute in typeInfo.GetCustomAttributes().OfType()) - { - using (var signal = new NetSignalInfo(type, signalAttribute.Name)) - { - foreach (var parameter in signalAttribute.Parameters) - { - signal.AddParameter(parameter); - } - type.AddSignal(signal); - } - } } } diff --git a/src/net/Qt.NetCore/NotifySignalAttribute.cs b/src/net/Qt.NetCore/NotifySignalAttribute.cs new file mode 100644 index 00000000..5e03adbb --- /dev/null +++ b/src/net/Qt.NetCore/NotifySignalAttribute.cs @@ -0,0 +1,20 @@ +using System; + +namespace Qt.NetCore +{ + [AttributeUsage(AttributeTargets.Property)] + public class NotifySignalAttribute : Attribute + { + public NotifySignalAttribute() + { + + } + + public NotifySignalAttribute(string name) + { + Name = name; + } + + public string Name { get; set; } + } +} \ No newline at end of file diff --git a/src/net/Qt.NetCore/Types/NetPropertyInfo.cs b/src/net/Qt.NetCore/Types/NetPropertyInfo.cs index 0fbad2e9..0481e43a 100644 --- a/src/net/Qt.NetCore/Types/NetPropertyInfo.cs +++ b/src/net/Qt.NetCore/Types/NetPropertyInfo.cs @@ -11,12 +11,14 @@ namespace Qt.NetCore.Types string name, NetTypeInfo returnType, bool canRead, - bool canWrite) + bool canWrite, + NetSignalInfo notifySignal) : this(Create(parentType, name, returnType, canRead, - canWrite)) + canWrite, + notifySignal)) { } @@ -31,13 +33,15 @@ namespace Qt.NetCore.Types string name, NetTypeInfo returnType, bool canRead, - bool canWrite) + bool canWrite, + NetSignalInfo notifySignal) { return Interop.NetPropertyInfo.Create(parentType?.Handle ?? IntPtr.Zero, name, returnType?.Handle ?? IntPtr.Zero, canRead, - canWrite); + canWrite, + notifySignal?.Handle ?? IntPtr.Zero); } public NetTypeInfo ParentType => new NetTypeInfo(Interop.NetPropertyInfo.GetParentType(Handle)); @@ -49,6 +53,15 @@ namespace Qt.NetCore.Types public bool CanRead => Interop.NetPropertyInfo.GetCanRead(Handle); public bool CanWrite => Interop.NetPropertyInfo.GetCanWrite(Handle); + + public NetSignalInfo NotifySignal + { + get + { + var result = Interop.NetPropertyInfo.GetNotifySignal(Handle); + return result == IntPtr.Zero ? null : new NetSignalInfo(result); + } + } protected override void DisposeUnmanaged(IntPtr ptr) { @@ -63,7 +76,8 @@ namespace Qt.NetCore.Types [MarshalAs(UnmanagedType.LPWStr)]string methodName, IntPtr returnType, bool canRead, - bool canWrite); + bool canWrite, + IntPtr notifySignal); [NativeSymbol(Entrypoint = "property_info_destroy")] void Destroy(IntPtr property); @@ -81,5 +95,8 @@ namespace Qt.NetCore.Types [NativeSymbol(Entrypoint = "property_info_canWrite")] bool GetCanWrite(IntPtr property); + + [NativeSymbol(Entrypoint = "property_info_getNotifySignal")] + IntPtr GetNotifySignal(IntPtr property); } } \ No newline at end of file