diff --git a/src/cpp/include/nodegui/core/Component/component_macro.h b/src/cpp/include/nodegui/core/Component/component_macro.h index 578c7f3cb..87fd4d1dd 100644 --- a/src/cpp/include/nodegui/core/Component/component_macro.h +++ b/src/cpp/include/nodegui/core/Component/component_macro.h @@ -4,7 +4,9 @@ #ifndef COMPONENT_WRAPPED_METHODS_EXPORT_DEFINE #define COMPONENT_WRAPPED_METHODS_EXPORT_DEFINE(ComponentWrapName) \ \ - InstanceValue("type", Napi::String::New(env, "native")), + InstanceValue("type", Napi::String::New(env, "native")), \ + InstanceValue("wrapperType", \ + Napi::String::New(env, #ComponentWrapName)), #endif #ifndef COMPONENT_WRAPPED_METHODS_DECLARATION diff --git a/src/cpp/include/nodegui/core/WrapperCache/wrappercache.h b/src/cpp/include/nodegui/core/WrapperCache/wrappercache.h index 822200104..4c43d33b4 100644 --- a/src/cpp/include/nodegui/core/WrapperCache/wrappercache.h +++ b/src/cpp/include/nodegui/core/WrapperCache/wrappercache.h @@ -47,12 +47,10 @@ class DLL_EXPORT WrapperCache : public QObject { Napi::Object get(Napi::Env env, T* object) { if (this->cache.contains(object)) { napi_value result = nullptr; - napi_get_reference_value(this->cache[object].env, this->cache[object].ref, - &result); + napi_get_reference_value(env, this->cache[object].ref, &result); return Napi::Object(env, result); } - Napi::HandleScope scope(env); Napi::Object wrapper = W::constructor.New({Napi::External::New(env, object)}); @@ -67,7 +65,6 @@ class DLL_EXPORT WrapperCache : public QObject { } static Napi::Object init(Napi::Env env, Napi::Object exports) { - Napi::HandleScope scope(env); exports.Set("WrapperCache_injectCallback", Napi::Function::New(env)); return exports; @@ -75,7 +72,6 @@ class DLL_EXPORT WrapperCache : public QObject { static Napi::Value injectDestroyCallback(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); - Napi::HandleScope scope(env); destroyedCallback = Napi::Persistent(info[0].As()); return env.Null(); @@ -95,6 +91,7 @@ class DLL_EXPORT WrapperCache : public QObject { Napi::Env env = destroyedCallback.Env(); Napi::HandleScope scope(env); destroyedCallback.Call( + env.Global(), {Napi::Value::From(env, extrautils::hashPointerTo53bit(object))}); } diff --git a/src/index.ts b/src/index.ts index 07e82f34a..2d32e0708 100644 --- a/src/index.ts +++ b/src/index.ts @@ -168,7 +168,13 @@ export { FlexLayout, FlexLayoutSignals } from './lib/core/FlexLayout'; // Others: export { StyleSheet } from './lib/core/Style/StyleSheet'; export { NativeElement, Component } from './lib/core/Component'; -export { checkIfNativeElement, checkIfNapiExternal } from './lib/utils/helpers'; +export { + checkIfNativeElement, + checkIfNapiExternal, + JsWrapFunction, + registerNativeWrapFunction as registerNativeWrapper, + wrapNative, +} from './lib/utils/helpers'; export { Margins } from './lib/utils/Margins'; // Test: diff --git a/src/lib/QtGui/QScreen.ts b/src/lib/QtGui/QScreen.ts index 613f120d0..dc908bc9c 100644 --- a/src/lib/QtGui/QScreen.ts +++ b/src/lib/QtGui/QScreen.ts @@ -1,9 +1,10 @@ import { NativeElement } from '../core/Component'; -import { checkIfNativeElement } from '../utils/helpers'; +import { checkIfNativeElement, registerNativeWrapFunction } from '../utils/helpers'; import { NodeObject, QObjectSignals } from '../QtCore/QObject'; import { QRect } from '../QtCore/QRect'; import { QSizeF } from '../QtCore/QSizeF'; import { QSize } from '../QtCore/QSize'; +import { wrapperCache } from '../core/WrapperCache'; export class QScreen extends NodeObject { native: NativeElement; @@ -104,3 +105,7 @@ export interface QScreenSignals extends QObjectSignals { refreshRateChanged: (refreshRate: number) => void; virtualGeometryChanged: (rect: QRect) => void; } + +registerNativeWrapFunction('QScreenWrap', (native: any) => { + return wrapperCache.get(QScreen, native); +}); diff --git a/src/lib/QtGui/QWindow.ts b/src/lib/QtGui/QWindow.ts index 6795f4ead..f5cd4b888 100644 --- a/src/lib/QtGui/QWindow.ts +++ b/src/lib/QtGui/QWindow.ts @@ -1,5 +1,5 @@ import { NativeElement } from '../core/Component'; -import { checkIfNativeElement } from '../utils/helpers'; +import { checkIfNativeElement, registerNativeWrapFunction } from '../utils/helpers'; import { NodeObject, QObjectSignals } from '../QtCore/QObject'; import { QScreen } from './QScreen'; import { wrapperCache } from '../core/WrapperCache'; @@ -24,3 +24,7 @@ export class QWindow extends NodeObject { export interface QWindowSignals extends QObjectSignals { screenChanged: (screen: QScreen) => void; } + +registerNativeWrapFunction('QWindowWrap', (native: any) => { + return wrapperCache.get(QWindow, native); +}); diff --git a/src/lib/core/EventWidget.ts b/src/lib/core/EventWidget.ts index c7d1a0ae4..b41950104 100644 --- a/src/lib/core/EventWidget.ts +++ b/src/lib/core/EventWidget.ts @@ -1,6 +1,6 @@ import { EventEmitter } from 'events'; import { NativeElement, Component, NativeRawPointer } from './Component'; -import { wrapWithActivateUvLoop } from '../utils/helpers'; +import { wrapNative, wrapWithActivateUvLoop } from '../utils/helpers'; function addDefaultErrorHandler(native: NativeElement, emitter: EventEmitter): void { native.subscribeToQtEvent('error'); @@ -54,10 +54,11 @@ export abstract class EventWidget extends Component { // Preserve the value of `_isQObjectEventProcessed` as we dispatch this event // to JS land, and restore it afterwards. This lets us support recursive event // dispatches on the same object. + const wrappedArgs = args.map(wrapNative); const previousEventProcessed = this._isEventProcessed; this._isEventProcessed = false; try { - this.emitter.emit(event, ...args); + this.emitter.emit(event, ...wrappedArgs); } catch (e) { console.log(`An exception was thrown while dispatching an event of type '${event.toString()}':`); console.log(e); diff --git a/src/lib/utils/helpers.ts b/src/lib/utils/helpers.ts index b9e80c177..b786850c2 100644 --- a/src/lib/utils/helpers.ts +++ b/src/lib/utils/helpers.ts @@ -25,3 +25,37 @@ export function wrapWithActivateUvLoop(func: T): T { }; return fn as any; } + +export type JsWrapFunction = (element: any) => any; +const nativeWrapperRegistry = new Map(); + +/** + * Register a function to wrap a specific Node API wrapper objects with a JS object. + * + * @param wrapperTypeName the C++ wrapper type name the wrap function applies to. + * @param jsWrapFunction function to wrap a native wrapper to a JS wrapper object. + */ +export function registerNativeWrapFunction(wrapperTypeName: string, jsWrapFunction: JsWrapFunction): void { + nativeWrapperRegistry.set(wrapperTypeName, jsWrapFunction); +} + +/** + * Try to wrap a native Node object with its JS wrapper. + * + * @param native the native object to wrap + * @return the JS object wrapping the native object or the native object if + * it couldn't be wrapped or doesn't need to be wrapped. + */ +// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types +export function wrapNative(native: any): any { + if (!checkIfNativeElement(native)) { + return native; + } + + const func: JsWrapFunction | undefined = nativeWrapperRegistry.get(native.wrapperType); + if (func == null) { + return native; + } + + return func(native); +}