mirror of
https://github.com/qmlnet/qmlnet.git
synced 2026-05-21 06:45:32 -06:00
Merge branch 'signals'
This commit is contained in:
commit
2d125c0304
28 changed files with 1033 additions and 158 deletions
|
|
@ -1,16 +1,19 @@
|
|||
#include <QQmlEngine>
|
||||
#include <QtNetCoreQml/qml/NetValue.h>
|
||||
#include <QtNetCoreQml/qml/NetValueMetaObject.h>
|
||||
|
||||
NetValue::NetValue(QSharedPointer<NetInstance> instance, QObject *parent)
|
||||
: instance(instance)
|
||||
{
|
||||
valueMeta = new NetValueMetaObject(this, instance);
|
||||
setParent(parent);
|
||||
}
|
||||
#include <QDebug>
|
||||
|
||||
NetValue::~NetValue()
|
||||
{
|
||||
|
||||
auto hit = netValues.find(instance.data());
|
||||
if(hit != netValues.end())
|
||||
{
|
||||
netValues.erase(hit);
|
||||
}
|
||||
qDebug("NetValue deleted: %s", qPrintable(instance->getTypeInfo()->getClassName()));
|
||||
if(instance != nullptr) {
|
||||
instance->release();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -18,3 +21,83 @@ QSharedPointer<NetInstance> NetValue::getNetInstance()
|
|||
{
|
||||
return instance;
|
||||
}
|
||||
|
||||
bool NetValue::activateSignal(QString signalName, QSharedPointer<NetVariantList> arguments)
|
||||
{
|
||||
// Build the signature so we can look it up.
|
||||
// Perf?
|
||||
QString signature = signalName;
|
||||
signature.append("(");
|
||||
if(arguments != NULL) {
|
||||
for(int argumentIndex = 0; argumentIndex <= arguments->count() - 1; argumentIndex++)
|
||||
{
|
||||
if(argumentIndex > 0) {
|
||||
signature.append(",");
|
||||
}
|
||||
signature.append("QVariant");
|
||||
}
|
||||
}
|
||||
signature.append(")");
|
||||
QByteArray normalizedSignalSignature = QMetaObject::normalizedSignature(signature.toLatin1().data());
|
||||
int signalMethodIndex = valueMeta->indexOfMethod(normalizedSignalSignature);
|
||||
|
||||
// If signal not found, dump the registered signals for debugging.
|
||||
if(signalMethodIndex < 0) {
|
||||
qDebug("Signal not found: %s", qPrintable(normalizedSignalSignature));
|
||||
qDebug("Current signals:");
|
||||
for (int i = 0; i < metaObject()->methodCount(); i++) {
|
||||
QMetaMethod method = metaObject()->method(i);
|
||||
if (method.methodType() == QMetaMethod::Signal) {
|
||||
qDebug("\t%s", qPrintable(method.methodSignature()));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Build the types needed to activate the signal
|
||||
QList<QSharedPointer<QVariant>> variantArgs;
|
||||
std::vector<void*> voidArgs;
|
||||
voidArgs.push_back(NULL); // For the return type, which is nothing for signals.
|
||||
if(arguments != NULL) {
|
||||
for(int x = 0 ; x < arguments->count(); x++) {
|
||||
QSharedPointer<QVariant> variant = QSharedPointer<QVariant>(new QVariant(arguments->get(x)->asQVariant()));
|
||||
variantArgs.append(variant);
|
||||
voidArgs.push_back((void *)variant.data());
|
||||
}
|
||||
}
|
||||
void** argsPointer = nullptr;
|
||||
if(voidArgs.size() > 0) {
|
||||
argsPointer = &voidArgs[0];
|
||||
}
|
||||
|
||||
// Activate the signal!
|
||||
valueMeta->activate(this, signalMethodIndex, argsPointer);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
NetValue* NetValue::forInstance(QSharedPointer<NetInstance> instance, bool autoCreate)
|
||||
{
|
||||
if(netValues.find(instance.data()) != netValues.end())
|
||||
{
|
||||
return netValues.at(instance.data());
|
||||
}
|
||||
if(!autoCreate)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
auto result = new NetValue(instance, nullptr);
|
||||
QQmlEngine::setObjectOwnership(result, QQmlEngine::JavaScriptOwnership);
|
||||
return result;
|
||||
}
|
||||
|
||||
NetValue::NetValue(QSharedPointer<NetInstance> instance, QObject *parent)
|
||||
: instance(instance)
|
||||
{
|
||||
valueMeta = new NetValueMetaObject(this, instance);
|
||||
setParent(parent);
|
||||
netValues[instance.data()] = this;
|
||||
qDebug("NetValue created: %s", qPrintable(instance->getTypeInfo()->getClassName()));
|
||||
}
|
||||
|
||||
std::map<NetInstance*, NetValue*> NetValue::netValues = std::map<NetInstance*, NetValue*>();
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
#ifndef NETVALUE_H
|
||||
#define NETVALUE_H
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <QtNetCoreQml.h>
|
||||
#include <QtNetCoreQml/types/NetInstance.h>
|
||||
#include <QtNetCoreQml/qml/NetVariantList.h>
|
||||
#include <QObject>
|
||||
#include <QSharedPointer>
|
||||
|
||||
|
|
@ -20,12 +23,20 @@ class NetValue : public QObject, NetValueInterface
|
|||
Q_OBJECT
|
||||
Q_INTERFACES(NetValueInterface)
|
||||
public:
|
||||
NetValue(QSharedPointer<NetInstance> instance, QObject *parent);
|
||||
virtual ~NetValue();
|
||||
QSharedPointer<NetInstance> getNetInstance();
|
||||
bool activateSignal(QString signalName, QSharedPointer<NetVariantList> arguments);
|
||||
|
||||
static NetValue* forInstance(QSharedPointer<NetInstance> instance, bool autoCreate = true);
|
||||
|
||||
protected:
|
||||
NetValue(QSharedPointer<NetInstance> instance, QObject *parent);
|
||||
|
||||
private:
|
||||
QSharedPointer<NetInstance> instance;
|
||||
NetValueMetaObject* valueMeta;
|
||||
|
||||
static std::map<NetInstance*, NetValue*> netValues;
|
||||
};
|
||||
|
||||
#endif // NETVALUE_H
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#include <QtNetCoreQml/types/NetTypeInfo.h>
|
||||
#include <QtNetCoreQml/types/NetMethodInfo.h>
|
||||
#include <QtNetCoreQml/types/NetPropertyInfo.h>
|
||||
#include <QtNetCoreQml/types/NetSignalInfo.h>
|
||||
#include <QtNetCoreQml/types/Callbacks.h>
|
||||
#include <QQmlEngine>
|
||||
#include <QDebug>
|
||||
|
|
@ -36,8 +37,7 @@ void metaPackValue(QSharedPointer<NetVariant> source, QVariant* destination) {
|
|||
case NetVariantTypeEnum_Object:
|
||||
{
|
||||
QSharedPointer<NetInstance> newInstance = source->getNetInstance();
|
||||
NetValue* netValue = new NetValue(newInstance, NULL);
|
||||
QQmlEngine::setObjectOwnership(netValue, QQmlEngine::JavaScriptOwnership);
|
||||
NetValue* netValue = NetValue::forInstance(newInstance);
|
||||
destination->setValue(netValue);
|
||||
break;
|
||||
}
|
||||
|
|
@ -193,6 +193,32 @@ QMetaObject *metaObjectFor(QSharedPointer<NetTypeInfo> typeInfo)
|
|||
mob.setClassName(typeInfo->getClassName().toLatin1());
|
||||
mob.setFlags(QMetaObjectBuilder::DynamicMetaObject);
|
||||
|
||||
// register all the signals for the type
|
||||
|
||||
if(typeInfo->getSignalCount() > 0) {
|
||||
for(uint index = 0; index <= typeInfo->getSignalCount() - 1; index++)
|
||||
{
|
||||
QSharedPointer<NetSignalInfo> 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());
|
||||
}
|
||||
}
|
||||
|
||||
if(typeInfo->getMethodCount() > 0) {
|
||||
for(uint index = 0; index <= typeInfo->getMethodCount() - 1; index++)
|
||||
{
|
||||
|
|
@ -290,7 +316,16 @@ int NetValueMetaObject::metaCall(QMetaObject::Call c, int idx, void **a)
|
|||
return value->qt_metacall(c, idx, a);
|
||||
}
|
||||
|
||||
QSharedPointer<NetMethodInfo> methodInfo = instance->getTypeInfo()->getMethodInfo(idx - offset);
|
||||
idx -= offset;
|
||||
if(idx < (int)instance->getTypeInfo()->getSignalCount()) {
|
||||
// This is a signal call, activate it!
|
||||
activate(value, idx + offset, a);
|
||||
return -1;
|
||||
}
|
||||
|
||||
idx -= instance->getTypeInfo()->getSignalCount();
|
||||
|
||||
QSharedPointer<NetMethodInfo> methodInfo = instance->getTypeInfo()->getMethodInfo(idx);
|
||||
|
||||
QSharedPointer<NetVariantList> parameters = QSharedPointer<NetVariantList>(new NetVariantList());
|
||||
|
||||
|
|
|
|||
|
|
@ -1,18 +1,18 @@
|
|||
#include <QtNetCoreQml/types/NetInstance.h>
|
||||
#include <QtNetCoreQml/types/Callbacks.h>
|
||||
#include <QtNetCoreQml/qml/NetValue.h>
|
||||
#include <QDebug>
|
||||
|
||||
NetInstance::NetInstance(NetGCHandle* gcHandle, QSharedPointer<NetTypeInfo> typeInfo) :
|
||||
gcHandle(gcHandle),
|
||||
typeInfo(typeInfo)
|
||||
{
|
||||
|
||||
qDebug("NetInstance created: %s", qPrintable(typeInfo->getClassName()));
|
||||
}
|
||||
|
||||
NetInstance::~NetInstance()
|
||||
{
|
||||
releaseGCHandle(gcHandle);
|
||||
gcHandle = NULL;
|
||||
typeInfo = NULL;
|
||||
release();
|
||||
}
|
||||
|
||||
NetGCHandle* NetInstance::getGCHandle()
|
||||
|
|
@ -25,6 +25,17 @@ QSharedPointer<NetTypeInfo> NetInstance::getTypeInfo()
|
|||
return typeInfo;
|
||||
}
|
||||
|
||||
void NetInstance::release()
|
||||
{
|
||||
if(gcHandle != nullptr) {
|
||||
releaseGCHandle(gcHandle);
|
||||
auto typeInfoClassName = typeInfo->getClassName();
|
||||
gcHandle = nullptr;
|
||||
typeInfo = nullptr;
|
||||
qDebug("NetInstance released: %s", qPrintable(typeInfoClassName));
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
Q_DECL_EXPORT NetInstanceContainer* net_instance_create(NetGCHandle* handle, NetTypeInfoContainer* typeContainer) {
|
||||
|
|
@ -37,8 +48,29 @@ Q_DECL_EXPORT void net_instance_destroy(NetInstanceContainer* container) {
|
|||
delete container;
|
||||
}
|
||||
|
||||
Q_DECL_EXPORT NetInstanceContainer* net_instance_clone(NetInstanceContainer* container) {
|
||||
NetInstanceContainer* result = new NetInstanceContainer{container->instance};
|
||||
return result;
|
||||
}
|
||||
|
||||
Q_DECL_EXPORT NetGCHandle* net_instance_getHandle(NetInstanceContainer* container) {
|
||||
return container->instance->getGCHandle();
|
||||
}
|
||||
|
||||
Q_DECL_EXPORT bool net_instance_activateSignal(NetInstanceContainer* container, LPWSTR signalName, NetVariantListContainer* parametersContainer) {
|
||||
NetValue* existing = NetValue::forInstance(container->instance);
|
||||
if(existing == NULL) {
|
||||
// Not alive in the QML world, so no signals to raise.
|
||||
return false;
|
||||
}
|
||||
QString signalNameString = QString::fromUtf16((const char16_t*)signalName);
|
||||
|
||||
QSharedPointer<NetVariantList> parameters;
|
||||
if(parametersContainer != NULL) {
|
||||
parameters = parametersContainer->list;
|
||||
}
|
||||
|
||||
return existing->activateSignal(signalNameString, parameters);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ public:
|
|||
~NetInstance();
|
||||
NetGCHandle* getGCHandle();
|
||||
QSharedPointer<NetTypeInfo> getTypeInfo();
|
||||
|
||||
void release();
|
||||
private:
|
||||
NetGCHandle* gcHandle;
|
||||
QSharedPointer<NetTypeInfo> typeInfo;
|
||||
|
|
|
|||
55
src/native/QtNetCoreQml/QtNetCoreQml/types/NetSignalInfo.cpp
Normal file
55
src/native/QtNetCoreQml/QtNetCoreQml/types/NetSignalInfo.cpp
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
#include <QtNetCoreQml/types/NetSignalInfo.h>
|
||||
#include <iostream>
|
||||
|
||||
NetSignalInfo::NetSignalInfo(QString name) :
|
||||
_name(name) {
|
||||
}
|
||||
|
||||
QString NetSignalInfo::getName() {
|
||||
return _name;
|
||||
}
|
||||
|
||||
void NetSignalInfo::addParameter(NetVariantTypeEnum type) {
|
||||
if(type == NetVariantTypeEnum_Invalid) return;
|
||||
_parameters.append(type);
|
||||
}
|
||||
|
||||
uint NetSignalInfo::getParameterCount() {
|
||||
return _parameters.size();
|
||||
}
|
||||
|
||||
NetVariantTypeEnum NetSignalInfo::getParameter(uint index) {
|
||||
if(index >= (uint)_parameters.length()) return NetVariantTypeEnum_Invalid;
|
||||
return _parameters.at(index);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
Q_DECL_EXPORT NetSignalInfoContainer* signal_info_create(LPWSTR name) {
|
||||
NetSignalInfoContainer* result = new NetSignalInfoContainer();
|
||||
NetSignalInfo* instance = new NetSignalInfo(QString::fromUtf16((const char16_t*)name));
|
||||
result->signal = QSharedPointer<NetSignalInfo>(instance);
|
||||
return result;
|
||||
}
|
||||
|
||||
Q_DECL_EXPORT void signal_info_destroy(NetSignalInfoContainer* container) {
|
||||
delete container;
|
||||
}
|
||||
|
||||
Q_DECL_EXPORT LPWSTR signal_info_getName(NetSignalInfoContainer* container) {
|
||||
return (LPWSTR)container->signal->getName().utf16();
|
||||
}
|
||||
|
||||
Q_DECL_EXPORT void signal_info_addParameter(NetSignalInfoContainer* container, NetVariantTypeEnum type) {
|
||||
container->signal->addParameter(type);
|
||||
}
|
||||
|
||||
Q_DECL_EXPORT uint signal_info_getParameterCount(NetSignalInfoContainer* container) {
|
||||
return container->signal->getParameterCount();
|
||||
}
|
||||
|
||||
Q_DECL_EXPORT NetVariantTypeEnum signal_info_getParameter(NetSignalInfoContainer* container, uint index) {
|
||||
return container->signal->getParameter(index);
|
||||
}
|
||||
|
||||
}
|
||||
24
src/native/QtNetCoreQml/QtNetCoreQml/types/NetSignalInfo.h
Normal file
24
src/native/QtNetCoreQml/QtNetCoreQml/types/NetSignalInfo.h
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
#ifndef NET_SIGNAL_INFO_METHOD_H
|
||||
#define NET_SIGNAL_INFO_METHOD_H
|
||||
|
||||
#include <QtNetCoreQml.h>
|
||||
#include <QtNetCoreQml/types/NetTypeInfo.h>
|
||||
#include <QSharedPointer>
|
||||
|
||||
class NetSignalInfo {
|
||||
public:
|
||||
NetSignalInfo(QString name);
|
||||
QString getName();
|
||||
void addParameter(NetVariantTypeEnum type);
|
||||
uint getParameterCount();
|
||||
NetVariantTypeEnum getParameter(uint index);
|
||||
private:
|
||||
QString _name;
|
||||
QList<NetVariantTypeEnum> _parameters;
|
||||
};
|
||||
|
||||
struct NetSignalInfoContainer {
|
||||
QSharedPointer<NetSignalInfo> signal;
|
||||
};
|
||||
|
||||
#endif // NET_SIGNAL_INFO_METHOD_H
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
#include <QtNetCoreQml/types/NetTypeInfo.h>
|
||||
#include <QtNetCoreQml/types/NetMethodInfo.h>
|
||||
#include <QtNetCoreQml/types/NetPropertyInfo.h>
|
||||
#include <QtNetCoreQml/types/NetSignalInfo.h>
|
||||
|
||||
NetTypeInfo::NetTypeInfo(QString fullTypeName) :
|
||||
metaObject(NULL),
|
||||
|
|
@ -60,6 +61,19 @@ QSharedPointer<NetPropertyInfo> NetTypeInfo::getProperty(uint index) {
|
|||
return _properties.at(index);
|
||||
}
|
||||
|
||||
void NetTypeInfo::addSignal(QSharedPointer<NetSignalInfo> signal) {
|
||||
_signals.append(signal);
|
||||
}
|
||||
|
||||
uint NetTypeInfo::getSignalCount() {
|
||||
return _signals.size();
|
||||
}
|
||||
|
||||
QSharedPointer<NetSignalInfo> NetTypeInfo::getSignal(uint index) {
|
||||
if(index >= (uint)_signals.size()) return QSharedPointer<NetSignalInfo>(NULL);
|
||||
return _signals.at(index);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
Q_DECL_EXPORT NetTypeInfoContainer* type_info_create(LPWSTR fullTypeName) {
|
||||
|
|
@ -129,4 +143,22 @@ Q_DECL_EXPORT NetPropertyInfoContainer* type_info_getProperty(NetTypeInfoContain
|
|||
return result;
|
||||
}
|
||||
|
||||
Q_DECL_EXPORT void type_info_addSignal(NetTypeInfoContainer* container, NetSignalInfoContainer* signalContainer) {
|
||||
container->netTypeInfo->addSignal(signalContainer->signal);
|
||||
}
|
||||
|
||||
Q_DECL_EXPORT uint type_info_getSignalCount(NetTypeInfoContainer* container) {
|
||||
return container->netTypeInfo->getSignalCount();
|
||||
}
|
||||
|
||||
Q_DECL_EXPORT NetSignalInfoContainer* type_info_getSignal(NetTypeInfoContainer* container, uint index) {
|
||||
QSharedPointer<NetSignalInfo> signal = container->netTypeInfo->getSignal(index);
|
||||
if(signal == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
NetSignalInfoContainer* result = new NetSignalInfoContainer();
|
||||
result->signal = signal;
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
class NetMethodInfo;
|
||||
class NetPropertyInfo;
|
||||
class NetSignalInfo;
|
||||
|
||||
class NetTypeInfo {
|
||||
public:
|
||||
|
|
@ -30,6 +31,10 @@ public:
|
|||
uint getPropertyCount();
|
||||
QSharedPointer<NetPropertyInfo> getProperty(uint index);
|
||||
|
||||
void addSignal(QSharedPointer<NetSignalInfo> signal);
|
||||
uint getSignalCount();
|
||||
QSharedPointer<NetSignalInfo> getSignal(uint index);
|
||||
|
||||
QMetaObject* metaObject;
|
||||
|
||||
private:
|
||||
|
|
@ -38,6 +43,7 @@ private:
|
|||
NetVariantTypeEnum _variantType;
|
||||
QList<QSharedPointer<NetMethodInfo>> _methods;
|
||||
QList<QSharedPointer<NetPropertyInfo>> _properties;
|
||||
QList<QSharedPointer<NetSignalInfo>> _signals;
|
||||
};
|
||||
|
||||
struct Q_DECL_EXPORT NetTypeInfoContainer {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@ SOURCES += \
|
|||
$$PWD/Callbacks.cpp \
|
||||
$$PWD/NetMethodInfo.cpp \
|
||||
$$PWD/NetPropertyInfo.cpp \
|
||||
$$PWD/NetInstance.cpp
|
||||
$$PWD/NetInstance.cpp \
|
||||
$$PWD/NetSignalInfo.cpp
|
||||
|
||||
HEADERS += \
|
||||
$$PWD/NetTypeInfo.h \
|
||||
|
|
@ -12,4 +13,5 @@ HEADERS += \
|
|||
$$PWD/Callbacks.h \
|
||||
$$PWD/NetMethodInfo.h \
|
||||
$$PWD/NetPropertyInfo.h \
|
||||
$$PWD/NetInstance.h
|
||||
$$PWD/NetInstance.h \
|
||||
$$PWD/NetSignalInfo.h
|
||||
|
|
|
|||
|
|
@ -10,31 +10,74 @@ namespace Qt.NetCore.Sandbox
|
|||
{
|
||||
class Program
|
||||
{
|
||||
[Signal("testSignal", NetVariantType.String)]
|
||||
public class TestQmlImport
|
||||
{
|
||||
public AnotherType Create()
|
||||
{
|
||||
return new AnotherType();
|
||||
}
|
||||
readonly AnotherType _anotherType = new AnotherType();
|
||||
|
||||
public void TestMethod(AnotherType anotherType)
|
||||
public AnotherType GetSharedInstance()
|
||||
{
|
||||
|
||||
return _anotherType;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Signal("testSignal", NetVariantType.String)]
|
||||
public class AnotherType
|
||||
{
|
||||
private static int _instanceCounter = 0;
|
||||
|
||||
public AnotherType()
|
||||
}
|
||||
|
||||
public class InstanceType
|
||||
{
|
||||
public InstanceType()
|
||||
{
|
||||
Console.WriteLine($"AnotherType:{Interlocked.Increment(ref _instanceCounter)}");
|
||||
|
||||
}
|
||||
|
||||
~AnotherType()
|
||||
public void Log(string logMessage)
|
||||
{
|
||||
Console.WriteLine($"AnotherType:{Interlocked.Decrement(ref _instanceCounter)}");
|
||||
Console.WriteLine(logMessage);
|
||||
}
|
||||
}
|
||||
|
||||
public class TestQmlInstanceHandling
|
||||
{
|
||||
private InstanceType _instanceType;
|
||||
private WeakReference<InstanceType> _weakInstanceTypeRef;
|
||||
|
||||
public int State { get; set; } = 0;
|
||||
|
||||
public TestQmlInstanceHandling()
|
||||
{
|
||||
_instanceType = new InstanceType();
|
||||
_weakInstanceTypeRef = new WeakReference<InstanceType>(_instanceType);
|
||||
}
|
||||
|
||||
public InstanceType GetInstance()
|
||||
{
|
||||
return _instanceType;
|
||||
}
|
||||
|
||||
public void DeleteInstance()
|
||||
{
|
||||
_instanceType = null;
|
||||
}
|
||||
|
||||
public void CreateNewInstance()
|
||||
{
|
||||
_instanceType = new InstanceType();
|
||||
_weakInstanceTypeRef = new WeakReference<InstanceType>(_instanceType);
|
||||
}
|
||||
|
||||
public bool IsInstanceAlive()
|
||||
{
|
||||
return _weakInstanceTypeRef.TryGetTarget(out var _);
|
||||
}
|
||||
|
||||
public void GarbageCollect()
|
||||
{
|
||||
Thread.Sleep(1000);
|
||||
GC.Collect(GC.MaxGeneration);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -55,7 +98,8 @@ namespace Qt.NetCore.Sandbox
|
|||
engine.AddImportPath(Path.Combine(Directory.GetCurrentDirectory(), "Qml"));
|
||||
|
||||
QQmlApplicationEngine.RegisterType<TestQmlImport>("test");
|
||||
|
||||
QQmlApplicationEngine.RegisterType<TestQmlInstanceHandling>("testInstances");
|
||||
|
||||
engine.Load("main.qml");
|
||||
|
||||
return app.Exec();
|
||||
|
|
|
|||
|
|
@ -9,6 +9,9 @@
|
|||
<ProjectReference Include="..\Qt.NetCore\Qt.NetCore.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Update="Images\placeholder.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="main.qml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
|
|
|
|||
|
|
@ -2,19 +2,138 @@ import QtQuick 2.7
|
|||
import QtQuick.Controls 2.0
|
||||
import QtQuick.Layouts 1.0
|
||||
import test 1.0
|
||||
import MyModule 1.0 as MyModule
|
||||
import testInstances 1.0
|
||||
|
||||
ApplicationWindow {
|
||||
visible: true
|
||||
width: 640
|
||||
height: 480
|
||||
title: qsTr("Hello World")
|
||||
|
||||
Item {
|
||||
Timer {
|
||||
id: signalTimer
|
||||
interval: 1000; running: true; repeat: true
|
||||
onTriggered: {
|
||||
console.log(Factorial.factorial(10))
|
||||
var o = test.GetSharedInstance()
|
||||
o.testSignal.connect(function(message) {
|
||||
console.log("Signal was raised: " + message)
|
||||
signalTimer.running = false
|
||||
})
|
||||
var o2 = test.GetSharedInstance()
|
||||
o2.testSignal("Hello")
|
||||
}
|
||||
}
|
||||
Timer {
|
||||
id: instanceCheckTimer
|
||||
property var instanceRef: null
|
||||
interval: 1000; running: true; repeat: true
|
||||
onTriggered: {
|
||||
testInstances.GarbageCollect()
|
||||
gc()
|
||||
switch(testInstances.State) {
|
||||
case 0:
|
||||
console.log("Creating two QML references")
|
||||
var ref1 = testInstances.GetInstance()
|
||||
var ref2 = testInstances.GetInstance()
|
||||
|
||||
ref1 = null
|
||||
ref2 = null
|
||||
|
||||
console.log("Created and deleted two references on QML side. IsAlive = " + testInstances.IsInstanceAlive())
|
||||
testInstances.State++
|
||||
break
|
||||
case 1:
|
||||
testInstances.DeleteInstance()
|
||||
console.log("Deleting .Net references. IsAlive = " + testInstances.IsInstanceAlive())
|
||||
testInstances.State++
|
||||
break
|
||||
case 2:
|
||||
if(!testInstances.IsInstanceAlive()) {
|
||||
console.log("Yeah! Instance has been released!");
|
||||
testInstances.State++
|
||||
}
|
||||
break
|
||||
case 3:
|
||||
testInstances.CreateNewInstance()
|
||||
console.log("Created new .Net Instance. IsAlive = " + testInstances.IsInstanceAlive())
|
||||
testInstances.State++
|
||||
break
|
||||
case 4:
|
||||
instanceRef = testInstances.GetInstance()
|
||||
var secondLocalRef = testInstances.GetInstance()
|
||||
|
||||
console.log("Created two QML refs. One scoped and one long living. IsAlive = " + testInstances.IsInstanceAlive())
|
||||
|
||||
testInstances.State++
|
||||
//secondLocalRef will be freed here
|
||||
break
|
||||
case 5:
|
||||
instanceRef.Log("Long living QML ref still works!")
|
||||
testInstances.State++
|
||||
break
|
||||
case 6:
|
||||
instanceRef.Log("Long living QML ref still works!")
|
||||
testInstances.State++
|
||||
break;
|
||||
case 7:
|
||||
console.log("Deleting long living QML ref. IsAlive = " + testInstances.IsInstanceAlive())
|
||||
instanceRef = null
|
||||
testInstances.State++
|
||||
break;
|
||||
case 8:
|
||||
console.assert(testInstances.IsInstanceAlive(), ".Net object IsAlive = " + testInstances.IsInstanceAlive())
|
||||
testInstances.State++
|
||||
break;
|
||||
case 9:
|
||||
console.assert(testInstances.IsInstanceAlive(), ".Net object IsAlive = " + testInstances.IsInstanceAlive())
|
||||
testInstances.State++
|
||||
break;
|
||||
case 10:
|
||||
console.log("Releasing .Net instance a second time. IsAlive = " + testInstances.IsInstanceAlive())
|
||||
testInstances.DeleteInstance()
|
||||
testInstances.State++
|
||||
break;
|
||||
case 11:
|
||||
if(!testInstances.IsInstanceAlive()) {
|
||||
console.log("Yeah! Instance has been released a second time!");
|
||||
testInstances.State++
|
||||
}
|
||||
break;
|
||||
case 12:
|
||||
testInstances.CreateNewInstance()
|
||||
console.log("Created new .Net Instance. IsAlive = " + testInstances.IsInstanceAlive())
|
||||
testInstances.State++
|
||||
break
|
||||
case 13:
|
||||
instanceRef = testInstances.GetInstance()
|
||||
testInstances.DeleteInstance()
|
||||
console.log("a QML ref and deleted the .Net ref. IsAlive = " + testInstances.IsInstanceAlive())
|
||||
|
||||
testInstances.State++
|
||||
break
|
||||
case 14:
|
||||
console.assert(testInstances.IsInstanceAlive(), ".Net object IsAlive = " + testInstances.IsInstanceAlive())
|
||||
testInstances.State++
|
||||
break;
|
||||
case 15:
|
||||
console.assert(testInstances.IsInstanceAlive(), ".Net object IsAlive = " + testInstances.IsInstanceAlive())
|
||||
testInstances.State++
|
||||
break;
|
||||
case 16:
|
||||
console.log("Releasing last QML ref. IsAlive = " + testInstances.IsInstanceAlive())
|
||||
instanceRef = null
|
||||
testInstances.State++
|
||||
break;
|
||||
case 17:
|
||||
if(!testInstances.IsInstanceAlive()) {
|
||||
console.log("Yeah! Instance has been released a third time!");
|
||||
testInstances.State++
|
||||
}
|
||||
break;
|
||||
default:
|
||||
instanceCheckTimer.running = false
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -26,4 +145,8 @@ ApplicationWindow {
|
|||
TestQmlImport {
|
||||
id: test
|
||||
}
|
||||
|
||||
TestQmlInstanceHandling {
|
||||
id: testInstances
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ namespace Qt.NetCore.Tests.Qml
|
|||
var testObject = new TestObject();
|
||||
var variant = new NetVariant();
|
||||
variant.Instance.Should().BeNull();
|
||||
variant.Instance = NetInstance.CreateFromObject(testObject);
|
||||
variant.Instance = NetInstance.GetForObject(testObject);
|
||||
variant.Instance.Should().NotBeNull();
|
||||
variant.Instance.Instance.Should().Be(testObject);
|
||||
variant.VariantType.Should().Be(NetVariantType.Object);
|
||||
|
|
|
|||
182
src/net/Qt.NetCore.Tests/Qml/SignalTests.cs
Normal file
182
src/net/Qt.NetCore.Tests/Qml/SignalTests.cs
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
using System;
|
||||
using Moq;
|
||||
using Qt.NetCore.Qml;
|
||||
using Xunit;
|
||||
|
||||
namespace Qt.NetCore.Tests.Qml
|
||||
{
|
||||
public class SignalTests : BaseQmlTests<SignalTests.ObjectTestsQml>
|
||||
{
|
||||
public class ObjectTestsQml
|
||||
{
|
||||
public virtual SignalObject GetSignalObject()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public virtual bool SignalRaised { get; set; }
|
||||
|
||||
public virtual void MethodWithArgs(string arg1, int arg2)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public virtual void TestMethod()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[Signal("testSignal")]
|
||||
[Signal("testSignalWithArgs", NetVariantType.String, NetVariantType.Int)]
|
||||
public class SignalObject
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Can_raise_signal_from_qml()
|
||||
{
|
||||
var signalObject = new SignalObject();
|
||||
Mock.Setup(x => x.GetSignalObject()).Returns(signalObject);
|
||||
Mock.Setup(x => x.SignalRaised).Returns(false);
|
||||
|
||||
NetTestHelper.RunQml(qmlApplicationEngine,
|
||||
@"
|
||||
import QtQuick 2.0
|
||||
import tests 1.0
|
||||
ObjectTestsQml {
|
||||
id: test
|
||||
Component.onCompleted: function() {
|
||||
var instance = test.GetSignalObject()
|
||||
instance.testSignal.connect(function() {
|
||||
test.SignalRaised = true
|
||||
})
|
||||
instance.testSignal()
|
||||
}
|
||||
}
|
||||
");
|
||||
|
||||
Mock.VerifySet(x => x.SignalRaised = true, Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Can_raise_signal_from_qml_with_args()
|
||||
{
|
||||
var signalObject = new SignalObject();
|
||||
Mock.Setup(x => x.GetSignalObject()).Returns(signalObject);
|
||||
Mock.Setup(x => x.SignalRaised).Returns(false);
|
||||
Mock.Setup(x => x.MethodWithArgs("arg1", 3));
|
||||
|
||||
NetTestHelper.RunQml(qmlApplicationEngine,
|
||||
@"
|
||||
import QtQuick 2.0
|
||||
import tests 1.0
|
||||
ObjectTestsQml {
|
||||
id: test
|
||||
Component.onCompleted: function() {
|
||||
var instance = test.GetSignalObject()
|
||||
instance.testSignalWithArgs.connect(function(arg1, arg2) {
|
||||
test.SignalRaised = true
|
||||
test.MethodWithArgs(arg1, arg2)
|
||||
})
|
||||
instance.testSignalWithArgs('arg1', 3)
|
||||
}
|
||||
}
|
||||
");
|
||||
|
||||
Mock.VerifySet(x => x.SignalRaised = true, Times.Once);
|
||||
Mock.Verify(x => x.MethodWithArgs("arg1", 3), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Can_raise_signal_from_qml_different_retrieval_of_net_instance()
|
||||
{
|
||||
var signalObject = new SignalObject();
|
||||
Mock.Setup(x => x.GetSignalObject()).Returns(signalObject);
|
||||
Mock.Setup(x => x.SignalRaised).Returns(false);
|
||||
|
||||
NetTestHelper.RunQml(qmlApplicationEngine,
|
||||
@"
|
||||
import QtQuick 2.0
|
||||
import tests 1.0
|
||||
ObjectTestsQml {
|
||||
id: test
|
||||
Component.onCompleted: function() {
|
||||
var instance1 = test.GetSignalObject()
|
||||
instance1.testSignal.connect(function() {
|
||||
test.SignalRaised = true
|
||||
})
|
||||
var instance2 = test.GetSignalObject()
|
||||
instance2.testSignal()
|
||||
}
|
||||
}
|
||||
");
|
||||
|
||||
Mock.VerifySet(x => x.SignalRaised = true, Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Can_raise_signal_from_net()
|
||||
{
|
||||
var signalObject = new SignalObject();
|
||||
Mock.Setup(x => x.GetSignalObject()).Returns(signalObject);
|
||||
Mock.Setup(x => x.TestMethod()).Callback(() =>
|
||||
{
|
||||
signalObject.ActivateSignal("testSignal");
|
||||
});
|
||||
Mock.Setup(x => x.SignalRaised).Returns(false);
|
||||
|
||||
NetTestHelper.RunQml(qmlApplicationEngine,
|
||||
@"
|
||||
import QtQuick 2.0
|
||||
import tests 1.0
|
||||
ObjectTestsQml {
|
||||
id: test
|
||||
Component.onCompleted: function() {
|
||||
var instance1 = test.GetSignalObject()
|
||||
instance1.testSignal.connect(function() {
|
||||
test.SignalRaised = true
|
||||
})
|
||||
test.TestMethod()
|
||||
}
|
||||
}
|
||||
");
|
||||
|
||||
Mock.VerifySet(x => x.SignalRaised = true, Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Can_raise_signal_from_net_with_args()
|
||||
{
|
||||
var signalObject = new SignalObject();
|
||||
Mock.Setup(x => x.GetSignalObject()).Returns(signalObject);
|
||||
Mock.Setup(x => x.TestMethod()).Callback(() =>
|
||||
{
|
||||
signalObject.ActivateSignal("testSignalWithArgs", "arg1", 3);
|
||||
});
|
||||
Mock.Setup(x => x.SignalRaised).Returns(false);
|
||||
Mock.Setup(x => x.MethodWithArgs("arg1", 3));
|
||||
|
||||
NetTestHelper.RunQml(qmlApplicationEngine,
|
||||
@"
|
||||
import QtQuick 2.0
|
||||
import tests 1.0
|
||||
ObjectTestsQml {
|
||||
id: test
|
||||
Component.onCompleted: function() {
|
||||
var instance1 = test.GetSignalObject()
|
||||
instance1.testSignalWithArgs.connect(function(arg1, arg2) {
|
||||
test.SignalRaised = true
|
||||
test.MethodWithArgs(arg1, arg2)
|
||||
})
|
||||
test.TestMethod()
|
||||
}
|
||||
}
|
||||
");
|
||||
|
||||
Mock.VerifySet(x => x.SignalRaised = true, Times.Once);
|
||||
Mock.Verify(x => x.MethodWithArgs("arg1", 3), Times.Once);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -37,7 +37,7 @@ namespace Qt.NetCore.Tests.Types
|
|||
{
|
||||
var o = new TestObject();
|
||||
reference = new WeakReference(o);
|
||||
instance = NetInstance.CreateFromObject(o);
|
||||
instance = NetInstance.GetForObject(o);
|
||||
}).Wait();
|
||||
|
||||
// NetInstance is still alive, so the weak reference must be alive as well.
|
||||
|
|
@ -69,7 +69,7 @@ namespace Qt.NetCore.Tests.Types
|
|||
var o = new TestObject();
|
||||
var type = NetTypeManager.GetTypeInfo<TestObject>();
|
||||
var method = type.GetMethod(0);
|
||||
var instance = NetInstance.CreateFromObject(o);
|
||||
var instance = NetInstance.GetForObject(o);
|
||||
|
||||
// This will jump to native, to then call the .NET delegate (round trip).
|
||||
// The purpose is to simulate Qml invoking a method, sending .NET instance back.
|
||||
|
|
|
|||
|
|
@ -15,20 +15,11 @@ namespace Qt.NetCore.Tests.Types
|
|||
public void Can_create_net_instance()
|
||||
{
|
||||
var o = new TestObject();
|
||||
var instance = NetInstance.CreateFromObject(o);
|
||||
var instance = NetInstance.GetForObject(o);
|
||||
|
||||
var returnedInstance = instance.Instance;
|
||||
|
||||
o.Should().Be(returnedInstance);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Can_create_instance_from_type_info()
|
||||
{
|
||||
var typeInfo = NetTypeManager.GetTypeInfo<TestObject>();
|
||||
var instance = NetInstance.InstantiateType(typeInfo);
|
||||
instance.Should().NotBeNull();
|
||||
instance.Instance.Should().BeOfType<TestObject>();
|
||||
}
|
||||
}
|
||||
}
|
||||
24
src/net/Qt.NetCore.Tests/Types/NetSignalInfoTests.cs
Normal file
24
src/net/Qt.NetCore.Tests/Types/NetSignalInfoTests.cs
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
using FluentAssertions;
|
||||
using Qt.NetCore.Types;
|
||||
using Xunit;
|
||||
|
||||
namespace Qt.NetCore.Tests.Types
|
||||
{
|
||||
public class NetSignalInfoTests
|
||||
{
|
||||
[Fact]
|
||||
public void Can_create_signal_info()
|
||||
{
|
||||
using (var signal = new NetSignalInfo("testSignal"))
|
||||
{
|
||||
signal.Name.Should().Be("testSignal");
|
||||
signal.ParameterCount.Should().Be(0);
|
||||
signal.GetParameter(0).Should().Be(NetVariantType.Invalid);
|
||||
signal.AddParameter(NetVariantType.Double);
|
||||
signal.ParameterCount.Should().Be(1);
|
||||
signal.GetParameter(0).Should().Be(NetVariantType.Double);
|
||||
signal.GetParameter(1).Should().Be(NetVariantType.Invalid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -12,5 +12,20 @@ namespace Qt.NetCore.Tests.Types
|
|||
var typeInfo = new NetTypeInfo("fullTypeName");
|
||||
typeInfo.FullTypeName.Should().Be("fullTypeName");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Can_add_signals()
|
||||
{
|
||||
var type = new NetTypeInfo("test");
|
||||
var signal = new NetSignalInfo("signalName");
|
||||
signal.AddParameter(NetVariantType.Bool);
|
||||
|
||||
type.GetSignal(0).Should().BeNull();
|
||||
type.SignalCount.Should().Be(0);
|
||||
type.AddSignal(signal);
|
||||
type.SignalCount.Should().Be(1);
|
||||
type.GetSignal(0).Name.Should().Be("signalName");
|
||||
type.GetSignal(0).GetParameter(0).Should().Be(NetVariantType.Bool);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -130,6 +130,25 @@ namespace Qt.NetCore.Tests.Types
|
|||
property.ParentType.ClassName.Should().Be("TestType6");
|
||||
}
|
||||
|
||||
[Signal("testSignal", NetVariantType.DateTime, NetVariantType.Object)]
|
||||
public class TestType7
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Can_get_signal()
|
||||
{
|
||||
var type = NetTypeManager.GetTypeInfo<TestType7>();
|
||||
|
||||
type.SignalCount.Should().Be(1);
|
||||
var signal = type.GetSignal(0);
|
||||
signal.Name.Should().Be("testSignal");
|
||||
signal.ParameterCount.Should().Be(2);
|
||||
signal.GetParameter(0).Should().Be(NetVariantType.DateTime);
|
||||
signal.GetParameter(1).Should().Be(NetVariantType.Object);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Null_type_returned_for_invalid_type()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using Qt.NetCore.Qml;
|
||||
|
|
@ -42,11 +43,11 @@ namespace Qt.NetCore.Internal
|
|||
type.PrefVariantType = NetVariantType.Object;
|
||||
|
||||
// Don't grab properties and methods for system-level types.
|
||||
if (IsPrimitive(typeInfo)) return;
|
||||
if (Helpers.IsPrimitive(typeInfo)) return;
|
||||
|
||||
foreach (var methodInfo in typeInfo.GetMethods(BindingFlags.Public | BindingFlags.Instance))
|
||||
{
|
||||
if (IsPrimitive(methodInfo.DeclaringType)) continue;
|
||||
if (Helpers.IsPrimitive(methodInfo.DeclaringType)) continue;
|
||||
|
||||
NetTypeInfo returnType = null;
|
||||
|
||||
|
|
@ -69,7 +70,7 @@ namespace Qt.NetCore.Internal
|
|||
|
||||
foreach (var propertyInfo in typeInfo.GetProperties(BindingFlags.Public | BindingFlags.Instance))
|
||||
{
|
||||
if (IsPrimitive(propertyInfo.DeclaringType)) continue;
|
||||
if (Helpers.IsPrimitive(propertyInfo.DeclaringType)) continue;
|
||||
|
||||
using (var property = new NetPropertyInfo(
|
||||
type,
|
||||
|
|
@ -81,6 +82,18 @@ namespace Qt.NetCore.Internal
|
|||
type.AddProperty(property);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var signalAttribute in typeInfo.GetCustomAttributes().OfType<SignalAttribute>())
|
||||
{
|
||||
using (var signal = new NetSignalInfo(signalAttribute.Name))
|
||||
{
|
||||
foreach (var parameter in signalAttribute.Parameters)
|
||||
{
|
||||
signal.AddParameter(parameter);
|
||||
}
|
||||
type.AddSignal(signal);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -99,10 +112,11 @@ namespace Qt.NetCore.Internal
|
|||
|
||||
var typeCreator = NetInstance.TypeCreator;
|
||||
var instance = typeCreator != null ? typeCreator.Create(typeInfo) : Activator.CreateInstance(typeInfo);
|
||||
|
||||
var instanceHandle = GCHandle.Alloc(instance);
|
||||
// NOTE: We DON'T wrap
|
||||
return Interop.NetInstance.Create(GCHandle.ToIntPtr(instanceHandle), type);
|
||||
|
||||
var netInstance = NetInstance.GetForObject(instance);
|
||||
// When .NET collects this NetInstance, we don't want it to delete this
|
||||
// handle. Ownership has been passed to the caller.
|
||||
return Interop.NetInstance.Clone(netInstance.Handle);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
|
@ -124,9 +138,7 @@ namespace Qt.NetCore.Internal
|
|||
if(propertInfo == null)
|
||||
throw new InvalidOperationException($"Invalid property {property.Name}");
|
||||
|
||||
var value = propertInfo.GetValue(o);
|
||||
|
||||
PackValue(ref value, result);
|
||||
Helpers.PackValue(propertInfo.GetValue(o), result);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -145,7 +157,7 @@ namespace Qt.NetCore.Internal
|
|||
throw new InvalidOperationException($"Invalid property {property.Name}");
|
||||
|
||||
object newValue = null;
|
||||
Unpackvalue(ref newValue, value);
|
||||
Helpers.Unpackvalue(ref newValue, value);
|
||||
|
||||
propertInfo.SetValue(o, newValue);
|
||||
}
|
||||
|
|
@ -169,7 +181,7 @@ namespace Qt.NetCore.Internal
|
|||
for (var x = 0; x < parameterCount; x++)
|
||||
{
|
||||
object v = null;
|
||||
Unpackvalue(ref v, parameters.Get(x));
|
||||
Helpers.Unpackvalue(ref v, parameters.Get(x));
|
||||
methodParameters.Add(v);
|
||||
}
|
||||
}
|
||||
|
|
@ -181,91 +193,18 @@ namespace Qt.NetCore.Internal
|
|||
{
|
||||
throw new InvalidOperationException($"Invalid method name {method.MethodName}");
|
||||
}
|
||||
|
||||
var returnValue = methodInfo.Invoke(instance, methodParameters?.ToArray());
|
||||
|
||||
var returnObject = methodInfo.Invoke(instance, methodParameters?.ToArray());
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
// this method doesn't have return type
|
||||
}
|
||||
else
|
||||
{
|
||||
PackValue(ref returnValue, result);
|
||||
Helpers.PackValue(returnObject, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsPrimitive(Type type)
|
||||
{
|
||||
if (type.Namespace == "System") return true;
|
||||
if (type.Namespace == "System.Threading.Tasks") return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private void PackValue(ref object source, NetVariant destination)
|
||||
{
|
||||
if (source == null)
|
||||
{
|
||||
destination.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
var type = source.GetType();
|
||||
if (type == typeof(bool))
|
||||
destination.Bool = (bool)source;
|
||||
else if(type == typeof(char))
|
||||
destination.Char = (char)source;
|
||||
else if(type == typeof(double))
|
||||
destination.Double = (double)source;
|
||||
else if (type == typeof(int))
|
||||
destination.Int = (int)source;
|
||||
else if(type == typeof(uint))
|
||||
destination.UInt = (uint)source;
|
||||
else if (type == typeof(string))
|
||||
destination.String = (string)source;
|
||||
else if(type == typeof(DateTime))
|
||||
destination.DateTime = (DateTime)source;
|
||||
else
|
||||
{
|
||||
destination.Instance = NetInstance.CreateFromObject(source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Unpackvalue(ref object destination, NetVariant source)
|
||||
{
|
||||
switch (source.VariantType)
|
||||
{
|
||||
case NetVariantType.Invalid:
|
||||
destination = null;
|
||||
break;
|
||||
case NetVariantType.Bool:
|
||||
destination = source.Bool;
|
||||
break;
|
||||
case NetVariantType.Char:
|
||||
destination = source.Char;
|
||||
break;
|
||||
case NetVariantType.Int:
|
||||
destination = source.Int;
|
||||
break;
|
||||
case NetVariantType.UInt:
|
||||
destination = source.UInt;
|
||||
break;
|
||||
case NetVariantType.Double:
|
||||
destination = source.Double;
|
||||
break;
|
||||
case NetVariantType.String:
|
||||
destination = source.String;
|
||||
break;
|
||||
case NetVariantType.DateTime:
|
||||
destination = source.DateTime;
|
||||
break;
|
||||
case NetVariantType.Object:
|
||||
destination = source.Instance.Instance;
|
||||
break;
|
||||
default:
|
||||
throw new Exception("Unsupported variant type.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
82
src/net/Qt.NetCore/Internal/Helpers.cs
Normal file
82
src/net/Qt.NetCore/Internal/Helpers.cs
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
using System;
|
||||
using Qt.NetCore.Qml;
|
||||
using Qt.NetCore.Types;
|
||||
|
||||
namespace Qt.NetCore.Internal
|
||||
{
|
||||
internal static class Helpers
|
||||
{
|
||||
public static bool IsPrimitive(Type type)
|
||||
{
|
||||
if (type.Namespace == "System") return true;
|
||||
if (type.Namespace == "System.Threading.Tasks") return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void PackValue(object source, NetVariant destination)
|
||||
{
|
||||
if (source == null)
|
||||
{
|
||||
destination.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
var type = source.GetType();
|
||||
if (type == typeof(bool))
|
||||
destination.Bool = (bool)source;
|
||||
else if(type == typeof(char))
|
||||
destination.Char = (char)source;
|
||||
else if(type == typeof(double))
|
||||
destination.Double = (double)source;
|
||||
else if (type == typeof(int))
|
||||
destination.Int = (int)source;
|
||||
else if(type == typeof(uint))
|
||||
destination.UInt = (uint)source;
|
||||
else if (type == typeof(string))
|
||||
destination.String = (string)source;
|
||||
else if(type == typeof(DateTime))
|
||||
destination.DateTime = (DateTime)source;
|
||||
else
|
||||
{
|
||||
destination.Instance = NetInstance.GetForObject(source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Unpackvalue(ref object destination, NetVariant source)
|
||||
{
|
||||
switch (source.VariantType)
|
||||
{
|
||||
case NetVariantType.Invalid:
|
||||
destination = null;
|
||||
break;
|
||||
case NetVariantType.Bool:
|
||||
destination = source.Bool;
|
||||
break;
|
||||
case NetVariantType.Char:
|
||||
destination = source.Char;
|
||||
break;
|
||||
case NetVariantType.Int:
|
||||
destination = source.Int;
|
||||
break;
|
||||
case NetVariantType.UInt:
|
||||
destination = source.UInt;
|
||||
break;
|
||||
case NetVariantType.Double:
|
||||
destination = source.Double;
|
||||
break;
|
||||
case NetVariantType.String:
|
||||
destination = source.String;
|
||||
break;
|
||||
case NetVariantType.DateTime:
|
||||
destination = source.DateTime;
|
||||
break;
|
||||
case NetVariantType.Object:
|
||||
destination = source.Instance.Instance;
|
||||
break;
|
||||
default:
|
||||
throw new Exception("Unsupported variant type.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -24,6 +24,7 @@ namespace Qt.NetCore
|
|||
NetInstance = NativeLibraryBuilder.Default.ActivateInterface<INetInstanceInterop>("QtNetCoreQml");
|
||||
NetVariantList = NativeLibraryBuilder.Default.ActivateInterface<INetVariantListInterop>("QtNetCoreQml");
|
||||
NetTestHelper = NativeLibraryBuilder.Default.ActivateInterface<INetTestHelperInterop>("QtNetCoreQml");
|
||||
NetSignalInfo = NativeLibraryBuilder.Default.ActivateInterface<INetSignalInfoInterop>("QtNetCoreQml");
|
||||
QResource = NativeLibraryBuilder.Default.ActivateInterface<IQResourceInterop>("QtNetCoreQml");
|
||||
|
||||
var cb = DefaultCallbacks.Callbacks();
|
||||
|
|
@ -52,6 +53,8 @@ namespace Qt.NetCore
|
|||
|
||||
public static INetTestHelperInterop NetTestHelper { get; }
|
||||
|
||||
public static INetSignalInfoInterop NetSignalInfo { get; }
|
||||
|
||||
public static IQResourceInterop QResource { get; set; }
|
||||
|
||||
public static void RegisterCallbacks(ICallbacks callbacks)
|
||||
|
|
|
|||
19
src/net/Qt.NetCore/SignalAttribute.cs
Normal file
19
src/net/Qt.NetCore/SignalAttribute.cs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
using System;
|
||||
|
||||
namespace Qt.NetCore
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
|
||||
public class SignalAttribute : Attribute
|
||||
{
|
||||
public SignalAttribute(string name, params NetVariantType[] parameters)
|
||||
{
|
||||
Name = name;
|
||||
Parameters = parameters;
|
||||
}
|
||||
|
||||
|
||||
public string Name { get; }
|
||||
|
||||
public NetVariantType[] Parameters { get; }
|
||||
}
|
||||
}
|
||||
17
src/net/Qt.NetCore/Signals.cs
Normal file
17
src/net/Qt.NetCore/Signals.cs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
using Qt.NetCore.Types;
|
||||
|
||||
namespace Qt.NetCore
|
||||
{
|
||||
public static class Signals
|
||||
{
|
||||
public static bool ActivateSignal(this object instance, string signalName, params object[] args)
|
||||
{
|
||||
var existing = NetInstance.GetForObject(instance, false /*don't create one if doesn't exist*/);
|
||||
if (existing == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return existing.ActivateSignal(signalName, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +1,16 @@
|
|||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using AdvancedDLSupport;
|
||||
using Qt.NetCore.Internal;
|
||||
using Qt.NetCore.Qml;
|
||||
|
||||
namespace Qt.NetCore.Types
|
||||
{
|
||||
public class NetInstance : BaseDisposable
|
||||
{
|
||||
static NetInstance()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public NetInstance(IntPtr gcHandle, NetTypeInfo type)
|
||||
:base(Interop.NetInstance.Create(gcHandle, type.Handle))
|
||||
private NetInstance(IntPtr gcHandle, NetTypeInfo type, bool ownsHandle = true)
|
||||
:base(Interop.NetInstance.Create(gcHandle, type.Handle), ownsHandle)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -23,22 +20,6 @@ namespace Qt.NetCore.Types
|
|||
|
||||
}
|
||||
|
||||
public static NetInstance CreateFromObject(object value)
|
||||
{
|
||||
if (value == null) return null;
|
||||
var typeInfo = NetTypeManager.GetTypeInfo(GetUnproxiedType(value.GetType()).AssemblyQualifiedName);
|
||||
if(typeInfo == null) throw new InvalidOperationException($"Couldn't create type info from {value.GetType().AssemblyQualifiedName}");
|
||||
var handle = GCHandle.Alloc(value);
|
||||
return new NetInstance(GCHandle.ToIntPtr(handle), typeInfo);
|
||||
}
|
||||
|
||||
public static NetInstance InstantiateType(NetTypeInfo type)
|
||||
{
|
||||
var result = Interop.Callbacks.InstantiateType(type.Handle);
|
||||
if (result == IntPtr.Zero) return null;
|
||||
return new NetInstance(result);
|
||||
}
|
||||
|
||||
public object Instance
|
||||
{
|
||||
get
|
||||
|
|
@ -48,11 +29,41 @@ namespace Qt.NetCore.Types
|
|||
}
|
||||
}
|
||||
|
||||
public NetInstance Clone()
|
||||
{
|
||||
return new NetInstance(Interop.NetInstance.Clone(Handle));
|
||||
}
|
||||
|
||||
public bool ActivateSignal(string signalName, params object[] parameters)
|
||||
{
|
||||
if (parameters != null && parameters.Length > 0)
|
||||
{
|
||||
using (var list = new NetVariantList())
|
||||
{
|
||||
foreach (var parameter in parameters)
|
||||
{
|
||||
using (var variant = new NetVariant())
|
||||
{
|
||||
Helpers.PackValue(parameter, variant);
|
||||
list.Add(variant);
|
||||
}
|
||||
}
|
||||
return Interop.NetInstance.ActivateSignal(Handle, signalName, list.Handle);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return Interop.NetInstance.ActivateSignal(Handle, signalName, IntPtr.Zero);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void DisposeUnmanaged(IntPtr ptr)
|
||||
{
|
||||
Interop.NetInstance.Destroy(ptr);
|
||||
}
|
||||
|
||||
#region Instance helpers
|
||||
|
||||
public static ITypeCreator TypeCreator { get; set; }
|
||||
|
||||
private static Type GetUnproxiedType(Type type)
|
||||
|
|
@ -62,6 +73,42 @@ namespace Qt.NetCore.Types
|
|||
|
||||
return type;
|
||||
}
|
||||
|
||||
private static readonly ConditionalWeakTable<object, NetInstance> ObjectNetInstanceConnections = new ConditionalWeakTable<object, NetInstance>();
|
||||
|
||||
public static bool ExistsForObject(object value)
|
||||
{
|
||||
return ObjectNetInstanceConnections.TryGetValue(value, out NetInstance netInstance);
|
||||
}
|
||||
|
||||
public static NetInstance GetForObject(object value, bool autoCreate = true)
|
||||
{
|
||||
if (value == null) return null;
|
||||
var alreadyExists = false;
|
||||
if (ObjectNetInstanceConnections.TryGetValue(value, out var netInstance))
|
||||
{
|
||||
alreadyExists = true;
|
||||
if (GCHandle.FromIntPtr(netInstance.Handle).IsAllocated)
|
||||
{
|
||||
return netInstance;
|
||||
}
|
||||
}
|
||||
|
||||
if (!autoCreate) return null;
|
||||
|
||||
var typeInfo = NetTypeManager.GetTypeInfo(GetUnproxiedType(value.GetType()).AssemblyQualifiedName);
|
||||
if(typeInfo == null) throw new InvalidOperationException($"Couldn't create type info from {value.GetType().AssemblyQualifiedName}");
|
||||
var handle = GCHandle.Alloc(value);
|
||||
var newNetInstance = new NetInstance(GCHandle.ToIntPtr(handle), typeInfo);
|
||||
if(alreadyExists)
|
||||
{
|
||||
ObjectNetInstanceConnections.Remove(value);
|
||||
}
|
||||
ObjectNetInstanceConnections.Add(value, newNetInstance);
|
||||
return newNetInstance;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public interface INetInstanceInterop
|
||||
|
|
@ -70,8 +117,12 @@ namespace Qt.NetCore.Types
|
|||
IntPtr Create(IntPtr handle, IntPtr type);
|
||||
[NativeSymbol(Entrypoint = "net_instance_destroy")]
|
||||
void Destroy(IntPtr instance);
|
||||
[NativeSymbol(Entrypoint = "net_instance_clone")]
|
||||
IntPtr Clone(IntPtr instance);
|
||||
|
||||
[NativeSymbol(Entrypoint = "net_instance_getHandle")]
|
||||
IntPtr GetHandle(IntPtr instance);
|
||||
[NativeSymbol(Entrypoint = "net_instance_activateSignal")]
|
||||
bool ActivateSignal(IntPtr instance, [MarshalAs(UnmanagedType.LPWStr)]string signalName, IntPtr variants);
|
||||
}
|
||||
}
|
||||
60
src/net/Qt.NetCore/Types/NetSignalInfo.cs
Normal file
60
src/net/Qt.NetCore/Types/NetSignalInfo.cs
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using AdvancedDLSupport;
|
||||
using Qt.NetCore.Internal;
|
||||
|
||||
namespace Qt.NetCore.Types
|
||||
{
|
||||
public class NetSignalInfo : BaseDisposable
|
||||
{
|
||||
internal NetSignalInfo(IntPtr handle, bool ownsHandle = true)
|
||||
: base(handle, ownsHandle)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public NetSignalInfo(string name)
|
||||
: this(Interop.NetSignalInfo.Create(name))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public string Name => Interop.NetSignalInfo.GetName(Handle);
|
||||
|
||||
public void AddParameter(NetVariantType type)
|
||||
{
|
||||
Interop.NetSignalInfo.AddParameter(Handle, type);
|
||||
}
|
||||
|
||||
public uint ParameterCount => Interop.NetSignalInfo.GetParameterCount(Handle);
|
||||
|
||||
public NetVariantType GetParameter(uint index)
|
||||
{
|
||||
return Interop.NetSignalInfo.GetParameter(Handle, index);
|
||||
}
|
||||
|
||||
protected override void DisposeUnmanaged(IntPtr ptr)
|
||||
{
|
||||
Interop.NetSignalInfo.Destroy(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
public interface INetSignalInfoInterop
|
||||
{
|
||||
[NativeSymbol(Entrypoint = "signal_info_create")]
|
||||
IntPtr Create([MarshalAs(UnmanagedType.LPWStr)] string name);
|
||||
[NativeSymbol(Entrypoint = "signal_info_destroy")]
|
||||
void Destroy(IntPtr signal);
|
||||
|
||||
[NativeSymbol(Entrypoint = "signal_info_getName")]
|
||||
[return: MarshalAs(UnmanagedType.LPWStr)]
|
||||
string GetName(IntPtr signal);
|
||||
|
||||
[NativeSymbol(Entrypoint = "signal_info_addParameter")]
|
||||
void AddParameter(IntPtr signal, NetVariantType type);
|
||||
[NativeSymbol(Entrypoint = "signal_info_getParameterCount")]
|
||||
uint GetParameterCount(IntPtr signal);
|
||||
[NativeSymbol(Entrypoint = "signal_info_getParameter")]
|
||||
NetVariantType GetParameter(IntPtr signal, uint index);
|
||||
}
|
||||
}
|
||||
|
|
@ -59,6 +59,20 @@ namespace Qt.NetCore.Types
|
|||
if (result == IntPtr.Zero) return null;
|
||||
return new NetPropertyInfo(result);
|
||||
}
|
||||
|
||||
public void AddSignal(NetSignalInfo signal)
|
||||
{
|
||||
Interop.NetTypeInfo.AddSignal(Handle, signal.Handle);
|
||||
}
|
||||
|
||||
public uint SignalCount => Interop.NetTypeInfo.GetSignalCount(Handle);
|
||||
|
||||
public NetSignalInfo GetSignal(uint index)
|
||||
{
|
||||
var result = Interop.NetTypeInfo.GetSignal(Handle, index);
|
||||
if (result == IntPtr.Zero) return null;
|
||||
return new NetSignalInfo(result);
|
||||
}
|
||||
|
||||
protected override void DisposeUnmanaged(IntPtr ptr)
|
||||
{
|
||||
|
|
@ -99,5 +113,12 @@ namespace Qt.NetCore.Types
|
|||
uint GetPropertyCount(IntPtr typeInfo);
|
||||
[NativeSymbol(Entrypoint = "type_info_getProperty")]
|
||||
IntPtr GetProperty(IntPtr typeInfo, uint index);
|
||||
|
||||
[NativeSymbol(Entrypoint = "type_info_addSignal")]
|
||||
void AddSignal(IntPtr typeInfo, IntPtr signal);
|
||||
[NativeSymbol(Entrypoint = "type_info_getSignalCount")]
|
||||
uint GetSignalCount(IntPtr typeInfo);
|
||||
[NativeSymbol(Entrypoint = "type_info_getSignal")]
|
||||
IntPtr GetSignal(IntPtr typeInfo, uint index);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue