| .github | ||
| config | ||
| deps/yoga | ||
| src | ||
| .gitignore | ||
| binding.gyp | ||
| demo.ts | ||
| LICENSE | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
| yarn.lock | ||
node-native-ui
A cross platform library to build native desktop apps. Based on Qt5.
Features
[] - Cross platform. Should work on major Linux flavours, Windows and MacOS [] - Low CPU and memory footprint. Current CPU stays at 0% on idle and memory usage is under 20mb for a hello world program. [ ] - (Partial support is present) Easily exstensible for creating custom native widgets (like react native). [] - Support for flex box layouting using Yoga. [] - Supports styling using css (includes actual cascading) or atleast useful subset of css properties. [] - Complete Nodejs api support (Currently runs on Node v12 - and is easily upgradable). [] - Can use all node compatible npm modules. [ ] - Native widget event listener support. [*] - Should be usable for commercial applications aswell. [ ] - (Partial) Should have a decent list of stylable native widgets. [ ] - Easy build and packaging process. [ ] - Good Devtools support (hot reload, live reload, debugging etc). [ ] - Good documentation and website. [ ] - Good documentation for contributors.
Development setup and getting started
This guide is for setting up node-native-ui for contributors of node-native-ui.
The actual getting started guide for users will be added once we reach a bit of stable level.
Make sure you have setup qode and installed it globally.
MacOSX:
Requirements
- Node version: > 9
- Python and gcc
Setting up
- Install latest version of Qt (5.12) via homebrew only.
brew install qt5
Windows:
Requirements
- Node version: > 9
- Python and MSVC++
Setting up -- Instructions will be added soon --
Linux:
Requirements
- Node version: > 9
- Python, Make, GCC, pkg-config and Qt5
On Ubuntu: $ sudo apt-get install pkg-config build-essentials should install everything except Qt5.
Setting up
- Make sure you have downloaded and installed Qt5 sdk.
- Before running
yard build, doexport PKG_CONFIG_PATH="<path to qt installation>/5.11.0/gcc_64/lib/pkgconfig"
Common:
-
Once you have setup the platform specific stuff as mentioned above, follow these:
-
git clonethis repo. -
Keep note of the install directory of qt. You should probably find it at
/usr/local/Cellar/qt/5.12.1. Copy this path and edit the fileconfig/common.gypi.
Change the field'qt_home_dir': '<!(echo $QN_QT_HOME_DIR)',to
'qt_home_dir': '/usr/local/Cellar/qt/5.12.1', -
yarn install -
yarn build:addon -
yarn dev
General Idea for development
- Create wrappers for each and every Qt class that you will use with N-API (using node-addon-api since it is c++) and export it onto JS side.
Learning Materials:
- Beginners guide to NodeJS Addon - https://medium.com/@atulanand94/beginners-guide-to-writing-nodejs-addons-using-c-and-n-api-node-addon-api-9b3b718a9a7f
- First read this: N-API in nodejs docs
- https://www.youtube.com/watch?v=-Oniup60Afs&feature=youtu.be
- See samples at https://github.com/nodejs/abi-stable-node-addon-examples/ 4.1. You can see the readme of https://github.com/nodejs/node-addon-api.git/
- See node-qt implementation. It is implemented in Nan (explained in video).
- Now try to match the implementation in node-qt and convert to N-API using examples from samples.
- Implementations not in node-qt need to be done with effort.
(OLD README but still helpful) To create a new class:
- Use the templates given and copy paste.
- replace the placeholders with the classnames.
- Implement methods.
- Add it to binding.gyp, qt.cpp and make sure you add the methods to the init block of the class.
To make a UI wrapper component that has access to event bridge to sent events to JS.
I am taking example of QPushButton
- create a
cbutton.h
#ifndef NQ_CBUTTON_H
#define NQ_CBUTTON_H
#include <QPushButton>
#include <QObject>
#include <napi.h>
#include "../../Extras/EventBridge/eventwidget.h"
class CButton: public EventWidget, public QPushButton
{
Q_OBJECT;
public:
CButton(QString btnText);
private slots:
void handleButtonClick();
};
#endif
-
Run qt moc on the file (because it has Q_OBJECT) to get cbutton_moc.h (do not edit anything in this file)
-
create a cbutton.cpp
#include "cbutton_moc.h"
CButton::CButton(QString btnText): EventWidget(EVT_WIDGET_CBUTTON_ID) //EVT_WIDGET_CBUTTON_ID is unique WIDGET ID For QPUSHBUTTON
{
this->setText(btnText);
QObject::connect(this, SIGNAL (released()), this, SLOT (handleButtonClick()));
}
void CButton::handleButtonClick()
{
this->addEvent("click","somepayload if needed"); //This addEvent method comes from EventWidget class it takes eventType and payload
}
- Now create a qpushbutton.h C++ wrapper that will be exported to JS. This should implement getHandlerCreationMetadata and also add it to one of the exported methods.
#ifndef NQ_QPUSH_BUTTON_H_
#define NQ_QPUSH_BUTTON_H_
#include <napi.h>
#include "cbutton.h"
class QPushButtonWrap : public Napi::ObjectWrap<QPushButtonWrap> {
public:
static Napi::Object init(Napi::Env env, Napi::Object exports);
QPushButtonWrap(const Napi::CallbackInfo& info);
~QPushButtonWrap();
CButton* getInternalInstance();
Napi::Value setStyleSheet(const Napi::CallbackInfo& info);
Napi::Value getHandlerCreationMetadata(const Napi::CallbackInfo& info);
private:
CButton* q_;
static Napi::FunctionReference constructor;
};
#endif
- In JS Side, after exporting c++ wrapper correctly just wrap QPushButton using connectToEventBridge. connectToEventBridge will add methods like setEventHandler, removeEventHandler, removeAllEventHandler to The QPushButton.
const { QPushButton } = require("../../qt");
const { connectToEventBridge } = require("../../Extras/EventBridge");
module.exports = connectToEventBridge(QPushButton);
- Thats it!
Caveats
-
All files using Q_OBJECT macro needs to be passed through qt moc. Qt moc will generate an extra header file that needs to included (check cbutton under QPushButton). To run the moc /path/to/clang_64/bin/moc cbutton.h -o cbutton_moc.h Now include cbutton_moc.h in the cbutton.cpp file. If you dont do this. Then it will give a symbol not found error.
-
For some cases ui doesnt repaint after changes. So, it better to run this->q_->repaint(); after calling any member function that is supposed to do a UI change.
Known issues:
MacOS - cocoa platform not found ! To fix this make sure you havent moved the qt folder where you installed it using the installer. If so, I recommend doing a clean install of qt and not to move the folder elsewhere. After installing set the variable QN_QT_HOME_DIR as mentioned above in the setup guide.
or you can also do: //TODO
const path = require("path");
const os = require("os");
const qt = require("../build/Release/qtnodeui.node");
if (os.platform() === "darwin") {
// Workaround for macos adding plugin to the path
const pathForPlugins = path.resolve(
__dirname,
"../dep/qt-5.11.0/darwin/x64/"
);
qt.PluginLoader.setPluginPath(pathForPlugins);
}
RUNNING MOC
To run moc:
moc headername.h -o headername_moc.h
#DEBUGGING
https://medium.com/cameron-nokes/how-to-debug-native-node-addons-in-mac-osx-66f69f81afcb
LICENSE
Since we do not in any way modify the code of Qt and only link to it dynamically, I beleive we are in compliance with the LGPL license requirements of QT. And hence this library can be licensed under its own License (for which we have chosen MIT License). The links to QT source code and appropriate license notices are attached. We try our best to abide by the software licenses and any non compliance is not by will. If there is some discrepancy please let us know in the issues and we will try and fix it up. If you follow the recommended build steps and do not statically link QT libraries on your own you are safe to use this library for commerical puropses (provided you abide by MIT License).
MIT


