Experimental menu and tests (#139)

* Adds basic tests for window and label

* Adds few test cases for QWidget

* Adds working tests setup

* Adds inital code for menus
This commit is contained in:
Atul R 2019-10-07 19:24:17 +02:00 committed by GitHub
parent 28483e669b
commit 36329a44b3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 5032 additions and 159 deletions

1
.gitignore vendored
View File

@ -4,4 +4,5 @@ build
dist
.vscode
.cache
coverage
.DS_Store

View File

@ -57,6 +57,8 @@ add_library(${CORE_WIDGETS_ADDON} SHARED
"${PROJECT_SOURCE_DIR}/src/cpp/lib/QtWidgets/QPlainTextEdit/qplaintextedit_wrap.cpp"
"${PROJECT_SOURCE_DIR}/src/cpp/lib/QtWidgets/QScrollArea/qscrollarea_wrap.cpp"
"${PROJECT_SOURCE_DIR}/src/cpp/lib/QtWidgets/QSystemTrayIcon/qsystemtrayicon_wrap.cpp"
"${PROJECT_SOURCE_DIR}/src/cpp/lib/QtWidgets/QMenuBar/qmenubar_wrap.cpp"
"${PROJECT_SOURCE_DIR}/src/cpp/lib/QtWidgets/QMenu/qmenu_wrap.cpp"
"${PROJECT_SOURCE_DIR}/src/cpp/lib/core/FlexLayout/flexlayout_wrap.cpp"
# Custom widgets (include them for automoc since they contain Q_OBJECT)
"${PROJECT_SOURCE_DIR}/src/cpp/include/nodegui/QtWidgets/QWidget/nwidget.hpp"
@ -73,6 +75,8 @@ add_library(${CORE_WIDGETS_ADDON} SHARED
"${PROJECT_SOURCE_DIR}/src/cpp/include/nodegui/QtWidgets/QScrollArea/nscrollarea.hpp"
"${PROJECT_SOURCE_DIR}/src/cpp/include/nodegui/QtWidgets/QTabWidget/ntabwidget.hpp"
"${PROJECT_SOURCE_DIR}/src/cpp/include/nodegui/QtWidgets/QSystemTrayIcon/nsystemtrayicon.hpp"
"${PROJECT_SOURCE_DIR}/src/cpp/include/nodegui/QtWidgets/QMenuBar/nmenubar.hpp"
"${PROJECT_SOURCE_DIR}/src/cpp/include/nodegui/QtWidgets/QMenu/nmenu.hpp"
)
AddCommonConfig(${CORE_WIDGETS_ADDON})

View File

@ -1,3 +1,8 @@
find_program(CCACHE_PROGRAM ccache)
if(CCACHE_PROGRAM)
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_PROGRAM}")
endif()
function(AddCommonConfig addonName)
target_compile_definitions(${addonName} PRIVATE
SPDLOG_COMPILED_LIB

5
config/tests/setup.js Normal file
View File

@ -0,0 +1,5 @@
const { QApplication } = require("../../dist");
module.exports = async () => {
global.qApp = QApplication.instance();
qApp.setQuitOnLastWindowClosed(false);
};

3
config/tests/teardown.js Normal file
View File

@ -0,0 +1,3 @@
module.exports = async () => {
global.qApp.quit();
};

16
jest.config.js Normal file
View File

@ -0,0 +1,16 @@
// For a detailed explanation regarding each configuration property, visit:
// https://jestjs.io/docs/en/configuration.html
module.exports = {
clearMocks: true,
coverageDirectory: "coverage",
collectCoverageFrom: ["**/*.{js,jsx,ts,tsx}", "!**/node_modules/**"],
forceCoverageMatch: ["**/*.{ts,tsx,js,jsx}", "!**/*.test.{ts,tsx,js,jsx}"],
moduleFileExtensions: ["js", "json", "jsx", "ts", "tsx", "node"],
roots: ["<rootDir>/src/lib"],
testEnvironment: "node",
transform: {
"^.+\\.tsx?$": "ts-jest"
},
globalSetup: "./config/tests/setup.js",
globalTeardown: "./config/tests/teardown.js"
};

4333
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -20,7 +20,7 @@
"postinstall": "npm run build:addon",
"build": "tsc && npm run build:addon",
"build:addon": "cross-env CMAKE_BUILD_PARALLEL_LEVEL=8 cmake-js compile",
"docs": "serve docs"
"test": "qode ./node_modules/.bin/jest"
},
"dependencies": {
"@nodegui/qode": "^1.0.5",
@ -32,9 +32,12 @@
},
"devDependencies": {
"@types/bindings": "^1.3.0",
"@types/jest": "^24.0.18",
"@types/node": "^12.0.2",
"jest": "^24.9.0",
"prettier": "^1.17.1",
"serve": "^11.1.0",
"typescript": "^3.4.5"
"ts-jest": "^24.1.0",
"typescript": "^3.6.3"
}
}

View File

@ -0,0 +1,36 @@
#pragma once
#include "core/Events/eventwidget_macro.h"
#include "core/Component/component_macro.h"
/*
This macro adds common QObject exported methods
The exported methods are taken into this macro to avoid writing them in each and every widget we export.
*/
#ifndef QOBJECT_WRAPPED_METHODS_DECLARATION
#define QOBJECT_WRAPPED_METHODS_DECLARATION \
\
EVENTWIDGET_WRAPPED_METHODS_DECLARATION \
\
Napi::Value inherits(const Napi::CallbackInfo& info){ \
Napi::Env env = info.Env(); \
Napi::HandleScope scope(env); \
Napi::String className = info[0].As<Napi::String>(); \
bool doesIt = this->instance->inherits(className.Utf8Value().c_str()); \
return Napi::Value::From(env, doesIt); \
} \
\
#endif //QOBJECT_WRAPPED_METHODS_DECLARATION
#ifndef QOBJECT_WRAPPED_METHODS_EXPORT_DEFINE
#define QOBJECT_WRAPPED_METHODS_EXPORT_DEFINE(ComponentWrapName) \
\
EVENTWIDGET_WRAPPED_METHODS_EXPORT_DEFINE(ComponentWrapName) \
COMPONENT_WRAPPED_METHODS_EXPORT_DEFINE \
\
InstanceMethod("inherits", &ComponentWrapName::inherits),\
\
#endif // QOBJECT_WRAPPED_METHODS_EXPORT_DEFINE

View File

@ -22,6 +22,8 @@ public:
Napi::Value exec(const Napi::CallbackInfo& info);
Napi::Value quit(const Napi::CallbackInfo& info);
Napi::Value exit(const Napi::CallbackInfo& info);
Napi::Value setQuitOnLastWindowClosed(const Napi::CallbackInfo& info);
Napi::Value quitOnLastWindowClosed(const Napi::CallbackInfo& info);
};
namespace StaticQApplicationWrapMethods {

View File

@ -17,6 +17,7 @@ class QLabelWrap : public Napi::ObjectWrap<QLabelWrap>{
static Napi::FunctionReference constructor;
//wrapped methods
Napi::Value setWordWrap(const Napi::CallbackInfo& info);
Napi::Value wordWrap(const Napi::CallbackInfo& info);
Napi::Value setText(const Napi::CallbackInfo& info);
Napi::Value text(const Napi::CallbackInfo &info);
Napi::Value setPixmap(const Napi::CallbackInfo &info);

View File

@ -17,7 +17,10 @@ public:
static Napi::FunctionReference constructor;
//wrapped methods
Napi::Value setCentralWidget(const Napi::CallbackInfo& info);
Napi::Value setMenuBar(const Napi::CallbackInfo& info);
Napi::Value menuBar(const Napi::CallbackInfo& info);
Napi::Value setMenuWidget(const Napi::CallbackInfo& info);
QWIDGET_WRAPPED_METHODS_DECLARATION
};

View File

@ -0,0 +1,14 @@
#pragma once
#include <QMenu>
#include <nodegui/core/NodeWidget/nodewidget.h>
class NMenu: public QMenu, public NodeWidget
{
Q_OBJECT
NODEWIDGET_IMPLEMENTATIONS(QMenu)
public:
using QMenu::QMenu; //inherit all constructors of QMenu
};

View File

@ -0,0 +1,21 @@
#pragma once
#include <napi.h>
#include <stdlib.h>
#include <nodegui/QtWidgets/QWidget/qwidget_macro.h>
#include "nmenu.hpp"
class QMenuWrap : public Napi::ObjectWrap<QMenuWrap>{
private:
std::unique_ptr<NMenu> instance;
public:
static Napi::Object init(Napi::Env env, Napi::Object exports);
QMenuWrap(const Napi::CallbackInfo& info);
~QMenuWrap();
NMenu* getInternalInstance();
static Napi::FunctionReference constructor;
//wrapped methods
Napi::Value setTitle(const Napi::CallbackInfo& info);
QWIDGET_WRAPPED_METHODS_DECLARATION
};

View File

@ -0,0 +1,14 @@
#pragma once
#include <QMenuBar>
#include <nodegui/core/NodeWidget/nodewidget.h>
class NMenuBar: public QMenuBar, public NodeWidget
{
Q_OBJECT
NODEWIDGET_IMPLEMENTATIONS(QMenuBar)
public:
using QMenuBar::QMenuBar; //inherit all constructors of QMenuBar
};

View File

@ -0,0 +1,22 @@
#pragma once
#include <napi.h>
#include <stdlib.h>
#include <nodegui/QtWidgets/QWidget/qwidget_macro.h>
#include "nmenubar.hpp"
class QMenuBarWrap : public Napi::ObjectWrap<QMenuBarWrap>{
private:
std::unique_ptr<NMenuBar> instance;
public:
static Napi::Object init(Napi::Env env, Napi::Object exports);
QMenuBarWrap(const Napi::CallbackInfo& info);
~QMenuBarWrap();
NMenuBar* getInternalInstance();
static Napi::FunctionReference constructor;
//wrapped methods
Napi::Value addMenu(const Napi::CallbackInfo& info);
Napi::Value setNativeMenuBar(const Napi::CallbackInfo& info);
QWIDGET_WRAPPED_METHODS_DECLARATION
};

View File

@ -2,8 +2,7 @@
#include "QtWidgets/QLayout/qlayout_wrap.h"
#include "core/YogaWidget/yogawidget_macro.h"
#include "core/Events/eventwidget_macro.h"
#include "core/Component/component_macro.h"
#include "QtCore/QObject/qobject_macro.h"
#include "QtGui/QIcon/qicon_wrap.h"
#include <QSize>
/*
@ -16,7 +15,7 @@
#define QWIDGET_WRAPPED_METHODS_DECLARATION \
\
YOGAWIDGET_WRAPPED_METHODS_DECLARATION \
EVENTWIDGET_WRAPPED_METHODS_DECLARATION \
QOBJECT_WRAPPED_METHODS_DECLARATION \
\
Napi::Value show(const Napi::CallbackInfo& info) { \
Napi::Env env = info.Env(); \
@ -24,7 +23,6 @@ Napi::Value show(const Napi::CallbackInfo& info) { \
this->instance->show(); \
return env.Null(); \
} \
\
Napi::Value resize(const Napi::CallbackInfo& info) { \
Napi::Env env = info.Env(); \
Napi::HandleScope scope(env); \
@ -33,13 +31,18 @@ Napi::Value resize(const Napi::CallbackInfo& info) { \
this->instance->resize(width.Int32Value(), height.Int32Value()); \
return env.Null(); \
} \
\
Napi::Value close(const Napi::CallbackInfo& info) { \
Napi::Env env = info.Env(); \
Napi::HandleScope scope(env); \
bool hasClosed = this->instance->close(); \
return Napi::Boolean::New(env, hasClosed); \
} \
Napi::Value isVisible(const Napi::CallbackInfo& info) { \
Napi::Env env = info.Env(); \
Napi::HandleScope scope(env); \
bool isVisible = this->instance->isVisible(); \
return Napi::Boolean::New(env, isVisible); \
} \
\
Napi::Value setLayout(const Napi::CallbackInfo& info){ \
Napi::Env env = info.Env(); \
@ -88,13 +91,6 @@ Napi::Value setWindowTitle(const Napi::CallbackInfo& info){ \
this->instance->setWindowTitle(title.c_str()); \
return env.Null(); \
} \
Napi::Value inherits(const Napi::CallbackInfo& info){ \
Napi::Env env = info.Env(); \
Napi::HandleScope scope(env); \
Napi::String className = info[0].As<Napi::String>(); \
std::string s = className.Utf8Value(); \
return Napi::Boolean::New(env, this->instance->inherits(s.c_str())); \
} \
Napi::Value styleSheet(const Napi::CallbackInfo& info){ \
Napi::Env env = info.Env(); \
Napi::HandleScope scope(env); \
@ -136,6 +132,12 @@ Napi::Value setMouseTracking(const Napi::CallbackInfo& info){ \
this->instance->setMouseTracking(isMouseTracked.Value()); \
return env.Null(); \
} \
Napi::Value hasMouseTracking(const Napi::CallbackInfo& info){ \
Napi::Env env = info.Env(); \
Napi::HandleScope scope(env); \
bool isMouseTracked = this->instance->hasMouseTracking(); \
return Napi::Value::From(env, isMouseTracked); \
} \
Napi::Value setEnabled(const Napi::CallbackInfo& info){ \
Napi::Env env = info.Env(); \
Napi::HandleScope scope(env); \
@ -143,6 +145,12 @@ Napi::Value setEnabled(const Napi::CallbackInfo& info){ \
this->instance->setEnabled(enabled.Value()); \
return env.Null(); \
} \
Napi::Value isEnabled(const Napi::CallbackInfo& info){ \
Napi::Env env = info.Env(); \
Napi::HandleScope scope(env); \
bool enabled = this->instance->isEnabled(); \
return Napi::Value::From(env, enabled); \
} \
Napi::Value setFixedSize(const Napi::CallbackInfo& info){ \
Napi::Env env = info.Env(); \
Napi::HandleScope scope(env); \
@ -246,6 +254,12 @@ Napi::Value setWindowOpacity(const Napi::CallbackInfo& info){ \
this->instance->setWindowOpacity(opacity); \
return env.Null(); \
} \
Napi::Value windowOpacity(const Napi::CallbackInfo& info){ \
Napi::Env env = info.Env(); \
Napi::HandleScope scope(env); \
float opacity = this->instance->windowOpacity(); \
return Napi::Value::From(env, opacity); \
} \
Napi::Value setWindowFlag(const Napi::CallbackInfo& info){ \
Napi::Env env = info.Env(); \
Napi::HandleScope scope(env); \
@ -261,14 +275,13 @@ Napi::Value setWindowFlag(const Napi::CallbackInfo& info){ \
#define QWIDGET_WRAPPED_METHODS_EXPORT_DEFINE(WidgetWrapName) \
\
YOGAWIDGET_WRAPPED_METHODS_EXPORT_DEFINE(WidgetWrapName) \
EVENTWIDGET_WRAPPED_METHODS_EXPORT_DEFINE(WidgetWrapName) \
COMPONENT_WRAPPED_METHODS_EXPORT_DEFINE \
QOBJECT_WRAPPED_METHODS_EXPORT_DEFINE(WidgetWrapName) \
InstanceMethod("show", &WidgetWrapName::show), \
InstanceMethod("resize",&WidgetWrapName::resize), \
InstanceMethod("isVisible",&WidgetWrapName::isVisible), \
InstanceMethod("close",&WidgetWrapName::close), \
InstanceMethod("setLayout",&WidgetWrapName::setLayout), \
InstanceMethod("setStyleSheet",&WidgetWrapName::setStyleSheet), \
InstanceMethod("inherits",&WidgetWrapName::inherits), \
InstanceMethod("setCursor",&WidgetWrapName::setCursor), \
InstanceMethod("setWindowIcon",&WidgetWrapName::setWindowIcon), \
InstanceMethod("setWindowState",&WidgetWrapName::setWindowState), \
@ -279,7 +292,9 @@ Napi::Value setWindowFlag(const Napi::CallbackInfo& info){ \
InstanceMethod("setObjectName",&WidgetWrapName::setObjectName), \
InstanceMethod("objectName",&WidgetWrapName::objectName), \
InstanceMethod("setMouseTracking",&WidgetWrapName::setMouseTracking), \
InstanceMethod("hasMouseTracking",&WidgetWrapName::hasMouseTracking), \
InstanceMethod("setEnabled",&WidgetWrapName::setEnabled), \
InstanceMethod("isEnabled",&WidgetWrapName::isEnabled), \
InstanceMethod("setFixedSize",&WidgetWrapName::setFixedSize), \
InstanceMethod("setGeometry",&WidgetWrapName::setGeometry), \
InstanceMethod("geometry",&WidgetWrapName::geometry), \
@ -293,6 +308,7 @@ Napi::Value setWindowFlag(const Napi::CallbackInfo& info){ \
InstanceMethod("setAttribute",&WidgetWrapName::setAttribute), \
InstanceMethod("testAttribute",&WidgetWrapName::testAttribute), \
InstanceMethod("setWindowOpacity",&WidgetWrapName::setWindowOpacity), \
InstanceMethod("windowOpacity",&WidgetWrapName::windowOpacity), \
InstanceMethod("setWindowFlag",&WidgetWrapName::setWindowFlag), \
#endif // QWIDGET_WRAPPED_METHODS_EXPORT_DEFINE

View File

@ -15,6 +15,8 @@ Napi::Object QApplicationWrap::init(Napi::Env env, Napi::Object exports)
InstanceMethod("processEvents", &QApplicationWrap::processEvents),
InstanceMethod("exec", &QApplicationWrap::exec),
InstanceMethod("quit", &QApplicationWrap::quit),
InstanceMethod("setQuitOnLastWindowClosed", &QApplicationWrap::setQuitOnLastWindowClosed),
InstanceMethod("quitOnLastWindowClosed", &QApplicationWrap::quitOnLastWindowClosed),
StaticMethod("instance", &StaticQApplicationWrapMethods::instance),
StaticMethod("clipboard", &StaticQApplicationWrapMethods::clipboard),
COMPONENT_WRAPPED_METHODS_EXPORT_DEFINE
@ -99,3 +101,19 @@ Napi::Value StaticQApplicationWrapMethods::clipboard(const Napi::CallbackInfo& i
QClipboard* clipboard = QApplication::clipboard();
return QClipboardWrap::constructor.New({ Napi::External<QClipboard>::New(env, clipboard) });
}
Napi::Value QApplicationWrap::setQuitOnLastWindowClosed(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
Napi::Boolean quit = info[0].As<Napi::Boolean>();
this->instance->setQuitOnLastWindowClosed(quit.Value());
return env.Null();
}
Napi::Value QApplicationWrap::quitOnLastWindowClosed(const Napi::CallbackInfo& info){
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
bool quit = this->instance->quitOnLastWindowClosed();
return Napi::Value::From(env, quit);
}

View File

@ -11,6 +11,7 @@ Napi::Object QLabelWrap::init(Napi::Env env, Napi::Object exports) {
char CLASSNAME[] = "QLabel";
Napi::Function func = DefineClass(env, CLASSNAME, {
InstanceMethod("setWordWrap", &QLabelWrap::setWordWrap),
InstanceMethod("wordWrap", &QLabelWrap::wordWrap),
InstanceMethod("setText", &QLabelWrap::setText),
InstanceMethod("text", &QLabelWrap::text),
InstanceMethod("setPixmap", &QLabelWrap::setPixmap),
@ -56,6 +57,14 @@ Napi::Value QLabelWrap::setWordWrap(const Napi::CallbackInfo& info) {
return env.Null();
}
Napi::Value QLabelWrap::wordWrap(const Napi::CallbackInfo &info)
{
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
bool isWordWrap = this->instance->wordWrap();
return Napi::Value::From(env, isWordWrap);
}
Napi::Value QLabelWrap::setText(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
Napi::HandleScope scope(env);

View File

@ -1,5 +1,6 @@
#include "QtWidgets/QMainWindow/qmainwindow_wrap.h"
#include "QtWidgets/QWidget/qwidget_wrap.h"
#include "QtWidgets/QMenuBar/qmenubar_wrap.h"
#include "Extras/Utils/nutils.h"
Napi::FunctionReference QMainWindowWrap::constructor;
@ -10,6 +11,8 @@ Napi::Object QMainWindowWrap::init(Napi::Env env, Napi::Object exports) {
Napi::Function func = DefineClass(env, CLASSNAME, {
QWIDGET_WRAPPED_METHODS_EXPORT_DEFINE(QMainWindowWrap)
InstanceMethod("setCentralWidget",&QMainWindowWrap::setCentralWidget),
InstanceMethod("setMenuBar",&QMainWindowWrap::setMenuBar),
InstanceMethod("setMenuWidget",&QMainWindowWrap::setMenuWidget),
});
constructor = Napi::Persistent(func);
exports.Set(CLASSNAME, func);
@ -33,8 +36,6 @@ QMainWindowWrap::QMainWindowWrap(const Napi::CallbackInfo& info): Napi::ObjectWr
}else {
Napi::TypeError::New(env, "Wrong number of arguments").ThrowAsJavaScriptException();
}
this->instance->setAttribute(Qt::WA_DeleteOnClose);
this->instance->installEventFilter(this->getInternalInstance());
}
QMainWindowWrap::~QMainWindowWrap() {
@ -58,3 +59,28 @@ Napi::Value QMainWindowWrap::setCentralWidget(const Napi::CallbackInfo& info){
return env.Null();
}
Napi::Value QMainWindowWrap::setMenuBar(const Napi::CallbackInfo& info){
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
Napi::Object menuObject = info[0].As<Napi::Object>();
QMenuBarWrap* menuBar = Napi::ObjectWrap<QMenuBarWrap>::Unwrap(menuObject);
this->instance->layout()->setMenuBar(menuBar->getInternalInstance());
return env.Null();
}
Napi::Value QMainWindowWrap::setMenuWidget(const Napi::CallbackInfo& info){
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
Napi::Object menuObject = info[0].As<Napi::Object>();
QWidgetWrap* menuWidget = Napi::ObjectWrap<QWidgetWrap>::Unwrap(menuObject);
this->instance->setMenuWidget(menuWidget->getInternalInstance());
return env.Null();
}

View File

@ -0,0 +1,53 @@
#include "QtWidgets/QMenu/qmenu_wrap.h"
#include <nodegui/QtWidgets/QWidget/qwidget_wrap.h>
#include <nodegui/Extras/Utils/nutils.h>
#include <QWidget>
Napi::FunctionReference QMenuWrap::constructor;
Napi::Object QMenuWrap::init(Napi::Env env, Napi::Object exports) {
Napi::HandleScope scope(env);
char CLASSNAME[] = "QMenu";
Napi::Function func = DefineClass(env, CLASSNAME, {
InstanceMethod("setTitle", &QMenuWrap::setTitle),
QWIDGET_WRAPPED_METHODS_EXPORT_DEFINE(QMenuWrap)
});
constructor = Napi::Persistent(func);
exports.Set(CLASSNAME, func);
return exports;
}
NMenu* QMenuWrap::getInternalInstance() {
return this->instance.get();
}
QMenuWrap::QMenuWrap(const Napi::CallbackInfo& info): Napi::ObjectWrap<QMenuWrap>(info) {
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
if(info.Length() == 1) {
Napi::Object parentObject = info[0].As<Napi::Object>();
QWidgetWrap* parentWidgetWrap = Napi::ObjectWrap<QWidgetWrap>::Unwrap(parentObject);
this->instance = std::make_unique<NMenu>(parentWidgetWrap->getInternalInstance()); //this sets the parent to current widget
}else if (info.Length() == 0){
this->instance = std::make_unique<NMenu>();
}else {
Napi::TypeError::New(env, "Wrong number of arguments").ThrowAsJavaScriptException();
}
// Adds measure function on yoga node so that widget size is calculated based on its text also.
YGNodeSetMeasureFunc(this->instance->getFlexNode(), &extrautils::measureQtWidget);
}
QMenuWrap::~QMenuWrap() {
this->instance.reset();
}
Napi::Value QMenuWrap::setTitle(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
Napi::String message = info[0].As<Napi::String>();
this->instance->setTitle(QString::fromStdString(message.Utf8Value()));
return env.Null();
}

View File

@ -0,0 +1,68 @@
#include "QtWidgets/QMenuBar/qmenubar_wrap.h"
#include "QtWidgets/QMenu/qmenu_wrap.h"
#include <nodegui/QtWidgets/QWidget/qwidget_wrap.h>
#include <nodegui/Extras/Utils/nutils.h>
#include <QWidget>
Napi::FunctionReference QMenuBarWrap::constructor;
Napi::Object QMenuBarWrap::init(Napi::Env env, Napi::Object exports) {
Napi::HandleScope scope(env);
char CLASSNAME[] = "QMenuBar";
Napi::Function func = DefineClass(env, CLASSNAME, {
InstanceMethod("addMenu", &QMenuBarWrap::addMenu),
InstanceMethod("setNativeMenuBar", &QMenuBarWrap::setNativeMenuBar),
QWIDGET_WRAPPED_METHODS_EXPORT_DEFINE(QMenuBarWrap)
});
constructor = Napi::Persistent(func);
exports.Set(CLASSNAME, func);
return exports;
}
NMenuBar* QMenuBarWrap::getInternalInstance() {
return this->instance.get();
}
QMenuBarWrap::QMenuBarWrap(const Napi::CallbackInfo& info): Napi::ObjectWrap<QMenuBarWrap>(info) {
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
if(info.Length() == 1) {
if(info[0].IsExternal()){
this->instance = std::unique_ptr<NMenuBar>(info[0].As<Napi::External<NMenuBar>>().Data());
} else {
Napi::Object parentObject = info[0].As<Napi::Object>();
QWidgetWrap* parentWidgetWrap = Napi::ObjectWrap<QWidgetWrap>::Unwrap(parentObject);
this->instance = std::make_unique<NMenuBar>(parentWidgetWrap->getInternalInstance()); //this sets the parent to current widget
}
} else if (info.Length() == 0){
this->instance = std::make_unique<NMenuBar>();
} else {
Napi::TypeError::New(env, "Wrong number of arguments").ThrowAsJavaScriptException();
}
// Adds measure function on yoga node so that widget size is calculated based on its text also.
YGNodeSetMeasureFunc(this->instance->getFlexNode(), &extrautils::measureQtWidget);
}
QMenuBarWrap::~QMenuBarWrap() {
this->instance.reset();
}
Napi::Value QMenuBarWrap::addMenu(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
Napi::Object menuObject = info[0].As<Napi::Object>();
QMenuWrap* menuWrap = Napi::ObjectWrap<QMenuWrap>::Unwrap(menuObject);
this->instance->addMenu(menuWrap->getInternalInstance());
return env.Null();
}
Napi::Value QMenuBarWrap::setNativeMenuBar(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
Napi::Boolean nativeMenuBar = info[0].As<Napi::Boolean>();
this->instance->setNativeMenuBar(nativeMenuBar.Value());
return env.Null();
}

View File

@ -21,6 +21,8 @@
#include "QtGui/QEvent/QKeyEvent/qkeyevent_wrap.h"
#include "QtWidgets/QScrollArea/qscrollarea_wrap.h"
#include "QtWidgets/QSystemTrayIcon/qsystemtrayicon_wrap.h"
#include "QtWidgets/QMenu/qmenu_wrap.h"
#include "QtWidgets/QMenuBar/qmenubar_wrap.h"
#include <napi.h>
// These cant be instantiated in JS Side
void InitPrivateHelpers(Napi::Env env){
@ -51,6 +53,8 @@ Napi::Object Main(Napi::Env env, Napi::Object exports) {
QLabelWrap::init(env, exports);
QScrollAreaWrap::init(env, exports);
QSystemTrayIconWrap::init(env, exports);
QMenuWrap::init(env, exports);
QMenuBarWrap::init(env, exports);
return exports;
}

View File

@ -25,8 +25,9 @@ import {
} from "./index";
import { ok, equal } from "assert";
import { existsSync, unlinkSync, readFileSync } from "fs";
import { resolve } from "path"
import { resolve } from "path";
import { QMenuBar } from "./lib/QtWidgets/QMenuBar";
import { QMenu } from "./lib/QtWidgets/QMenu";
const win = new QMainWindow();
@ -45,8 +46,8 @@ checkbox.setText("Check me out?");
checkbox.setObjectName("check");
checkbox.setChecked(true);
checkbox.addEventListener(QCheckBoxEvents.toggled, checked => {
console.log(`${checked ? 'checked' : 'unchecked'}`);
label1.setInlineStyle(`color: ${checked ? 'green' : 'red'}`);
console.log(`${checked ? "checked" : "unchecked"}`);
label1.setInlineStyle(`color: ${checked ? "green" : "red"}`);
});
const dial = new QDial();
@ -64,9 +65,7 @@ const nodeguiLogo = new QIcon(
resolve(__dirname, "../extras/assets/nodegui.png")
);
const icon = new QIcon(
resolve(__dirname, "../extras/assets/start_icon.png")
);
const icon = new QIcon(resolve(__dirname, "../extras/assets/start_icon.png"));
button.setIcon(icon);
const tabs = new QTabWidget();
@ -86,7 +85,7 @@ tabs.addTab(tab2, icon, "Tab 2");
const progressBar = new QProgressBar();
progressBar.setValue(6);
equal(progressBar.value(), 6)
equal(progressBar.value(), 6);
progressBar.setMinimum(1);
progressBar.setMaximum(15);
@ -104,22 +103,8 @@ textEdit.setWordWrapMode(QTextOptionWrapMode.NoWrap);
const scrollArea = new QScrollArea();
scrollArea.setInlineStyle("flex: 1; width:'100%';");
// NodeWidget.inherits tests
ok(tab1.inherits("QWidget"))
ok(tabs.inherits("QTabWidget"))
ok(tabs.inherits("QWidget"))
ok(tabs.inherits("QObject"))
ok(!tabs.inherits("QProgressBar"))
ok(progressBar.inherits("QProgressBar"))
ok(!progressBar.inherits("QTabWidget"))
ok(tabs.inherits("QWidget"))
ok(tabs.inherits("QObject"))
ok(!tabs.inherits("unknown"))
const imageLabel = new QLabel();
const pixmap = new QPixmap(
resolve(__dirname, "../extras/assets/kitchen.png")
);
const pixmap = new QPixmap(resolve(__dirname, "../extras/assets/kitchen.png"));
imageLabel.setPixmap(pixmap);
scrollArea.setWidget(imageLabel);
@ -147,7 +132,7 @@ const trayIcon = new QIcon(
);
const tray = new QSystemTrayIcon();
tray.setIcon(trayIcon);
tray.show()
tray.show();
if (rootView.layout) {
rootView.layout.addWidget(tabs);
@ -161,7 +146,13 @@ if (rootView.layout) {
rootView.layout.addWidget(dial);
}
(async ()=>{
const menuBar = new QMenuBar();
const menu = new QMenu();
win.setMenuBar(menuBar);
menu.setTitle("hello");
menuBar.addMenu(menu);
menuBar.setNativeMenuBar(false);
win.setCentralWidget(rootView);
win.setStyleSheet(`
#root {
@ -171,12 +162,11 @@ win.setStyleSheet(`
justify-content: 'space-around';
}
`);
win.setWindowIcon(nodeguiLogo);
await win.setWindowTitle("NodeGUI Demo");
win.resize(400, 700);
win.show();
await win.setWindowState(WindowState.WindowActive);
})();
win.setWindowIcon(nodeguiLogo);
win.setWindowTitle("NodeGUI Demo");
win.resize(400, 700);
win.show();
win.setWindowState(WindowState.WindowActive);
(global as any).win = win; // To prevent win from being garbage collected.
(global as any).systemTray = tray; // To prevent system tray from being garbage collected.

View File

@ -4,7 +4,11 @@ import "./lib/core/bootstrap";
export * from "./lib/QtEnums";
// Gui:
export { QApplication } from "./lib/QtGui/QApplication";
export { QPixmap, ReadWriteImageFormats, ImageFormats } from "./lib/QtGui/QPixmap";
export {
QPixmap,
ReadWriteImageFormats,
ImageFormats
} from "./lib/QtGui/QPixmap";
export { QIcon, QIconMode, QIconState } from "./lib/QtGui/QIcon";
export { QCursor } from "./lib/QtGui/QCursor";
export { QTextOptionWrapMode } from "./lib/QtGui/QTextOption";
@ -29,13 +33,18 @@ export { QPushButton, QPushButtonEvents } from "./lib/QtWidgets/QPushButton";
export { QSpinBox, QSpinBoxEvents } from "./lib/QtWidgets/QSpinBox";
export { QRadioButton, QRadioButtonEvents } from "./lib/QtWidgets/QRadioButton";
export { QTabWidget, QTabWidgetEvents } from "./lib/QtWidgets/QTabWidget";
export { QMenu, QMenuEvents } from "./lib/QtWidgets/QMenu";
export { QMenuBar, QMenuBarEvents } from "./lib/QtWidgets/QMenuBar";
export {
QPlainTextEdit,
QPlainTextEditEvents,
LineWrapMode
} from "./lib/QtWidgets/QPlainTextEdit";
export { QScrollArea, QScrollAreaEvents } from "./lib/QtWidgets/QScrollArea";
export { QSystemTrayIcon, QSystemTrayIconEvents } from './lib/QtWidgets/QSystemTrayIcon';
export {
QSystemTrayIcon,
QSystemTrayIconEvents
} from "./lib/QtWidgets/QSystemTrayIcon";
// Layouts:
export { QGridLayout } from "./lib/QtWidgets/QGridLayout";
export { FlexLayout } from "./lib/core/FlexLayout";

View File

@ -0,0 +1,7 @@
import { EventWidget } from "../../core/EventWidget";
export abstract class QObject extends EventWidget {
inherits(className: string) {
return this.native.inherits(className);
}
}

View File

@ -33,4 +33,10 @@ export class QApplication extends Component {
exit = (exitCode: number) => {
return this.native.exit(exitCode);
};
setQuitOnLastWindowClosed = (quit: Boolean) => {
this.native.setQuitOnLastWindowClosed(quit);
};
quitOnLastWindowClosed = () => {
return this.native.quitOnLastWindowClosed();
};
}

View File

@ -0,0 +1,31 @@
const { QLabel } = require("./index");
const { QPixmap } = require("../../QtGui/QPixmap");
describe("QLabel", () => {
let label = new QLabel();
afterAll(() => {
label.close();
label = null;
});
it("instantiate a label instance", () => {
const label = new QLabel();
expect(label.inherits("QLabel")).toBe(true);
});
it("setText", () => {
const label = new QLabel();
label.setText("hello");
expect(label.text()).toEqual("hello");
});
it("setWordWrap", () => {
const label = new QLabel();
label.setWordWrap(true);
expect(label.wordWrap()).toEqual(true);
label.setWordWrap(false);
expect(label.wordWrap()).toEqual(false);
});
it("setPixmap", () => {
const label = new QLabel();
const pixMap = new QPixmap();
label.setPixmap(pixMap);
expect(label.pixmap()).toEqual(pixMap);
});
});

View File

@ -9,8 +9,7 @@ export const QLabelEvents = Object.freeze({
});
export class QLabel extends NodeWidget {
native: NativeElement;
text?: string | number;
pixmap?: QPixmap;
private _pixmap?: QPixmap;
constructor(parent?: NodeWidget) {
let native;
if (parent) {
@ -21,22 +20,24 @@ export class QLabel extends NodeWidget {
super(native);
this.native = native;
this.parent = parent;
// bind member functions
this.setWordWrap.bind(this);
this.setText.bind(this);
}
setWordWrap(on: boolean) {
// react:✓ TODO://getter
this.native.setWordWrap(on);
}
wordWrap(): boolean {
return this.native.wordWrap();
}
setText(text: string | number) {
// react:✓ TODO://getter
this.text = text;
this.native.setText(`${text}`);
}
text() {
return this.native.text();
}
setPixmap(pixMap: QPixmap) {
// react:✓ TODO://getter
this.native.setPixmap(pixMap.native);
this.pixmap = pixMap;
this._pixmap = pixMap;
}
pixmap() {
return this._pixmap;
}
}

View File

@ -0,0 +1,20 @@
const { QMainWindow } = require("./index");
const { QWidget } = require("../QWidget");
describe("QMainWindow", () => {
let win = new QMainWindow();
afterAll(() => {
win.close();
win = null;
});
it("should be able to instantiate a window instance", () => {
const win = new QMainWindow();
expect(win.inherits("QMainWindow")).toBe(true);
});
it("setCentalWidget", () => {
const win = new QMainWindow();
const widget = new QWidget();
win.setCentralWidget(widget);
expect(win.centralWidget).toEqual(widget);
});
});

View File

@ -3,6 +3,7 @@ import { NodeWidget } from "../QWidget";
import { BaseWidgetEvents } from "../../core/EventWidget";
import { NativeElement } from "../../core/Component";
import { NodeLayout } from "../QLayout";
import { QMenuBar } from "../QMenuBar";
export const QMainWindowEvents = Object.freeze({
...BaseWidgetEvents
@ -10,6 +11,8 @@ export const QMainWindowEvents = Object.freeze({
export class QMainWindow extends NodeWidget {
native: NativeElement;
public centralWidget?: NodeWidget;
private _menuBar?: QMenuBar;
private _menuBarWidget?: NodeWidget;
constructor(parent?: NodeWidget) {
let native;
if (parent) {
@ -36,6 +39,16 @@ export class QMainWindow extends NodeWidget {
this.native.setCentralWidget(widget.native, widget.getFlexNode());
this.centralWidget = widget;
}
setMenuBar(menuBar: QMenuBar) {
this.native.setMenuBar(menuBar.native);
this._menuBar = menuBar;
}
menuBar(): QMenuBar | undefined {
return this._menuBar;
}
setMenuWidget(menuWidget: NodeWidget) {
this.native.setMenuWidget(menuWidget.native);
}
get layout() {
if (this.centralWidget) {
return this.centralWidget.layout;

View File

@ -0,0 +1,25 @@
import { BaseWidgetEvents } from "../core/EventWidget";
import { NativeElement } from "../core/Component";
import { NodeWidget } from "./QWidget";
import addon from "../utils/addon";
export const QMenuEvents = Object.freeze({
...BaseWidgetEvents
});
export class QMenu extends NodeWidget {
native: NativeElement;
constructor(parent?: NodeWidget) {
let native;
if (parent) {
native = new addon.QMenu(parent.native);
} else {
native = new addon.QMenu();
}
super(native);
this.native = native;
this.parent = parent;
}
setTitle(title: string) {
this.native.setTitle(title);
}
}

View File

@ -0,0 +1,38 @@
import { QMenu } from "./QMenu";
import { BaseWidgetEvents } from "../core/EventWidget";
import { NativeElement } from "../core/Component";
import { NodeWidget } from "./QWidget";
import addon from "../utils/addon";
import { checkIfNativeElement } from "../utils/helpers";
export const QMenuBarEvents = Object.freeze({
...BaseWidgetEvents
});
type arg = NodeWidget | NativeElement;
export class QMenuBar extends NodeWidget {
native: NativeElement;
constructor(arg?: arg) {
let native;
let parent;
if (checkIfNativeElement(arg)) {
native = arg as NativeElement;
} else if (typeof arg === "object") {
native = new addon.QMenuBar(arg.native);
parent = arg as NodeWidget;
} else {
native = new addon.QMenuBar();
}
super(native);
this.native = native;
this.parent = parent;
}
addMenu(menu: QMenu) {
this.native.addMenu(menu.native);
}
setNativeMenuBar(nativeMenuBar: boolean) {
this.native.setNativeMenuBar(nativeMenuBar);
}
}

View File

@ -0,0 +1,74 @@
import { QWidget } from ".";
describe("QWidget", () => {
let view: QWidget;
view = new QWidget();
afterAll(() => {
if (view) {
view.close();
(view as any) = null;
}
});
it("instantiate a view instance", () => {
expect(view.inherits("QWidget")).toBe(true);
});
it("show", () => {
view.show();
expect(view.isVisible()).toEqual(true);
});
it("hide", () => {
view.hide();
expect(view.isVisible()).toEqual(false);
});
it("setStyleSheet", async () => {
await view.setStyleSheet(`
#test {
color: blue;
}`);
expect(view.styleSheet()).toEqual(`
#test {
color: blue;
}`);
});
it("setInlineStyle", async () => {
view.setObjectName("test");
await view.setInlineStyle("color: black;");
expect(view.styleSheet()).toEqual(`
#test {
color: black;
}
`);
});
it("setGeometry", () => {
view.setGeometry(10, 11, 12, 13);
const rect = view.geometry();
expect(rect.x).toEqual(10);
expect(rect.y).toEqual(11);
expect(rect.width).toEqual(12);
expect(rect.height).toEqual(13);
});
it("setObjectName", () => {
view.setObjectName("abcd");
expect(view.objectName()).toBe("abcd");
});
it("setMouseTracking", () => {
view.setMouseTracking(true);
expect(view.hasMouseTracking()).toBe(true);
view.setMouseTracking(false);
expect(view.hasMouseTracking()).toBe(false);
});
it("setEnabled", () => {
view.setEnabled(true);
expect(view.isEnabled()).toBe(true);
view.setEnabled(false);
expect(view.isEnabled()).toBe(false);
});
it("setFixedSize", () => {
view.setFixedSize(10, 12);
expect(view.size()).toEqual({ width: 10, height: 12 });
});
it("setWindowOpacity", () => {
view.setWindowOpacity(0.6);
expect(view.windowOpacity().toFixed(1)).toBe(`0.6`);
});
});

View File

@ -1,6 +1,6 @@
import addon from "../../utils/addon";
import { NodeLayout } from "../QLayout";
import { EventWidget, BaseWidgetEvents } from "../../core/EventWidget";
import { BaseWidgetEvents } from "../../core/EventWidget";
import { NativeElement } from "../../core/Component";
import { FlexLayout } from "../../core/FlexLayout";
import { WidgetAttribute, WindowType } from "../../QtEnums";
@ -13,138 +13,127 @@ import {
prepareInlineStyleSheet
} from "../../core/Style/StyleSheet";
import { checkIfNativeElement } from "../../utils/helpers";
import { YogaWidget } from "../../core/YogaWidget";
// All Widgets should extend from NodeWidget
// Implement all native QWidget methods here so that all widgets get access to those aswell
export abstract class NodeWidget extends EventWidget {
export abstract class NodeWidget extends YogaWidget {
layout?: NodeLayout;
type: string = "widget";
show = () => {
// react:✓
show() {
this.native.show();
};
hide = () => {
// react:✓
}
hide() {
this.native.hide();
};
close = () => {
// react:⛔️
}
isVisible(): boolean {
return this.native.isVisible();
}
close() {
return this.native.close();
};
setStyleSheet = async (styleSheet: string) => {
// react:✓
}
async setStyleSheet(styleSheet: string) {
const preparedSheet = await StyleSheet.create(styleSheet);
await applyStyleSheet(this, preparedSheet);
};
styleSheet = (): string => {
// react:✓
}
styleSheet(): string {
return this.native.styleSheet();
};
setInlineStyle = async (style: string) => {
// react:✓
}
async setInlineStyle(style: string) {
const preparedSheet = await prepareInlineStyleSheet(this, style);
await applyStyleSheet(this, preparedSheet);
};
setGeometry = (x: number, y: number, w: number, h: number) => {
// react:✓
}
setGeometry(x: number, y: number, w: number, h: number) {
this.native.setGeometry(x, y, w, h);
};
geometry = (): Rect => {
// react:✓
}
geometry(): Rect {
return this.native.geometry();
};
setObjectName = (objectName: string) => {
// react:✓
}
setObjectName(objectName: string) {
this.native.setObjectName(objectName);
};
objectName = (): string => {
// react:✓
}
objectName(): string {
return this.native.objectName();
};
setMouseTracking = (isMouseTracked: boolean) => {
// react:✓, //TODO:getter
}
setMouseTracking(isMouseTracked: boolean) {
this.native.setMouseTracking(isMouseTracked);
};
setEnabled = (enabled: boolean) => {
// react:✓, //TODO:getter
}
hasMouseTracking(): boolean {
return this.native.hasMouseTracking();
}
setEnabled(enabled: boolean) {
this.native.setEnabled(enabled);
};
setWindowOpacity = (opacity: Number) => {
// react:✓, //TODO:getter
}
isEnabled(): boolean {
return this.native.isEnabled();
}
setWindowOpacity(opacity: Number) {
this.native.setWindowOpacity(opacity);
};
setWindowTitle = async (title: string) => {
// react:✓, //TODO:getter
}
windowOpacity() {
return this.native.windowOpacity();
}
setWindowTitle(title: string) {
//TODO:getter
return this.native.setWindowTitle(title);
};
inherits(className: string) {
return this.native.inherits(className);
};
setWindowState = async (state: WindowState) => {
// react:✓, //TODO:getter
}
setWindowState(state: WindowState) {
//TODO:getter
return this.native.setWindowState(state);
};
}
setCursor(cursor: CursorShape | QCursor) {
// react:✓, //TODO:getter
//TODO:getter
this.native.setCursor(cursor);
}
setWindowIcon(icon: QIcon) {
// react:✓, //TODO:getter
//TODO:getter
this.native.setWindowIcon(icon.native);
}
setMinimumSize = (minw: number, minh: number) => {
// react:✓
setMinimumSize(minw: number, minh: number) {
this.native.setMinimumSize(minw, minh);
};
setMaximumSize = (maxw: number, maxh: number) => {
// react:✓
}
setMaximumSize(maxw: number, maxh: number) {
this.native.setMaximumSize(maxw, maxh);
};
setFixedSize = (width: number, height: number) => {
// react:✓
}
setFixedSize(width: number, height: number) {
this.native.setFixedSize(width, height);
};
resize = (width: number, height: number) => {
// react:✓
}
resize(width: number, height: number) {
this.native.resize(width, height);
};
size = (): { width: number; height: number } => {
// react:✓
}
size(): { width: number; height: number } {
return this.native.size();
};
move = (x: number, y: number) => {
// react:✓
}
move(x: number, y: number) {
this.native.move(x, y);
};
pos = (): { x: number; y: number } => {
// react:✓
}
pos(): { x: number; y: number } {
return this.native.pos();
};
repaint = () => {
}
repaint() {
// react:⛔️
this.native.repaint();
};
update = () => {
}
update() {
// react:⛔️
this.native.update();
};
updateGeometry = () => {
}
updateGeometry() {
// react:⛔️
this.native.updateGeometry();
};
setAttribute = (attribute: WidgetAttribute, switchOn: boolean) => {
}
setAttribute(attribute: WidgetAttribute, switchOn: boolean) {
// react:⛔️
return this.native.setAttribute(attribute, switchOn);
};
testAttribute = (attribute: WidgetAttribute): boolean => {
}
testAttribute(attribute: WidgetAttribute): boolean {
// react:⛔️
return this.native.testAttribute(attribute);
};
setWindowFlag = (windowType: WindowType, switchOn: boolean) => {
}
setWindowFlag(windowType: WindowType, switchOn: boolean) {
// react:⛔️
return this.native.setWindowFlag(windowType, switchOn);
};
setLayout = (parentLayout: NodeLayout) => {
// react:✓
}
setLayout(parentLayout: NodeLayout) {
const flexLayout = parentLayout as FlexLayout;
if (flexLayout.setFlexNode) {
//if flex layout set the flexnode
@ -152,7 +141,7 @@ export abstract class NodeWidget extends EventWidget {
}
this.native.setLayout(parentLayout.native);
this.layout = parentLayout;
};
}
}
type Rect = {

View File

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

View File

@ -1,7 +1,7 @@
import { Component } from "../Component";
import { QObject } from "../../QtCore/QObject";
export type FlexNode = {};
export abstract class YogaWidget extends Component {
export abstract class YogaWidget extends QObject {
getFlexNode = (): FlexNode => {
return this.native.getFlexNode();
};