Add QObject.parent() and a heap of wrapper management
This commit is contained in:
parent
7bf97ef618
commit
cbb3f99dfa
@ -35,6 +35,10 @@ class DLL_EXPORT NAbstractItemModel : public QAbstractItemModel,
|
|||||||
return *newIndex;
|
return *newIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QObject *parent() const {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
QModelIndex parent(const QModelIndex& child) const override {
|
QModelIndex parent(const QModelIndex& child) const override {
|
||||||
Napi::Env env = this->dispatchOnNode.Env();
|
Napi::Env env = this->dispatchOnNode.Env();
|
||||||
Napi::HandleScope scope(env);
|
Napi::HandleScope scope(env);
|
||||||
|
|||||||
@ -5,6 +5,8 @@
|
|||||||
#include "Extras/Utils/nutils.h"
|
#include "Extras/Utils/nutils.h"
|
||||||
#include "QtCore/QVariant/qvariant_wrap.h"
|
#include "QtCore/QVariant/qvariant_wrap.h"
|
||||||
#include "core/Events/eventwidget_macro.h"
|
#include "core/Events/eventwidget_macro.h"
|
||||||
|
#include "core/WrapperCache/wrappercache.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
This macro adds common QObject exported methods
|
This macro adds common QObject exported methods
|
||||||
@ -88,6 +90,15 @@
|
|||||||
int id = info[0].As<Napi::Number>().Int32Value(); \
|
int id = info[0].As<Napi::Number>().Int32Value(); \
|
||||||
this->instance->killTimer(id); \
|
this->instance->killTimer(id); \
|
||||||
return env.Null(); \
|
return env.Null(); \
|
||||||
|
} \
|
||||||
|
Napi::Value parent(const Napi::CallbackInfo& info) { \
|
||||||
|
Napi::Env env = info.Env(); \
|
||||||
|
QObject *parent = this->instance->parent(); \
|
||||||
|
if (parent) { \
|
||||||
|
return WrapperCache::instance.getWrapper(env, parent); \
|
||||||
|
} else { \
|
||||||
|
return env.Null(); \
|
||||||
|
} \
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ideally this macro below should go in
|
// Ideally this macro below should go in
|
||||||
@ -131,7 +142,8 @@
|
|||||||
InstanceMethod("dumpObjectInfo", &ComponentWrapName::dumpObjectInfo), \
|
InstanceMethod("dumpObjectInfo", &ComponentWrapName::dumpObjectInfo), \
|
||||||
InstanceMethod("setParent", &ComponentWrapName::setParent), \
|
InstanceMethod("setParent", &ComponentWrapName::setParent), \
|
||||||
InstanceMethod("startTimer", &ComponentWrapName::startTimer), \
|
InstanceMethod("startTimer", &ComponentWrapName::startTimer), \
|
||||||
InstanceMethod("killTimer", &ComponentWrapName::killTimer),
|
InstanceMethod("killTimer", &ComponentWrapName::killTimer), \
|
||||||
|
InstanceMethod("parent", &ComponentWrapName::parent),
|
||||||
|
|
||||||
#endif // QOBJECT_WRAPPED_METHODS_EXPORT_DEFINE
|
#endif // QOBJECT_WRAPPED_METHODS_EXPORT_DEFINE
|
||||||
|
|
||||||
|
|||||||
@ -20,5 +20,6 @@ class DLL_EXPORT QObjectWrap : public Napi::ObjectWrap<QObjectWrap> {
|
|||||||
NObject* getInternalInstance();
|
NObject* getInternalInstance();
|
||||||
// class constructor
|
// class constructor
|
||||||
static Napi::FunctionReference constructor;
|
static Napi::FunctionReference constructor;
|
||||||
|
static Napi::Object wrapFunc(Napi::Env env, QObject* qobject);
|
||||||
// wrapped methods
|
// wrapped methods
|
||||||
};
|
};
|
||||||
|
|||||||
@ -14,6 +14,8 @@ struct CachedObject {
|
|||||||
napi_env env;
|
napi_env env;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef Napi::Object (*WrapFunc)(Napi::Env, QObject *);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* C++ side cache for wrapper objects.
|
* C++ side cache for wrapper objects.
|
||||||
*
|
*
|
||||||
@ -26,6 +28,7 @@ class DLL_EXPORT WrapperCache : public QObject {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
QMap<uint64_t, CachedObject> cache;
|
QMap<uint64_t, CachedObject> cache;
|
||||||
|
QMap<QString, WrapFunc> wrapperRegistry;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
@ -64,6 +67,35 @@ class DLL_EXPORT WrapperCache : public QObject {
|
|||||||
return wrapper;
|
return wrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void registerWrapper(QString typeName, WrapFunc wrapFunc) {
|
||||||
|
this->wrapperRegistry[typeName] = wrapFunc;
|
||||||
|
}
|
||||||
|
|
||||||
|
Napi::Value getWrapper(Napi::Env env, QObject* qobject) {
|
||||||
|
if (qobject == nullptr) {
|
||||||
|
return env.Null();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t ptrHash = extrautils::hashPointerTo53bit(qobject);
|
||||||
|
if (this->cache.contains(ptrHash)) {
|
||||||
|
napi_value result = nullptr;
|
||||||
|
napi_get_reference_value(env, this->cache[ptrHash].ref, &result);
|
||||||
|
|
||||||
|
napi_valuetype valuetype;
|
||||||
|
napi_typeof(env, result, &valuetype);
|
||||||
|
if (valuetype != napi_null) {
|
||||||
|
return Napi::Object(env, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// QString className(object->metaObject()->className());
|
||||||
|
// if (this->wrapperRegistry.contains(className)) {
|
||||||
|
// this->wrapperRegistry[className]
|
||||||
|
// }
|
||||||
|
|
||||||
|
return env.Null();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store a mapping from Qt Object to wrapper
|
* Store a mapping from Qt Object to wrapper
|
||||||
*
|
*
|
||||||
@ -89,8 +121,8 @@ class DLL_EXPORT WrapperCache : public QObject {
|
|||||||
static Napi::Object init(Napi::Env env, Napi::Object exports) {
|
static Napi::Object init(Napi::Env env, Napi::Object exports) {
|
||||||
exports.Set("WrapperCache_injectCallback",
|
exports.Set("WrapperCache_injectCallback",
|
||||||
Napi::Function::New<injectDestroyCallback>(env));
|
Napi::Function::New<injectDestroyCallback>(env));
|
||||||
// exports.Set("WrapperCache_storeJS",
|
exports.Set("WrapperCache_store",
|
||||||
// Napi::Function::New<storeJS>(env));
|
Napi::Function::New<storeJS>(env));
|
||||||
return exports;
|
return exports;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,6 +133,17 @@ class DLL_EXPORT WrapperCache : public QObject {
|
|||||||
return env.Null();
|
return env.Null();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Napi::Value storeJS(const Napi::CallbackInfo& info) {
|
||||||
|
Napi::Env env = info.Env();
|
||||||
|
|
||||||
|
Napi::Object objectWrapper = info[0].As<Napi::Object>();
|
||||||
|
QObject* qobject = info[1].As<Napi::External<QObject>>().Data();
|
||||||
|
|
||||||
|
uint64_t ptrHash = extrautils::hashPointerTo53bit(qobject);
|
||||||
|
instance.store(env, ptrHash, qobject, objectWrapper, false);
|
||||||
|
return env.Null();
|
||||||
|
}
|
||||||
|
|
||||||
static Napi::FunctionReference destroyedCallback;
|
static Napi::FunctionReference destroyedCallback;
|
||||||
|
|
||||||
public Q_SLOTS:
|
public Q_SLOTS:
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
#include "QtCore/QObject/qobject_wrap.h"
|
#include "QtCore/QObject/qobject_wrap.h"
|
||||||
|
|
||||||
#include "Extras/Utils/nutils.h"
|
#include "Extras/Utils/nutils.h"
|
||||||
|
#include "core/WrapperCache/wrappercache.h"
|
||||||
|
|
||||||
Napi::FunctionReference QObjectWrap::constructor;
|
Napi::FunctionReference QObjectWrap::constructor;
|
||||||
|
|
||||||
@ -11,6 +12,7 @@ Napi::Object QObjectWrap::init(Napi::Env env, Napi::Object exports) {
|
|||||||
env, CLASSNAME, {QOBJECT_WRAPPED_METHODS_EXPORT_DEFINE(QObjectWrap)});
|
env, CLASSNAME, {QOBJECT_WRAPPED_METHODS_EXPORT_DEFINE(QObjectWrap)});
|
||||||
constructor = Napi::Persistent(func);
|
constructor = Napi::Persistent(func);
|
||||||
exports.Set(CLASSNAME, func);
|
exports.Set(CLASSNAME, func);
|
||||||
|
WrapperCache::instance.registerWrapper(QString("NObject"), QObjectWrap::wrapFunc);
|
||||||
return exports;
|
return exports;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,4 +39,11 @@ QObjectWrap::QObjectWrap(const Napi::CallbackInfo& info)
|
|||||||
.ThrowAsJavaScriptException();
|
.ThrowAsJavaScriptException();
|
||||||
}
|
}
|
||||||
this->rawData = extrautils::configureQObject(this->getInternalInstance());
|
this->rawData = extrautils::configureQObject(this->getInternalInstance());
|
||||||
|
// WrapperCache::instance.store<QObject, QObjectWrap>(env, this->getInternalInstance(), this);
|
||||||
|
}
|
||||||
|
|
||||||
|
Napi::Object QObjectWrap::wrapFunc(Napi::Env env, QObject *qobject) {
|
||||||
|
// Qtype *exactQObject = dynamic_cast<Qtype*>(qobject)
|
||||||
|
Napi::Object wrapper = QObjectWrap::constructor.New({Napi::External<QObject>::New(env, qobject)});
|
||||||
|
return wrapper;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { checkIfNativeElement } from '../utils/helpers';
|
|||||||
import addon from '../utils/addon';
|
import addon from '../utils/addon';
|
||||||
import { QVariant, QVariantType } from './QVariant';
|
import { QVariant, QVariantType } from './QVariant';
|
||||||
import { TimerType } from '../QtEnums/TimerType';
|
import { TimerType } from '../QtEnums/TimerType';
|
||||||
|
import { wrapperCache } from '../core/WrapperCache';
|
||||||
|
|
||||||
export class QObject<Signals extends QObjectSignals = QObjectSignals> extends EventWidget<Signals> {
|
export class QObject<Signals extends QObjectSignals = QObjectSignals> extends EventWidget<Signals> {
|
||||||
constructor(nativeElementOrParent?: NativeElement | QObject) {
|
constructor(nativeElementOrParent?: NativeElement | QObject) {
|
||||||
@ -18,6 +19,9 @@ export class QObject<Signals extends QObjectSignals = QObjectSignals> extends Ev
|
|||||||
native = new addon.QObject();
|
native = new addon.QObject();
|
||||||
}
|
}
|
||||||
super(native);
|
super(native);
|
||||||
|
|
||||||
|
wrapperCache.store(this);
|
||||||
|
|
||||||
this.setNodeParent(parent);
|
this.setNodeParent(parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,6 +55,9 @@ export class QObject<Signals extends QObjectSignals = QObjectSignals> extends Ev
|
|||||||
this.native.setParent(null);
|
this.native.setParent(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
parent(): QObject {
|
||||||
|
return wrapperCache.getWrapper(this.native.parent());
|
||||||
|
}
|
||||||
startTimer(intervalMS: number, timerType = TimerType.CoarseTimer): number {
|
startTimer(intervalMS: number, timerType = TimerType.CoarseTimer): number {
|
||||||
return this.native.startTimer(intervalMS, timerType);
|
return this.native.startTimer(intervalMS, timerType);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,29 +13,68 @@ import { NativeElement } from './Component';
|
|||||||
* wrapper automatically and unexpectedly garbage collected.
|
* wrapper automatically and unexpectedly garbage collected.
|
||||||
*/
|
*/
|
||||||
export class WrapperCache {
|
export class WrapperCache {
|
||||||
private _cache = new Map<number, any>();
|
private _strongCache = new Map<number, QObject>();
|
||||||
|
private _weakCache = new Map<number, WeakRef<QObject>>();
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
addon.WrapperCache_injectCallback(this._objectDestroyedCallback.bind(this));
|
addon.WrapperCache_injectCallback(this._objectDestroyedCallback.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
private _objectDestroyedCallback(objectId: number): void {
|
private _objectDestroyedCallback(objectId: number): void {
|
||||||
if (!this._cache.has(objectId)) {
|
if (this._strongCache.has(objectId)) {
|
||||||
return;
|
const wrapper = this._strongCache.get(objectId);
|
||||||
|
wrapper.native = null;
|
||||||
|
this._strongCache.delete(objectId);
|
||||||
|
}
|
||||||
|
|
||||||
|
const wrapperRef = this._weakCache.get(objectId);
|
||||||
|
if (wrapperRef != null) {
|
||||||
|
const wrapper = wrapperRef.deref();
|
||||||
|
if (wrapper != null) {
|
||||||
|
wrapper.native = null;
|
||||||
|
this._weakCache.delete(objectId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const wrapper = this._cache.get(objectId);
|
|
||||||
wrapper.native = null;
|
|
||||||
this._cache.delete(objectId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get<T>(wrapperConstructor: { new (native: any): T }, native: NativeElement): T {
|
get<T extends QObject>(wrapperConstructor: { new (native: any): T }, native: NativeElement): T {
|
||||||
const id = native.__id__();
|
const id = native.__id__();
|
||||||
if (this._cache.has(id)) {
|
if (this._strongCache.has(id)) {
|
||||||
return this._cache.get(id) as T;
|
return this._strongCache.get(id) as T;
|
||||||
}
|
}
|
||||||
const wrapper = new wrapperConstructor(native);
|
const wrapper = new wrapperConstructor(native);
|
||||||
this._cache.set(id, wrapper);
|
this._strongCache.set(id, wrapper);
|
||||||
return wrapper;
|
return wrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getWrapper(native: any): QObject | null {
|
||||||
|
if (native == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const id = native.__id__();
|
||||||
|
|
||||||
|
if (this._strongCache.has(id)) {
|
||||||
|
return this._strongCache.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ref = this._weakCache.get(id);
|
||||||
|
if (ref != null) {
|
||||||
|
const wrapper = ref.deref();
|
||||||
|
if (wrapper != null) {
|
||||||
|
return wrapper;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null; // FIXME: Create new wrapper on demand.
|
||||||
|
}
|
||||||
|
|
||||||
|
store(wrapper: QObject): void {
|
||||||
|
if (wrapper.native != null) {
|
||||||
|
const id = wrapper.native.__id__();
|
||||||
|
this._weakCache.set(id, new WeakRef<QObject>(wrapper));
|
||||||
|
|
||||||
|
addon.WrapperCache_store(wrapper.native, wrapper.native.__external_qobject__());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
export const wrapperCache = new WrapperCache();
|
export const wrapperCache = new WrapperCache();
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { QObject } from '../../QtCore/QObject';
|
||||||
import { QApplication } from '../../QtGui/QApplication';
|
import { QApplication } from '../../QtGui/QApplication';
|
||||||
import { CacheTestQObject } from './CacheTestQObject';
|
import { CacheTestQObject } from './CacheTestQObject';
|
||||||
|
|
||||||
@ -43,5 +44,20 @@ describe('WrapperCache using CacheTestQObject', () => {
|
|||||||
expect(foo).not.toEqual(bar);
|
expect(foo).not.toEqual(bar);
|
||||||
expect(foo.native.__id__()).not.toEqual(bar.native.__id__());
|
expect(foo.native.__id__()).not.toEqual(bar.native.__id__());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('QObject.parent() can be null', () => {
|
||||||
|
const a = new QObject();
|
||||||
|
expect(a.parent()).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('QObject.parent() === QObject.parent()', () => {
|
||||||
|
const a = new QObject();
|
||||||
|
const b = new QObject(a);
|
||||||
|
expect(a.native.__id__()).toEqual(b.parent().native.__id__());
|
||||||
|
expect(a).toEqual(b.parent());
|
||||||
|
(<any>a)['magic'] = true;
|
||||||
|
expect((<any>b.parent())['magic']).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
qApp.quit();
|
qApp.quit();
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ES2015",
|
"target": "ES2021",
|
||||||
"module": "commonjs",
|
"module": "commonjs",
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
"sourceMap": false,
|
"sourceMap": false,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user