Adds support for keyboard shortcuts, systray menu and systray actions (now run nodegui apps as a service). (#156)

* Added actions and tooltips to qmenu

* Adds support for nested submenu creation

* Added supported for onclick handlers in actions

* Adds support for keyboard shortcuts

* adds enabled and shortcut context

* Adds support for shortcuts and menus

* Added support for keyboard shortcuts
This commit is contained in:
Atul R 2019-10-26 15:31:02 +02:00 committed by GitHub
parent 2541feebe7
commit cb535e33e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 621 additions and 160 deletions

View File

@ -41,6 +41,7 @@ add_library(${CORE_WIDGETS_ADDON} SHARED
"${PROJECT_SOURCE_DIR}/src/cpp/lib/QtGui/QPixmap/qpixmap_wrap.cpp"
"${PROJECT_SOURCE_DIR}/src/cpp/lib/QtGui/QIcon/qicon_wrap.cpp"
"${PROJECT_SOURCE_DIR}/src/cpp/lib/QtGui/QCursor/qcursor_wrap.cpp"
"${PROJECT_SOURCE_DIR}/src/cpp/lib/QtGui/QKeySequence/qkeysequence_wrap.cpp"
"${PROJECT_SOURCE_DIR}/src/cpp/lib/QtWidgets/QWidget/qwidget_wrap.cpp"
"${PROJECT_SOURCE_DIR}/src/cpp/lib/QtWidgets/QGridLayout/qgridlayout_wrap.cpp"
"${PROJECT_SOURCE_DIR}/src/cpp/lib/QtWidgets/QDial/qdial_wrap.cpp"
@ -57,6 +58,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/QAction/qaction_wrap.cpp"
"${PROJECT_SOURCE_DIR}/src/cpp/lib/QtWidgets/QShortcut/qshortcut_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"
@ -75,6 +78,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/QAction/naction.hpp"
"${PROJECT_SOURCE_DIR}/src/cpp/include/nodegui/QtWidgets/QShortcut/nshortcut.hpp"
"${PROJECT_SOURCE_DIR}/src/cpp/include/nodegui/QtWidgets/QMenuBar/nmenubar.hpp"
"${PROJECT_SOURCE_DIR}/src/cpp/include/nodegui/QtWidgets/QMenu/nmenu.hpp"
)

View File

@ -19,7 +19,7 @@
"dev": "npm run build && qode dist/demo.js",
"postinstall": "npm run build:addon",
"build": "tsc && npm run build:addon",
"build:addon": "cross-env CMAKE_BUILD_PARALLEL_LEVEL=8 cmake-js compile",
"build:addon": "cross-env CMAKE_BUILD_PARALLEL_LEVEL=8 cmake-js build",
"test": "qode ./node_modules/.bin/jest"
},
"dependencies": {

View File

@ -0,0 +1,21 @@
#pragma once
#include <napi.h>
#include <stdlib.h>
#include <QKeySequence>
#include "core/Component/component_macro.h"
class QKeySequenceWrap : public Napi::ObjectWrap<QKeySequenceWrap>
{
private:
std::unique_ptr<QKeySequence> instance;
public:
static Napi::FunctionReference constructor;
static Napi::Object init(Napi::Env env, Napi::Object exports);
QKeySequenceWrap(const Napi::CallbackInfo &info);
~QKeySequenceWrap();
QKeySequence *getInternalInstance();
// Wrapped methods
Napi::Value count(const Napi::CallbackInfo& info);
};

View File

@ -0,0 +1,38 @@
#pragma once
#include <QAction>
#include "core/NodeWidget/nodewidget.h"
#include "napi.h"
class NAction: public QAction, public EventWidget
{
Q_OBJECT
EVENTWIDGET_IMPLEMENTATIONS(QAction)
public:
using QAction::QAction; //inherit all constructors of QAction
void connectWidgetSignalsToEventEmitter() {
// Qt Connects: Implement all signal connects here
QObject::connect(this, &QAction::triggered, [=](bool checked) {
Napi::Env env = this->emitOnNode.Env();
Napi::HandleScope scope(env);
this->emitOnNode.Call({ Napi::String::New(env, "triggered"), Napi::Value::From(env, checked) });
});
QObject::connect(this, &QAction::changed, [=]() {
Napi::Env env = this->emitOnNode.Env();
Napi::HandleScope scope(env);
this->emitOnNode.Call({ Napi::String::New(env, "changed") });
});
QObject::connect(this, &QAction::hovered, [=]() {
Napi::Env env = this->emitOnNode.Env();
Napi::HandleScope scope(env);
this->emitOnNode.Call({ Napi::String::New(env, "hovered") });
});
QObject::connect(this, &QAction::toggled, [=](bool 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

@ -0,0 +1,27 @@
#pragma once
#include <napi.h>
#include "naction.hpp"
#include "QtWidgets/QWidget/qwidget_macro.h"
class QActionWrap : public Napi::ObjectWrap<QActionWrap>{
private:
NAction* instance;
public:
static Napi::Object init(Napi::Env env, Napi::Object exports);
QActionWrap(const Napi::CallbackInfo& info);
~QActionWrap();
NAction* getInternalInstance();
//class constructor
static Napi::FunctionReference constructor;
//wrapped methods
Napi::Value setText(const Napi::CallbackInfo& info);
Napi::Value setEnabled(const Napi::CallbackInfo& info);
Napi::Value setIcon(const Napi::CallbackInfo& info);
Napi::Value setMenu(const Napi::CallbackInfo& info);
Napi::Value setShortcut(const Napi::CallbackInfo& info);
Napi::Value setShortcutContext(const Napi::CallbackInfo& info);
EVENTWIDGET_WRAPPED_METHODS_DECLARATION
};

View File

@ -15,6 +15,7 @@ class QMenuWrap : public Napi::ObjectWrap<QMenuWrap>{
static Napi::FunctionReference constructor;
//wrapped methods
Napi::Value setTitle(const Napi::CallbackInfo& info);
Napi::Value addAction(const Napi::CallbackInfo& info);
QWIDGET_WRAPPED_METHODS_DECLARATION
};

View File

@ -0,0 +1,28 @@
#pragma once
#include <QShortcut>
#include "core/NodeWidget/nodewidget.h"
#include "napi.h"
class NShortcut: public QShortcut, public EventWidget
{
Q_OBJECT
EVENTWIDGET_IMPLEMENTATIONS(QShortcut)
public:
using QShortcut::QShortcut; //inherit all constructors of QShortcut
void connectWidgetSignalsToEventEmitter() {
// Qt Connects: Implement all signal connects here
QObject::connect(this, &QShortcut::activated, [=]() {
Napi::Env env = this->emitOnNode.Env();
Napi::HandleScope scope(env);
this->emitOnNode.Call({ Napi::String::New(env, "activated") });
});
QObject::connect(this, &QShortcut::activatedAmbiguously, [=]() {
Napi::Env env = this->emitOnNode.Env();
Napi::HandleScope scope(env);
this->emitOnNode.Call({ Napi::String::New(env, "activatedAmbiguously") });
});
}
};

View File

@ -0,0 +1,25 @@
#pragma once
#include <napi.h>
#include "nshortcut.hpp"
#include "QtWidgets/QWidget/qwidget_macro.h"
class QShortcutWrap : public Napi::ObjectWrap<QShortcutWrap>{
private:
NShortcut* instance;
public:
static Napi::Object init(Napi::Env env, Napi::Object exports);
QShortcutWrap(const Napi::CallbackInfo& info);
~QShortcutWrap();
NShortcut* getInternalInstance();
//class constructor
static Napi::FunctionReference constructor;
//wrapped methods
Napi::Value setEnabled(const Napi::CallbackInfo& info);
Napi::Value setAutoRepeat(const Napi::CallbackInfo& info);
Napi::Value setKey(const Napi::CallbackInfo& info);
Napi::Value setContext(const Napi::CallbackInfo& info);
EVENTWIDGET_WRAPPED_METHODS_DECLARATION
};

View File

@ -19,6 +19,8 @@ class QSystemTrayIconWrap : public Napi::ObjectWrap<QSystemTrayIconWrap>{
Napi::Value hide(const Napi::CallbackInfo& info);
Napi::Value setIcon(const Napi::CallbackInfo& info);
Napi::Value isVisible(const Napi::CallbackInfo& info);
Napi::Value setToolTip(const Napi::CallbackInfo& info);
Napi::Value setContextMenu(const Napi::CallbackInfo& info);
EVENTWIDGET_WRAPPED_METHODS_DECLARATION
};

View File

@ -15,6 +15,7 @@ Napi::Object QApplicationWrap::init(Napi::Env env, Napi::Object exports)
InstanceMethod("processEvents", &QApplicationWrap::processEvents),
InstanceMethod("exec", &QApplicationWrap::exec),
InstanceMethod("quit", &QApplicationWrap::quit),
InstanceMethod("exit", &QApplicationWrap::exit),
InstanceMethod("setQuitOnLastWindowClosed", &QApplicationWrap::setQuitOnLastWindowClosed),
InstanceMethod("quitOnLastWindowClosed", &QApplicationWrap::quitOnLastWindowClosed),
StaticMethod("instance", &StaticQApplicationWrapMethods::instance),

View File

@ -0,0 +1,58 @@
#include "QtGui/QKeySequence/qkeysequence_wrap.h"
#include "QtGui/QPixmap/qpixmap_wrap.h"
#include "Extras/Utils/nutils.h"
#include "deps/spdlog/spdlog.h"
Napi::FunctionReference QKeySequenceWrap::constructor;
Napi::Object QKeySequenceWrap::init(Napi::Env env, Napi::Object exports)
{
Napi::HandleScope scope(env);
char CLASSNAME[] = "QKeySequence";
Napi::Function func = DefineClass(env, CLASSNAME, {
InstanceMethod("count", &QKeySequenceWrap::count),
COMPONENT_WRAPPED_METHODS_EXPORT_DEFINE
});
constructor = Napi::Persistent(func);
exports.Set(CLASSNAME, func);
return exports;
}
QKeySequenceWrap::QKeySequenceWrap(const Napi::CallbackInfo &info) : Napi::ObjectWrap<QKeySequenceWrap>(info)
{
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
if (info.Length() == 1)
{
Napi::String sequenceString = info[0].As<Napi::String>();
QString keySequence = QString::fromStdString(sequenceString.Utf8Value());
this->instance = std::make_unique<QKeySequence>(keySequence);
}
else if (info.Length() == 0)
{
this->instance = std::make_unique<QKeySequence>();
}
else
{
Napi::TypeError::New(env, "Wrong number of arguments").ThrowAsJavaScriptException();
}
}
QKeySequenceWrap::~QKeySequenceWrap()
{
this->instance.reset();
}
QKeySequence *QKeySequenceWrap::getInternalInstance()
{
return this->instance.get();
}
Napi::Value QKeySequenceWrap::count(const Napi::CallbackInfo& info)
{
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
int count = this->instance->count();
return Napi::Value::From(env, count);
}

View File

@ -0,0 +1,109 @@
#include "QtWidgets/QAction/qaction_wrap.h"
#include "QtWidgets/QWidget/qwidget_wrap.h"
#include "QtGui/QKeySequence/qkeysequence_wrap.h"
#include "QtWidgets/QMenu/qmenu_wrap.h"
#include "Extras/Utils/nutils.h"
#include <QWidget>
#include <QDebug>
Napi::FunctionReference QActionWrap::constructor;
Napi::Object QActionWrap::init(Napi::Env env, Napi::Object exports) {
Napi::HandleScope scope(env);
char CLASSNAME[] = "QAction";
Napi::Function func = DefineClass(env, CLASSNAME, {
InstanceMethod("setText", &QActionWrap::setText),
InstanceMethod("setEnabled", &QActionWrap::setEnabled),
InstanceMethod("setIcon", &QActionWrap::setIcon),
InstanceMethod("setMenu", &QActionWrap::setMenu),
InstanceMethod("setShortcut", &QActionWrap::setShortcut),
InstanceMethod("setShortcutContext", &QActionWrap::setShortcutContext),
COMPONENT_WRAPPED_METHODS_EXPORT_DEFINE
EVENTWIDGET_WRAPPED_METHODS_EXPORT_DEFINE(QActionWrap)
});
constructor = Napi::Persistent(func);
exports.Set(CLASSNAME, func);
return exports;
}
NAction* QActionWrap::getInternalInstance() {
return this->instance;
}
QActionWrap::QActionWrap(const Napi::CallbackInfo& info): Napi::ObjectWrap<QActionWrap>(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 = new NAction(parentWidgetWrap->getInternalInstance()); //this sets the parent to current widget
}else if (info.Length() == 0){
this->instance = new NAction();
}else {
Napi::TypeError::New(env, "Wrong number of arguments").ThrowAsJavaScriptException();
}
}
QActionWrap::~QActionWrap() {
// delete this->instance; This will be destroyed by the qmenu (since it takes the ownership)
}
Napi::Value QActionWrap::setText(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
Napi::String text = info[0].As<Napi::String>();
this->instance->setText(QString::fromStdString(text.Utf8Value()));
return env.Null();
}
Napi::Value QActionWrap::setEnabled(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
Napi::Boolean enabled = info[0].As<Napi::Boolean>();
this->instance->setEnabled(enabled.Value());
return env.Null();
}
Napi::Value QActionWrap::setIcon(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
Napi::Object iconObject = info[0].As<Napi::Object>();
QIconWrap *iconWrap = Napi::ObjectWrap<QIconWrap>::Unwrap(iconObject);
this->instance->setIcon(*iconWrap->getInternalInstance());
return env.Null();
}
Napi::Value QActionWrap::setMenu(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->setMenu(menuWrap->getInternalInstance());
return env.Null();
}
Napi::Value QActionWrap::setShortcut(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
Napi::Object shortcutSequence = info[0].As<Napi::Object>();
QKeySequenceWrap *keysequence = Napi::ObjectWrap<QKeySequenceWrap>::Unwrap(shortcutSequence);
this->instance->setShortcut(*keysequence->getInternalInstance());
return env.Null();
}
Napi::Value QActionWrap::setShortcutContext(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
Napi::Number shortcutContextEnum = info[0].As<Napi::Number>();
int shortCutContext = shortcutContextEnum.Int32Value();
this->instance->setShortcutContext(static_cast<Qt::ShortcutContext>(shortCutContext));
qDebug()<<"shortCutContext: "<<shortCutContext;
return env.Null();
}

View File

@ -1,6 +1,7 @@
#include "QtWidgets/QMenu/qmenu_wrap.h"
#include <nodegui/QtWidgets/QWidget/qwidget_wrap.h>
#include <nodegui/Extras/Utils/nutils.h>
#include "QtWidgets/QAction/qaction_wrap.h"
#include <QWidget>
Napi::FunctionReference QMenuWrap::constructor;
@ -10,6 +11,7 @@ Napi::Object QMenuWrap::init(Napi::Env env, Napi::Object exports) {
char CLASSNAME[] = "QMenu";
Napi::Function func = DefineClass(env, CLASSNAME, {
InstanceMethod("setTitle", &QMenuWrap::setTitle),
InstanceMethod("addAction", &QMenuWrap::addAction),
QWIDGET_WRAPPED_METHODS_EXPORT_DEFINE(QMenuWrap)
});
constructor = Napi::Persistent(func);
@ -51,3 +53,14 @@ Napi::Value QMenuWrap::setTitle(const Napi::CallbackInfo& info) {
return env.Null();
}
Napi::Value QMenuWrap::addAction(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
Napi::Object actionObject = info[0].As<Napi::Object>();
QActionWrap *actionWrap = Napi::ObjectWrap<QActionWrap>::Unwrap(actionObject);
this->instance->addAction(actionWrap->getInternalInstance());
//TODO: see if we need to return from here an pointer to qaction or not.
return env.Null();
}

View File

@ -0,0 +1,84 @@
#include "QtWidgets/QShortcut/qshortcut_wrap.h"
#include "QtWidgets/QWidget/qwidget_wrap.h"
#include "QtGui/QKeySequence/qkeysequence_wrap.h"
#include "QtWidgets/QMenu/qmenu_wrap.h"
#include "Extras/Utils/nutils.h"
#include <QWidget>
#include <QDebug>
Napi::FunctionReference QShortcutWrap::constructor;
Napi::Object QShortcutWrap::init(Napi::Env env, Napi::Object exports) {
Napi::HandleScope scope(env);
char CLASSNAME[] = "QShortcut";
Napi::Function func = DefineClass(env, CLASSNAME, {
InstanceMethod("setEnabled", &QShortcutWrap::setEnabled),
InstanceMethod("setAutoRepeat", &QShortcutWrap::setAutoRepeat),
InstanceMethod("setKey", &QShortcutWrap::setKey),
InstanceMethod("setContext", &QShortcutWrap::setContext),
COMPONENT_WRAPPED_METHODS_EXPORT_DEFINE
EVENTWIDGET_WRAPPED_METHODS_EXPORT_DEFINE(QShortcutWrap)
});
constructor = Napi::Persistent(func);
exports.Set(CLASSNAME, func);
return exports;
}
NShortcut* QShortcutWrap::getInternalInstance() {
return this->instance;
}
QShortcutWrap::QShortcutWrap(const Napi::CallbackInfo& info): Napi::ObjectWrap<QShortcutWrap>(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 = new NShortcut(parentWidgetWrap->getInternalInstance()); //this sets the parent to current widget
} else {
Napi::TypeError::New(env, "Wrong number of arguments").ThrowAsJavaScriptException();
}
}
QShortcutWrap::~QShortcutWrap() {
// delete this->instance; This will be destroyed by the qmenu (since it takes the ownership)
}
Napi::Value QShortcutWrap::setEnabled(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
Napi::Boolean enabled = info[0].As<Napi::Boolean>();
this->instance->setEnabled(enabled.Value());
return env.Null();
}
Napi::Value QShortcutWrap::setAutoRepeat(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
Napi::Boolean enabled = info[0].As<Napi::Boolean>();
this->instance->setAutoRepeat(enabled.Value());
return env.Null();
}
Napi::Value QShortcutWrap::setKey(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
Napi::Object shortcutSequence = info[0].As<Napi::Object>();
QKeySequenceWrap *keysequence = Napi::ObjectWrap<QKeySequenceWrap>::Unwrap(shortcutSequence);
this->instance->setKey(*keysequence->getInternalInstance());
return env.Null();
}
Napi::Value QShortcutWrap::setContext(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
Napi::Number shortcutContextEnum = info[0].As<Napi::Number>();
int shortCutContext = shortcutContextEnum.Int32Value();
this->instance->setContext(static_cast<Qt::ShortcutContext>(shortCutContext));
qDebug()<<"shortCutContext: "<<shortCutContext;
return env.Null();
}

View File

@ -1,5 +1,6 @@
#include "QtWidgets/QSystemTrayIcon/qsystemtrayicon_wrap.h"
#include "QtWidgets/QWidget/qwidget_wrap.h"
#include "QtWidgets/QMenu/qmenu_wrap.h"
#include "Extras/Utils/nutils.h"
#include <QWidget>
#include <iostream>
@ -15,6 +16,8 @@ Napi::Object QSystemTrayIconWrap::init(Napi::Env env, Napi::Object exports) {
InstanceMethod("hide", &QSystemTrayIconWrap::hide),
InstanceMethod("setIcon", &QSystemTrayIconWrap::setIcon),
InstanceMethod("isVisible", &QSystemTrayIconWrap::isVisible),
InstanceMethod("setToolTip", &QSystemTrayIconWrap::setToolTip),
InstanceMethod("setContextMenu", &QSystemTrayIconWrap::setContextMenu),
COMPONENT_WRAPPED_METHODS_EXPORT_DEFINE
EVENTWIDGET_WRAPPED_METHODS_EXPORT_DEFINE(QSystemTrayIconWrap)
@ -81,3 +84,21 @@ Napi::Value QSystemTrayIconWrap::isVisible(const Napi::CallbackInfo& info) {
return Napi::Boolean::New(env, visibility);
}
Napi::Value QSystemTrayIconWrap::setToolTip(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
Napi::String toolTip = info[0].As<Napi::String>();
this->instance->setToolTip(QString::fromStdString(toolTip.Utf8Value()));
return env.Null();
}
Napi::Value QSystemTrayIconWrap::setContextMenu(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->setContextMenu(menuWrap->getInternalInstance());
return env.Null();
}

View File

@ -3,6 +3,7 @@
#include "QtWidgets/QWidget/qwidget_wrap.h"
#include "QtGui/QPixmap/qpixmap_wrap.h"
#include "QtGui/QIcon/qicon_wrap.h"
#include "QtGui/QKeySequence/qkeysequence_wrap.h"
#include "QtGui/QCursor/qcursor_wrap.h"
#include "QtWidgets/QGridLayout/qgridlayout_wrap.h"
#include "QtWidgets/QLayout/qlayout_wrap.h"
@ -21,6 +22,8 @@
#include "QtGui/QEvent/QKeyEvent/qkeyevent_wrap.h"
#include "QtWidgets/QScrollArea/qscrollarea_wrap.h"
#include "QtWidgets/QSystemTrayIcon/qsystemtrayicon_wrap.h"
#include "QtWidgets/QAction/qaction_wrap.h"
#include "QtWidgets/QShortcut/qshortcut_wrap.h"
#include "QtWidgets/QMenu/qmenu_wrap.h"
#include "QtWidgets/QMenuBar/qmenubar_wrap.h"
#include <napi.h>
@ -35,6 +38,7 @@ Napi::Object Main(Napi::Env env, Napi::Object exports) {
QClipboardWrap::init(env, exports);
QWidgetWrap::init(env, exports);
QPixmapWrap::init(env, exports);
QKeySequenceWrap::init(env, exports);
QIconWrap::init(env, exports);
QCursorWrap::init(env, exports);
QGridLayoutWrap::init(env, exports);
@ -53,6 +57,8 @@ Napi::Object Main(Napi::Env env, Napi::Object exports) {
QLabelWrap::init(env, exports);
QScrollAreaWrap::init(env, exports);
QSystemTrayIconWrap::init(env, exports);
QActionWrap::init(env, exports);
QShortcutWrap::init(env, exports);
QMenuWrap::init(env, exports);
QMenuBarWrap::init(env, exports);
return exports;

View File

@ -1,172 +1,80 @@
import {
QMainWindow,
QLabel,
QCheckBox,
QLineEdit,
QPushButton,
QProgressBar,
QRadioButton,
FlexLayout,
QWidget,
QIcon,
QDial,
QPlainTextEdit,
QTabWidget,
QGridLayout,
QScrollArea,
QPixmap,
CursorShape,
WindowState,
QTextOptionWrapMode,
QCheckBoxEvents,
QSystemTrayIcon,
ReadWriteImageFormats,
QPushButtonEvents
} from "./index";
import { ok, equal } from "assert";
import { existsSync, unlinkSync, readFileSync } from "fs";
import { resolve } from "path";
import { QMenuBar } from "./lib/QtWidgets/QMenuBar";
import { QMainWindow } from "./index";
import { QMenu } from "./lib/QtWidgets/QMenu";
import path from "path";
import { QIcon } from "./lib/QtGui/QIcon";
import { QSystemTrayIcon } from "./lib/QtWidgets/QSystemTrayIcon";
import { QAction } from "./lib/QtWidgets/QAction";
import { QApplication } from "./lib/QtGui/QApplication";
import { QKeySequence } from "./lib/QtGui/QKeySequence";
import { ShortcutContext } from "./lib/QtEnums";
import { QMenuBar } from "./lib/QtWidgets/QMenuBar";
import { QShortcut, QShortcutEvents } from "./lib/QtWidgets/QShortcut";
const win = new QMainWindow();
const label1 = new QLabel();
label1.setText("Hello world 1 🧙");
label1.setInlineStyle("font-size: 20px;");
label1.setCursor(CursorShape.ForbiddenCursor);
const label2 = new QLabel();
label2.setText("Hello world 2 💻");
label2.setInlineStyle("font-size: 20px;");
label2.setCursor(CursorShape.ForbiddenCursor);
const checkbox = new QCheckBox();
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"}`);
const shortcut = new QShortcut(win);
shortcut.setKey(new QKeySequence("Ctrl+M"));
shortcut.setEnabled(true);
shortcut.addEventListener(QShortcutEvents.activated, () => {
console.log("Shortcut Activated");
});
const dial = new QDial();
checkbox.setObjectName("dial");
const lineEdit = new QLineEdit();
lineEdit.setPlaceholderText("Enter your thoughts here");
lineEdit.setObjectName("editable");
const button = new QPushButton();
button.setText("Push Push Push!");
button.setObjectName("btn");
const nodeguiLogo = new QIcon(
resolve(__dirname, "../extras/assets/nodegui.png")
);
const icon = new QIcon(resolve(__dirname, "../extras/assets/start_icon.png"));
button.setIcon(icon);
const tabs = new QTabWidget();
tabs.setTabsClosable(true);
const tab1 = new QWidget();
const tab2 = new QWidget();
tab1.setLayout(new QGridLayout());
tab2.setLayout(new QGridLayout());
if (tab1.layout && tab2.layout) {
tab1.layout.addWidget(label1);
tab2.layout.addWidget(label2);
}
tabs.addTab(tab1, icon, "Tab 1");
tabs.addTab(tab2, icon, "Tab 2");
const progressBar = new QProgressBar();
progressBar.setValue(6);
equal(progressBar.value(), 6);
progressBar.setMinimum(1);
progressBar.setMaximum(15);
const radioButton = new QRadioButton();
radioButton.setText("Roger that!");
const rootView = new QWidget();
rootView.setObjectName("root");
rootView.setLayout(new FlexLayout());
const textEdit = new QPlainTextEdit();
textEdit.setPlainText("Hello");
textEdit.setWordWrapMode(QTextOptionWrapMode.NoWrap);
const scrollArea = new QScrollArea();
scrollArea.setInlineStyle("flex: 1; width:'100%';");
const imageLabel = new QLabel();
const pixmap = new QPixmap(resolve(__dirname, "../extras/assets/kitchen.png"));
imageLabel.setPixmap(pixmap);
scrollArea.setWidget(imageLabel);
function testQPixmapSave(fileName: string, format?: ReadWriteImageFormats) {
try {
existsSync(fileName) && unlinkSync(fileName);
ok(!existsSync(fileName));
equal(pixmap.save(fileName, format), true);
ok(existsSync(fileName));
// ideally we want to use file-type, jimp or magica to verify tmp.png has the correct encoding and/or is identical to source img.
ok(readFileSync(fileName).byteLength > 1000);
} catch (error) {
console.error("QPixmap.save test failed", error, error.stack);
} finally {
unlinkSync(fileName);
}
}
testQPixmapSave("tmp.png");
testQPixmapSave("tmp.jpg");
testQPixmapSave("tmp_jpg", "JPG");
testQPixmapSave("tmp_bmp", "BMP");
(global as any).shortcut = shortcut;
const trayIcon = new QIcon(
resolve(__dirname, "../extras/assets/nodegui_white.png")
path.resolve(__dirname, "../extras/assets/nodegui_white.png")
);
const tray = new QSystemTrayIcon();
tray.setIcon(trayIcon);
tray.show();
if (rootView.layout) {
rootView.layout.addWidget(tabs);
rootView.layout.addWidget(checkbox);
rootView.layout.addWidget(radioButton);
rootView.layout.addWidget(lineEdit);
rootView.layout.addWidget(button);
rootView.layout.addWidget(progressBar);
rootView.layout.addWidget(textEdit);
rootView.layout.addWidget(scrollArea);
rootView.layout.addWidget(dial);
}
const menuBar = new QMenuBar();
tray.setToolTip("hello");
const menu = new QMenu();
const menuBar = new QMenuBar();
win.setMenuBar(menuBar);
menu.setTitle("hello");
menuBar.addMenu(menu);
menuBar.setNativeMenuBar(false);
win.setCentralWidget(rootView);
win.setStyleSheet(`
#root {
flex: 1;
height: '100%';
align-items: 'center';
justify-content: 'space-around';
tray.setContextMenu(menu);
const showHideAction = new QAction();
showHideAction.setText("Hide");
showHideAction.setIcon(trayIcon);
showHideAction.addEventListener("triggered", () => {
console.log("Hide called");
if (win.isVisible()) {
win.hide();
showHideAction.setText("Show");
} else {
win.show();
showHideAction.setText("Hide");
}
`);
win.setWindowIcon(nodeguiLogo);
});
showHideAction.setShortcut(new QKeySequence("Ctrl+L"));
showHideAction.setShortcutContext(ShortcutContext.ApplicationShortcut);
menu.addAction(showHideAction);
const subMenu = new QMenu();
const subMenuAction = new QAction();
subMenuAction.setText("subAction");
subMenu.addAction(subMenuAction);
const actionWithSubmenu = new QAction();
actionWithSubmenu.setMenu(subMenu);
actionWithSubmenu.setText("subMenu");
menu.addAction(actionWithSubmenu);
const quitAction = new QAction();
quitAction.setText("Quit");
quitAction.addEventListener("triggered", () => {
const app = QApplication.instance();
app.exit(0);
});
menu.addAction(quitAction);
menu.setTitle("TestMenu");
win.setWindowTitle("NodeGUI Demo");
win.resize(400, 700);
win.show();
win.setWindowState(WindowState.WindowActive);
// menuBar.addMenu(menu);
const qApp = QApplication.instance();
qApp.setQuitOnLastWindowClosed(false);
(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,6 +4,7 @@ import "./lib/core/bootstrap";
export * from "./lib/QtEnums";
// Gui:
export { QApplication } from "./lib/QtGui/QApplication";
export { QKeySequence } from "./lib/QtGui/QKeySequence";
export {
QPixmap,
ReadWriteImageFormats,
@ -45,6 +46,8 @@ export {
QSystemTrayIcon,
QSystemTrayIconEvents
} from "./lib/QtWidgets/QSystemTrayIcon";
export { QAction, QActionEvents } from "./lib/QtWidgets/QAction";
export { QShortcut, QShortcutEvents } from "./lib/QtWidgets/QShortcut";
// Layouts:
export { QGridLayout } from "./lib/QtWidgets/QGridLayout";
export { FlexLayout } from "./lib/core/FlexLayout";

View File

@ -0,0 +1,17 @@
import addon from "../../utils/addon";
import { Component, NativeElement } from "../../core/Component";
export class QKeySequence extends Component {
native: NativeElement;
constructor(keySequence?: string) {
super();
if (typeof keySequence === "string") {
this.native = new addon.QKeySequence(keySequence);
} else {
this.native = new addon.QKeySequence();
}
}
count(): number {
return this.native.count();
}
}

View File

@ -0,0 +1,50 @@
import addon from "../../utils/addon";
import { NodeWidget } from "../QWidget";
import { EventWidget, BaseWidgetEvents } from "../../core/EventWidget";
import { NativeElement } from "../../core/Component";
import { QMenu } from "../QMenu";
import { QIcon } from "../../QtGui/QIcon";
import { QKeySequence } from "../../QtGui/QKeySequence";
import { ShortcutContext } from "../../QtEnums";
export const QActionEvents = Object.freeze({
triggered: "triggered",
changed: "changed",
hovered: "hovered",
toggled: "toggled"
});
export class QAction extends EventWidget {
native: NativeElement;
icon?: QIcon;
menu?: QMenu;
constructor(parent?: NodeWidget) {
let native;
if (parent) {
native = new addon.QAction(parent.native);
} else {
native = new addon.QAction();
}
super(native);
this.native = native;
}
setText(text: string) {
this.native.setText(text);
}
setEnabled(enabled: boolean) {
this.native.setEnabled(enabled);
}
setIcon(icon: QIcon) {
this.icon = icon;
this.native.setIcon(icon.native);
}
setMenu(menu: QMenu) {
this.menu = menu;
this.native.setMenu(menu.native);
}
setShortcut(keysequence: QKeySequence) {
this.native.setShortcut(keysequence.native);
}
setShortcutContext(shortcutContext: ShortcutContext) {
this.native.setShortcutContext(shortcutContext);
}
}

View File

@ -2,12 +2,14 @@ import { BaseWidgetEvents } from "../core/EventWidget";
import { NativeElement } from "../core/Component";
import { NodeWidget } from "./QWidget";
import addon from "../utils/addon";
import { QAction } from "./QAction";
export const QMenuEvents = Object.freeze({
...BaseWidgetEvents
});
export class QMenu extends NodeWidget {
native: NativeElement;
actions: Set<QAction>;
constructor(parent?: NodeWidget) {
let native;
if (parent) {
@ -18,8 +20,14 @@ export class QMenu extends NodeWidget {
super(native);
this.native = native;
this.parent = parent;
this.actions = new Set();
}
setTitle(title: string) {
this.native.setTitle(title);
}
addAction(action: QAction): QAction {
this.native.addAction(action.native);
this.actions.add(action);
return action;
}
}

View File

@ -0,0 +1,32 @@
import addon from "../../utils/addon";
import { NodeWidget } from "../QWidget";
import { EventWidget } from "../../core/EventWidget";
import { NativeElement } from "../../core/Component";
import { QKeySequence } from "../../QtGui/QKeySequence";
import { ShortcutContext } from "../../QtEnums";
export const QShortcutEvents = Object.freeze({
activated: "activated",
activatedAmbiguously: "activatedAmbiguously"
});
export class QShortcut extends EventWidget {
native: NativeElement;
constructor(parent: NodeWidget) {
let native = new addon.QShortcut(parent.native);
super(native);
this.native = native;
}
setEnabled(enabled: boolean) {
this.native.setEnabled(enabled);
}
setAutoRepeat(on: boolean) {
this.native.setAutoRepeat(on);
}
setKey(keysequence: QKeySequence) {
this.native.setKey(keysequence.native);
}
setContext(shortcutContext: ShortcutContext) {
this.native.setContext(shortcutContext);
}
}

View File

@ -3,12 +3,14 @@ import { NodeWidget } from "../QWidget";
import { EventWidget, BaseWidgetEvents } from "../../core/EventWidget";
import { NativeElement } from "../../core/Component";
import { QIcon } from "../../QtGui/QIcon";
import { QMenu } from "../QMenu";
export const QSystemTrayIconEvents = Object.freeze({
...BaseWidgetEvents,
...BaseWidgetEvents
});
export class QSystemTrayIcon extends EventWidget {
native: NativeElement;
contextMenu?: QMenu;
constructor(parent?: NodeWidget) {
let native;
if (parent) {
@ -18,11 +20,6 @@ export class QSystemTrayIcon extends EventWidget {
}
super(native);
this.native = native;
// bind member functions
this.show = this.show.bind(this);
this.hide = this.hide.bind(this);
this.setIcon = this.setIcon.bind(this);
this.isVisible = this.isVisible.bind(this);
}
show() {
this.native.show();
@ -36,4 +33,11 @@ export class QSystemTrayIcon extends EventWidget {
isVisible(): boolean {
return this.native.isVisible();
}
setToolTip(tooltip: string) {
this.native.setToolTip(tooltip);
}
setContextMenu(menu: QMenu) {
this.contextMenu = menu;
this.native.setContextMenu(this.contextMenu.native);
}
}