Adds custom keyevent handler. And remove dependency on napi-thread-safe-callback

This commit is contained in:
Atul R 2019-06-23 17:34:54 +02:00
parent ba409c8c14
commit e15d6b14ac
16 changed files with 124 additions and 48 deletions

View File

@ -29,6 +29,7 @@
"../src/cpp/QtWidgets/QProgressBar/qprogressbar_wrap.cpp",
"../src/cpp/QtWidgets/QRadioButton/qradiobutton_wrap.cpp",
"../src/cpp/QtWidgets/QLineEdit/qlineedit_wrap.cpp",
"../src/cpp/core/Events/types/KeyEvent/keyevent_wrap.cpp",
],
}
]

View File

@ -1,7 +1,7 @@
{
"includes": [],
"target_defaults": {
"include_dirs": ['../deps/', "<!@(node -p \"require('napi-thread-safe-callback').include\")"],
"include_dirs": ['../deps/'],
"cflags": ['-DSPDLOG_COMPILED_LIB'],
"sources": [
"../deps/yoga/Yoga.cpp",

View File

@ -11,6 +11,7 @@ import { QProgressBar } from "./src/lib/QtWidgets/QProgressBar";
import { QRadioButton } from "./src/lib/QtWidgets/QRadioButton";
import { QLineEdit } from "./src/lib/QtWidgets/QLineEdit";
import { FlexLayout } from "./src/lib/core/FlexLayout";
import { KeyEvent } from "./src/lib/QtGui/QKeyEvent";
// Test all widgets in this one. This works as of now!
const testGridLayout = () => {
@ -70,8 +71,10 @@ const testFlexLayout = () => {
// -> view2 -> button
const win = new QMainWindow();
win.addEventListener("MouseMove", (...args) => {
console.log(...args);
win.addEventListener("KeyPress", nativeEvent => {
const evt = new KeyEvent(nativeEvent);
console.log(evt.text());
console.log("KeyPress", evt);
});
win.setObjectName("win");
win.resize(300, 300);
@ -142,5 +145,5 @@ const testFlexLayout = () => {
return win;
};
// (global as any).win1 = testGridLayout(); //to keep gc from collecting
(global as any).win1 = testGridLayout(); //to keep gc from collecting
(global as any).win2 = testFlexLayout(); //to keep gc from collecting

View File

@ -112,24 +112,24 @@ public:
void connectWidgetSignalsToEventEmitter() {
// Qt Connects: Implement all signal connects here
QObject::connect(this, &QPushButton::clicked, [=](bool checked) {
this->emitOnNode->call([=](Napi::Env env, std::vector<napi_value>& args) {
args = { Napi::String::New(env, "clicked"), Napi::Value::From(env, checked) };
});
Napi::Env env = this->emitOnNode.Env();
Napi::HandleScope scope(env);
this->emitOnNode.Call({ Napi::String::New(env, "clicked"), Napi::Value::From(env, checked) });
});
QObject::connect(this, &QPushButton::released, [=]() {
this->emitOnNode->call([=](Napi::Env env, std::vector<napi_value>& args) {
args = { Napi::String::New(env, "released") };
});
Napi::Env env = this->emitOnNode.Env();
Napi::HandleScope scope(env);
this->emitOnNode.Call({ Napi::String::New(env, "released") });
});
QObject::connect(this, &QPushButton::pressed, [=]() {
this->emitOnNode->call([=](Napi::Env env, std::vector<napi_value>& args) {
args = { Napi::String::New(env, "pressed") };
});
Napi::Env env = this->emitOnNode.Env();
Napi::HandleScope scope(env);
this->emitOnNode.Call({ Napi::String::New(env, "pressed") });
});
QObject::connect(this, &QPushButton::toggled, [=](bool checked) {
this->emitOnNode->call([=](Napi::Env env, std::vector<napi_value>& args) {
args = { Napi::String::New(env, "toggled"), Napi::Value::From(env, checked) };
});
Napi::Env env = this->emitOnNode.Env();
Napi::HandleScope scope(env);
this->emitOnNode.Call({ Napi::String::New(env, "toggled"), Napi::Value::From(env, checked) });
});
}
};
@ -148,8 +148,4 @@ We need to run Qt's MOC (Meta Object Compiler) on the file whenever we use Q_OBJ
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.
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 `connectWidgetSignalsToEventEmitter`. 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
> Note that we **can't** just store Napi::Function emit directly and use it. This is because we would need access to `Napi::Env` while making a call and there is no way to do it asynchronously.
> Since NAPI (node-addon-api) doesnt support asynchronous callbacks properly yet. (Although work in underway) we use this third party library (https://github.com/mika-fischer/napi-thread-safe-callback) to do so. This library provides us a way to access the Napi::Env variable whenever we need it.
```

View File

@ -25,7 +25,6 @@
},
"dependencies": {
"bindings": "^1.5.0",
"napi-thread-safe-callback": "^0.0.6",
"node-addon-api": "^1.6.3"
},
"gypfile": true

View File

@ -13,24 +13,24 @@ public:
void connectWidgetSignalsToEventEmitter() {
// Qt Connects: Implement all signal connects here
QObject::connect(this, &QPushButton::clicked, [=](bool checked) {
this->emitOnNode->call([=](Napi::Env env, std::vector<napi_value>& args) {
args = { Napi::String::New(env, "clicked"), Napi::Value::From(env, checked) };
});
Napi::Env env = this->emitOnNode.Env();
Napi::HandleScope scope(env);
this->emitOnNode.Call({ Napi::String::New(env, "clicked"), Napi::Value::From(env, checked) });
});
QObject::connect(this, &QPushButton::released, [=]() {
this->emitOnNode->call([=](Napi::Env env, std::vector<napi_value>& args) {
args = { Napi::String::New(env, "released") };
});
Napi::Env env = this->emitOnNode.Env();
Napi::HandleScope scope(env);
this->emitOnNode.Call({ Napi::String::New(env, "released") });
});
QObject::connect(this, &QPushButton::pressed, [=]() {
this->emitOnNode->call([=](Napi::Env env, std::vector<napi_value>& args) {
args = { Napi::String::New(env, "pressed") };
});
Napi::Env env = this->emitOnNode.Env();
Napi::HandleScope scope(env);
this->emitOnNode.Call({ Napi::String::New(env, "pressed") });
});
QObject::connect(this, &QPushButton::toggled, [=](bool checked) {
this->emitOnNode->call([=](Napi::Env env, std::vector<napi_value>& args) {
args = { Napi::String::New(env, "toggled"), Napi::Value::From(env, checked) };
});
Napi::Env env = this->emitOnNode.Env();
Napi::HandleScope scope(env);
this->emitOnNode.Call({ Napi::String::New(env, "toggled"), Napi::Value::From(env, checked) });
});
}
};

View File

@ -9,7 +9,6 @@
class QPushButtonWrap : public Napi::ObjectWrap<QPushButtonWrap> {
private:
NPushButton* instance;
// std::unique_ptr<ThreadSafeCallback> emitOnNode;
public:
static Napi::Object init(Napi::Env env, Napi::Object exports);
QPushButtonWrap(const Napi::CallbackInfo& info);

View File

@ -17,10 +17,14 @@ void EventWidget::event(QEvent* event){
try {
QEvent::Type evtType = event->type();
std::string eventTypeString = subscribedEvents.at(evtType);
this->emitOnNode->call([=](Napi::Env env, std::vector<napi_value>& args) {
Napi::Value nativeEvent = Napi::External<QEvent>::New(env, event);
args = { Napi::String::New(env, eventTypeString), nativeEvent };
});
Napi::Env env = this->emitOnNode.Env();
Napi::HandleScope scope(env);
Napi::Value nativeEvent = Napi::External<QEvent>::New(env, event);
std::vector<napi_value> args = { Napi::String::New(env, eventTypeString), nativeEvent };
this->emitOnNode.Call(args);
} catch (...) {
// Do nothing
}
@ -34,6 +38,6 @@ void EventWidget::connectWidgetSignalsToEventEmitter(){
EventWidget::~EventWidget(){
if(this->emitOnNode){
this->emitOnNode.release();
this->emitOnNode.Reset();
}
}

View File

@ -1,12 +1,12 @@
#pragma once
#include <QEvent>
#include <napi-thread-safe-callback.hpp>
#include "src/cpp/core/Events/eventsmap.h"
#include <napi.h>
class EventWidget {
public:
std::unique_ptr<ThreadSafeCallback> emitOnNode = nullptr;
Napi::FunctionReference emitOnNode;
std::unordered_map<QEvent::Type, std::string> subscribedEvents;
void subscribeToQtEvent(std::string evtString);

View File

@ -14,7 +14,7 @@
\
Napi::Value initNodeEventEmitter(const Napi::CallbackInfo& info) { \
Napi::Env env = info.Env(); \
this->instance->emitOnNode = std::make_unique<ThreadSafeCallback>(info[0].As<Napi::Function>()); \
this->instance->emitOnNode = Napi::Persistent(info[0].As<Napi::Function>()); \
this->instance->connectWidgetSignalsToEventEmitter(); \
return env.Null(); \
} \

View File

@ -0,0 +1,44 @@
#include "keyevent_wrap.h"
#include "src/cpp/Extras/Utils/nutils.h"
#include <QString>
#include "deps/spdlog/spdlog.h"
Napi::FunctionReference QKeyEventWrap::constructor;
Napi::Object QKeyEventWrap::init(Napi::Env env, Napi::Object exports) {
Napi::HandleScope scope(env);
char CLASSNAME[] = "QKeyEvent";
Napi::Function func = DefineClass(env, CLASSNAME, {
InstanceMethod("text", &QKeyEventWrap::text),
});
constructor = Napi::Persistent(func);
exports.Set(CLASSNAME, func);
return exports;
}
QKeyEvent* QKeyEventWrap::getInternalInstance() {
return this->instance;
}
QKeyEventWrap::QKeyEventWrap(const Napi::CallbackInfo& info): Napi::ObjectWrap<QKeyEventWrap>(info) {
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
if(info.Length() == 1) {
Napi::External<QKeyEvent> eventObject = info[0].As<Napi::External<QKeyEvent>>();
this->instance = eventObject.Data();
} else {
extrautils::throwTypeError(env, "Wrong number of arguments");
}
}
QKeyEventWrap::~QKeyEventWrap() {
// Do not destroy instance here. It will be done by Qt Event loop.
}
Napi::Value QKeyEventWrap::text(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
int keyText = this->instance->key();
Napi::String keyValue = Napi::String::New(env, std::to_string(keyText));
return keyValue;
}

View File

@ -0,0 +1,20 @@
#pragma once
#include <napi.h>
#include <QKeyEvent>
class QKeyEventWrap : public Napi::ObjectWrap<QKeyEventWrap>{
private:
QKeyEvent* instance;
public:
static Napi::Object init(Napi::Env env, Napi::Object exports);
QKeyEventWrap(const Napi::CallbackInfo& info);
~QKeyEventWrap();
QKeyEvent* getInternalInstance();
//class constructor
static Napi::FunctionReference constructor;
//wrapped methods
Napi::Value text(const Napi::CallbackInfo& info);
// Napi::Value setFlexNode(const Napi::CallbackInfo& info);
};

View File

@ -10,6 +10,7 @@
#include "src/cpp/QtWidgets/QRadioButton/qradiobutton_wrap.h"
#include "src/cpp/QtWidgets/QLineEdit/qlineedit_wrap.h"
#include "src/cpp/core/FlexLayout/flexlayout_wrap.h"
#include "src/cpp/core/Events/types/KeyEvent/keyevent_wrap.h"
#include <napi.h>
// These cant be instantiated in JS Side
@ -29,6 +30,7 @@ Napi::Object Main(Napi::Env env, Napi::Object exports) {
QProgressBarWrap::init(env, exports);
QRadioButtonWrap::init(env, exports);
QLineEditWrap::init(env, exports);
QKeyEventWrap::init(env, exports);
return QLabelWrap::init(env, exports);
}

View File

@ -0,0 +1,13 @@
import addon from "../../core/addon";
import { NativeElement } from "../../core/Component";
import { NativeEvent } from "../../core/EventWidget";
export class KeyEvent {
native: NativeElement;
constructor(event: NativeEvent) {
this.native = new addon.QKeyEvent(event);
}
text = (): string => {
return this.native.text();
};
}

View File

@ -2,7 +2,7 @@ import { EventEmitter } from "events";
import { YogaWidget } from "../YogaWidget";
import { NativeElement } from "../Component";
type NativeEvent = {};
export type NativeEvent = {};
export abstract class EventWidget extends YogaWidget {
private emitter: EventEmitter;
constructor(native: NativeElement) {

View File

@ -413,11 +413,6 @@ mkdirp@^0.5.0:
dependencies:
minimist "0.0.8"
napi-thread-safe-callback@^0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/napi-thread-safe-callback/-/napi-thread-safe-callback-0.0.6.tgz#ef86a149b5312e480f74e89a614e6d9e3b17b456"
integrity sha512-X7uHCOCdY4u0yamDxDrv3jF2NtYc8A1nvPzBQgvpoSX+WB3jAe2cVNsY448V1ucq7Whf9Wdy02HEUoLW5rJKWg==
node-addon-api@^1.6.3:
version "1.6.3"
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-1.6.3.tgz#3998d4593e2dca2ea82114670a4eb003386a9fe1"