From bfd45b3a5458a73b369d7a8aa0285a691a02004e Mon Sep 17 00:00:00 2001 From: Simon Edwards Date: Fri, 22 Oct 2021 22:13:17 +0200 Subject: [PATCH 01/10] Add `QScreen` and `QWindow`, remove the deprecated `QDesktopWidget` --- CMakeLists.txt | 4 +- .../nodegui/QtCore/QObject/qobject_macro.h | 9 +- .../nodegui/QtGui/QScreen/qscreen_wrap.h | 29 +++++ .../nodegui/QtGui/QWindow/qwindow_wrap.h | 30 +++++ .../QDesktopWidget/nqdesktopwidget.hpp | 15 --- .../QDesktopWidget/qdesktopwidget_wrap.h | 24 ---- .../nodegui/QtWidgets/QWidget/qwidget_macro.h | 15 ++- src/cpp/lib/QtGui/QScreen/qscreen_wrap.cpp | 104 +++++++++++++++++ src/cpp/lib/QtGui/QWindow/qwindow_wrap.cpp | 57 ++++++++++ .../QDesktopWidget/qdesktopwidget_wrap.cpp | 78 ------------- src/cpp/main.cpp | 6 +- src/index.ts | 3 +- src/lib/QtGui/QApplication.ts | 4 - src/lib/QtGui/QScreen.ts | 106 ++++++++++++++++++ src/lib/QtGui/QWindow.ts | 26 +++++ src/lib/QtWidgets/QDesktopWidget.ts | 50 --------- src/lib/QtWidgets/QWidget.ts | 12 +- 17 files changed, 390 insertions(+), 182 deletions(-) create mode 100644 src/cpp/include/nodegui/QtGui/QScreen/qscreen_wrap.h create mode 100644 src/cpp/include/nodegui/QtGui/QWindow/qwindow_wrap.h delete mode 100644 src/cpp/include/nodegui/QtWidgets/QDesktopWidget/nqdesktopwidget.hpp delete mode 100644 src/cpp/include/nodegui/QtWidgets/QDesktopWidget/qdesktopwidget_wrap.h create mode 100644 src/cpp/lib/QtGui/QScreen/qscreen_wrap.cpp create mode 100644 src/cpp/lib/QtGui/QWindow/qwindow_wrap.cpp delete mode 100644 src/cpp/lib/QtWidgets/QDesktopWidget/qdesktopwidget_wrap.cpp create mode 100644 src/lib/QtGui/QScreen.ts create mode 100644 src/lib/QtGui/QWindow.ts delete mode 100644 src/lib/QtWidgets/QDesktopWidget.ts diff --git a/CMakeLists.txt b/CMakeLists.txt index 752fde426..7bdd5c41a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -65,7 +65,9 @@ add_library(${CORE_WIDGETS_ADDON} SHARED "${PROJECT_SOURCE_DIR}/src/cpp/lib/QtGui/QKeySequence/qkeysequence_wrap.cpp" "${PROJECT_SOURCE_DIR}/src/cpp/lib/QtGui/QMovie/qmovie_wrap.cpp" "${PROJECT_SOURCE_DIR}/src/cpp/lib/QtGui/QPalette/qpalette_wrap.cpp" + "${PROJECT_SOURCE_DIR}/src/cpp/lib/QtGui/QScreen/qscreen_wrap.cpp" "${PROJECT_SOURCE_DIR}/src/cpp/lib/QtGui/QStyle/qstyle_wrap.cpp" + "${PROJECT_SOURCE_DIR}/src/cpp/lib/QtGui/QWindow/qwindow_wrap.cpp" "${PROJECT_SOURCE_DIR}/src/cpp/lib/QtCore/QAbstractItemModel/qabstractitemmodel_wrap.cpp" "${PROJECT_SOURCE_DIR}/src/cpp/lib/QtCore/QDate/qdate_wrap.cpp" "${PROJECT_SOURCE_DIR}/src/cpp/lib/QtCore/QDateTime/qdatetime_wrap.cpp" @@ -147,7 +149,6 @@ add_library(${CORE_WIDGETS_ADDON} SHARED "${PROJECT_SOURCE_DIR}/src/cpp/lib/QtWidgets/QStandardItemModel/qstandarditemmodel_wrap.cpp" "${PROJECT_SOURCE_DIR}/src/cpp/lib/QtWidgets/QStandardItem/qstandarditem_wrap.cpp" "${PROJECT_SOURCE_DIR}/src/cpp/lib/QtWidgets/QSvgWidget/qsvgwidget_wrap.cpp" - "${PROJECT_SOURCE_DIR}/src/cpp/lib/QtWidgets/QDesktopWidget/qdesktopwidget_wrap.cpp" "${PROJECT_SOURCE_DIR}/src/cpp/lib/QtWidgets/QStyleFactory/qstylefactory_wrap.cpp" # Custom widgets (include them for automoc since they contain Q_OBJECT) "${PROJECT_SOURCE_DIR}/src/cpp/include/nodegui/QtCore/QAbstractItemModel/nabstractitemmodel.hpp" @@ -213,7 +214,6 @@ add_library(${CORE_WIDGETS_ADDON} SHARED "${PROJECT_SOURCE_DIR}/src/cpp/include/nodegui/QtWidgets/QTextBrowser/ntextbrowser.hpp" "${PROJECT_SOURCE_DIR}/src/cpp/include/nodegui/QtWidgets/QTextEdit/ntextedit.hpp" "${PROJECT_SOURCE_DIR}/src/cpp/include/nodegui/QtWidgets/QSvgWidget/nsvgwidget.hpp" - "${PROJECT_SOURCE_DIR}/src/cpp/include/nodegui/QtWidgets/QDesktopWidget/nqdesktopwidget.hpp" "${PROJECT_SOURCE_DIR}/src/cpp/include/nodegui/QtWidgets/QHeaderView/nheaderview.hpp" ) diff --git a/src/cpp/include/nodegui/QtCore/QObject/qobject_macro.h b/src/cpp/include/nodegui/QtCore/QObject/qobject_macro.h index 5d4123c4d..0635b04f2 100644 --- a/src/cpp/include/nodegui/QtCore/QObject/qobject_macro.h +++ b/src/cpp/include/nodegui/QtCore/QObject/qobject_macro.h @@ -90,9 +90,9 @@ #endif // QOBJECT_WRAPPED_METHODS_EXPORT_DEFINE -#ifndef QOBJECT_SIGNALS -#define QOBJECT_SIGNALS \ - QObject::connect(this, &QObject::objectNameChanged, \ +#ifndef QOBJECT_SIGNALS_ON_TARGET +#define QOBJECT_SIGNALS_ON_TARGET(target) \ + QObject::connect(target, &QObject::objectNameChanged, \ [=](const QString& objectName) { \ Napi::Env env = this->emitOnNode.Env(); \ Napi::HandleScope scope(env); \ @@ -100,5 +100,8 @@ {Napi::String::New(env, "objectNameChanged"), \ Napi::Value::From(env, objectName.toStdString())}); \ }); +#endif // QOBJECT_SIGNALS_ON_TARGET +#ifndef QOBJECT_SIGNALS +#define QOBJECT_SIGNALS QOBJECT_SIGNALS_ON_TARGET(this) #endif // QOBJECT_SIGNALS diff --git a/src/cpp/include/nodegui/QtGui/QScreen/qscreen_wrap.h b/src/cpp/include/nodegui/QtGui/QScreen/qscreen_wrap.h new file mode 100644 index 000000000..d53f02cbe --- /dev/null +++ b/src/cpp/include/nodegui/QtGui/QScreen/qscreen_wrap.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +#include +#include + +#include "Extras/Export/export.h" +#include "QtCore/QObject/qobject_macro.h" + +class DLL_EXPORT QScreenWrap : public Napi::ObjectWrap, public EventWidget { + QOBJECT_WRAPPED_METHODS_DECLARATION + // Note: We don't use EVENTWIDGET_IMPLEMENTATIONS() here because this class doesn't handle any QEvents. + + private: + QPointer instance; + + public: + // class constructor + static Napi::FunctionReference constructor; + + static Napi::Object init(Napi::Env env, Napi::Object exports); + QScreenWrap(const Napi::CallbackInfo& info); + QScreen* getInternalInstance(); + + virtual void connectSignalsToEventEmitter(); + + // Wrapped methods +}; diff --git a/src/cpp/include/nodegui/QtGui/QWindow/qwindow_wrap.h b/src/cpp/include/nodegui/QtGui/QWindow/qwindow_wrap.h new file mode 100644 index 000000000..0ae4009a3 --- /dev/null +++ b/src/cpp/include/nodegui/QtGui/QWindow/qwindow_wrap.h @@ -0,0 +1,30 @@ +#pragma once + +#include + +#include +#include + +#include "Extras/Export/export.h" +#include "QtCore/QObject/qobject_macro.h" + +class DLL_EXPORT QWindowWrap : public Napi::ObjectWrap, public EventWidget { + QOBJECT_WRAPPED_METHODS_DECLARATION + // Note: We don't use EVENTWIDGET_IMPLEMENTATIONS() here because this class doesn't handle any QEvents. + + private: + QPointer instance; + + public: + // class constructor + static Napi::FunctionReference constructor; + + static Napi::Object init(Napi::Env env, Napi::Object exports); + QWindowWrap(const Napi::CallbackInfo& info); + QWindow* getInternalInstance(); + + virtual void connectSignalsToEventEmitter(); + + // wrapped methods + Napi::Value screen(const Napi::CallbackInfo& info); +}; diff --git a/src/cpp/include/nodegui/QtWidgets/QDesktopWidget/nqdesktopwidget.hpp b/src/cpp/include/nodegui/QtWidgets/QDesktopWidget/nqdesktopwidget.hpp deleted file mode 100644 index cd563fd87..000000000 --- a/src/cpp/include/nodegui/QtWidgets/QDesktopWidget/nqdesktopwidget.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include -#include - -#include "core/NodeWidget/nodewidget.h" - -class NQDesktopWidget : public QDesktopWidget, public NodeWidget { - public: - Q_OBJECT - NODEWIDGET_IMPLEMENTATIONS(QDesktopWidget) - public: - using QDesktopWidget::QDesktopWidget; // inherit all constructors of - // QStatusBar -}; diff --git a/src/cpp/include/nodegui/QtWidgets/QDesktopWidget/qdesktopwidget_wrap.h b/src/cpp/include/nodegui/QtWidgets/QDesktopWidget/qdesktopwidget_wrap.h deleted file mode 100644 index 845425907..000000000 --- a/src/cpp/include/nodegui/QtWidgets/QDesktopWidget/qdesktopwidget_wrap.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once -#include - -#include "QtWidgets/QWidget/qwidget_macro.h" -#include "napi.h" -#include "nqdesktopwidget.hpp" - -class QDesktopWidgetWrap : public Napi::ObjectWrap { - private: - QPointer instance; - - public: - static Napi::Object init(Napi::Env env, Napi::Object exports); - QDesktopWidgetWrap(const Napi::CallbackInfo &info); - ~QDesktopWidgetWrap(); - NQDesktopWidget *getInternalInstance(); - static Napi::FunctionReference constructor; - // wrapped methods - Napi::Value availableGeometry(const Napi::CallbackInfo &info); - Napi::Value screenGeometry(const Napi::CallbackInfo &info); - Napi::Value screenNumber(const Napi::CallbackInfo &info); - - QWIDGET_WRAPPED_METHODS_DECLARATION -}; diff --git a/src/cpp/include/nodegui/QtWidgets/QWidget/qwidget_macro.h b/src/cpp/include/nodegui/QtWidgets/QWidget/qwidget_macro.h index 91310178e..8fe90a218 100644 --- a/src/cpp/include/nodegui/QtWidgets/QWidget/qwidget_macro.h +++ b/src/cpp/include/nodegui/QtWidgets/QWidget/qwidget_macro.h @@ -10,6 +10,7 @@ #include "QtGui/QCursor/qcursor_wrap.h" #include "QtGui/QIcon/qicon_wrap.h" #include "QtGui/QStyle/qstyle_wrap.h" +#include "QtGui/QWindow/qwindow_wrap.h" #include "QtWidgets/QAction/qaction_wrap.h" #include "QtWidgets/QLayout/qlayout_wrap.h" #include "core/YogaWidget/yogawidget_macro.h" @@ -537,6 +538,17 @@ bool modified = info[0].As().Value(); \ this->instance->setWindowModified(modified); \ return env.Null(); \ + } \ + Napi::Value windowHandle(const Napi::CallbackInfo& info) { \ + Napi::Env env = info.Env(); \ + Napi::HandleScope scope(env); \ + QWindow* window = this->instance->windowHandle(); \ + if (window) { \ + return QWindowWrap::constructor.New( \ + {Napi::External::New(env, window)}); \ + } else { \ + return env.Null(); \ + } \ } #endif // QWIDGET_WRAPPED_METHODS_DECLARATION @@ -613,7 +625,8 @@ InstanceMethod("setDisabled", &WidgetWrapName::setDisabled), \ InstanceMethod("setHidden", &WidgetWrapName::setHidden), \ InstanceMethod("setVisible", &WidgetWrapName::setVisible), \ - InstanceMethod("setWindowModified", &WidgetWrapName::setWindowModified), + InstanceMethod("setWindowModified", &WidgetWrapName::setWindowModified), \ + InstanceMethod("windowHandle", &WidgetWrapName::windowHandle), #endif // QWIDGET_WRAPPED_METHODS_EXPORT_DEFINE diff --git a/src/cpp/lib/QtGui/QScreen/qscreen_wrap.cpp b/src/cpp/lib/QtGui/QScreen/qscreen_wrap.cpp new file mode 100644 index 000000000..0ab10ce8a --- /dev/null +++ b/src/cpp/lib/QtGui/QScreen/qscreen_wrap.cpp @@ -0,0 +1,104 @@ +#include "QtGui/QScreen/qscreen_wrap.h" +#include "QtCore/QRect/qrect_wrap.h" +#include "QtCore/QSizeF/qsizef_wrap.h" +#include "Extras/Utils/nutils.h" + +Napi::FunctionReference QScreenWrap::constructor; + +Napi::Object QScreenWrap::init(Napi::Env env, Napi::Object exports) { + Napi::HandleScope scope(env); + char CLASSNAME[] = "QScreen"; + Napi::Function func = + DefineClass(env, CLASSNAME, + {//InstanceMethod("clear", &QScreenWrap::clear), + QOBJECT_WRAPPED_METHODS_EXPORT_DEFINE(QScreenWrap)}); + constructor = Napi::Persistent(func); + exports.Set(CLASSNAME, func); + return exports; +} + +QScreenWrap::QScreenWrap(const Napi::CallbackInfo& info) + : Napi::ObjectWrap(info) { + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + if (info[0].IsExternal()) { + this->instance = info[0].As>().Data(); + } else { + Napi::TypeError::New(env, "Incorrect initialization of QScreenWrap") + .ThrowAsJavaScriptException(); + } + this->rawData = extrautils::configureComponent(this->getInternalInstance()); +} + +QScreen* QScreenWrap::getInternalInstance() { return this->instance; } + +void QScreenWrap::connectSignalsToEventEmitter() { + QOBJECT_SIGNALS_ON_TARGET(this->instance.data()); + + QObject::connect(this->instance.data(), &QScreen::availableGeometryChanged, [=](const QRect& geometry) { + Napi::Env env = this->emitOnNode.Env(); + Napi::HandleScope scope(env); + auto instance = QRectWrap::constructor.New({Napi::External::New(env, + new QRect(geometry.x(), geometry.y(), geometry.width(), geometry.height()))}); + this->emitOnNode.Call({Napi::String::New(env, "availableGeometryChanged"), instance}); + }); + + QObject::connect(this->instance.data(), &QScreen::geometryChanged, [=](const QRect& geometry) { + Napi::Env env = this->emitOnNode.Env(); + Napi::HandleScope scope(env); + auto instance = QRectWrap::constructor.New({Napi::External::New(env, + new QRect(geometry.x(), geometry.y(), geometry.width(), geometry.height()))}); + this->emitOnNode.Call({Napi::String::New(env, "geometryChanged"), instance}); + }); + + QObject::connect(this->instance.data(), &QScreen::logicalDotsPerInchChanged, [=](qreal dpi) { + Napi::Env env = this->emitOnNode.Env(); + Napi::HandleScope scope(env); + this->emitOnNode.Call( + {Napi::String::New(env, "logicalDotsPerInchChanged"), Napi::Value::From(env, dpi)}); + }); + + QObject::connect(this->instance.data(), &QScreen::orientationChanged, [=](Qt::ScreenOrientation orientation) { + Napi::Env env = this->emitOnNode.Env(); + Napi::HandleScope scope(env); + this->emitOnNode.Call( + {Napi::String::New(env, "orientationChanged"), Napi::Value::From(env, static_cast(orientation))}); + }); + + QObject::connect(this->instance.data(), &QScreen::physicalDotsPerInchChanged, [=](qreal dpi) { + Napi::Env env = this->emitOnNode.Env(); + Napi::HandleScope scope(env); + this->emitOnNode.Call( + {Napi::String::New(env, "physicalDotsPerInchChanged"), Napi::Value::From(env, dpi)}); + }); + + QObject::connect(this->instance.data(), &QScreen::physicalSizeChanged, [=](const QSizeF& size) { + Napi::Env env = this->emitOnNode.Env(); + Napi::HandleScope scope(env); + auto instance = QSizeFWrap::constructor.New({Napi::External::New(env, + new QSizeF(size))}); + this->emitOnNode.Call({Napi::String::New(env, "physicalSizeChanged"), instance}); + }); + + QObject::connect(this->instance.data(), &QScreen::primaryOrientationChanged, [=](Qt::ScreenOrientation orientation) { + Napi::Env env = this->emitOnNode.Env(); + Napi::HandleScope scope(env); + this->emitOnNode.Call( + {Napi::String::New(env, "primaryOrientationChanged"), Napi::Value::From(env, static_cast(orientation))}); + }); + + QObject::connect(this->instance.data(), &QScreen::refreshRateChanged, [=](qreal refreshRate) { + Napi::Env env = this->emitOnNode.Env(); + Napi::HandleScope scope(env); + this->emitOnNode.Call( + {Napi::String::New(env, "refreshRateChanged"), Napi::Value::From(env, refreshRate)}); + }); + + QObject::connect(this->instance.data(), &QScreen::virtualGeometryChanged, [=](const QRect& rect) { + Napi::Env env = this->emitOnNode.Env(); + Napi::HandleScope scope(env); + auto instance = QRectWrap::constructor.New({Napi::External::New(env, + new QRect(rect.x(), rect.y(), rect.width(), rect.height()))}); + this->emitOnNode.Call({Napi::String::New(env, "virtualGeometryChanged"), instance}); + }); +} diff --git a/src/cpp/lib/QtGui/QWindow/qwindow_wrap.cpp b/src/cpp/lib/QtGui/QWindow/qwindow_wrap.cpp new file mode 100644 index 000000000..4a994a080 --- /dev/null +++ b/src/cpp/lib/QtGui/QWindow/qwindow_wrap.cpp @@ -0,0 +1,57 @@ +#include "QtGui/QWindow/qwindow_wrap.h" +#include "QtGui/QScreen/qscreen_wrap.h" +#include "Extras/Utils/nutils.h" + +Napi::FunctionReference QWindowWrap::constructor; + +Napi::Object QWindowWrap::init(Napi::Env env, Napi::Object exports) { + Napi::HandleScope scope(env); + char CLASSNAME[] = "QWindow"; + Napi::Function func = DefineClass( + env, CLASSNAME, + {InstanceMethod("screen", &QWindowWrap::screen), + QOBJECT_WRAPPED_METHODS_EXPORT_DEFINE(QWindowWrap)}); + constructor = Napi::Persistent(func); + exports.Set(CLASSNAME, func); + return exports; +} + +QWindow* QWindowWrap::getInternalInstance() { return this->instance; } + +QWindowWrap::QWindowWrap(const Napi::CallbackInfo& info) + : Napi::ObjectWrap(info) { + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + if (info.Length() == 1 && info[0].IsExternal()) { + this->instance = info[0].As>().Data(); + } else { + Napi::TypeError::New(env, "Wrong number of arguments to QWindow.") + .ThrowAsJavaScriptException(); + } + this->rawData = extrautils::configureQObject(this->getInternalInstance()); +} + +void QWindowWrap::connectSignalsToEventEmitter() { + QOBJECT_SIGNALS_ON_TARGET(this->instance.data()); + + QObject::connect(this->instance.data(), &QWindow::screenChanged, [=](QScreen* screen) { + Napi::Env env = this->emitOnNode.Env(); + Napi::HandleScope scope(env); + this->emitOnNode.Call( + {Napi::String::New(env, "screenChanged")}); + }); +} + +Napi::Value QWindowWrap::screen(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + + QScreen* screen = this->instance->screen(); + if (screen) { + auto instance = QScreenWrap::constructor.New( + {Napi::External::New(env, screen)}); + return instance; + } else { + return env.Null(); + } +} diff --git a/src/cpp/lib/QtWidgets/QDesktopWidget/qdesktopwidget_wrap.cpp b/src/cpp/lib/QtWidgets/QDesktopWidget/qdesktopwidget_wrap.cpp deleted file mode 100644 index 9bf8f5e50..000000000 --- a/src/cpp/lib/QtWidgets/QDesktopWidget/qdesktopwidget_wrap.cpp +++ /dev/null @@ -1,78 +0,0 @@ -#include "QtWidgets/QDesktopWidget/qdesktopwidget_wrap.h" - -#include - -#include "Extras/Utils/nutils.h" -#include "QtCore/QRect/qrect_wrap.h" -#include "QtWidgets/QWidget/qwidget_wrap.h" - -Napi::FunctionReference QDesktopWidgetWrap::constructor; - -Napi::Object QDesktopWidgetWrap::init(Napi::Env env, Napi::Object exports) { - Napi::HandleScope scope(env); - char CLASSNAME[] = "QDesktopWidget"; - Napi::Function func = DefineClass( - env, CLASSNAME, - {InstanceMethod("screenGeometry", &QDesktopWidgetWrap::screenGeometry), - InstanceMethod("availableGeometry", - &QDesktopWidgetWrap::availableGeometry), - InstanceMethod("screenNumber", &QDesktopWidgetWrap::screenNumber), - QWIDGET_WRAPPED_METHODS_EXPORT_DEFINE(QDesktopWidgetWrap)}); - constructor = Napi::Persistent(func); - exports.Set(CLASSNAME, func); - return exports; -} - -NQDesktopWidget *QDesktopWidgetWrap::getInternalInstance() { - return this->instance; -} - -QDesktopWidgetWrap::QDesktopWidgetWrap(const Napi::CallbackInfo &info) - : Napi::ObjectWrap(info) { - Napi::Env env = info.Env(); - Napi::HandleScope scope(env); - - if (info.Length() == 0) { - this->instance = new NQDesktopWidget(); - } else { - Napi::TypeError::New(env, "Wrong number of arguments") - .ThrowAsJavaScriptException(); - } - this->rawData = extrautils::configureQWidget( - this->getInternalInstance(), this->getInternalInstance()->getFlexNode(), - true); -} - -QDesktopWidgetWrap::~QDesktopWidgetWrap() { - extrautils::safeDelete(this->instance); -} - -Napi::Value QDesktopWidgetWrap::screenGeometry(const Napi::CallbackInfo &info) { - Napi::Env env = info.Env(); - Napi::HandleScope scope(env); - - Napi::Number screen = info[0].As(); - QRect rect = this->instance->screenGeometry(screen); - auto instance = QRectWrap::constructor.New( - {Napi::External::New(env, new QRect(rect))}); - return instance; -} - -Napi::Value QDesktopWidgetWrap::availableGeometry( - const Napi::CallbackInfo &info) { - Napi::Env env = info.Env(); - Napi::HandleScope scope(env); - - Napi::Number screen = info[0].As(); - QRect rect = this->instance->availableGeometry(screen); - auto instance = QRectWrap::constructor.New( - {Napi::External::New(env, new QRect(rect))}); - return instance; -} - -Napi::Value QDesktopWidgetWrap::screenNumber(const Napi::CallbackInfo &info) { - Napi::Env env = info.Env(); - Napi::HandleScope scope(env); - int value = this->instance->screenNumber(); - return Napi::Value::From(env, value); -} diff --git a/src/cpp/main.cpp b/src/cpp/main.cpp index 14963003c..ea7526d3e 100644 --- a/src/cpp/main.cpp +++ b/src/cpp/main.cpp @@ -45,7 +45,9 @@ #include "QtGui/QPen/qpen_wrap.h" #include "QtGui/QPicture/qpicture_wrap.h" #include "QtGui/QPixmap/qpixmap_wrap.h" +#include "QtGui/QScreen/qscreen_wrap.h" #include "QtGui/QStyle/qstyle_wrap.h" +#include "QtGui/QWindow/qwindow_wrap.h" #include "QtWidgets/QAction/qaction_wrap.h" #include "QtWidgets/QBoxLayout/qboxlayout_wrap.h" #include "QtWidgets/QButtonGroup/qbuttongroup_wrap.h" @@ -55,7 +57,6 @@ #include "QtWidgets/QComboBox/qcombobox_wrap.h" #include "QtWidgets/QDateEdit/qdateedit_wrap.h" #include "QtWidgets/QDateTimeEdit/qdatetimeedit_wrap.h" -#include "QtWidgets/QDesktopWidget/qdesktopwidget_wrap.h" #include "QtWidgets/QDial/qdial_wrap.h" #include "QtWidgets/QDialog/qdialog_wrap.h" #include "QtWidgets/QDoubleSpinBox/qdoublespinbox_wrap.h" @@ -225,13 +226,14 @@ Napi::Object Main(Napi::Env env, Napi::Object exports) { QStandardItemModelWrap::init(env, exports); QStandardItemWrap::init(env, exports); QSvgWidgetWrap::init(env, exports); - QDesktopWidgetWrap::init(env, exports); QPaintEventWrap::init(env, exports); QPaletteWrap::init(env, exports); QAbstractItemModelWrap::init(env, exports); QHeaderViewWrap::init(env, exports); QItemSelectionModelWrap::init(env, exports); QStyleFactoryWrap::init(env, exports); + QScreenWrap::init(env, exports); + QWindowWrap::init(env, exports); return exports; } diff --git a/src/index.ts b/src/index.ts index a63d50611..6b0d63ac8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -32,6 +32,8 @@ export { QDropEvent } from './lib/QtGui/QEvent/QDropEvent'; export { QDragMoveEvent } from './lib/QtGui/QEvent/QDragMoveEvent'; export { QDragLeaveEvent } from './lib/QtGui/QEvent/QDragLeaveEvent'; export { QPaintEvent } from './lib/QtGui/QEvent/QPaintEvent'; +export { QScreen } from './lib/QtGui/QScreen'; +export { QWindow } from './lib/QtGui/QWindow'; export { WidgetEventTypes } from './lib/core/EventWidget'; // Abstract: export { NodeWidget, QWidget, QWidgetSignals } from './lib/QtWidgets/QWidget'; @@ -61,7 +63,6 @@ export { QCheckBox, QCheckBoxSignals } from './lib/QtWidgets/QCheckBox'; export { QColorDialog, QColorDialogSignals } from './lib/QtWidgets/QColorDialog'; export { QDateEdit } from './lib/QtWidgets/QDateEdit'; export { QDateTimeEdit, NodeDateTimeEdit, QDateTimeEditSignals } from './lib/QtWidgets/QDateTimeEdit'; -export { QDesktopWidget } from './lib/QtWidgets/QDesktopWidget'; export { QLabel, QLabelSignals } from './lib/QtWidgets/QLabel'; export { QLCDNumber, QLCDNumberSignals, Mode, SegmentStyle } from './lib/QtWidgets/QLCDNumber'; export { QDial, QDialSignals } from './lib/QtWidgets/QDial'; diff --git a/src/lib/QtGui/QApplication.ts b/src/lib/QtGui/QApplication.ts index fdd9fad67..5802dbd80 100644 --- a/src/lib/QtGui/QApplication.ts +++ b/src/lib/QtGui/QApplication.ts @@ -4,7 +4,6 @@ import { checkIfNativeElement } from '../utils/helpers'; import { QClipboard } from './QClipboard'; import { QStyle } from './QStyle'; import { QObjectSignals, NodeObject } from '../QtCore/QObject'; -import { QDesktopWidget } from '../QtWidgets/QDesktopWidget'; import { QPalette } from './QPalette'; import { StyleSheet } from '../core/Style/StyleSheet'; import memoizeOne from 'memoize-one'; @@ -80,9 +79,6 @@ export class QApplication extends NodeObject { static style(): QStyle { return new QStyle(addon.QApplication.style()); } - static desktop(): QDesktopWidget { - return new QDesktopWidget(); - } } export interface QApplicationSignals extends QObjectSignals { diff --git a/src/lib/QtGui/QScreen.ts b/src/lib/QtGui/QScreen.ts new file mode 100644 index 000000000..613f120d0 --- /dev/null +++ b/src/lib/QtGui/QScreen.ts @@ -0,0 +1,106 @@ +import { NativeElement } from '../core/Component'; +import { checkIfNativeElement } from '../utils/helpers'; +import { NodeObject, QObjectSignals } from '../QtCore/QObject'; +import { QRect } from '../QtCore/QRect'; +import { QSizeF } from '../QtCore/QSizeF'; +import { QSize } from '../QtCore/QSize'; + +export class QScreen extends NodeObject { + native: NativeElement; + constructor(native: NativeElement) { + super(native); + if (checkIfNativeElement(native)) { + this.native = native; + } else { + throw new Error('QScreen cannot be initialised this way.'); + } + } + + availableGeometry(): QRect { + return QRect.fromQVariant(this.property('availableGeometry')); + } + availableSize(): QSize { + return QSize.fromQVariant(this.property('availableSize')); + } + availableVirtualGeometry(): QRect { + return QRect.fromQVariant(this.property('availableVirtualGeometry')); + } + availableVirtualSize(): QSize { + return QSize.fromQVariant(this.property('availableVirtualSize')); + } + depth(): number { + return this.property('depth').toInt(); + } + devicePixelRatio(): number { + return this.property('devicePixelRatio').toDouble(); + } + geometry(): QRect { + return QRect.fromQVariant(this.property('geometry')); + } + logicalDotsPerInch(): number { + return this.property('logicalDotsPerInch').toDouble(); + } + logicalDotsPerInchX(): number { + return this.property('logicalDotsPerInchX').toDouble(); + } + logicalDotsPerInchY(): number { + return this.property('logicalDotsPerInchY').toDouble(); + } + manufacturer(): string { + return this.property('manufacturer').toString(); + } + model(): string { + return this.property('model').toString(); + } + name(): string { + return this.property('name').toString(); + } + nativeOrientation(): ScreenOrientation { + return this.property('nativeOrientation').toInt(); + } + orientation(): ScreenOrientation { + return this.property('orientation').toInt(); + } + physicalDotsPerInch(): number { + return this.property('physicalDotsPerInch').toDouble(); + } + physicalDotsPerInchX(): number { + return this.property('physicalDotsPerInchX').toDouble(); + } + physicalDotsPerInchY(): number { + return this.property('physicalDotsPerInchY').toDouble(); + } + physicalSize(): QSizeF { + return QSizeF.fromQVariant(this.property('physicalSize')); + } + primaryOrientation(): ScreenOrientation { + return this.property('primaryOrientation').toInt(); + } + refreshRate(): number { + return this.property('refreshRate').toDouble(); + } + serialNumber(): string { + return this.property('serialNumber').toString(); + } + size(): QSize { + return QSize.fromQVariant(this.property('size')); + } + virtualGeometry(): QRect { + return QRect.fromQVariant(this.property('virtualGeometry')); + } + virtualSize(): QSize { + return QSize.fromQVariant(this.property('virtualSize')); + } +} + +export interface QScreenSignals extends QObjectSignals { + availableGeometryChanged: (geometry: QRect) => void; + geometryChanged: (geometry: QRect) => void; + logicalDotsPerInchChanged: (dpi: number) => void; + orientationChanged: (orientation: ScreenOrientation) => void; + physicalDotsPerInchChanged: (dpi: number) => void; + physicalSizeChanged: (size: QSizeF) => void; + primaryOrientationChanged: (orientation: ScreenOrientation) => void; + refreshRateChanged: (refreshRate: number) => void; + virtualGeometryChanged: (rect: QRect) => void; +} diff --git a/src/lib/QtGui/QWindow.ts b/src/lib/QtGui/QWindow.ts new file mode 100644 index 000000000..0edec5903 --- /dev/null +++ b/src/lib/QtGui/QWindow.ts @@ -0,0 +1,26 @@ +import { NativeElement } from '../core/Component'; +import { checkIfNativeElement } from '../utils/helpers'; +import { NodeObject, QObjectSignals } from '../QtCore/QObject'; +import { QScreen } from './QScreen'; + +export class QWindow extends NodeObject { + native: NativeElement; + constructor(native: NativeElement) { + super(native); + + if (checkIfNativeElement(native)) { + this.native = native; + } else { + throw new Error('QWindow cannot be initialised this way.'); + } + } + + screen(): QScreen { + const screenNative = this.native.screen(); + return new QScreen(screenNative); + } +} + +export interface QWindowSignals extends QObjectSignals { + screenChanged: () => void; +} diff --git a/src/lib/QtWidgets/QDesktopWidget.ts b/src/lib/QtWidgets/QDesktopWidget.ts deleted file mode 100644 index 6cba77dbd..000000000 --- a/src/lib/QtWidgets/QDesktopWidget.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { QRect } from '../QtCore/QRect'; -import { NodeWidget, QWidgetSignals } from './QWidget'; -import { NativeElement } from '../core/Component'; -import addon from '../utils/addon'; - -/** - -> QDesktopWidget is a class that provides access to screen information on multi-head systems.. - -* **This class is a JS wrapper around Qt's [QDesktopWidget Class](https://doc.qt.io/qt-5/qdesktopwidget.html)** - -The QDesktopWidget class provides information about the user's desktop, such as its total size, number of screens, the geometry of each screen, and whether they are configured as separate desktops or a single virtual desktop. - -### Example - -```js -const { QDesktopWidget } = require("@nodegui/nodegui"); - -const desktop = new QDesktopWidget(); -const availableGeometry = desktop.availableGeometry(); -const screenGeometry = desktop.screenGeometry(); -console.log(availableGeometry.width() + 'x' + availableGeometry.height()); -console.log(screenGeometry.width() + 'x' + screenGeometry.height()); -console.log(desktop.screenNumber()); -``` - */ -export type QDesktopWidgetSignals = QWidgetSignals; -export class QDesktopWidget extends NodeWidget { - native: NativeElement; - constructor(parent?: NodeWidget) { - let native; - if (parent) { - native = new addon.QDesktopWidget(parent.native); - } else { - native = new addon.QDesktopWidget(); - } - super(native); - this.native = native; - this.nodeParent = parent; - } - availableGeometry(screen = -1): QRect { - return new QRect(this.native.availableGeometry(screen)); - } - screenGeometry(screen = -1): QRect { - return new QRect(this.native.screenGeometry(screen)); - } - screenNumber(): number { - return this.native.screenNumber(); - } -} diff --git a/src/lib/QtWidgets/QWidget.ts b/src/lib/QtWidgets/QWidget.ts index 437ec633a..5d52297fa 100644 --- a/src/lib/QtWidgets/QWidget.ts +++ b/src/lib/QtWidgets/QWidget.ts @@ -15,9 +15,10 @@ import { QRect } from '../QtCore/QRect'; import { QObjectSignals } from '../QtCore/QObject'; import { QFont } from '../QtGui/QFont'; import { QAction } from './QAction'; +import { QScreen } from '../QtGui/QScreen'; import memoizeOne from 'memoize-one'; import { QGraphicsEffect } from './QGraphicsEffect'; -import { QSizePolicyPolicy, QStyle } from '../..'; +import { QSizePolicyPolicy, QStyle, QWindow } from '../..'; /** @@ -246,6 +247,7 @@ export abstract class NodeWidget extends YogaWid resize(width: number, height: number): void { this.native.resize(width, height); } + // TODO: QScreen *QWidget::screen() const setAcceptDrops(on: boolean): void { this.native.setAcceptDrops(on); } @@ -421,7 +423,13 @@ export abstract class NodeWidget extends YogaWid // TODO: QWidget * window() const // TODO: QString windowFilePath() const // TODO: Qt::WindowFlags windowFlags() const - // TODO: QWindow * windowHandle() const + windowHandle(): QWindow | null { + const handle = this.native.windowHandle(); + if (handle != null) { + return new QWindow(handle); + } + return null; + } // TODO: QIcon windowIcon() const // TODO: Qt::WindowModality windowModality() const windowOpacity(): number { From 8ce6dde45bd52fc1e60b864ec0fe249cde2aec38 Mon Sep 17 00:00:00 2001 From: Simon Edwards Date: Fri, 22 Oct 2021 22:13:17 +0200 Subject: [PATCH 02/10] Add `QScreen` and `QWindow`, remove the deprecated `QDesktopWidget` --- CMakeLists.txt | 4 +- .../nodegui/QtCore/QObject/qobject_macro.h | 9 +- .../nodegui/QtGui/QScreen/qscreen_wrap.h | 31 +++++ .../nodegui/QtGui/QWindow/qwindow_wrap.h | 32 +++++ .../QDesktopWidget/nqdesktopwidget.hpp | 15 -- .../QDesktopWidget/qdesktopwidget_wrap.h | 24 ---- .../nodegui/QtWidgets/QWidget/qwidget_macro.h | 15 +- src/cpp/lib/QtGui/QScreen/qscreen_wrap.cpp | 131 ++++++++++++++++++ src/cpp/lib/QtGui/QWindow/qwindow_wrap.cpp | 58 ++++++++ .../QDesktopWidget/qdesktopwidget_wrap.cpp | 78 ----------- src/cpp/main.cpp | 6 +- src/index.ts | 3 +- src/lib/QtGui/QApplication.ts | 4 - src/lib/QtGui/QScreen.ts | 106 ++++++++++++++ src/lib/QtGui/QWindow.ts | 26 ++++ src/lib/QtWidgets/QDesktopWidget.ts | 50 ------- src/lib/QtWidgets/QWidget.ts | 12 +- 17 files changed, 422 insertions(+), 182 deletions(-) create mode 100644 src/cpp/include/nodegui/QtGui/QScreen/qscreen_wrap.h create mode 100644 src/cpp/include/nodegui/QtGui/QWindow/qwindow_wrap.h delete mode 100644 src/cpp/include/nodegui/QtWidgets/QDesktopWidget/nqdesktopwidget.hpp delete mode 100644 src/cpp/include/nodegui/QtWidgets/QDesktopWidget/qdesktopwidget_wrap.h create mode 100644 src/cpp/lib/QtGui/QScreen/qscreen_wrap.cpp create mode 100644 src/cpp/lib/QtGui/QWindow/qwindow_wrap.cpp delete mode 100644 src/cpp/lib/QtWidgets/QDesktopWidget/qdesktopwidget_wrap.cpp create mode 100644 src/lib/QtGui/QScreen.ts create mode 100644 src/lib/QtGui/QWindow.ts delete mode 100644 src/lib/QtWidgets/QDesktopWidget.ts diff --git a/CMakeLists.txt b/CMakeLists.txt index 752fde426..7bdd5c41a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -65,7 +65,9 @@ add_library(${CORE_WIDGETS_ADDON} SHARED "${PROJECT_SOURCE_DIR}/src/cpp/lib/QtGui/QKeySequence/qkeysequence_wrap.cpp" "${PROJECT_SOURCE_DIR}/src/cpp/lib/QtGui/QMovie/qmovie_wrap.cpp" "${PROJECT_SOURCE_DIR}/src/cpp/lib/QtGui/QPalette/qpalette_wrap.cpp" + "${PROJECT_SOURCE_DIR}/src/cpp/lib/QtGui/QScreen/qscreen_wrap.cpp" "${PROJECT_SOURCE_DIR}/src/cpp/lib/QtGui/QStyle/qstyle_wrap.cpp" + "${PROJECT_SOURCE_DIR}/src/cpp/lib/QtGui/QWindow/qwindow_wrap.cpp" "${PROJECT_SOURCE_DIR}/src/cpp/lib/QtCore/QAbstractItemModel/qabstractitemmodel_wrap.cpp" "${PROJECT_SOURCE_DIR}/src/cpp/lib/QtCore/QDate/qdate_wrap.cpp" "${PROJECT_SOURCE_DIR}/src/cpp/lib/QtCore/QDateTime/qdatetime_wrap.cpp" @@ -147,7 +149,6 @@ add_library(${CORE_WIDGETS_ADDON} SHARED "${PROJECT_SOURCE_DIR}/src/cpp/lib/QtWidgets/QStandardItemModel/qstandarditemmodel_wrap.cpp" "${PROJECT_SOURCE_DIR}/src/cpp/lib/QtWidgets/QStandardItem/qstandarditem_wrap.cpp" "${PROJECT_SOURCE_DIR}/src/cpp/lib/QtWidgets/QSvgWidget/qsvgwidget_wrap.cpp" - "${PROJECT_SOURCE_DIR}/src/cpp/lib/QtWidgets/QDesktopWidget/qdesktopwidget_wrap.cpp" "${PROJECT_SOURCE_DIR}/src/cpp/lib/QtWidgets/QStyleFactory/qstylefactory_wrap.cpp" # Custom widgets (include them for automoc since they contain Q_OBJECT) "${PROJECT_SOURCE_DIR}/src/cpp/include/nodegui/QtCore/QAbstractItemModel/nabstractitemmodel.hpp" @@ -213,7 +214,6 @@ add_library(${CORE_WIDGETS_ADDON} SHARED "${PROJECT_SOURCE_DIR}/src/cpp/include/nodegui/QtWidgets/QTextBrowser/ntextbrowser.hpp" "${PROJECT_SOURCE_DIR}/src/cpp/include/nodegui/QtWidgets/QTextEdit/ntextedit.hpp" "${PROJECT_SOURCE_DIR}/src/cpp/include/nodegui/QtWidgets/QSvgWidget/nsvgwidget.hpp" - "${PROJECT_SOURCE_DIR}/src/cpp/include/nodegui/QtWidgets/QDesktopWidget/nqdesktopwidget.hpp" "${PROJECT_SOURCE_DIR}/src/cpp/include/nodegui/QtWidgets/QHeaderView/nheaderview.hpp" ) diff --git a/src/cpp/include/nodegui/QtCore/QObject/qobject_macro.h b/src/cpp/include/nodegui/QtCore/QObject/qobject_macro.h index 5d4123c4d..0635b04f2 100644 --- a/src/cpp/include/nodegui/QtCore/QObject/qobject_macro.h +++ b/src/cpp/include/nodegui/QtCore/QObject/qobject_macro.h @@ -90,9 +90,9 @@ #endif // QOBJECT_WRAPPED_METHODS_EXPORT_DEFINE -#ifndef QOBJECT_SIGNALS -#define QOBJECT_SIGNALS \ - QObject::connect(this, &QObject::objectNameChanged, \ +#ifndef QOBJECT_SIGNALS_ON_TARGET +#define QOBJECT_SIGNALS_ON_TARGET(target) \ + QObject::connect(target, &QObject::objectNameChanged, \ [=](const QString& objectName) { \ Napi::Env env = this->emitOnNode.Env(); \ Napi::HandleScope scope(env); \ @@ -100,5 +100,8 @@ {Napi::String::New(env, "objectNameChanged"), \ Napi::Value::From(env, objectName.toStdString())}); \ }); +#endif // QOBJECT_SIGNALS_ON_TARGET +#ifndef QOBJECT_SIGNALS +#define QOBJECT_SIGNALS QOBJECT_SIGNALS_ON_TARGET(this) #endif // QOBJECT_SIGNALS diff --git a/src/cpp/include/nodegui/QtGui/QScreen/qscreen_wrap.h b/src/cpp/include/nodegui/QtGui/QScreen/qscreen_wrap.h new file mode 100644 index 000000000..a3076152a --- /dev/null +++ b/src/cpp/include/nodegui/QtGui/QScreen/qscreen_wrap.h @@ -0,0 +1,31 @@ +#pragma once + +#include + +#include +#include + +#include "Extras/Export/export.h" +#include "QtCore/QObject/qobject_macro.h" + +class DLL_EXPORT QScreenWrap : public Napi::ObjectWrap, + public EventWidget { + QOBJECT_WRAPPED_METHODS_DECLARATION + // Note: We don't use EVENTWIDGET_IMPLEMENTATIONS() here because this class + // doesn't handle any QEvents. + + private: + QPointer instance; + + public: + // class constructor + static Napi::FunctionReference constructor; + + static Napi::Object init(Napi::Env env, Napi::Object exports); + QScreenWrap(const Napi::CallbackInfo& info); + QScreen* getInternalInstance(); + + virtual void connectSignalsToEventEmitter(); + + // Wrapped methods +}; diff --git a/src/cpp/include/nodegui/QtGui/QWindow/qwindow_wrap.h b/src/cpp/include/nodegui/QtGui/QWindow/qwindow_wrap.h new file mode 100644 index 000000000..419a95691 --- /dev/null +++ b/src/cpp/include/nodegui/QtGui/QWindow/qwindow_wrap.h @@ -0,0 +1,32 @@ +#pragma once + +#include + +#include +#include + +#include "Extras/Export/export.h" +#include "QtCore/QObject/qobject_macro.h" + +class DLL_EXPORT QWindowWrap : public Napi::ObjectWrap, + public EventWidget { + QOBJECT_WRAPPED_METHODS_DECLARATION + // Note: We don't use EVENTWIDGET_IMPLEMENTATIONS() here because this class + // doesn't handle any QEvents. + + private: + QPointer instance; + + public: + // class constructor + static Napi::FunctionReference constructor; + + static Napi::Object init(Napi::Env env, Napi::Object exports); + QWindowWrap(const Napi::CallbackInfo& info); + QWindow* getInternalInstance(); + + virtual void connectSignalsToEventEmitter(); + + // wrapped methods + Napi::Value screen(const Napi::CallbackInfo& info); +}; diff --git a/src/cpp/include/nodegui/QtWidgets/QDesktopWidget/nqdesktopwidget.hpp b/src/cpp/include/nodegui/QtWidgets/QDesktopWidget/nqdesktopwidget.hpp deleted file mode 100644 index cd563fd87..000000000 --- a/src/cpp/include/nodegui/QtWidgets/QDesktopWidget/nqdesktopwidget.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include -#include - -#include "core/NodeWidget/nodewidget.h" - -class NQDesktopWidget : public QDesktopWidget, public NodeWidget { - public: - Q_OBJECT - NODEWIDGET_IMPLEMENTATIONS(QDesktopWidget) - public: - using QDesktopWidget::QDesktopWidget; // inherit all constructors of - // QStatusBar -}; diff --git a/src/cpp/include/nodegui/QtWidgets/QDesktopWidget/qdesktopwidget_wrap.h b/src/cpp/include/nodegui/QtWidgets/QDesktopWidget/qdesktopwidget_wrap.h deleted file mode 100644 index 845425907..000000000 --- a/src/cpp/include/nodegui/QtWidgets/QDesktopWidget/qdesktopwidget_wrap.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once -#include - -#include "QtWidgets/QWidget/qwidget_macro.h" -#include "napi.h" -#include "nqdesktopwidget.hpp" - -class QDesktopWidgetWrap : public Napi::ObjectWrap { - private: - QPointer instance; - - public: - static Napi::Object init(Napi::Env env, Napi::Object exports); - QDesktopWidgetWrap(const Napi::CallbackInfo &info); - ~QDesktopWidgetWrap(); - NQDesktopWidget *getInternalInstance(); - static Napi::FunctionReference constructor; - // wrapped methods - Napi::Value availableGeometry(const Napi::CallbackInfo &info); - Napi::Value screenGeometry(const Napi::CallbackInfo &info); - Napi::Value screenNumber(const Napi::CallbackInfo &info); - - QWIDGET_WRAPPED_METHODS_DECLARATION -}; diff --git a/src/cpp/include/nodegui/QtWidgets/QWidget/qwidget_macro.h b/src/cpp/include/nodegui/QtWidgets/QWidget/qwidget_macro.h index 91310178e..041fcd626 100644 --- a/src/cpp/include/nodegui/QtWidgets/QWidget/qwidget_macro.h +++ b/src/cpp/include/nodegui/QtWidgets/QWidget/qwidget_macro.h @@ -10,6 +10,7 @@ #include "QtGui/QCursor/qcursor_wrap.h" #include "QtGui/QIcon/qicon_wrap.h" #include "QtGui/QStyle/qstyle_wrap.h" +#include "QtGui/QWindow/qwindow_wrap.h" #include "QtWidgets/QAction/qaction_wrap.h" #include "QtWidgets/QLayout/qlayout_wrap.h" #include "core/YogaWidget/yogawidget_macro.h" @@ -537,6 +538,17 @@ bool modified = info[0].As().Value(); \ this->instance->setWindowModified(modified); \ return env.Null(); \ + } \ + Napi::Value windowHandle(const Napi::CallbackInfo& info) { \ + Napi::Env env = info.Env(); \ + Napi::HandleScope scope(env); \ + QWindow* window = this->instance->windowHandle(); \ + if (window) { \ + return QWindowWrap::constructor.New( \ + {Napi::External::New(env, window)}); \ + } else { \ + return env.Null(); \ + } \ } #endif // QWIDGET_WRAPPED_METHODS_DECLARATION @@ -613,7 +625,8 @@ InstanceMethod("setDisabled", &WidgetWrapName::setDisabled), \ InstanceMethod("setHidden", &WidgetWrapName::setHidden), \ InstanceMethod("setVisible", &WidgetWrapName::setVisible), \ - InstanceMethod("setWindowModified", &WidgetWrapName::setWindowModified), + InstanceMethod("setWindowModified", &WidgetWrapName::setWindowModified), \ + InstanceMethod("windowHandle", &WidgetWrapName::windowHandle), #endif // QWIDGET_WRAPPED_METHODS_EXPORT_DEFINE diff --git a/src/cpp/lib/QtGui/QScreen/qscreen_wrap.cpp b/src/cpp/lib/QtGui/QScreen/qscreen_wrap.cpp new file mode 100644 index 000000000..29c512759 --- /dev/null +++ b/src/cpp/lib/QtGui/QScreen/qscreen_wrap.cpp @@ -0,0 +1,131 @@ +#include "QtGui/QScreen/qscreen_wrap.h" + +#include "Extras/Utils/nutils.h" +#include "QtCore/QRect/qrect_wrap.h" +#include "QtCore/QSizeF/qsizef_wrap.h" + +Napi::FunctionReference QScreenWrap::constructor; + +Napi::Object QScreenWrap::init(Napi::Env env, Napi::Object exports) { + Napi::HandleScope scope(env); + char CLASSNAME[] = "QScreen"; + Napi::Function func = + DefineClass(env, CLASSNAME, + {// InstanceMethod("clear", &QScreenWrap::clear), + QOBJECT_WRAPPED_METHODS_EXPORT_DEFINE(QScreenWrap)}); + constructor = Napi::Persistent(func); + exports.Set(CLASSNAME, func); + return exports; +} + +QScreenWrap::QScreenWrap(const Napi::CallbackInfo& info) + : Napi::ObjectWrap(info) { + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + if (info[0].IsExternal()) { + this->instance = info[0].As>().Data(); + } else { + Napi::TypeError::New(env, "Incorrect initialization of QScreenWrap") + .ThrowAsJavaScriptException(); + } + this->rawData = extrautils::configureComponent(this->getInternalInstance()); +} + +QScreen* QScreenWrap::getInternalInstance() { return this->instance; } + +void QScreenWrap::connectSignalsToEventEmitter() { + QOBJECT_SIGNALS_ON_TARGET(this->instance.data()); + + QObject::connect( + this->instance.data(), &QScreen::availableGeometryChanged, + [=](const QRect& geometry) { + Napi::Env env = this->emitOnNode.Env(); + Napi::HandleScope scope(env); + auto instance = QRectWrap::constructor.New({Napi::External::New( + env, new QRect(geometry.x(), geometry.y(), geometry.width(), + geometry.height()))}); + this->emitOnNode.Call( + {Napi::String::New(env, "availableGeometryChanged"), instance}); + }); + + QObject::connect( + this->instance.data(), &QScreen::geometryChanged, + [=](const QRect& geometry) { + Napi::Env env = this->emitOnNode.Env(); + Napi::HandleScope scope(env); + auto instance = QRectWrap::constructor.New({Napi::External::New( + env, new QRect(geometry.x(), geometry.y(), geometry.width(), + geometry.height()))}); + this->emitOnNode.Call( + {Napi::String::New(env, "geometryChanged"), instance}); + }); + + QObject::connect(this->instance.data(), &QScreen::logicalDotsPerInchChanged, + [=](qreal dpi) { + Napi::Env env = this->emitOnNode.Env(); + Napi::HandleScope scope(env); + this->emitOnNode.Call( + {Napi::String::New(env, "logicalDotsPerInchChanged"), + Napi::Value::From(env, dpi)}); + }); + + QObject::connect( + this->instance.data(), &QScreen::orientationChanged, + [=](Qt::ScreenOrientation orientation) { + Napi::Env env = this->emitOnNode.Env(); + Napi::HandleScope scope(env); + this->emitOnNode.Call( + {Napi::String::New(env, "orientationChanged"), + Napi::Value::From(env, static_cast(orientation))}); + }); + + QObject::connect(this->instance.data(), &QScreen::physicalDotsPerInchChanged, + [=](qreal dpi) { + Napi::Env env = this->emitOnNode.Env(); + Napi::HandleScope scope(env); + this->emitOnNode.Call( + {Napi::String::New(env, "physicalDotsPerInchChanged"), + Napi::Value::From(env, dpi)}); + }); + + QObject::connect( + this->instance.data(), &QScreen::physicalSizeChanged, + [=](const QSizeF& size) { + Napi::Env env = this->emitOnNode.Env(); + Napi::HandleScope scope(env); + auto instance = QSizeFWrap::constructor.New( + {Napi::External::New(env, new QSizeF(size))}); + this->emitOnNode.Call( + {Napi::String::New(env, "physicalSizeChanged"), instance}); + }); + + QObject::connect( + this->instance.data(), &QScreen::primaryOrientationChanged, + [=](Qt::ScreenOrientation orientation) { + Napi::Env env = this->emitOnNode.Env(); + Napi::HandleScope scope(env); + this->emitOnNode.Call( + {Napi::String::New(env, "primaryOrientationChanged"), + Napi::Value::From(env, static_cast(orientation))}); + }); + + QObject::connect( + this->instance.data(), &QScreen::refreshRateChanged, + [=](qreal refreshRate) { + Napi::Env env = this->emitOnNode.Env(); + Napi::HandleScope scope(env); + this->emitOnNode.Call({Napi::String::New(env, "refreshRateChanged"), + Napi::Value::From(env, refreshRate)}); + }); + + QObject::connect( + this->instance.data(), &QScreen::virtualGeometryChanged, + [=](const QRect& rect) { + Napi::Env env = this->emitOnNode.Env(); + Napi::HandleScope scope(env); + auto instance = QRectWrap::constructor.New({Napi::External::New( + env, new QRect(rect.x(), rect.y(), rect.width(), rect.height()))}); + this->emitOnNode.Call( + {Napi::String::New(env, "virtualGeometryChanged"), instance}); + }); +} diff --git a/src/cpp/lib/QtGui/QWindow/qwindow_wrap.cpp b/src/cpp/lib/QtGui/QWindow/qwindow_wrap.cpp new file mode 100644 index 000000000..7d2633f10 --- /dev/null +++ b/src/cpp/lib/QtGui/QWindow/qwindow_wrap.cpp @@ -0,0 +1,58 @@ +#include "QtGui/QWindow/qwindow_wrap.h" + +#include "Extras/Utils/nutils.h" +#include "QtGui/QScreen/qscreen_wrap.h" + +Napi::FunctionReference QWindowWrap::constructor; + +Napi::Object QWindowWrap::init(Napi::Env env, Napi::Object exports) { + Napi::HandleScope scope(env); + char CLASSNAME[] = "QWindow"; + Napi::Function func = + DefineClass(env, CLASSNAME, + {InstanceMethod("screen", &QWindowWrap::screen), + QOBJECT_WRAPPED_METHODS_EXPORT_DEFINE(QWindowWrap)}); + constructor = Napi::Persistent(func); + exports.Set(CLASSNAME, func); + return exports; +} + +QWindow* QWindowWrap::getInternalInstance() { return this->instance; } + +QWindowWrap::QWindowWrap(const Napi::CallbackInfo& info) + : Napi::ObjectWrap(info) { + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + if (info.Length() == 1 && info[0].IsExternal()) { + this->instance = info[0].As>().Data(); + } else { + Napi::TypeError::New(env, "Wrong number of arguments to QWindow.") + .ThrowAsJavaScriptException(); + } + this->rawData = extrautils::configureQObject(this->getInternalInstance()); +} + +void QWindowWrap::connectSignalsToEventEmitter() { + QOBJECT_SIGNALS_ON_TARGET(this->instance.data()); + + QObject::connect( + this->instance.data(), &QWindow::screenChanged, [=](QScreen* screen) { + Napi::Env env = this->emitOnNode.Env(); + Napi::HandleScope scope(env); + this->emitOnNode.Call({Napi::String::New(env, "screenChanged")}); + }); +} + +Napi::Value QWindowWrap::screen(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + + QScreen* screen = this->instance->screen(); + if (screen) { + auto instance = QScreenWrap::constructor.New( + {Napi::External::New(env, screen)}); + return instance; + } else { + return env.Null(); + } +} diff --git a/src/cpp/lib/QtWidgets/QDesktopWidget/qdesktopwidget_wrap.cpp b/src/cpp/lib/QtWidgets/QDesktopWidget/qdesktopwidget_wrap.cpp deleted file mode 100644 index 9bf8f5e50..000000000 --- a/src/cpp/lib/QtWidgets/QDesktopWidget/qdesktopwidget_wrap.cpp +++ /dev/null @@ -1,78 +0,0 @@ -#include "QtWidgets/QDesktopWidget/qdesktopwidget_wrap.h" - -#include - -#include "Extras/Utils/nutils.h" -#include "QtCore/QRect/qrect_wrap.h" -#include "QtWidgets/QWidget/qwidget_wrap.h" - -Napi::FunctionReference QDesktopWidgetWrap::constructor; - -Napi::Object QDesktopWidgetWrap::init(Napi::Env env, Napi::Object exports) { - Napi::HandleScope scope(env); - char CLASSNAME[] = "QDesktopWidget"; - Napi::Function func = DefineClass( - env, CLASSNAME, - {InstanceMethod("screenGeometry", &QDesktopWidgetWrap::screenGeometry), - InstanceMethod("availableGeometry", - &QDesktopWidgetWrap::availableGeometry), - InstanceMethod("screenNumber", &QDesktopWidgetWrap::screenNumber), - QWIDGET_WRAPPED_METHODS_EXPORT_DEFINE(QDesktopWidgetWrap)}); - constructor = Napi::Persistent(func); - exports.Set(CLASSNAME, func); - return exports; -} - -NQDesktopWidget *QDesktopWidgetWrap::getInternalInstance() { - return this->instance; -} - -QDesktopWidgetWrap::QDesktopWidgetWrap(const Napi::CallbackInfo &info) - : Napi::ObjectWrap(info) { - Napi::Env env = info.Env(); - Napi::HandleScope scope(env); - - if (info.Length() == 0) { - this->instance = new NQDesktopWidget(); - } else { - Napi::TypeError::New(env, "Wrong number of arguments") - .ThrowAsJavaScriptException(); - } - this->rawData = extrautils::configureQWidget( - this->getInternalInstance(), this->getInternalInstance()->getFlexNode(), - true); -} - -QDesktopWidgetWrap::~QDesktopWidgetWrap() { - extrautils::safeDelete(this->instance); -} - -Napi::Value QDesktopWidgetWrap::screenGeometry(const Napi::CallbackInfo &info) { - Napi::Env env = info.Env(); - Napi::HandleScope scope(env); - - Napi::Number screen = info[0].As(); - QRect rect = this->instance->screenGeometry(screen); - auto instance = QRectWrap::constructor.New( - {Napi::External::New(env, new QRect(rect))}); - return instance; -} - -Napi::Value QDesktopWidgetWrap::availableGeometry( - const Napi::CallbackInfo &info) { - Napi::Env env = info.Env(); - Napi::HandleScope scope(env); - - Napi::Number screen = info[0].As(); - QRect rect = this->instance->availableGeometry(screen); - auto instance = QRectWrap::constructor.New( - {Napi::External::New(env, new QRect(rect))}); - return instance; -} - -Napi::Value QDesktopWidgetWrap::screenNumber(const Napi::CallbackInfo &info) { - Napi::Env env = info.Env(); - Napi::HandleScope scope(env); - int value = this->instance->screenNumber(); - return Napi::Value::From(env, value); -} diff --git a/src/cpp/main.cpp b/src/cpp/main.cpp index 14963003c..ea7526d3e 100644 --- a/src/cpp/main.cpp +++ b/src/cpp/main.cpp @@ -45,7 +45,9 @@ #include "QtGui/QPen/qpen_wrap.h" #include "QtGui/QPicture/qpicture_wrap.h" #include "QtGui/QPixmap/qpixmap_wrap.h" +#include "QtGui/QScreen/qscreen_wrap.h" #include "QtGui/QStyle/qstyle_wrap.h" +#include "QtGui/QWindow/qwindow_wrap.h" #include "QtWidgets/QAction/qaction_wrap.h" #include "QtWidgets/QBoxLayout/qboxlayout_wrap.h" #include "QtWidgets/QButtonGroup/qbuttongroup_wrap.h" @@ -55,7 +57,6 @@ #include "QtWidgets/QComboBox/qcombobox_wrap.h" #include "QtWidgets/QDateEdit/qdateedit_wrap.h" #include "QtWidgets/QDateTimeEdit/qdatetimeedit_wrap.h" -#include "QtWidgets/QDesktopWidget/qdesktopwidget_wrap.h" #include "QtWidgets/QDial/qdial_wrap.h" #include "QtWidgets/QDialog/qdialog_wrap.h" #include "QtWidgets/QDoubleSpinBox/qdoublespinbox_wrap.h" @@ -225,13 +226,14 @@ Napi::Object Main(Napi::Env env, Napi::Object exports) { QStandardItemModelWrap::init(env, exports); QStandardItemWrap::init(env, exports); QSvgWidgetWrap::init(env, exports); - QDesktopWidgetWrap::init(env, exports); QPaintEventWrap::init(env, exports); QPaletteWrap::init(env, exports); QAbstractItemModelWrap::init(env, exports); QHeaderViewWrap::init(env, exports); QItemSelectionModelWrap::init(env, exports); QStyleFactoryWrap::init(env, exports); + QScreenWrap::init(env, exports); + QWindowWrap::init(env, exports); return exports; } diff --git a/src/index.ts b/src/index.ts index a63d50611..6b0d63ac8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -32,6 +32,8 @@ export { QDropEvent } from './lib/QtGui/QEvent/QDropEvent'; export { QDragMoveEvent } from './lib/QtGui/QEvent/QDragMoveEvent'; export { QDragLeaveEvent } from './lib/QtGui/QEvent/QDragLeaveEvent'; export { QPaintEvent } from './lib/QtGui/QEvent/QPaintEvent'; +export { QScreen } from './lib/QtGui/QScreen'; +export { QWindow } from './lib/QtGui/QWindow'; export { WidgetEventTypes } from './lib/core/EventWidget'; // Abstract: export { NodeWidget, QWidget, QWidgetSignals } from './lib/QtWidgets/QWidget'; @@ -61,7 +63,6 @@ export { QCheckBox, QCheckBoxSignals } from './lib/QtWidgets/QCheckBox'; export { QColorDialog, QColorDialogSignals } from './lib/QtWidgets/QColorDialog'; export { QDateEdit } from './lib/QtWidgets/QDateEdit'; export { QDateTimeEdit, NodeDateTimeEdit, QDateTimeEditSignals } from './lib/QtWidgets/QDateTimeEdit'; -export { QDesktopWidget } from './lib/QtWidgets/QDesktopWidget'; export { QLabel, QLabelSignals } from './lib/QtWidgets/QLabel'; export { QLCDNumber, QLCDNumberSignals, Mode, SegmentStyle } from './lib/QtWidgets/QLCDNumber'; export { QDial, QDialSignals } from './lib/QtWidgets/QDial'; diff --git a/src/lib/QtGui/QApplication.ts b/src/lib/QtGui/QApplication.ts index fdd9fad67..5802dbd80 100644 --- a/src/lib/QtGui/QApplication.ts +++ b/src/lib/QtGui/QApplication.ts @@ -4,7 +4,6 @@ import { checkIfNativeElement } from '../utils/helpers'; import { QClipboard } from './QClipboard'; import { QStyle } from './QStyle'; import { QObjectSignals, NodeObject } from '../QtCore/QObject'; -import { QDesktopWidget } from '../QtWidgets/QDesktopWidget'; import { QPalette } from './QPalette'; import { StyleSheet } from '../core/Style/StyleSheet'; import memoizeOne from 'memoize-one'; @@ -80,9 +79,6 @@ export class QApplication extends NodeObject { static style(): QStyle { return new QStyle(addon.QApplication.style()); } - static desktop(): QDesktopWidget { - return new QDesktopWidget(); - } } export interface QApplicationSignals extends QObjectSignals { diff --git a/src/lib/QtGui/QScreen.ts b/src/lib/QtGui/QScreen.ts new file mode 100644 index 000000000..613f120d0 --- /dev/null +++ b/src/lib/QtGui/QScreen.ts @@ -0,0 +1,106 @@ +import { NativeElement } from '../core/Component'; +import { checkIfNativeElement } from '../utils/helpers'; +import { NodeObject, QObjectSignals } from '../QtCore/QObject'; +import { QRect } from '../QtCore/QRect'; +import { QSizeF } from '../QtCore/QSizeF'; +import { QSize } from '../QtCore/QSize'; + +export class QScreen extends NodeObject { + native: NativeElement; + constructor(native: NativeElement) { + super(native); + if (checkIfNativeElement(native)) { + this.native = native; + } else { + throw new Error('QScreen cannot be initialised this way.'); + } + } + + availableGeometry(): QRect { + return QRect.fromQVariant(this.property('availableGeometry')); + } + availableSize(): QSize { + return QSize.fromQVariant(this.property('availableSize')); + } + availableVirtualGeometry(): QRect { + return QRect.fromQVariant(this.property('availableVirtualGeometry')); + } + availableVirtualSize(): QSize { + return QSize.fromQVariant(this.property('availableVirtualSize')); + } + depth(): number { + return this.property('depth').toInt(); + } + devicePixelRatio(): number { + return this.property('devicePixelRatio').toDouble(); + } + geometry(): QRect { + return QRect.fromQVariant(this.property('geometry')); + } + logicalDotsPerInch(): number { + return this.property('logicalDotsPerInch').toDouble(); + } + logicalDotsPerInchX(): number { + return this.property('logicalDotsPerInchX').toDouble(); + } + logicalDotsPerInchY(): number { + return this.property('logicalDotsPerInchY').toDouble(); + } + manufacturer(): string { + return this.property('manufacturer').toString(); + } + model(): string { + return this.property('model').toString(); + } + name(): string { + return this.property('name').toString(); + } + nativeOrientation(): ScreenOrientation { + return this.property('nativeOrientation').toInt(); + } + orientation(): ScreenOrientation { + return this.property('orientation').toInt(); + } + physicalDotsPerInch(): number { + return this.property('physicalDotsPerInch').toDouble(); + } + physicalDotsPerInchX(): number { + return this.property('physicalDotsPerInchX').toDouble(); + } + physicalDotsPerInchY(): number { + return this.property('physicalDotsPerInchY').toDouble(); + } + physicalSize(): QSizeF { + return QSizeF.fromQVariant(this.property('physicalSize')); + } + primaryOrientation(): ScreenOrientation { + return this.property('primaryOrientation').toInt(); + } + refreshRate(): number { + return this.property('refreshRate').toDouble(); + } + serialNumber(): string { + return this.property('serialNumber').toString(); + } + size(): QSize { + return QSize.fromQVariant(this.property('size')); + } + virtualGeometry(): QRect { + return QRect.fromQVariant(this.property('virtualGeometry')); + } + virtualSize(): QSize { + return QSize.fromQVariant(this.property('virtualSize')); + } +} + +export interface QScreenSignals extends QObjectSignals { + availableGeometryChanged: (geometry: QRect) => void; + geometryChanged: (geometry: QRect) => void; + logicalDotsPerInchChanged: (dpi: number) => void; + orientationChanged: (orientation: ScreenOrientation) => void; + physicalDotsPerInchChanged: (dpi: number) => void; + physicalSizeChanged: (size: QSizeF) => void; + primaryOrientationChanged: (orientation: ScreenOrientation) => void; + refreshRateChanged: (refreshRate: number) => void; + virtualGeometryChanged: (rect: QRect) => void; +} diff --git a/src/lib/QtGui/QWindow.ts b/src/lib/QtGui/QWindow.ts new file mode 100644 index 000000000..0edec5903 --- /dev/null +++ b/src/lib/QtGui/QWindow.ts @@ -0,0 +1,26 @@ +import { NativeElement } from '../core/Component'; +import { checkIfNativeElement } from '../utils/helpers'; +import { NodeObject, QObjectSignals } from '../QtCore/QObject'; +import { QScreen } from './QScreen'; + +export class QWindow extends NodeObject { + native: NativeElement; + constructor(native: NativeElement) { + super(native); + + if (checkIfNativeElement(native)) { + this.native = native; + } else { + throw new Error('QWindow cannot be initialised this way.'); + } + } + + screen(): QScreen { + const screenNative = this.native.screen(); + return new QScreen(screenNative); + } +} + +export interface QWindowSignals extends QObjectSignals { + screenChanged: () => void; +} diff --git a/src/lib/QtWidgets/QDesktopWidget.ts b/src/lib/QtWidgets/QDesktopWidget.ts deleted file mode 100644 index 6cba77dbd..000000000 --- a/src/lib/QtWidgets/QDesktopWidget.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { QRect } from '../QtCore/QRect'; -import { NodeWidget, QWidgetSignals } from './QWidget'; -import { NativeElement } from '../core/Component'; -import addon from '../utils/addon'; - -/** - -> QDesktopWidget is a class that provides access to screen information on multi-head systems.. - -* **This class is a JS wrapper around Qt's [QDesktopWidget Class](https://doc.qt.io/qt-5/qdesktopwidget.html)** - -The QDesktopWidget class provides information about the user's desktop, such as its total size, number of screens, the geometry of each screen, and whether they are configured as separate desktops or a single virtual desktop. - -### Example - -```js -const { QDesktopWidget } = require("@nodegui/nodegui"); - -const desktop = new QDesktopWidget(); -const availableGeometry = desktop.availableGeometry(); -const screenGeometry = desktop.screenGeometry(); -console.log(availableGeometry.width() + 'x' + availableGeometry.height()); -console.log(screenGeometry.width() + 'x' + screenGeometry.height()); -console.log(desktop.screenNumber()); -``` - */ -export type QDesktopWidgetSignals = QWidgetSignals; -export class QDesktopWidget extends NodeWidget { - native: NativeElement; - constructor(parent?: NodeWidget) { - let native; - if (parent) { - native = new addon.QDesktopWidget(parent.native); - } else { - native = new addon.QDesktopWidget(); - } - super(native); - this.native = native; - this.nodeParent = parent; - } - availableGeometry(screen = -1): QRect { - return new QRect(this.native.availableGeometry(screen)); - } - screenGeometry(screen = -1): QRect { - return new QRect(this.native.screenGeometry(screen)); - } - screenNumber(): number { - return this.native.screenNumber(); - } -} diff --git a/src/lib/QtWidgets/QWidget.ts b/src/lib/QtWidgets/QWidget.ts index 437ec633a..5d52297fa 100644 --- a/src/lib/QtWidgets/QWidget.ts +++ b/src/lib/QtWidgets/QWidget.ts @@ -15,9 +15,10 @@ import { QRect } from '../QtCore/QRect'; import { QObjectSignals } from '../QtCore/QObject'; import { QFont } from '../QtGui/QFont'; import { QAction } from './QAction'; +import { QScreen } from '../QtGui/QScreen'; import memoizeOne from 'memoize-one'; import { QGraphicsEffect } from './QGraphicsEffect'; -import { QSizePolicyPolicy, QStyle } from '../..'; +import { QSizePolicyPolicy, QStyle, QWindow } from '../..'; /** @@ -246,6 +247,7 @@ export abstract class NodeWidget extends YogaWid resize(width: number, height: number): void { this.native.resize(width, height); } + // TODO: QScreen *QWidget::screen() const setAcceptDrops(on: boolean): void { this.native.setAcceptDrops(on); } @@ -421,7 +423,13 @@ export abstract class NodeWidget extends YogaWid // TODO: QWidget * window() const // TODO: QString windowFilePath() const // TODO: Qt::WindowFlags windowFlags() const - // TODO: QWindow * windowHandle() const + windowHandle(): QWindow | null { + const handle = this.native.windowHandle(); + if (handle != null) { + return new QWindow(handle); + } + return null; + } // TODO: QIcon windowIcon() const // TODO: Qt::WindowModality windowModality() const windowOpacity(): number { From fa52c674efca54b41efd19b8db16e39f1fffff29 Mon Sep 17 00:00:00 2001 From: Simon Edwards Date: Sat, 23 Oct 2021 18:48:45 +0200 Subject: [PATCH 03/10] Support `addEventListener()` when the wrapper handles signals --- .../nodegui/QtCore/QObject/qobject_macro.h | 11 ++- .../nodegui/QtGui/QScreen/qscreen_wrap.h | 2 +- .../nodegui/QtGui/QWindow/qwindow_wrap.h | 2 +- .../nodegui/core/Events/eventwidget_macro.h | 93 ++++++++++--------- 4 files changed, 57 insertions(+), 51 deletions(-) diff --git a/src/cpp/include/nodegui/QtCore/QObject/qobject_macro.h b/src/cpp/include/nodegui/QtCore/QObject/qobject_macro.h index 0635b04f2..f07e5984a 100644 --- a/src/cpp/include/nodegui/QtCore/QObject/qobject_macro.h +++ b/src/cpp/include/nodegui/QtCore/QObject/qobject_macro.h @@ -12,10 +12,10 @@ and every widget we export. */ -#ifndef QOBJECT_WRAPPED_METHODS_DECLARATION -#define QOBJECT_WRAPPED_METHODS_DECLARATION \ +#ifndef QOBJECT_WRAPPED_METHODS_DECLARATION_WITH_EVENT_SOURCE +#define QOBJECT_WRAPPED_METHODS_DECLARATION_WITH_EVENT_SOURCE(source) \ \ - EVENTWIDGET_WRAPPED_METHODS_DECLARATION \ + EVENTWIDGET_WRAPPED_METHODS_DECLARATION_WITH_EVENT_SOURCE(source) \ \ Napi::Value inherits(const Napi::CallbackInfo& info) { \ Napi::Env env = info.Env(); \ @@ -73,6 +73,11 @@ return env.Null(); \ } +#endif // QOBJECT_WRAPPED_METHODS_DECLARATION_WITH_EVENT_SOURCE + +#ifndef QOBJECT_WRAPPED_METHODS_DECLARATION +#define QOBJECT_WRAPPED_METHODS_DECLARATION \ + QOBJECT_WRAPPED_METHODS_DECLARATION_WITH_EVENT_SOURCE(this->instance.data()) #endif // QOBJECT_WRAPPED_METHODS_DECLARATION #ifndef QOBJECT_WRAPPED_METHODS_EXPORT_DEFINE diff --git a/src/cpp/include/nodegui/QtGui/QScreen/qscreen_wrap.h b/src/cpp/include/nodegui/QtGui/QScreen/qscreen_wrap.h index a3076152a..68b77a714 100644 --- a/src/cpp/include/nodegui/QtGui/QScreen/qscreen_wrap.h +++ b/src/cpp/include/nodegui/QtGui/QScreen/qscreen_wrap.h @@ -10,7 +10,7 @@ class DLL_EXPORT QScreenWrap : public Napi::ObjectWrap, public EventWidget { - QOBJECT_WRAPPED_METHODS_DECLARATION + QOBJECT_WRAPPED_METHODS_DECLARATION_WITH_EVENT_SOURCE(this) // Note: We don't use EVENTWIDGET_IMPLEMENTATIONS() here because this class // doesn't handle any QEvents. diff --git a/src/cpp/include/nodegui/QtGui/QWindow/qwindow_wrap.h b/src/cpp/include/nodegui/QtGui/QWindow/qwindow_wrap.h index 419a95691..ccbdb2594 100644 --- a/src/cpp/include/nodegui/QtGui/QWindow/qwindow_wrap.h +++ b/src/cpp/include/nodegui/QtGui/QWindow/qwindow_wrap.h @@ -10,7 +10,7 @@ class DLL_EXPORT QWindowWrap : public Napi::ObjectWrap, public EventWidget { - QOBJECT_WRAPPED_METHODS_DECLARATION + QOBJECT_WRAPPED_METHODS_DECLARATION_WITH_EVENT_SOURCE(this) // Note: We don't use EVENTWIDGET_IMPLEMENTATIONS() here because this class // doesn't handle any QEvents. diff --git a/src/cpp/include/nodegui/core/Events/eventwidget_macro.h b/src/cpp/include/nodegui/core/Events/eventwidget_macro.h index 1a7aed96f..3afaeb39b 100644 --- a/src/cpp/include/nodegui/core/Events/eventwidget_macro.h +++ b/src/cpp/include/nodegui/core/Events/eventwidget_macro.h @@ -33,54 +33,55 @@ struct InitHelper { } }; -#ifndef EVENTWIDGET_WRAPPED_METHODS_DECLARATION -#define EVENTWIDGET_WRAPPED_METHODS_DECLARATION \ - COMPONENT_WRAPPED_METHODS_DECLARATION \ - Napi::Value initNodeEventEmitter(const Napi::CallbackInfo& info) { \ - Napi::Env env = info.Env(); \ - EventWidget* eventWidget = \ - dynamic_cast(this->instance.data()); \ - if (eventWidget) { \ - eventWidget->emitOnNode = \ - Napi::Persistent(info[0].As()); \ - } \ - InitHelperinstance.data())>::type>:: \ - connectSignalsToEventEmitter(this->instance.data()); \ - return env.Null(); \ - } \ - Napi::Value getNodeEventEmitter(const Napi::CallbackInfo& info) { \ - Napi::Env env = info.Env(); \ - EventWidget* eventWidget = \ - dynamic_cast(this->instance.data()); \ - if (eventWidget && eventWidget->emitOnNode) { \ - return eventWidget->emitOnNode.Value(); \ - } else { \ - return env.Null(); \ - } \ - } \ - Napi::Value subscribeToQtEvent(const Napi::CallbackInfo& info) { \ - Napi::Env env = info.Env(); \ - Napi::String eventString = info[0].As(); \ - EventWidget* eventWidget = \ - dynamic_cast(this->instance.data()); \ - bool success = false; \ - if (eventWidget) { \ - eventWidget->subscribeToQtEvent(eventString.Utf8Value()); \ - success = true; \ - } \ - return Napi::Boolean::New(env, success); \ - } \ - Napi::Value unSubscribeToQtEvent(const Napi::CallbackInfo& info) { \ - Napi::Env env = info.Env(); \ - Napi::String eventString = info[0].As(); \ - EventWidget* eventWidget = \ - dynamic_cast(this->instance.data()); \ - if (eventWidget) { \ - eventWidget->unSubscribeToQtEvent(eventString.Utf8Value()); \ - } \ - return env.Null(); \ +#ifndef EVENTWIDGET_WRAPPED_METHODS_DECLARATION_WITH_EVENT_SOURCE +#define EVENTWIDGET_WRAPPED_METHODS_DECLARATION_WITH_EVENT_SOURCE(source) \ + COMPONENT_WRAPPED_METHODS_DECLARATION \ + Napi::Value initNodeEventEmitter(const Napi::CallbackInfo& info) { \ + Napi::Env env = info.Env(); \ + EventWidget* eventWidget = dynamic_cast(source); \ + if (eventWidget) { \ + eventWidget->emitOnNode = \ + Napi::Persistent(info[0].As()); \ + } \ + InitHelper::type>:: \ + connectSignalsToEventEmitter(source); \ + return env.Null(); \ + } \ + Napi::Value getNodeEventEmitter(const Napi::CallbackInfo& info) { \ + Napi::Env env = info.Env(); \ + EventWidget* eventWidget = dynamic_cast(source); \ + if (eventWidget && eventWidget->emitOnNode) { \ + return eventWidget->emitOnNode.Value(); \ + } else { \ + return env.Null(); \ + } \ + } \ + Napi::Value subscribeToQtEvent(const Napi::CallbackInfo& info) { \ + Napi::Env env = info.Env(); \ + Napi::String eventString = info[0].As(); \ + EventWidget* eventWidget = dynamic_cast(source); \ + bool success = false; \ + if (eventWidget) { \ + eventWidget->subscribeToQtEvent(eventString.Utf8Value()); \ + success = true; \ + } \ + return Napi::Boolean::New(env, success); \ + } \ + Napi::Value unSubscribeToQtEvent(const Napi::CallbackInfo& info) { \ + Napi::Env env = info.Env(); \ + Napi::String eventString = info[0].As(); \ + EventWidget* eventWidget = dynamic_cast(source); \ + if (eventWidget) { \ + eventWidget->unSubscribeToQtEvent(eventString.Utf8Value()); \ + } \ + return env.Null(); \ } +#endif // EVENTWIDGET_WRAPPED_METHODS_DECLARATION_WITH_EVENT_SOURCE +#ifndef EVENTWIDGET_WRAPPED_METHODS_DECLARATION +#define EVENTWIDGET_WRAPPED_METHODS_DECLARATION \ + EVENTWIDGET_WRAPPED_METHODS_DECLARATION_WITH_EVENT_SOURCE( \ + this->instance.data()) #endif // EVENTWIDGET_WRAPPED_METHODS_DECLARATION #ifndef EVENTWIDGET_WRAPPED_METHODS_EXPORT_DEFINE From 05c690dcd9880b5a5e5146cfd11bb31e825f6e5f Mon Sep 17 00:00:00 2001 From: Simon Edwards Date: Sun, 24 Oct 2021 11:19:05 +0200 Subject: [PATCH 04/10] Fill in more screen and DPI support in `QApplication` --- .../QtGui/QApplication/napplication.hpp | 29 +++++++++ .../QtGui/QApplication/qapplication_wrap.h | 5 +- .../QtGui/QApplication/qapplication_wrap.cpp | 41 +++++++++++++ src/lib/QtGui/QApplication.ts | 60 ++++++++++++------- 4 files changed, 113 insertions(+), 22 deletions(-) diff --git a/src/cpp/include/nodegui/QtGui/QApplication/napplication.hpp b/src/cpp/include/nodegui/QtGui/QApplication/napplication.hpp index 2d9960f7a..498b2fa0f 100644 --- a/src/cpp/include/nodegui/QtGui/QApplication/napplication.hpp +++ b/src/cpp/include/nodegui/QtGui/QApplication/napplication.hpp @@ -5,6 +5,7 @@ #include "Extras/Export/export.h" #include "QtCore/QObject/qobject_macro.h" +#include "QtGui/QScreen/qscreen_wrap.h" #include "napi.h" class DLL_EXPORT NApplication : public QApplication, public EventWidget { @@ -22,5 +23,33 @@ class DLL_EXPORT NApplication : public QApplication, public EventWidget { Napi::HandleScope scope(env); this->emitOnNode.Call({Napi::String::New(env, "focusWindowChanged")}); }); + + QObject::connect( + this, &QGuiApplication::primaryScreenChanged, [=](QScreen* screen) { + Napi::Env env = this->emitOnNode.Env(); + Napi::HandleScope scope(env); + auto instance = QScreenWrap::constructor.New( + {Napi::External::New(env, screen)}); + this->emitOnNode.Call( + {Napi::String::New(env, "primaryScreenChanged"), instance}); + }); + + QObject::connect(this, &QGuiApplication::screenAdded, [=](QScreen* screen) { + Napi::Env env = this->emitOnNode.Env(); + Napi::HandleScope scope(env); + auto instance = QScreenWrap::constructor.New( + {Napi::External::New(env, screen)}); + this->emitOnNode.Call({Napi::String::New(env, "screenAdded"), instance}); + }); + + QObject::connect(this, &QGuiApplication::screenRemoved, + [=](QScreen* screen) { + Napi::Env env = this->emitOnNode.Env(); + Napi::HandleScope scope(env); + auto instance = QScreenWrap::constructor.New( + {Napi::External::New(env, screen)}); + this->emitOnNode.Call( + {Napi::String::New(env, "screenRemoved"), instance}); + }); } }; diff --git a/src/cpp/include/nodegui/QtGui/QApplication/qapplication_wrap.h b/src/cpp/include/nodegui/QtGui/QApplication/qapplication_wrap.h index a48c99854..a046a1e7c 100644 --- a/src/cpp/include/nodegui/QtGui/QApplication/qapplication_wrap.h +++ b/src/cpp/include/nodegui/QtGui/QApplication/qapplication_wrap.h @@ -30,11 +30,14 @@ class DLL_EXPORT QApplicationWrap : public Napi::ObjectWrap { Napi::Value quitOnLastWindowClosed(const Napi::CallbackInfo& info); Napi::Value palette(const Napi::CallbackInfo& info); Napi::Value setStyleSheet(const Napi::CallbackInfo& info); + Napi::Value devicePixelRatio(const Napi::CallbackInfo& info); }; namespace StaticQApplicationWrapMethods { -DLL_EXPORT Napi::Value instance(const Napi::CallbackInfo& info); DLL_EXPORT Napi::Value clipboard(const Napi::CallbackInfo& info); +DLL_EXPORT Napi::Value instance(const Napi::CallbackInfo& info); +DLL_EXPORT Napi::Value primaryScreen(const Napi::CallbackInfo& info); +DLL_EXPORT Napi::Value screens(const Napi::CallbackInfo& info); DLL_EXPORT Napi::Value setStyle(const Napi::CallbackInfo& info); DLL_EXPORT Napi::Value style(const Napi::CallbackInfo& info); } // namespace StaticQApplicationWrapMethods diff --git a/src/cpp/lib/QtGui/QApplication/qapplication_wrap.cpp b/src/cpp/lib/QtGui/QApplication/qapplication_wrap.cpp index 77c79152b..37596b117 100644 --- a/src/cpp/lib/QtGui/QApplication/qapplication_wrap.cpp +++ b/src/cpp/lib/QtGui/QApplication/qapplication_wrap.cpp @@ -23,10 +23,14 @@ Napi::Object QApplicationWrap::init(Napi::Env env, Napi::Object exports) { &QApplicationWrap::quitOnLastWindowClosed), InstanceMethod("palette", &QApplicationWrap::palette), InstanceMethod("setStyleSheet", &QApplicationWrap::setStyleSheet), + InstanceMethod("devicePixelRatio", &QApplicationWrap::devicePixelRatio), StaticMethod("instance", &StaticQApplicationWrapMethods::instance), StaticMethod("clipboard", &StaticQApplicationWrapMethods::clipboard), StaticMethod("setStyle", &StaticQApplicationWrapMethods::setStyle), StaticMethod("style", &StaticQApplicationWrapMethods::style), + StaticMethod("primaryScreen", + &StaticQApplicationWrapMethods::primaryScreen), + StaticMethod("screens", &StaticQApplicationWrapMethods::screens), QOBJECT_WRAPPED_METHODS_EXPORT_DEFINE(QApplicationWrap)}); constructor = Napi::Persistent(func); exports.Set(CLASSNAME, func); @@ -164,3 +168,40 @@ Napi::Value QApplicationWrap::quitOnLastWindowClosed( bool quit = this->instance->quitOnLastWindowClosed(); return Napi::Value::From(env, quit); } + +Napi::Value StaticQApplicationWrapMethods::primaryScreen( + const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + auto screen = QApplication::primaryScreen(); + if (screen) { + return QScreenWrap::constructor.New( + {Napi::External::New(env, screen)}); + } else { + return env.Null(); + } +} + +Napi::Value StaticQApplicationWrapMethods::screens( + const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + + auto screens = QApplication::screens(); + Napi::Array jsArray = Napi::Array::New(env, screens.size()); + for (int i = 0; i < screens.size(); i++) { + QScreen* screen = screens[i]; + auto instance = QScreenWrap::constructor.New( + {Napi::External::New(env, screen)}); + jsArray[i] = instance; + } + return jsArray; +} + +Napi::Value QApplicationWrap::devicePixelRatio(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + + qreal result = this->instance->devicePixelRatio(); + return Napi::Value::From(env, result); +} diff --git a/src/lib/QtGui/QApplication.ts b/src/lib/QtGui/QApplication.ts index 5802dbd80..945e1447f 100644 --- a/src/lib/QtGui/QApplication.ts +++ b/src/lib/QtGui/QApplication.ts @@ -7,6 +7,7 @@ import { QObjectSignals, NodeObject } from '../QtCore/QObject'; import { QPalette } from './QPalette'; import { StyleSheet } from '../core/Style/StyleSheet'; import memoizeOne from 'memoize-one'; +import { QScreen } from './QScreen'; /** @@ -41,37 +42,51 @@ export class QApplication extends NodeObject { this.setStyleSheet = memoizeOne(this.setStyleSheet); } - static clipboard(): QClipboard { - return new QClipboard(addon.QApplication.clipboard()); + devicePixelRatio(): number { + return this.native.devicePixelRatio(); + } + exec(): number { + return this.native.exec(); + } + exit(exitCode: number): number { + return this.native.exit(exitCode); + } + palette(): QPalette { + return new QPalette(this.native.palette()); } processEvents(): void { this.native.processEvents(); } - exec(): number { - return this.native.exec(); + quit(): number { + return this.native.quit(); + } + quitOnLastWindowClosed(): boolean { + return this.native.quitOnLastWindowClosed(); + } + setQuitOnLastWindowClosed(quit: boolean): void { + this.native.setQuitOnLastWindowClosed(quit); + } + setStyleSheet(styleSheet: string): void { + const preparedSheet = StyleSheet.create(styleSheet); + this.native.setStyleSheet(preparedSheet); + } + static clipboard(): QClipboard { + return new QClipboard(addon.QApplication.clipboard()); } static instance(): QApplication { const nativeQApp = addon.QApplication.instance(); return new QApplication(nativeQApp); } - quit(): number { - return this.native.quit(); + static primaryScreen(): QScreen | null { + const screenNative = addon.QApplication.primaryScreen(); + if (screenNative == null) { + return null; + } + return new QScreen(screenNative); } - exit(exitCode: number): number { - return this.native.exit(exitCode); - } - setQuitOnLastWindowClosed(quit: boolean): void { - this.native.setQuitOnLastWindowClosed(quit); - } - quitOnLastWindowClosed(): boolean { - return this.native.quitOnLastWindowClosed(); - } - palette(): QPalette { - return new QPalette(this.native.palette()); - } - setStyleSheet(styleSheet: string): void { - const preparedSheet = StyleSheet.create(styleSheet); - this.native.setStyleSheet(preparedSheet); + static screens(): QScreen[] { + const screenNativeList = addon.QApplication.screens(); + return screenNativeList.map((screenNative: any) => new QScreen(screenNative)); } static setStyle(style: QStyle): void { addon.QApplication.setStyle(style.native); @@ -83,4 +98,7 @@ export class QApplication extends NodeObject { export interface QApplicationSignals extends QObjectSignals { focusWindowChanged: () => void; + primaryScreenChanged: (screen: QScreen) => void; + screenAdded: (screen: QScreen) => void; + screenRemoved: (screen: QScreen) => void; } From 710cfa3d317c45b58f9055560b949d4b65891513 Mon Sep 17 00:00:00 2001 From: Simon Edwards Date: Wed, 27 Oct 2021 10:46:12 +0200 Subject: [PATCH 05/10] Add wrapper caching. Try it on `QScreen`. --- CMakeLists.txt | 5 ++ src/cpp/include/nodegui/Extras/Utils/nutils.h | 1 + .../nodegui/QtCore/QObject/qobject_macro.h | 9 ++- .../nodegui/core/WrapperCache/wrappercache.h | 34 +++++++++ src/cpp/lib/Extras/Utils/nutils.cpp | 14 ++++ .../QtGui/QApplication/qapplication_wrap.cpp | 7 +- src/cpp/lib/QtGui/QWindow/qwindow_wrap.cpp | 5 +- .../lib/core/WrapperCache/wrappercache.cpp | 70 +++++++++++++++++++ src/cpp/main.cpp | 3 + src/lib/QtGui/QApplication.ts | 5 +- src/lib/QtGui/QWindow.ts | 4 +- src/lib/core/WrapperCache.ts | 44 ++++++++++++ .../development/signal_and_event_handling.md | 2 +- 13 files changed, 190 insertions(+), 13 deletions(-) create mode 100644 src/cpp/include/nodegui/core/WrapperCache/wrappercache.h create mode 100644 src/cpp/lib/core/WrapperCache/wrappercache.cpp create mode 100644 src/lib/core/WrapperCache.ts diff --git a/CMakeLists.txt b/CMakeLists.txt index 7bdd5c41a..a9092645c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,9 @@ set(CORE_WIDGETS_ADDON "nodegui_core") project(${CORE_WIDGETS_ADDON}) + +# Note: CMake+moc also use this list when finding files which `moc` applied. + add_library(${CORE_WIDGETS_ADDON} SHARED "${CMAKE_JS_SRC}" "${PROJECT_SOURCE_DIR}/src/cpp/main.cpp" @@ -24,6 +27,8 @@ add_library(${CORE_WIDGETS_ADDON} SHARED "${PROJECT_SOURCE_DIR}/src/cpp/lib/core/Events/eventsmap.cpp" "${PROJECT_SOURCE_DIR}/src/cpp/lib/core/Events/eventwidget.cpp" "${PROJECT_SOURCE_DIR}/src/cpp/lib/core/YogaWidget/yogawidget.cpp" + "${PROJECT_SOURCE_DIR}/src/cpp/include/nodegui/core/WrapperCache/wrappercache.h" + "${PROJECT_SOURCE_DIR}/src/cpp/lib/core/WrapperCache/wrappercache.cpp" # core deps "${PROJECT_SOURCE_DIR}/src/cpp/include/deps/yoga/log.cpp" "${PROJECT_SOURCE_DIR}/src/cpp/include/deps/yoga/Utils.cpp" diff --git a/src/cpp/include/nodegui/Extras/Utils/nutils.h b/src/cpp/include/nodegui/Extras/Utils/nutils.h index b6519b68f..5142443ba 100644 --- a/src/cpp/include/nodegui/Extras/Utils/nutils.h +++ b/src/cpp/include/nodegui/Extras/Utils/nutils.h @@ -17,6 +17,7 @@ DLL_EXPORT void* configureQWidget(QWidget* widget, YGNodeRef node, bool isLeafNode = false); DLL_EXPORT void* configureQObject(QObject* object); DLL_EXPORT void* configureComponent(void* component); +DLL_EXPORT uint64_t hashPointerTo53bit(const void* input); template void safeDelete(QPointer& component) { diff --git a/src/cpp/include/nodegui/QtCore/QObject/qobject_macro.h b/src/cpp/include/nodegui/QtCore/QObject/qobject_macro.h index f07e5984a..be1471ec0 100644 --- a/src/cpp/include/nodegui/QtCore/QObject/qobject_macro.h +++ b/src/cpp/include/nodegui/QtCore/QObject/qobject_macro.h @@ -17,6 +17,12 @@ \ EVENTWIDGET_WRAPPED_METHODS_DECLARATION_WITH_EVENT_SOURCE(source) \ \ + Napi::Value __id__(const Napi::CallbackInfo& info) { \ + Napi::Env env = info.Env(); \ + Napi::HandleScope scope(env); \ + return Napi::Value::From( \ + env, extrautils::hashPointerTo53bit(this->instance.data())); \ + } \ Napi::Value inherits(const Napi::CallbackInfo& info) { \ Napi::Env env = info.Env(); \ Napi::HandleScope scope(env); \ @@ -85,7 +91,8 @@ \ EVENTWIDGET_WRAPPED_METHODS_EXPORT_DEFINE(ComponentWrapName) \ \ - InstanceMethod("inherits", &ComponentWrapName::inherits), \ + InstanceMethod("__id__", &ComponentWrapName::__id__), \ + InstanceMethod("inherits", &ComponentWrapName::inherits), \ InstanceMethod("setProperty", &ComponentWrapName::setProperty), \ InstanceMethod("property", &ComponentWrapName::property), \ InstanceMethod("setObjectName", &ComponentWrapName::setObjectName), \ diff --git a/src/cpp/include/nodegui/core/WrapperCache/wrappercache.h b/src/cpp/include/nodegui/core/WrapperCache/wrappercache.h new file mode 100644 index 000000000..a8982d2bf --- /dev/null +++ b/src/cpp/include/nodegui/core/WrapperCache/wrappercache.h @@ -0,0 +1,34 @@ +#pragma once + +#include + +#include +#include + +#include "Extras/Export/export.h" +#include "QtGui/QScreen/qscreen_wrap.h" + +struct CachedObject { + napi_ref ref; + napi_env env; +}; + +class DLL_EXPORT WrapperCache : public QObject { + Q_OBJECT + + private: + QMap cache; + + public: + WrapperCache(); + Napi::Object get(const Napi::CallbackInfo& info, QScreen* screen); + + static WrapperCache instance; + static Napi::Object init(Napi::Env env, Napi::Object exports); + static Napi::Value injectDestroyCallback(const Napi::CallbackInfo& info); + + static Napi::FunctionReference destroyedCallback; + + public Q_SLOTS: + void handleDestroyed(const QObject* object); +}; diff --git a/src/cpp/lib/Extras/Utils/nutils.cpp b/src/cpp/lib/Extras/Utils/nutils.cpp index bd1f87a49..b623b7ed8 100644 --- a/src/cpp/lib/Extras/Utils/nutils.cpp +++ b/src/cpp/lib/Extras/Utils/nutils.cpp @@ -110,6 +110,20 @@ void* extrautils::configureQWidget(QWidget* widget, YGNodeRef node, return configureQObject(widget); } +uint64_t extrautils::hashPointerTo53bit(const void* input) { + // Hash the address of the object down to something which will + // fit into the JS 53bit safe integer space. + uint64_t address = reinterpret_cast(input); + uint64_t top8Bits = address & 0xff00000000000000u; + uint64_t foldedBits = (top8Bits >> 11) ^ address; + + // Clear the top 8bits which we folded, now shift out the last 3 bits + // Pointers are aligned on 64bit architectures to at least 8bytes + // boundaries. + uint64_t result = (foldedBits & ~0xff00000000000000u) >> 3; + return result; +} + Napi::FunctionReference NUtilsWrap::constructor; Napi::Object NUtilsWrap::init(Napi::Env env, Napi::Object exports) { diff --git a/src/cpp/lib/QtGui/QApplication/qapplication_wrap.cpp b/src/cpp/lib/QtGui/QApplication/qapplication_wrap.cpp index 37596b117..b2d94bd5f 100644 --- a/src/cpp/lib/QtGui/QApplication/qapplication_wrap.cpp +++ b/src/cpp/lib/QtGui/QApplication/qapplication_wrap.cpp @@ -5,6 +5,7 @@ #include "QtGui/QPalette/qpalette_wrap.h" #include "QtGui/QStyle/qstyle_wrap.h" #include "core/Integration/qode-api.h" +#include "core/WrapperCache/wrappercache.h" Napi::FunctionReference QApplicationWrap::constructor; @@ -175,8 +176,7 @@ Napi::Value StaticQApplicationWrapMethods::primaryScreen( Napi::HandleScope scope(env); auto screen = QApplication::primaryScreen(); if (screen) { - return QScreenWrap::constructor.New( - {Napi::External::New(env, screen)}); + return WrapperCache::instance.get(info, screen); } else { return env.Null(); } @@ -191,8 +191,7 @@ Napi::Value StaticQApplicationWrapMethods::screens( Napi::Array jsArray = Napi::Array::New(env, screens.size()); for (int i = 0; i < screens.size(); i++) { QScreen* screen = screens[i]; - auto instance = QScreenWrap::constructor.New( - {Napi::External::New(env, screen)}); + auto instance = WrapperCache::instance.get(info, screen); jsArray[i] = instance; } return jsArray; diff --git a/src/cpp/lib/QtGui/QWindow/qwindow_wrap.cpp b/src/cpp/lib/QtGui/QWindow/qwindow_wrap.cpp index 7d2633f10..7fabeaaac 100644 --- a/src/cpp/lib/QtGui/QWindow/qwindow_wrap.cpp +++ b/src/cpp/lib/QtGui/QWindow/qwindow_wrap.cpp @@ -2,6 +2,7 @@ #include "Extras/Utils/nutils.h" #include "QtGui/QScreen/qscreen_wrap.h" +#include "core/WrapperCache/wrappercache.h" Napi::FunctionReference QWindowWrap::constructor; @@ -49,9 +50,7 @@ Napi::Value QWindowWrap::screen(const Napi::CallbackInfo& info) { QScreen* screen = this->instance->screen(); if (screen) { - auto instance = QScreenWrap::constructor.New( - {Napi::External::New(env, screen)}); - return instance; + return WrapperCache::instance.get(info, screen); } else { return env.Null(); } diff --git a/src/cpp/lib/core/WrapperCache/wrappercache.cpp b/src/cpp/lib/core/WrapperCache/wrappercache.cpp new file mode 100644 index 000000000..d99637251 --- /dev/null +++ b/src/cpp/lib/core/WrapperCache/wrappercache.cpp @@ -0,0 +1,70 @@ +#include "core/WrapperCache/wrappercache.h" + +#include "Extras/Utils/nutils.h" + +DLL_EXPORT WrapperCache WrapperCache::instance; + +Napi::FunctionReference WrapperCache::destroyedCallback; + +WrapperCache::WrapperCache() {} + +Napi::Object WrapperCache::init(Napi::Env env, Napi::Object exports) { + Napi::HandleScope scope(env); + + exports.Set("WrapperCache_injectCallback", + Napi::Function::New(env)); + return exports; +} + +Napi::Value WrapperCache::injectDestroyCallback( + const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + + destroyedCallback = Napi::Persistent(info[0].As()); + return env.Null(); +} + +Napi::Object WrapperCache::get(const Napi::CallbackInfo& info, + QScreen* screen) { + Napi::Env env = info.Env(); + + if (this->cache.contains(screen)) { + napi_value result = nullptr; + napi_get_reference_value(this->cache[screen].env, this->cache[screen].ref, + &result); + return Napi::Object(env, result); + } + + Napi::HandleScope scope(env); + Napi::Object wrapper = + QScreenWrap::constructor.New({Napi::External::New(env, screen)}); + + napi_ref ref = nullptr; + napi_create_reference(env, wrapper, 1, &ref); + this->cache[screen].env = napi_env(env); + this->cache[screen].ref = ref; + + QObject::connect(screen, &QObject::destroyed, this, + &WrapperCache::handleDestroyed); + + return wrapper; +} + +void WrapperCache::handleDestroyed(const QObject* object) { + if (!this->cache.contains(object)) { + return; + } + + uint32_t result = 0; + Napi::Env env = this->cache[object].env; + napi_reference_unref(env, this->cache[object].ref, &result); + this->cache.remove(object); + + // Callback to JS with the address/ID of the destroyed object. So that it + // can clear it out of the cache. + if (destroyedCallback) { + destroyedCallback.Call( + {Napi::Value::From(env, extrautils::hashPointerTo53bit(object))}); + } +} diff --git a/src/cpp/main.cpp b/src/cpp/main.cpp index ea7526d3e..12cb0e0d6 100644 --- a/src/cpp/main.cpp +++ b/src/cpp/main.cpp @@ -114,6 +114,8 @@ #include "QtWidgets/QWidget/qwidget_wrap.h" #include "core/FlexLayout/flexlayout_wrap.h" #include "core/Integration/integration.h" +#include "core/WrapperCache/wrappercache.h" + // These cant be instantiated in JS Side void InitPrivateHelpers(Napi::Env env) { qodeIntegration::integrate(); @@ -123,6 +125,7 @@ void InitPrivateHelpers(Napi::Env env) { Napi::Object Main(Napi::Env env, Napi::Object exports) { InitPrivateHelpers(env); NUtilsWrap::init(env, exports); + WrapperCache::init(env, exports); QApplicationWrap::init(env, exports); QDateWrap::init(env, exports); QDateTimeWrap::init(env, exports); diff --git a/src/lib/QtGui/QApplication.ts b/src/lib/QtGui/QApplication.ts index 945e1447f..ed4714389 100644 --- a/src/lib/QtGui/QApplication.ts +++ b/src/lib/QtGui/QApplication.ts @@ -8,6 +8,7 @@ import { QPalette } from './QPalette'; import { StyleSheet } from '../core/Style/StyleSheet'; import memoizeOne from 'memoize-one'; import { QScreen } from './QScreen'; +import { wrapperCache } from '../core/WrapperCache'; /** @@ -82,11 +83,11 @@ export class QApplication extends NodeObject { if (screenNative == null) { return null; } - return new QScreen(screenNative); + return wrapperCache.get(QScreen, screenNative); } static screens(): QScreen[] { const screenNativeList = addon.QApplication.screens(); - return screenNativeList.map((screenNative: any) => new QScreen(screenNative)); + return screenNativeList.map((screenNative: any) => wrapperCache.get(QScreen, screenNative)); } static setStyle(style: QStyle): void { addon.QApplication.setStyle(style.native); diff --git a/src/lib/QtGui/QWindow.ts b/src/lib/QtGui/QWindow.ts index 0edec5903..2e13d7220 100644 --- a/src/lib/QtGui/QWindow.ts +++ b/src/lib/QtGui/QWindow.ts @@ -2,6 +2,7 @@ import { NativeElement } from '../core/Component'; import { checkIfNativeElement } from '../utils/helpers'; import { NodeObject, QObjectSignals } from '../QtCore/QObject'; import { QScreen } from './QScreen'; +import { wrapperCache } from '../core/WrapperCache'; export class QWindow extends NodeObject { native: NativeElement; @@ -16,8 +17,7 @@ export class QWindow extends NodeObject { } screen(): QScreen { - const screenNative = this.native.screen(); - return new QScreen(screenNative); + return wrapperCache.get(QScreen, this.native.screen()); } } diff --git a/src/lib/core/WrapperCache.ts b/src/lib/core/WrapperCache.ts new file mode 100644 index 000000000..23c2689e2 --- /dev/null +++ b/src/lib/core/WrapperCache.ts @@ -0,0 +1,44 @@ +import addon from '../utils/addon'; + +/** + * JS side cache for wrapper objects. + * + * This is mainly used for caching wrappers of Qt objects which are not + * directly created by our Nodejs application. The purpose of the cache + * is to keep "alive" wrapper objects and their underlying C++ wrappers + * which may be connected to Qt signals from the real Qt object. + * This makes it easier for application to grab one of these objects, + * set up event handlers, and then let the object go and *not* have the + * wrapper automatically and unexpectedly garbage collected. + */ +export class WrapperCache { + private _cache = new Map(); + + constructor() { + addon.WrapperCache_injectCallback(this._objectDestroyedCallback.bind(this)); + } + + private _objectDestroyedCallback(objectId: number): void { + console.log(`_objectDestroyedCallback() id: ${objectId}`); + if (!this._cache.has(objectId)) { + return; + } + const wrapper = this._cache.get(objectId); + wrapper.native = null; + this._cache.delete(objectId); + } + + get(wrapperConstructor: { new (native: any): T }, native: any): T { + const id = native.__id__(); + + console.log(`WrapperCache.get() id: ${id}`); + + if (this._cache.has(id)) { + return this._cache.get(id) as T; + } + const wrapper = new wrapperConstructor(native); + this._cache.set(id, wrapper); + return wrapper; + } +} +export const wrapperCache = new WrapperCache(); diff --git a/website/docs/development/signal_and_event_handling.md b/website/docs/development/signal_and_event_handling.md index 766a851e3..23ee272de 100644 --- a/website/docs/development/signal_and_event_handling.md +++ b/website/docs/development/signal_and_event_handling.md @@ -144,5 +144,5 @@ We need to run Qt's MOC (Meta Object Compiler) on the file whenever we use Q_OBJ # How does it work ? 1. On JS side for each widget instance we create an instance of NodeJS's Event Emitter. This is done by the class `EventWidget` from which `NodeWidget` inherits -2. We send this event emiiter's `emit` function to the C++ side by calling `initNodeEventEmitter` method and store a pointer to the event emitter's emit function using `emitOnNode`. initNodeEventEmitter function is added by a macro from EventWidget (c++). You can find the initNodeEventEmitter method with the event widget macros. +2. We send this event emitter's `emit` function to the C++ side by calling `initNodeEventEmitter` method and store a pointer to the event emitter's emit function using `emitOnNode`. initNodeEventEmitter function is added by a macro from EventWidget (c++). You can find the initNodeEventEmitter method with the event widget macros. 3. We setup Qt's connect method for all the signals that we want to listen to and call the emitOnNode (which is actually emit from Event emitter) whenever a signal arrives. This is done manually on every widget by overriding the method `connectSignalsToEventEmitter`. Check `npushbutton.h` for details. This takes care of all the signals of the widgets. Now to export all qt events of the widget, we had overriden the widgets `event(Event*)` method to listen to events received by the widget and send it to the event emitter. This is done inside the EVENTWIDGET_IMPLEMENTATIONS macro From 123123605c5b6f2beb77642b2238f166c5344daf Mon Sep 17 00:00:00 2001 From: Simon Edwards Date: Wed, 27 Oct 2021 12:20:49 +0200 Subject: [PATCH 06/10] Make the wrapper cache callback work --- src/cpp/lib/core/WrapperCache/wrappercache.cpp | 11 ++++++----- src/lib/core/WrapperCache.ts | 4 ---- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/cpp/lib/core/WrapperCache/wrappercache.cpp b/src/cpp/lib/core/WrapperCache/wrappercache.cpp index d99637251..8b14dee68 100644 --- a/src/cpp/lib/core/WrapperCache/wrappercache.cpp +++ b/src/cpp/lib/core/WrapperCache/wrappercache.cpp @@ -56,15 +56,16 @@ void WrapperCache::handleDestroyed(const QObject* object) { return; } - uint32_t result = 0; - Napi::Env env = this->cache[object].env; - napi_reference_unref(env, this->cache[object].ref, &result); - this->cache.remove(object); - // Callback to JS with the address/ID of the destroyed object. So that it // can clear it out of the cache. if (destroyedCallback) { + Napi::Env env = destroyedCallback.Env(); + Napi::HandleScope scope(env); destroyedCallback.Call( {Napi::Value::From(env, extrautils::hashPointerTo53bit(object))}); } + + uint32_t result = 0; + napi_reference_unref(this->cache[object].env, this->cache[object].ref, &result); + this->cache.remove(object); } diff --git a/src/lib/core/WrapperCache.ts b/src/lib/core/WrapperCache.ts index 23c2689e2..794e9a646 100644 --- a/src/lib/core/WrapperCache.ts +++ b/src/lib/core/WrapperCache.ts @@ -19,7 +19,6 @@ export class WrapperCache { } private _objectDestroyedCallback(objectId: number): void { - console.log(`_objectDestroyedCallback() id: ${objectId}`); if (!this._cache.has(objectId)) { return; } @@ -30,9 +29,6 @@ export class WrapperCache { get(wrapperConstructor: { new (native: any): T }, native: any): T { const id = native.__id__(); - - console.log(`WrapperCache.get() id: ${id}`); - if (this._cache.has(id)) { return this._cache.get(id) as T; } From 2b2d2a65b2088dfd213eb0b2ee727e61ce898727 Mon Sep 17 00:00:00 2001 From: Simon Edwards Date: Sun, 31 Oct 2021 09:32:37 +0100 Subject: [PATCH 07/10] Make the wrapper cache generic. Apply it to `QWindow` and `QScreen` --- .../nodegui/QtWidgets/QWidget/qwidget_macro.h | 4 +- .../nodegui/core/WrapperCache/wrappercache.h | 85 +++++++++++++++++-- .../QtGui/QApplication/qapplication_wrap.cpp | 5 +- src/cpp/lib/QtGui/QWindow/qwindow_wrap.cpp | 2 +- .../lib/core/WrapperCache/wrappercache.cpp | 64 -------------- src/lib/QtWidgets/QWidget.ts | 4 +- src/lib/core/WrapperCache.ts | 3 +- .../development/signal_and_event_handling.md | 2 +- 8 files changed, 90 insertions(+), 79 deletions(-) diff --git a/src/cpp/include/nodegui/QtWidgets/QWidget/qwidget_macro.h b/src/cpp/include/nodegui/QtWidgets/QWidget/qwidget_macro.h index 041fcd626..4d50885e7 100644 --- a/src/cpp/include/nodegui/QtWidgets/QWidget/qwidget_macro.h +++ b/src/cpp/include/nodegui/QtWidgets/QWidget/qwidget_macro.h @@ -13,6 +13,7 @@ #include "QtGui/QWindow/qwindow_wrap.h" #include "QtWidgets/QAction/qaction_wrap.h" #include "QtWidgets/QLayout/qlayout_wrap.h" +#include "core/WrapperCache/wrappercache.h" #include "core/YogaWidget/yogawidget_macro.h" /* @@ -544,8 +545,7 @@ Napi::HandleScope scope(env); \ QWindow* window = this->instance->windowHandle(); \ if (window) { \ - return QWindowWrap::constructor.New( \ - {Napi::External::New(env, window)}); \ + return WrapperCache::instance.get(info, window); \ } else { \ return env.Null(); \ } \ diff --git a/src/cpp/include/nodegui/core/WrapperCache/wrappercache.h b/src/cpp/include/nodegui/core/WrapperCache/wrappercache.h index a8982d2bf..ba4e25906 100644 --- a/src/cpp/include/nodegui/core/WrapperCache/wrappercache.h +++ b/src/cpp/include/nodegui/core/WrapperCache/wrappercache.h @@ -13,6 +13,13 @@ struct CachedObject { napi_env env; }; +/** + * C++ side cache for wrapper objects. + * + * This can cache wrappers for QObjects and uses the Qt "destroyed" signal to + * track lifetime and remove objects from the cache. It has a JS side component + * `WrapperCache.ts` which can cache the JS side wrapper object. + */ class DLL_EXPORT WrapperCache : public QObject { Q_OBJECT @@ -20,15 +27,81 @@ class DLL_EXPORT WrapperCache : public QObject { QMap cache; public: - WrapperCache(); - Napi::Object get(const Napi::CallbackInfo& info, QScreen* screen); - + /** + * Singleton instance. Use this to access the cache. + */ static WrapperCache instance; - static Napi::Object init(Napi::Env env, Napi::Object exports); - static Napi::Value injectDestroyCallback(const Napi::CallbackInfo& info); + + /** + * Get a wrapper for a given Qt object. + * + * @param T - (template argument) The Qt class of the object being cached, + * e.g. `QScreen`. + * @param W - (template argument) The wrapper type which matches the object + * `QScreenWrap`. + * @param object - Pointer to the QObject for which a wrapper is required. + * @return The JS wrapper object. + */ + template + Napi::Object get(const Napi::CallbackInfo& info, T* object) { + Napi::Env env = info.Env(); + + if (this->cache.contains(object)) { + napi_value result = nullptr; + napi_get_reference_value(this->cache[object].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)}); + + napi_ref ref = nullptr; + napi_create_reference(env, wrapper, 1, &ref); + this->cache[object].env = napi_env(env); + this->cache[object].ref = ref; + + QObject::connect(object, &QObject::destroyed, this, + &WrapperCache::handleDestroyed); + return wrapper; + } + + static Napi::Object init(Napi::Env env, Napi::Object exports) { + Napi::HandleScope scope(env); + exports.Set("WrapperCache_injectCallback", + Napi::Function::New(env)); + return exports; + } + + 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(); + } static Napi::FunctionReference destroyedCallback; public Q_SLOTS: - void handleDestroyed(const QObject* object); + void handleDestroyed(const QObject* object) { + if (!this->cache.contains(object)) { + return; + } + + // Callback to JS with the address/ID of the destroyed object. So that it + // can clear it out of the cache. + if (destroyedCallback) { + Napi::Env env = destroyedCallback.Env(); + Napi::HandleScope scope(env); + destroyedCallback.Call( + {Napi::Value::From(env, extrautils::hashPointerTo53bit(object))}); + } + + uint32_t result = 0; + napi_reference_unref(this->cache[object].env, this->cache[object].ref, + &result); + this->cache.remove(object); + } }; diff --git a/src/cpp/lib/QtGui/QApplication/qapplication_wrap.cpp b/src/cpp/lib/QtGui/QApplication/qapplication_wrap.cpp index b2d94bd5f..f4f165088 100644 --- a/src/cpp/lib/QtGui/QApplication/qapplication_wrap.cpp +++ b/src/cpp/lib/QtGui/QApplication/qapplication_wrap.cpp @@ -176,7 +176,7 @@ Napi::Value StaticQApplicationWrapMethods::primaryScreen( Napi::HandleScope scope(env); auto screen = QApplication::primaryScreen(); if (screen) { - return WrapperCache::instance.get(info, screen); + return WrapperCache::instance.get(info, screen); } else { return env.Null(); } @@ -191,7 +191,8 @@ Napi::Value StaticQApplicationWrapMethods::screens( Napi::Array jsArray = Napi::Array::New(env, screens.size()); for (int i = 0; i < screens.size(); i++) { QScreen* screen = screens[i]; - auto instance = WrapperCache::instance.get(info, screen); + auto instance = + WrapperCache::instance.get(info, screen); jsArray[i] = instance; } return jsArray; diff --git a/src/cpp/lib/QtGui/QWindow/qwindow_wrap.cpp b/src/cpp/lib/QtGui/QWindow/qwindow_wrap.cpp index 7fabeaaac..81f92398a 100644 --- a/src/cpp/lib/QtGui/QWindow/qwindow_wrap.cpp +++ b/src/cpp/lib/QtGui/QWindow/qwindow_wrap.cpp @@ -50,7 +50,7 @@ Napi::Value QWindowWrap::screen(const Napi::CallbackInfo& info) { QScreen* screen = this->instance->screen(); if (screen) { - return WrapperCache::instance.get(info, screen); + return WrapperCache::instance.get(info, screen); } else { return env.Null(); } diff --git a/src/cpp/lib/core/WrapperCache/wrappercache.cpp b/src/cpp/lib/core/WrapperCache/wrappercache.cpp index 8b14dee68..50ec70db3 100644 --- a/src/cpp/lib/core/WrapperCache/wrappercache.cpp +++ b/src/cpp/lib/core/WrapperCache/wrappercache.cpp @@ -5,67 +5,3 @@ DLL_EXPORT WrapperCache WrapperCache::instance; Napi::FunctionReference WrapperCache::destroyedCallback; - -WrapperCache::WrapperCache() {} - -Napi::Object WrapperCache::init(Napi::Env env, Napi::Object exports) { - Napi::HandleScope scope(env); - - exports.Set("WrapperCache_injectCallback", - Napi::Function::New(env)); - return exports; -} - -Napi::Value WrapperCache::injectDestroyCallback( - const Napi::CallbackInfo& info) { - Napi::Env env = info.Env(); - Napi::HandleScope scope(env); - - destroyedCallback = Napi::Persistent(info[0].As()); - return env.Null(); -} - -Napi::Object WrapperCache::get(const Napi::CallbackInfo& info, - QScreen* screen) { - Napi::Env env = info.Env(); - - if (this->cache.contains(screen)) { - napi_value result = nullptr; - napi_get_reference_value(this->cache[screen].env, this->cache[screen].ref, - &result); - return Napi::Object(env, result); - } - - Napi::HandleScope scope(env); - Napi::Object wrapper = - QScreenWrap::constructor.New({Napi::External::New(env, screen)}); - - napi_ref ref = nullptr; - napi_create_reference(env, wrapper, 1, &ref); - this->cache[screen].env = napi_env(env); - this->cache[screen].ref = ref; - - QObject::connect(screen, &QObject::destroyed, this, - &WrapperCache::handleDestroyed); - - return wrapper; -} - -void WrapperCache::handleDestroyed(const QObject* object) { - if (!this->cache.contains(object)) { - return; - } - - // Callback to JS with the address/ID of the destroyed object. So that it - // can clear it out of the cache. - if (destroyedCallback) { - Napi::Env env = destroyedCallback.Env(); - Napi::HandleScope scope(env); - destroyedCallback.Call( - {Napi::Value::From(env, extrautils::hashPointerTo53bit(object))}); - } - - uint32_t result = 0; - napi_reference_unref(this->cache[object].env, this->cache[object].ref, &result); - this->cache.remove(object); -} diff --git a/src/lib/QtWidgets/QWidget.ts b/src/lib/QtWidgets/QWidget.ts index 5d52297fa..0b7e1c894 100644 --- a/src/lib/QtWidgets/QWidget.ts +++ b/src/lib/QtWidgets/QWidget.ts @@ -15,10 +15,10 @@ import { QRect } from '../QtCore/QRect'; import { QObjectSignals } from '../QtCore/QObject'; import { QFont } from '../QtGui/QFont'; import { QAction } from './QAction'; -import { QScreen } from '../QtGui/QScreen'; import memoizeOne from 'memoize-one'; import { QGraphicsEffect } from './QGraphicsEffect'; import { QSizePolicyPolicy, QStyle, QWindow } from '../..'; +import { wrapperCache } from '../core/WrapperCache'; /** @@ -426,7 +426,7 @@ export abstract class NodeWidget extends YogaWid windowHandle(): QWindow | null { const handle = this.native.windowHandle(); if (handle != null) { - return new QWindow(handle); + return wrapperCache.get(QWindow, handle); } return null; } diff --git a/src/lib/core/WrapperCache.ts b/src/lib/core/WrapperCache.ts index 794e9a646..13a218d22 100644 --- a/src/lib/core/WrapperCache.ts +++ b/src/lib/core/WrapperCache.ts @@ -1,4 +1,5 @@ import addon from '../utils/addon'; +import { NativeElement } from './Component'; /** * JS side cache for wrapper objects. @@ -27,7 +28,7 @@ export class WrapperCache { this._cache.delete(objectId); } - get(wrapperConstructor: { new (native: any): T }, native: any): T { + get(wrapperConstructor: { new (native: any): T }, native: NativeElement): T { const id = native.__id__(); if (this._cache.has(id)) { return this._cache.get(id) as T; diff --git a/website/docs/development/signal_and_event_handling.md b/website/docs/development/signal_and_event_handling.md index 23ee272de..93fe8e4ba 100644 --- a/website/docs/development/signal_and_event_handling.md +++ b/website/docs/development/signal_and_event_handling.md @@ -91,7 +91,7 @@ Steps: Inherit from both QPushButton and NodeWidget. Make sure you have added NODEWIDGET_IMPLEMENTATIONS macro. This adds a crucial method for events support. It will override `event(QEvent *)` method of QPushbutton so that nodejs can listen to the events of this widget. This makes sure we convert all the QEvent's of this widget to an event for the nodejs event emitter. -Also make sure to connect all the signals of the widgets to the event emitter instance from NodeJS. This way we kindof convert the signal to a simple nodejs event. +Also make sure to connect all the signals of the widgets to the event emitter instance from NodeJS. This way we kind of convert the signal to a simple nodejs event. ```cpp #pragma once From f7f86d5cfacb796b031ff3e8ead1f35d252481a4 Mon Sep 17 00:00:00 2001 From: Simon Edwards Date: Sun, 31 Oct 2021 17:02:43 +0100 Subject: [PATCH 08/10] Pass env to `WrapperCache::get()` --- .../QtGui/QApplication/napplication.hpp | 27 ++++++++++--------- .../nodegui/QtWidgets/QWidget/qwidget_macro.h | 2 +- .../nodegui/core/WrapperCache/wrappercache.h | 5 ++-- .../QtGui/QApplication/qapplication_wrap.cpp | 4 +-- src/cpp/lib/QtGui/QWindow/qwindow_wrap.cpp | 7 +++-- src/lib/QtGui/QWindow.ts | 2 +- 6 files changed, 25 insertions(+), 22 deletions(-) diff --git a/src/cpp/include/nodegui/QtGui/QApplication/napplication.hpp b/src/cpp/include/nodegui/QtGui/QApplication/napplication.hpp index 498b2fa0f..3d051945c 100644 --- a/src/cpp/include/nodegui/QtGui/QApplication/napplication.hpp +++ b/src/cpp/include/nodegui/QtGui/QApplication/napplication.hpp @@ -6,6 +6,7 @@ #include "Extras/Export/export.h" #include "QtCore/QObject/qobject_macro.h" #include "QtGui/QScreen/qscreen_wrap.h" +#include "core/WrapperCache/wrappercache.h" #include "napi.h" class DLL_EXPORT NApplication : public QApplication, public EventWidget { @@ -28,8 +29,8 @@ class DLL_EXPORT NApplication : public QApplication, public EventWidget { this, &QGuiApplication::primaryScreenChanged, [=](QScreen* screen) { Napi::Env env = this->emitOnNode.Env(); Napi::HandleScope scope(env); - auto instance = QScreenWrap::constructor.New( - {Napi::External::New(env, screen)}); + auto instance = + WrapperCache::instance.get(env, screen); this->emitOnNode.Call( {Napi::String::New(env, "primaryScreenChanged"), instance}); }); @@ -37,19 +38,19 @@ class DLL_EXPORT NApplication : public QApplication, public EventWidget { QObject::connect(this, &QGuiApplication::screenAdded, [=](QScreen* screen) { Napi::Env env = this->emitOnNode.Env(); Napi::HandleScope scope(env); - auto instance = QScreenWrap::constructor.New( - {Napi::External::New(env, screen)}); + auto instance = + WrapperCache::instance.get(env, screen); this->emitOnNode.Call({Napi::String::New(env, "screenAdded"), instance}); }); - QObject::connect(this, &QGuiApplication::screenRemoved, - [=](QScreen* screen) { - Napi::Env env = this->emitOnNode.Env(); - Napi::HandleScope scope(env); - auto instance = QScreenWrap::constructor.New( - {Napi::External::New(env, screen)}); - this->emitOnNode.Call( - {Napi::String::New(env, "screenRemoved"), instance}); - }); + QObject::connect( + this, &QGuiApplication::screenRemoved, [=](QScreen* screen) { + Napi::Env env = this->emitOnNode.Env(); + Napi::HandleScope scope(env); + auto instance = + WrapperCache::instance.get(env, screen); + this->emitOnNode.Call( + {Napi::String::New(env, "screenRemoved"), instance}); + }); } }; diff --git a/src/cpp/include/nodegui/QtWidgets/QWidget/qwidget_macro.h b/src/cpp/include/nodegui/QtWidgets/QWidget/qwidget_macro.h index 4d50885e7..e287b6a17 100644 --- a/src/cpp/include/nodegui/QtWidgets/QWidget/qwidget_macro.h +++ b/src/cpp/include/nodegui/QtWidgets/QWidget/qwidget_macro.h @@ -545,7 +545,7 @@ Napi::HandleScope scope(env); \ QWindow* window = this->instance->windowHandle(); \ if (window) { \ - return WrapperCache::instance.get(info, window); \ + return WrapperCache::instance.get(env, window); \ } else { \ return env.Null(); \ } \ diff --git a/src/cpp/include/nodegui/core/WrapperCache/wrappercache.h b/src/cpp/include/nodegui/core/WrapperCache/wrappercache.h index ba4e25906..822200104 100644 --- a/src/cpp/include/nodegui/core/WrapperCache/wrappercache.h +++ b/src/cpp/include/nodegui/core/WrapperCache/wrappercache.h @@ -39,13 +39,12 @@ class DLL_EXPORT WrapperCache : public QObject { * e.g. `QScreen`. * @param W - (template argument) The wrapper type which matches the object * `QScreenWrap`. + * @param env = Napi environment * @param object - Pointer to the QObject for which a wrapper is required. * @return The JS wrapper object. */ template - Napi::Object get(const Napi::CallbackInfo& info, T* object) { - Napi::Env env = info.Env(); - + 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, diff --git a/src/cpp/lib/QtGui/QApplication/qapplication_wrap.cpp b/src/cpp/lib/QtGui/QApplication/qapplication_wrap.cpp index f4f165088..f38df9d96 100644 --- a/src/cpp/lib/QtGui/QApplication/qapplication_wrap.cpp +++ b/src/cpp/lib/QtGui/QApplication/qapplication_wrap.cpp @@ -176,7 +176,7 @@ Napi::Value StaticQApplicationWrapMethods::primaryScreen( Napi::HandleScope scope(env); auto screen = QApplication::primaryScreen(); if (screen) { - return WrapperCache::instance.get(info, screen); + return WrapperCache::instance.get(env, screen); } else { return env.Null(); } @@ -192,7 +192,7 @@ Napi::Value StaticQApplicationWrapMethods::screens( for (int i = 0; i < screens.size(); i++) { QScreen* screen = screens[i]; auto instance = - WrapperCache::instance.get(info, screen); + WrapperCache::instance.get(env, screen); jsArray[i] = instance; } return jsArray; diff --git a/src/cpp/lib/QtGui/QWindow/qwindow_wrap.cpp b/src/cpp/lib/QtGui/QWindow/qwindow_wrap.cpp index 81f92398a..df2c9abc2 100644 --- a/src/cpp/lib/QtGui/QWindow/qwindow_wrap.cpp +++ b/src/cpp/lib/QtGui/QWindow/qwindow_wrap.cpp @@ -40,7 +40,10 @@ void QWindowWrap::connectSignalsToEventEmitter() { this->instance.data(), &QWindow::screenChanged, [=](QScreen* screen) { Napi::Env env = this->emitOnNode.Env(); Napi::HandleScope scope(env); - this->emitOnNode.Call({Napi::String::New(env, "screenChanged")}); + auto instance = + WrapperCache::instance.get(env, screen); + this->emitOnNode.Call( + {Napi::String::New(env, "screenChanged"), instance}); }); } @@ -50,7 +53,7 @@ Napi::Value QWindowWrap::screen(const Napi::CallbackInfo& info) { QScreen* screen = this->instance->screen(); if (screen) { - return WrapperCache::instance.get(info, screen); + return WrapperCache::instance.get(env, screen); } else { return env.Null(); } diff --git a/src/lib/QtGui/QWindow.ts b/src/lib/QtGui/QWindow.ts index 2e13d7220..6795f4ead 100644 --- a/src/lib/QtGui/QWindow.ts +++ b/src/lib/QtGui/QWindow.ts @@ -22,5 +22,5 @@ export class QWindow extends NodeObject { } export interface QWindowSignals extends QObjectSignals { - screenChanged: () => void; + screenChanged: (screen: QScreen) => void; } From 2dc8319b5015d11aaac41edf04b505ce9a5e57b8 Mon Sep 17 00:00:00 2001 From: Simon Edwards Date: Fri, 5 Nov 2021 21:10:01 +0100 Subject: [PATCH 09/10] Add some tests for the wrapper cache --- CMakeLists.txt | 4 ++ .../include/nodegui/test/CacheTestQObject.h | 20 ++++++ .../nodegui/test/cachetestqobject_wrap.h | 34 +++++++++ src/cpp/lib/test/CacheTestQObject.cpp | 28 ++++++++ src/cpp/lib/test/cachetestqobject_wrap.cpp | 71 +++++++++++++++++++ src/cpp/main.cpp | 5 ++ src/index.ts | 3 + src/lib/QtWidgets/QWidget.ts | 5 +- src/lib/core/__test__/CacheTestQObject.ts | 30 ++++++++ src/lib/core/__test__/WrapperCache.test.ts | 47 ++++++++++++ 10 files changed, 245 insertions(+), 2 deletions(-) create mode 100644 src/cpp/include/nodegui/test/CacheTestQObject.h create mode 100644 src/cpp/include/nodegui/test/cachetestqobject_wrap.h create mode 100644 src/cpp/lib/test/CacheTestQObject.cpp create mode 100644 src/cpp/lib/test/cachetestqobject_wrap.cpp create mode 100644 src/lib/core/__test__/CacheTestQObject.ts create mode 100644 src/lib/core/__test__/WrapperCache.test.ts diff --git a/CMakeLists.txt b/CMakeLists.txt index a9092645c..05f815ad6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -220,6 +220,10 @@ add_library(${CORE_WIDGETS_ADDON} SHARED "${PROJECT_SOURCE_DIR}/src/cpp/include/nodegui/QtWidgets/QTextEdit/ntextedit.hpp" "${PROJECT_SOURCE_DIR}/src/cpp/include/nodegui/QtWidgets/QSvgWidget/nsvgwidget.hpp" "${PROJECT_SOURCE_DIR}/src/cpp/include/nodegui/QtWidgets/QHeaderView/nheaderview.hpp" + # Test + "${PROJECT_SOURCE_DIR}/src/cpp/include/nodegui/test/CacheTestQObject.h" + "${PROJECT_SOURCE_DIR}/src/cpp/lib/test/CacheTestQObject.cpp" + "${PROJECT_SOURCE_DIR}/src/cpp/lib/test/cachetestqobject_wrap.cpp" ) AddCommonConfig(${CORE_WIDGETS_ADDON}) diff --git a/src/cpp/include/nodegui/test/CacheTestQObject.h b/src/cpp/include/nodegui/test/CacheTestQObject.h new file mode 100644 index 000000000..6e47348e8 --- /dev/null +++ b/src/cpp/include/nodegui/test/CacheTestQObject.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +#include "Extras/Export/export.h" + +class CacheTestQObject; + +class DLL_EXPORT CacheTestQObject : public QObject { + Q_OBJECT + private: + CacheTestQObject* _foo; + CacheTestQObject* _bar; + + public: + CacheTestQObject(); + CacheTestQObject* foo(); + void clearFoo(); + CacheTestQObject* bar(); +}; diff --git a/src/cpp/include/nodegui/test/cachetestqobject_wrap.h b/src/cpp/include/nodegui/test/cachetestqobject_wrap.h new file mode 100644 index 000000000..f888a7a36 --- /dev/null +++ b/src/cpp/include/nodegui/test/cachetestqobject_wrap.h @@ -0,0 +1,34 @@ +#pragma once + +#include + +#include + +#include "Extras/Export/export.h" +#include "QtCore/QObject/qobject_macro.h" +#include "test/CacheTestQObject.h" + +class DLL_EXPORT CacheTestQObjectWrap + : public Napi::ObjectWrap, + public EventWidget { + QOBJECT_WRAPPED_METHODS_DECLARATION_WITH_EVENT_SOURCE(this) + + private: + QPointer instance; + + public: + // class constructor + static Napi::FunctionReference constructor; + + static Napi::Object init(Napi::Env env, Napi::Object exports); + + CacheTestQObjectWrap(const Napi::CallbackInfo& info); + CacheTestQObject* getInternalInstance(); + + virtual void connectSignalsToEventEmitter(); + + // wrapped methods + Napi::Value foo(const Napi::CallbackInfo& info); + Napi::Value clearFoo(const Napi::CallbackInfo& info); + Napi::Value bar(const Napi::CallbackInfo& info); +}; diff --git a/src/cpp/lib/test/CacheTestQObject.cpp b/src/cpp/lib/test/CacheTestQObject.cpp new file mode 100644 index 000000000..21f735730 --- /dev/null +++ b/src/cpp/lib/test/CacheTestQObject.cpp @@ -0,0 +1,28 @@ +#include "test/CacheTestQObject.h" + +CacheTestQObject::CacheTestQObject() : _foo(0), _bar(0) {} + +CacheTestQObject* CacheTestQObject::foo() { + if (_foo) { + return _foo; + } + _foo = new CacheTestQObject(); + _foo->setParent(this); + return _foo; +} + +void CacheTestQObject::clearFoo() { + if (_foo) { + delete _foo; + _foo = nullptr; + } +} + +CacheTestQObject* CacheTestQObject::bar() { + if (_bar) { + return _bar; + } + _bar = new CacheTestQObject(); + _bar->setParent(this); + return _bar; +} diff --git a/src/cpp/lib/test/cachetestqobject_wrap.cpp b/src/cpp/lib/test/cachetestqobject_wrap.cpp new file mode 100644 index 000000000..f8d301993 --- /dev/null +++ b/src/cpp/lib/test/cachetestqobject_wrap.cpp @@ -0,0 +1,71 @@ +#include "test/cachetestqobject_wrap.h" + +#include "Extras/Utils/nutils.h" +#include "core/WrapperCache/wrappercache.h" + +Napi::FunctionReference CacheTestQObjectWrap::constructor; + +Napi::Object CacheTestQObjectWrap::init(Napi::Env env, Napi::Object exports) { + Napi::HandleScope scope(env); + char CLASSNAME[] = "CacheTestQObject"; + Napi::Function func = DefineClass( + env, CLASSNAME, + {InstanceMethod("foo", &CacheTestQObjectWrap::foo), + InstanceMethod("clearFoo", &CacheTestQObjectWrap::clearFoo), + InstanceMethod("bar", &CacheTestQObjectWrap::bar), + QOBJECT_WRAPPED_METHODS_EXPORT_DEFINE(CacheTestQObjectWrap)}); + constructor = Napi::Persistent(func); + exports.Set(CLASSNAME, func); + return exports; +} + +CacheTestQObject* CacheTestQObjectWrap::getInternalInstance() { + return this->instance; +} + +CacheTestQObjectWrap::CacheTestQObjectWrap(const Napi::CallbackInfo& info) + : Napi::ObjectWrap(info) { + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + if (info.Length() == 1 && info[0].IsExternal()) { + this->instance = info[0].As>().Data(); + } else { + if (info.Length() == 0) { + this->instance = new CacheTestQObject(); + } else { + Napi::TypeError::New(env, + "Wrong number of arguments to CacheTestQObject.") + .ThrowAsJavaScriptException(); + } + } + this->rawData = extrautils::configureQObject(this->getInternalInstance()); +} + +void CacheTestQObjectWrap::connectSignalsToEventEmitter() { + QOBJECT_SIGNALS_ON_TARGET(this->instance.data()); +} + +Napi::Value CacheTestQObjectWrap::foo(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + + CacheTestQObject* foo = this->instance->foo(); + return WrapperCache::instance.get( + env, foo); +} + +Napi::Value CacheTestQObjectWrap::clearFoo(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + this->instance->clearFoo(); + return env.Null(); +} + +Napi::Value CacheTestQObjectWrap::bar(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + + CacheTestQObject* bar = this->instance->bar(); + return WrapperCache::instance.get( + env, bar); +} diff --git a/src/cpp/main.cpp b/src/cpp/main.cpp index 12cb0e0d6..e56134500 100644 --- a/src/cpp/main.cpp +++ b/src/cpp/main.cpp @@ -115,6 +115,7 @@ #include "core/FlexLayout/flexlayout_wrap.h" #include "core/Integration/integration.h" #include "core/WrapperCache/wrappercache.h" +#include "test/cachetestqobject_wrap.h" // These cant be instantiated in JS Side void InitPrivateHelpers(Napi::Env env) { @@ -237,6 +238,10 @@ Napi::Object Main(Napi::Env env, Napi::Object exports) { QStyleFactoryWrap::init(env, exports); QScreenWrap::init(env, exports); QWindowWrap::init(env, exports); + + // Test + CacheTestQObjectWrap::init(env, exports); + return exports; } diff --git a/src/index.ts b/src/index.ts index 6b0d63ac8..07e82f34a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -170,3 +170,6 @@ export { StyleSheet } from './lib/core/Style/StyleSheet'; export { NativeElement, Component } from './lib/core/Component'; export { checkIfNativeElement, checkIfNapiExternal } from './lib/utils/helpers'; export { Margins } from './lib/utils/Margins'; + +// Test: +export { CacheTestQObject } from './lib/core/__test__/CacheTestQObject'; diff --git a/src/lib/QtWidgets/QWidget.ts b/src/lib/QtWidgets/QWidget.ts index 7e160acbe..e07c90208 100644 --- a/src/lib/QtWidgets/QWidget.ts +++ b/src/lib/QtWidgets/QWidget.ts @@ -15,11 +15,12 @@ import { QRect } from '../QtCore/QRect'; import { QObjectSignals } from '../QtCore/QObject'; import { QFont } from '../QtGui/QFont'; import { QAction } from './QAction'; -import { QScreen } from '../QtGui/QScreen'; import memoizeOne from 'memoize-one'; import { QGraphicsEffect } from './QGraphicsEffect'; -import { QSizePolicyPolicy, QStyle, QWindow } from '../..'; import { wrapperCache } from '../core/WrapperCache'; +import { QSizePolicyPolicy } from './QSizePolicy'; +import { QStyle } from '../QtGui/QStyle'; +import { QWindow } from '../QtGui/QWindow'; /** diff --git a/src/lib/core/__test__/CacheTestQObject.ts b/src/lib/core/__test__/CacheTestQObject.ts new file mode 100644 index 000000000..79e42305a --- /dev/null +++ b/src/lib/core/__test__/CacheTestQObject.ts @@ -0,0 +1,30 @@ +import addon from '../../utils/addon'; +import { NativeElement } from '../Component'; +import { NodeObject, QObjectSignals } from '../../QtCore/QObject'; +import { wrapperCache } from '../../core/WrapperCache'; + +export class CacheTestQObject extends NodeObject { + native: NativeElement; + constructor(arg?: NativeElement) { + let native; + if (native == null) { + native = new addon.CacheTestQObject(); + } else { + native = arg; + } + super(native); + this.native = native; + } + + foo(): CacheTestQObject { + return wrapperCache.get(CacheTestQObject, this.native.foo()); + } + + clearFoo(): void { + this.native.clearFoo(); + } + + bar(): CacheTestQObject { + return wrapperCache.get(CacheTestQObject, this.native.bar()); + } +} diff --git a/src/lib/core/__test__/WrapperCache.test.ts b/src/lib/core/__test__/WrapperCache.test.ts new file mode 100644 index 000000000..fe5cde994 --- /dev/null +++ b/src/lib/core/__test__/WrapperCache.test.ts @@ -0,0 +1,47 @@ +import { QApplication } from '../../QtGui/QApplication'; +import { CacheTestQObject } from './CacheTestQObject'; + +describe('WrapperCache using CacheTestQObject', () => { + const qApp = QApplication.instance(); + qApp.setQuitOnLastWindowClosed(true); + + it('Cached foo', () => { + const a = new CacheTestQObject(); + expect(a).not.toBeNull(); + + const foo = a.foo(); + expect(foo).not.toBeNull(); + + const foo2 = a.foo(); + expect(foo).toBe(foo2); + expect(foo.native.__id__()).toBe(foo2.native.__id__()); + }); + + it('clearFoo() and wrapper expiration', () => { + const a = new CacheTestQObject(); + const foo = a.foo(); + a.clearFoo(); + expect(foo.native).toBeNull(); + }); + + it('clearFoo() and new wrapper', () => { + const a = new CacheTestQObject(); + const foo = a.foo(); + const fooId = foo.native.__id__(); + a.clearFoo(); + expect(foo.native).toBeNull(); + + const foo2 = a.foo(); + expect(foo2).not.toBe(foo); + expect(foo2.native.__id__()).not.toBe(fooId); + }); + + it('Cached foo and bar', () => { + const a = new CacheTestQObject(); + const foo = a.foo(); + const bar = a.bar(); + expect(foo).not.toEqual(bar); + expect(foo.native.__id__()).not.toEqual(bar.native.__id__()); + }); + qApp.quit(); +}); From 7ae4a5cb7ef611e474dd912c4993bfef9c6e20f0 Mon Sep 17 00:00:00 2001 From: Simon Edwards Date: Sat, 6 Nov 2021 20:41:04 +0100 Subject: [PATCH 10/10] Wrap some native objects during event dispatch --- .../nodegui/core/Component/component_macro.h | 4 ++- .../nodegui/core/WrapperCache/wrappercache.h | 7 ++-- src/index.ts | 8 ++++- src/lib/QtGui/QScreen.ts | 7 +++- src/lib/QtGui/QWindow.ts | 6 +++- src/lib/core/EventWidget.ts | 5 +-- src/lib/utils/helpers.ts | 34 +++++++++++++++++++ 7 files changed, 60 insertions(+), 11 deletions(-) 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); +}