diff --git a/.all-contributorsrc b/.all-contributorsrc index 0fba73f12..fcb6d6c2e 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -85,6 +85,42 @@ "contributions": [ "code" ] + }, + { + "login": "soonoo", + "name": "Soonwoo Hong", + "avatar_url": "https://avatars2.githubusercontent.com/u/5436405?v=4", + "profile": "https://github.com/soonoo", + "contributions": [ + "code" + ] + }, + { + "login": "illBeRoy", + "name": "Roy Sommer", + "avatar_url": "https://avatars2.githubusercontent.com/u/6681893?v=4", + "profile": "https://github.com/illBeRoy", + "contributions": [ + "code" + ] + }, + { + "login": "paulocoghi", + "name": "Paulo Coghi", + "avatar_url": "https://avatars1.githubusercontent.com/u/378397?v=4", + "profile": "https://github.com/paulocoghi", + "contributions": [ + "ideas" + ] + }, + { + "login": "balthild", + "name": "Balthild Ires", + "avatar_url": "https://avatars2.githubusercontent.com/u/2662758?v=4", + "profile": "https://balthild.com", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 7, diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..8d38c2255 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,32 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. Mac, Linux, Win] + - NodeGUI version + - OS Version + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..bbcbbe7d6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/README.md b/README.md index a5ab523f7..0ca4bb90e 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # NodeGUI [![Join the NodeGUI community on Spectrum](https://withspectrum.github.io/badge/badge.svg)](https://spectrum.chat/nodegui) -[![All Contributors](https://img.shields.io/badge/all_contributors-9-orange.svg?style=flat-square)](#contributors) +[![All Contributors](https://img.shields.io/badge/all_contributors-13-orange.svg?style=flat-square)](#contributors) Build **performant**, **native** and **cross-platform** desktop applications with **JavaScript** + powerful **CSS like styling**.πŸš€ @@ -70,15 +70,23 @@ Looking to contribute? If you wish to implement a new widget/add more features a `npm run build [--qt_home_dir=/path/to/qt]` -### LICENSE +## Funding + +NodeGUI is an open source project and requires your support. If you like this project, please consider supporting my work with Ko-fi. Alternatively, Issues on NodeGui can be funded by anyone via Issuehunt and the amount will be distributed to respective contributors. + +

+Buy Me a Coffee at ko-fi.com     + issuehunt +

+ +## Special Thanks + +- [Logo: Thanks to Vishwas Shetty from the Noun Project.](https://github.com/nodegui/nodegui/blob/master/extras/legal/logo/thanks.md) + +## License MIT -## Note - -> Since we do not in any way modify the code of Qt and only link to it dynamically, I believe 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 purposes (provided you abide by MIT License). ## Maintainers ✨ @@ -91,6 +99,7 @@ People maintaining this project. + ## Contributors ✨ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): @@ -110,6 +119,10 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d Átila Camurça Alves
Átila Camurça Alves

πŸ“– James Hibbard
James Hibbard

πŸ’» + Soonwoo Hong
Soonwoo Hong

πŸ’» + Roy Sommer
Roy Sommer

πŸ’» + Paulo Coghi
Paulo Coghi

πŸ€” + Balthild Ires
Balthild Ires

πŸ’» diff --git a/config/application.gypi b/config/application.gypi index 7742ca49a..89e19e63d 100644 --- a/config/application.gypi +++ b/config/application.gypi @@ -11,6 +11,7 @@ "../src/cpp/core/YogaWidget/yogawidget.cpp", # wrapped cpps. Move non wrapped ones to shared gypi "../src/cpp/QtGui/QApplication/qapplication_wrap.cpp", + "../src/cpp/QtGui/QClipboard/qclipboard_wrap.cpp", "../src/cpp/QtGui/QEvent/QKeyEvent/qkeyevent_wrap.cpp", "../src/cpp/QtGui/QPixmap/qpixmap_wrap.cpp", "../src/cpp/QtGui/QIcon/qicon_wrap.cpp", diff --git a/docs/CNAME b/docs/CNAME new file mode 100644 index 000000000..334ded21e --- /dev/null +++ b/docs/CNAME @@ -0,0 +1 @@ +docs.nodegui.org \ No newline at end of file diff --git a/docs/README.md b/docs/README.md index 25e6be324..664922125 100644 --- a/docs/README.md +++ b/docs/README.md @@ -53,6 +53,8 @@ - [FlexLayout](api/FlexLayout.md) - [QPixmap](api/QPixmap.md) - [QIcon](api/QIcon.md) +- [QCursor](api/QCursor.md) +- [QClipboard](api/QClipboard.md) - [Qt Enums](api/QtEnums.md) ### Internal Modules diff --git a/docs/api/QApplication.md b/docs/api/QApplication.md index 907325bf1..52cb53140 100644 --- a/docs/api/QApplication.md +++ b/docs/api/QApplication.md @@ -21,10 +21,14 @@ qApp.quit(); QApplication can access all the static methods defined in [Component](api/Component.md). Additionally it also has the following static methods. -#### `qApp.instance()` +#### `QApplication.instance()` Returns the already initialised QApplication instance. It calls the native method [QApplication: instance](https://doc.qt.io/qt-5/qcoreapplication.html#instance). +#### `QApplication.clipboard()` + +Returns the object for interacting with the clipboard. It calls the native method [QApplication: clipboard](https://doc.qt.io/qt-5/qguiapplication.html#clipboard). See QClipboard. + ### Instance Properties QApplication can access all the instance properties defined in [Component](api/Component.md) diff --git a/docs/api/QClipboard.md b/docs/api/QClipboard.md new file mode 100644 index 000000000..439604b0a --- /dev/null +++ b/docs/api/QClipboard.md @@ -0,0 +1,52 @@ +## Class: QClipboard + +> The QClipboard class provides access to the window system clipboard. + +**This class is a JS wrapper around Qt's [QClipboard class](https://doc.qt.io/qt-5/QClipboard.html)** + +**QClipboard inherits from [Component](api/Component.md)** + +### Example + +```javascript +const { + QClipboard, + QClipboardMode, + QApplication +} = require("@nodegui/nodegui"); + +const clipboard = QApplication.clipboard(); +const text = clipboard.text(QClipboardMode.Clipboard); +``` + +### Static Methods + +QClipboard can access all the static methods defined in [Component](api/Component.md) + +### Instance Properties + +QClipboard can access all the instance properties defined in [Component](api/Component.md) + +### Instance Methods + +QClipboard can access all the instance methods defined in [Component](api/Component.md). Additionally it has: + +### `clipboard.clear(mode)` + +Clear the clipboard contents. It calls the native method [QClipboard: clear](https://doc.qt.io/qt-5/qclipboard.html#clear). + +- `mode` - This enum type is used to control which part of the system clipboard is used. See https://doc.qt.io/qt-5/qclipboard.html#Mode-enum + +### `clipboard.setText(text, mode)` + +Copies text into the clipboard as plain text. It calls the native method [QClipboard: setText](https://doc.qt.io/qt-5/qclipboard.html#setText). + +- `text` - The text you want to copy to clipboard. + +- `mode` - This enum type is used to control which part of the system clipboard is used. See https://doc.qt.io/qt-5/qclipboard.html#Mode-enum + +### `clipboard.text(mode)` + +Returns the clipboard text as plain text, or an empty string if the clipboard does not contain any text. It calls the native method [QClipboard: text](https://doc.qt.io/qt-5/qclipboard.html#text). + +- `mode` - This enum type is used to control which part of the system clipboard is used. See https://doc.qt.io/qt-5/qclipboard.html#Mode-enum diff --git a/docs/api/QIcon.md b/docs/api/QIcon.md index 2a6cd1e08..11a88e226 100644 --- a/docs/api/QIcon.md +++ b/docs/api/QIcon.md @@ -30,3 +30,14 @@ QIcon can access all the instance properties defined in [Component](api/Componen ### Instance Methods QIcon can access all the instance methods defined in [Component](api/Component.md) +Additionally it also has the following instance methods: + +#### `icon.pixmap(width, height, mode?, state?)` + +Returns a pixmap with the requested size, mode, and state, generating one if necessary. The pixmap might be smaller than requested, but never larger. +. It calls the native method [QIcon: pixmap](https://doc.qt.io/qt-5/qicon.html#pixmap-3). + +- `width`: number, +- `height`: number +- `mode?`: QIconMode +- `state?`: QIconState diff --git a/docs/api/QPlainTextEdit.md b/docs/api/QPlainTextEdit.md index 0a820a80d..688bc894a 100644 --- a/docs/api/QPlainTextEdit.md +++ b/docs/api/QPlainTextEdit.md @@ -28,24 +28,54 @@ QPlainTextEdit can access all the static methods defined in [NodeWidget](api/Nod QPlainTextEdit can access all the instance properties defined in [NodeWidget](api/NodeWidget.md). +#### `plainTextEdit.placeholderText` + +The placeholder text set on the plainTextEdit. + ### Instance Methods QPlainTextEdit can access all the instance methods defined in [NodeWidget](api/NodeWidget.md). -#### [`plainTextEdit.setPlainText(text)`](https://doc.qt.io/qt-5/qplaintextedit.html#setPlainText) +#### `plainTextEdit.setPlainText(text)` -Sets the given text to the plainTextEdit. +Sets the given text to the plainTextEdit. It calls the native method [QPlainTextEdit: setPlainText](https://doc.qt.io/qt-5/qplaintextedit.html#setPlainText). - `text` string -#### [`plainTextEdit.toPlainText()`](https://doc.qt.io/qt-5/qplaintextedit.html#toPlainText) +#### `plainTextEdit.setPlaceholderText(text)` -Returns the text of the text edit as plain text. +Sets the given text to the plainTextEdit's placeholder. -#### [`plainTextEdit.setReadOnly(isReadOnly)`](https://doc.qt.io/qt-5/qplaintextedit.html#readOnly-prop) +- `text` string -Sets the plainTextEdit to be read only. +#### `plainTextEdit.toPlainText()` -#### [`plainTextEdit.clear()`](https://doc.qt.io/qt-5/qplaintextedit.html#clear) +Returns the text of the text edit as plain text. [QPlainTextEdit: toPlainText](https://doc.qt.io/qt-5/qplaintextedit.html#toPlainText). -Deletes all the text in the text edit. +#### `plainTextEdit.setReadOnly(isReadOnly)` + +Sets the plainTextEdit to be read only. [QPlainTextEdit: isReadOnly](https://doc.qt.io/qt-5/qplaintextedit.html#readOnly-prop). + +#### `plainTextEdit.clear()` + +Deletes all the text in the text edit.[QPlainTextEdit: clear](https://doc.qt.io/qt-5/qplaintextedit.html#clear). + +#### `plainTextEdit.setWordWrapMode(mode)` + +This property holds the mode QPlainTextEdit will use when wrapping text by words.[QPlainTextEdit: setWordWrapMode](https://doc.qt.io/qt-5/qplaintextedit.html#wordWrapMode-prop). + +- mode: QTextOptionWrapMode + +#### `plainTextEdit.wordWrapMode()` + +returns word wrap mode. [QPlainTextEdit: wordWrapMode](https://doc.qt.io/qt-5/qplaintextedit.html#wordWrapMode-prop). + +#### `plainTextEdit.setLineWrapMode(mode)` + +This property holds the line wrap mode. [QPlainTextEdit: setLineWrapMode](https://doc.qt.io/qt-5/qplaintextedit.html#lineWrapMode-prop). + +- mode: LineWrapMode + +#### `plainTextEdit.lineWrapMode()` + +returns line wrap mode. [QPlainTextEdit: setLineWrapMode](https://doc.qt.io/qt-5/qplaintextedit.html#lineWrapMode-prop). diff --git a/extras/legal/logo/thanks.md b/extras/legal/logo/thanks.md index dd7ccacf7..16b9c9a73 100644 --- a/extras/legal/logo/thanks.md +++ b/extras/legal/logo/thanks.md @@ -1,6 +1,6 @@ # Logo -logo +logo Special thanks to the Noun Project for the logo. diff --git a/package-lock.json b/package-lock.json index 728fd4be0..16d2aa1c8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,13 +1,13 @@ { "name": "@nodegui/nodegui", - "version": "0.1.7", + "version": "0.1.9", "lockfileVersion": 1, "requires": true, "dependencies": { "@nodegui/qode": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@nodegui/qode/-/qode-1.0.4.tgz", - "integrity": "sha512-CEEAI1mJ+4eH6G2OlNtn75vUxTBw/06IcEkXlwkDMI/mU5iE52WHToybYba77gOeoxrGx3uIwkfWRV2LRVyp/Q==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@nodegui/qode/-/qode-1.0.5.tgz", + "integrity": "sha512-JKb4JcC03VCdodmiL35VfznsCKHpiKOYbtgf9+KTHhH5p7IXvr+0k/LIq112T6Rpxd5zXa1pbsnfa2F8ReyWEg==", "requires": { "env-paths": "^2.2.0", "extract-zip": "^1.6.7", @@ -975,21 +975,28 @@ "integrity": "sha512-2+DuKodWvwRTrCfKOeR24KIc5unKjOh8mz17NCzVnHWfjAdDqbfbjqh7gUT+BkXBRQM52+xCHciKWonJ3CbJMQ==" }, "node-gyp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-4.0.0.tgz", - "integrity": "sha512-2XiryJ8sICNo6ej8d0idXDEMKfVfFK7kekGCtJAuelGsYHQxhj13KTf95swTCN2dZ/4lTfZ84Fu31jqJEEgjWA==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-5.0.3.tgz", + "integrity": "sha512-z/JdtkFGUm0QaQUusvloyYuGDub3nUbOo5de1Fz57cM++osBTvQatBUSTlF1k/w8vFHPxxXW6zxGvkxXSpaBkQ==", "requires": { + "env-paths": "^1.0.0", "glob": "^7.0.3", "graceful-fs": "^4.1.2", "mkdirp": "^0.5.0", "nopt": "2 || 3", "npmlog": "0 || 1 || 2 || 3 || 4", - "osenv": "0", "request": "^2.87.0", "rimraf": "2", "semver": "~5.3.0", "tar": "^4.4.8", "which": "1" + }, + "dependencies": { + "env-paths": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-1.0.0.tgz", + "integrity": "sha1-QWgTO0K7BcOKNbGuQ5fIKYqzaeA=" + } } }, "nopt": { @@ -1054,25 +1061,6 @@ "wrappy": "1" } }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" - }, - "osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, "p-cancelable": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", @@ -1163,9 +1151,9 @@ "dev": true }, "psl": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.3.0.tgz", - "integrity": "sha512-avHdspHO+9rQTLbv1RO+MPYeP/SzsCoxofjVnHanETfQhTJrmB0HlDoW+EiN/R+C0BZ+gERab9NY0lPN2TxNag==" + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.3.1.tgz", + "integrity": "sha512-2KLd5fKOdAfShtY2d/8XDWVRnmp3zp40Qt6ge2zBPFARLXOGUf2fHD5eg+TV/5oxBtQKVhjUaKFsAaE4HnwfSA==" }, "pump": { "version": "3.0.0", diff --git a/package.json b/package.json index b9696d890..2a9dc432f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@nodegui/nodegui", - "version": "0.1.7", + "version": "0.1.9", "description": "A cross platform library to build native desktop apps.", "main": "dist/index.js", "typings": "dist/index.d.ts", @@ -26,10 +26,10 @@ "docs": "serve docs" }, "dependencies": { - "@nodegui/qode": "^1.0.4", + "@nodegui/qode": "^1.0.5", "cuid": "^2.1.6", "node-addon-api": "^1.6.3", - "node-gyp": "^4.0.0", + "node-gyp": "^5.0.3", "postcss-nodegui-autoprefixer": "0.0.7" }, "devDependencies": { diff --git a/scripts/automoc.js b/scripts/automoc.js index 432c5b483..1805095af 100644 --- a/scripts/automoc.js +++ b/scripts/automoc.js @@ -1,6 +1,7 @@ const path = require("path"); const fs = require("fs"); const childProcess = require("child_process"); +const { qtHome } = require("@nodegui/qode"); const ROOT_DIR = path.resolve(__dirname, "../"); const MOC_AUTOGEN_DIR = path.resolve(ROOT_DIR, "src/cpp/autogen"); @@ -50,13 +51,22 @@ const main = () => { eachHeaderPath, includeFilePath ); - console.log(command); - childProcess.exec(command, {}, error => { - if (error) { - console.error(`exec error: ${error}`); - return; + const mocPath = path.resolve(process.env.QT_INSTALL_DIR || qtHome, "bin"); + childProcess.exec( + command, + { + env: { + ...process.env, + PATH: `${mocPath}${path.delimiter}${process.env.PATH}` + } + }, + error => { + if (error) { + console.error(`exec error: ${error}`); + return; + } } - }); + ); return outfilePath; }); generateMocGypiFile(outFiles); diff --git a/src/cpp/QtGui/QApplication/qapplication_wrap.cpp b/src/cpp/QtGui/QApplication/qapplication_wrap.cpp index c4ad0d056..7ea729e83 100644 --- a/src/cpp/QtGui/QApplication/qapplication_wrap.cpp +++ b/src/cpp/QtGui/QApplication/qapplication_wrap.cpp @@ -1,6 +1,7 @@ #include "qapplication_wrap.h" #include "src/cpp/core/Component/component_macro.h" #include "src/cpp/Extras/Utils/nutils.h" +#include "src/cpp/QtGui/QClipboard/qclipboard_wrap.h" Napi::FunctionReference QApplicationWrap::constructor; int QApplicationWrap::argc = 0; @@ -15,6 +16,7 @@ Napi::Object QApplicationWrap::init(Napi::Env env, Napi::Object exports) InstanceMethod("exec", &QApplicationWrap::exec), InstanceMethod("quit", &QApplicationWrap::quit), StaticMethod("instance", &StaticQApplicationWrapMethods::instance), + StaticMethod("clipboard", &StaticQApplicationWrapMethods::clipboard), COMPONENT_WRAPPED_METHODS_EXPORT_DEFINE }); constructor = Napi::Persistent(func); @@ -28,22 +30,24 @@ QApplicationWrap::QApplicationWrap(const Napi::CallbackInfo& info) Napi::Env env = info.Env(); Napi::HandleScope scope(env); if(info.Length() == 1) { - this->instance = std::unique_ptr(info[0].As>().Data()); + this->instance = info[0].As>().Data(); } else if (info.Length() == 0){ - this->instance = std::make_unique(this->argc, this->argv); + this->instance = new QApplication(this->argc, this->argv); + this->_wasManuallyCreated = true; } else { Napi::TypeError::New(env, "Wrong number of arguments").ThrowAsJavaScriptException(); } } - QApplicationWrap::~QApplicationWrap() { - this->instance.reset(); + if(this->_wasManuallyCreated){ + delete this->instance; + } } QApplication* QApplicationWrap::getInternalInstance() { - return this->instance.get(); + return this->instance; } Napi::Value QApplicationWrap::processEvents(const Napi::CallbackInfo& info) @@ -87,3 +91,11 @@ Napi::Value StaticQApplicationWrapMethods::instance(const Napi::CallbackInfo& in Napi::Object instance = QApplicationWrap::constructor.New({ Napi::External::New(env, app) }); return instance; } + +Napi::Value StaticQApplicationWrapMethods::clipboard(const Napi::CallbackInfo& info) +{ + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + QClipboard* clipboard = QApplication::clipboard(); + return QClipboardWrap::constructor.New({ Napi::External::New(env, clipboard) }); +} diff --git a/src/cpp/QtGui/QApplication/qapplication_wrap.h b/src/cpp/QtGui/QApplication/qapplication_wrap.h index 114172fca..a5850f007 100644 --- a/src/cpp/QtGui/QApplication/qapplication_wrap.h +++ b/src/cpp/QtGui/QApplication/qapplication_wrap.h @@ -1,15 +1,15 @@ #pragma once #include -#include #include class QApplicationWrap : public Napi::ObjectWrap { private: - std::unique_ptr instance; + QApplication* instance; static int argc; static char** argv; + bool _wasManuallyCreated = false; public: static Napi::FunctionReference constructor; @@ -26,4 +26,5 @@ public: namespace StaticQApplicationWrapMethods { Napi::Value instance(const Napi::CallbackInfo& info); + Napi::Value clipboard(const Napi::CallbackInfo& info); } \ No newline at end of file diff --git a/src/cpp/QtGui/QClipboard/qclipboard_wrap.cpp b/src/cpp/QtGui/QClipboard/qclipboard_wrap.cpp new file mode 100644 index 000000000..48d56e993 --- /dev/null +++ b/src/cpp/QtGui/QClipboard/qclipboard_wrap.cpp @@ -0,0 +1,63 @@ +#include "qclipboard_wrap.h" +#include "src/cpp/Extras/Utils/nutils.h" +#include "deps/spdlog/spdlog.h" + +Napi::FunctionReference QClipboardWrap::constructor; + +Napi::Object QClipboardWrap::init(Napi::Env env, Napi::Object exports) +{ + Napi::HandleScope scope(env); + char CLASSNAME[] = "QClipboard"; + Napi::Function func = DefineClass(env, CLASSNAME, { + InstanceMethod("clear", &QClipboardWrap::clear), + InstanceMethod("setText", &QClipboardWrap::setText), + InstanceMethod("text", &QClipboardWrap::text), + COMPONENT_WRAPPED_METHODS_EXPORT_DEFINE + }); + constructor = Napi::Persistent(func); + exports.Set(CLASSNAME, func); + return exports; +} + +QClipboardWrap::QClipboardWrap(const Napi::CallbackInfo &info) : Napi::ObjectWrap(info) +{ + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + if (info[0].IsExternal()) { + this->instance = info[0].As>().Data(); + } + else { + Napi::TypeError::New(env, "Incorrect initialization of QClipboardWrap").ThrowAsJavaScriptException(); + } +} + +QClipboard *QClipboardWrap::getInternalInstance() +{ + return this->instance; +} + +Napi::Value QClipboardWrap::clear(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + Napi::Number mode = info[0].As(); + this->instance->clear(static_cast(mode.Int32Value())); + return env.Null(); +} + +Napi::Value QClipboardWrap::setText(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + Napi::String text = info[0].As(); + Napi::Number mode = info[1].As(); + QString clipboardText = text.Utf8Value().c_str(); + this->instance->setText(clipboardText, static_cast(mode.Int32Value())); + return env.Null(); +} + +Napi::Value QClipboardWrap::text(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + Napi::Number mode = info[0].As(); + QString text = this->instance->text(static_cast(mode.Int32Value())); + return Napi::Value::From(env, text.toStdString()); +} \ No newline at end of file diff --git a/src/cpp/QtGui/QClipboard/qclipboard_wrap.h b/src/cpp/QtGui/QClipboard/qclipboard_wrap.h new file mode 100644 index 000000000..386a0e98d --- /dev/null +++ b/src/cpp/QtGui/QClipboard/qclipboard_wrap.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include +#include "src/cpp/core/Component/component_macro.h" + +class QClipboardWrap : public Napi::ObjectWrap +{ +private: + QClipboard* instance; +public: + static Napi::FunctionReference constructor; + static Napi::Object init(Napi::Env env, Napi::Object exports); + QClipboardWrap(const Napi::CallbackInfo &info); + QClipboard *getInternalInstance(); + // Wrapped methods + Napi::Value clear(const Napi::CallbackInfo& info); + Napi::Value setText(const Napi::CallbackInfo& info); + Napi::Value text(const Napi::CallbackInfo& info); +}; diff --git a/src/cpp/QtGui/QIcon/qicon_wrap.cpp b/src/cpp/QtGui/QIcon/qicon_wrap.cpp index 218a744de..5185c6768 100644 --- a/src/cpp/QtGui/QIcon/qicon_wrap.cpp +++ b/src/cpp/QtGui/QIcon/qicon_wrap.cpp @@ -1,4 +1,5 @@ #include "qicon_wrap.h" +#include "src/cpp/QtGui/QPixmap/qpixmap_wrap.h" #include "src/cpp/Extras/Utils/nutils.h" #include "deps/spdlog/spdlog.h" @@ -9,6 +10,7 @@ Napi::Object QIconWrap::init(Napi::Env env, Napi::Object exports) Napi::HandleScope scope(env); char CLASSNAME[] = "QIcon"; Napi::Function func = DefineClass(env, CLASSNAME, { + InstanceMethod("pixmap", &QIconWrap::pixmap), COMPONENT_WRAPPED_METHODS_EXPORT_DEFINE }); constructor = Napi::Persistent(func); @@ -45,3 +47,30 @@ QIcon *QIconWrap::getInternalInstance() { return this->instance.get(); } + +Napi::Value QIconWrap::pixmap(const Napi::CallbackInfo& info) +{ + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + + Napi::Number widthValue = info[0].As(); + Napi::Number heightValue = info[1].As(); + int width = widthValue.Int32Value(); + int height = heightValue.Int32Value(); + + QIcon::Mode mode = QIcon::Normal; + if (info.Length() > 2) { + int modeInt = info[2].As().Int32Value(); + mode = static_cast(modeInt); + } + + QIcon::State state = QIcon::Off; + if (info.Length() > 3) { + int stateInt = info[3].As().Int32Value(); + state = static_cast(stateInt); + } + + QPixmap* pixmap = new QPixmap(this->instance->pixmap(width, height, mode, state)); + auto instance = QPixmapWrap::constructor.New({ Napi::External::New(env, pixmap) }); + return instance; +} diff --git a/src/cpp/QtGui/QIcon/qicon_wrap.h b/src/cpp/QtGui/QIcon/qicon_wrap.h index a4e9dd953..549a0e60c 100644 --- a/src/cpp/QtGui/QIcon/qicon_wrap.h +++ b/src/cpp/QtGui/QIcon/qicon_wrap.h @@ -17,4 +17,5 @@ public: ~QIconWrap(); QIcon *getInternalInstance(); // Wrapped methods + Napi::Value pixmap(const Napi::CallbackInfo& info); }; diff --git a/src/cpp/QtWidgets/QCheckBox/ncheckbox.h b/src/cpp/QtWidgets/QCheckBox/ncheckbox.h index e9e660c5b..8ed6054d5 100644 --- a/src/cpp/QtWidgets/QCheckBox/ncheckbox.h +++ b/src/cpp/QtWidgets/QCheckBox/ncheckbox.h @@ -2,12 +2,21 @@ #include #include "src/cpp/core/NodeWidget/nodewidget.h" +#include "napi.h" class NCheckBox: public QCheckBox, public NodeWidget { NODEWIDGET_IMPLEMENTATIONS(QCheckBox) public: using QCheckBox::QCheckBox; //inherit all constructors of QCheckBox + + void connectWidgetSignalsToEventEmitter() { + QObject::connect(this, &QCheckBox::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) }); + }); + } }; diff --git a/src/cpp/QtWidgets/QMainWindow/nmainwindow.h b/src/cpp/QtWidgets/QMainWindow/nmainwindow.h index dbcb5b947..0d62b5500 100644 --- a/src/cpp/QtWidgets/QMainWindow/nmainwindow.h +++ b/src/cpp/QtWidgets/QMainWindow/nmainwindow.h @@ -9,25 +9,6 @@ class NMainWindow: public QMainWindow, public NodeWidget NODEWIDGET_IMPLEMENTATIONS(QMainWindow) public: using QMainWindow::QMainWindow; //inherit all constructors of QMainWindow -private: - void calculateLayout(){ - YGDirection direction = YGNodeStyleGetDirection(this->getFlexNode()); - YGNodeCalculateLayout(this->getFlexNode(),width(),height(),direction); - } - bool eventFilter(QObject *object, QEvent *event) { // This will be installed on mainwidgetwrap - switch(event->type()) { - case QEvent::LayoutRequest: - case QEvent::ChildRemoved: { - calculateLayout(); break; - } - default: ; // do nothing - } - return QMainWindow::eventFilter(object, event); - } - void resizeEvent(QResizeEvent * event){ - calculateLayout(); - QMainWindow::resizeEvent(event); - } }; diff --git a/src/cpp/QtWidgets/QPlainTextEdit/nplaintextedit.h b/src/cpp/QtWidgets/QPlainTextEdit/nplaintextedit.h index b1da3ec04..2b5d5d911 100644 --- a/src/cpp/QtWidgets/QPlainTextEdit/nplaintextedit.h +++ b/src/cpp/QtWidgets/QPlainTextEdit/nplaintextedit.h @@ -17,5 +17,40 @@ public: Napi::HandleScope scope(env); this->emitOnNode.Call({Napi::String::New(env, "textChanged")}); }); + QObject::connect(this, &QPlainTextEdit::blockCountChanged, [=](int newBlockCount) { + Napi::Env env = this->emitOnNode.Env(); + Napi::HandleScope scope(env); + this->emitOnNode.Call({Napi::String::New(env, "blockCountChanged"), Napi::Value::From(env, newBlockCount)}); + }); + QObject::connect(this, &QPlainTextEdit::copyAvailable, [=](bool yes) { + Napi::Env env = this->emitOnNode.Env(); + Napi::HandleScope scope(env); + this->emitOnNode.Call({Napi::String::New(env, "copyAvailable"), Napi::Value::From(env, yes)}); + }); + QObject::connect(this, &QPlainTextEdit::cursorPositionChanged, [=]() { + Napi::Env env = this->emitOnNode.Env(); + Napi::HandleScope scope(env); + this->emitOnNode.Call({Napi::String::New(env, "cursorPositionChanged")}); + }); + QObject::connect(this, &QPlainTextEdit::modificationChanged, [=](bool charged) { + Napi::Env env = this->emitOnNode.Env(); + Napi::HandleScope scope(env); + this->emitOnNode.Call({Napi::String::New(env, "modificationChanged"), Napi::Value::From(env, charged)}); + }); + QObject::connect(this, &QPlainTextEdit::redoAvailable, [=](bool available) { + Napi::Env env = this->emitOnNode.Env(); + Napi::HandleScope scope(env); + this->emitOnNode.Call({Napi::String::New(env, "redoAvailable"), Napi::Value::From(env, available)}); + }); + QObject::connect(this, &QPlainTextEdit::selectionChanged, [=]() { + Napi::Env env = this->emitOnNode.Env(); + Napi::HandleScope scope(env); + this->emitOnNode.Call({Napi::String::New(env, "selectionChanged")}); + }); + QObject::connect(this, &QPlainTextEdit::undoAvailable, [=](bool available) { + Napi::Env env = this->emitOnNode.Env(); + Napi::HandleScope scope(env); + this->emitOnNode.Call({Napi::String::New(env, "undoAvailable"), Napi::Value::From(env, available)}); + }); } }; diff --git a/src/cpp/QtWidgets/QPlainTextEdit/qplaintextedit_wrap.cpp b/src/cpp/QtWidgets/QPlainTextEdit/qplaintextedit_wrap.cpp index 797dc031a..93645f6b5 100644 --- a/src/cpp/QtWidgets/QPlainTextEdit/qplaintextedit_wrap.cpp +++ b/src/cpp/QtWidgets/QPlainTextEdit/qplaintextedit_wrap.cpp @@ -12,9 +12,14 @@ Napi::Object QPlainTextEditWrap::init(Napi::Env env, Napi::Object exports) { char CLASSNAME[] = "QPlainTextEdit"; Napi::Function func = DefineClass(env, CLASSNAME, { InstanceMethod("setPlainText",&QPlainTextEditWrap::setPlainText), + InstanceMethod("setPlaceholderText",&QPlainTextEditWrap::setPlaceholderText), InstanceMethod("toPlainText",&QPlainTextEditWrap::toPlainText), InstanceMethod("setReadOnly", &QPlainTextEditWrap::setReadOnly), InstanceMethod("clear", &QPlainTextEditWrap::clear), + InstanceMethod("setWordWrapMode", &QPlainTextEditWrap::setWordWrapMode), + InstanceMethod("wordWrapMode", &QPlainTextEditWrap::wordWrapMode), + InstanceMethod("setLineWrapMode", &QPlainTextEditWrap::setLineWrapMode), + InstanceMethod("lineWrapMode", &QPlainTextEditWrap::lineWrapMode), QWIDGET_WRAPPED_METHODS_EXPORT_DEFINE(QPlainTextEditWrap) QABSTRACTSCROLLAREA_WRAPPED_METHODS_EXPORT_DEFINE(QPlainTextEditWrap) }); @@ -56,6 +61,14 @@ Napi::Value QPlainTextEditWrap::setPlainText(const Napi::CallbackInfo& info){ return env.Null(); } +Napi::Value QPlainTextEditWrap::setPlaceholderText(const Napi::CallbackInfo& info){ + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + Napi::String text = info[0].As(); + this->instance->setPlaceholderText(text.Utf8Value().c_str()); + return env.Null(); +} + Napi::Value QPlainTextEditWrap::setReadOnly(const Napi::CallbackInfo &info) { Napi::Env env = info.Env(); @@ -77,3 +90,33 @@ Napi::Value QPlainTextEditWrap::clear(const Napi::CallbackInfo &info){ this->instance->clear(); return env.Null(); } + +Napi::Value QPlainTextEditWrap::setWordWrapMode(const Napi::CallbackInfo &info){ + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + Napi::Number mode = info[0].As(); + this->instance->setWordWrapMode(static_cast(mode.Int32Value())); + return env.Null(); +} + +Napi::Value QPlainTextEditWrap::wordWrapMode(const Napi::CallbackInfo &info){ + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + int value = static_cast(this->instance->wordWrapMode()); + return Napi::Number::From(env, value); +} + +Napi::Value QPlainTextEditWrap::setLineWrapMode(const Napi::CallbackInfo &info){ + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + Napi::Number mode = info[0].As(); + this->instance->setLineWrapMode(static_cast(mode.Int32Value())); + return env.Null(); +} + +Napi::Value QPlainTextEditWrap::lineWrapMode(const Napi::CallbackInfo &info){ + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + int value = static_cast(this->instance->lineWrapMode()); + return Napi::Number::From(env, value); +} diff --git a/src/cpp/QtWidgets/QPlainTextEdit/qplaintextedit_wrap.h b/src/cpp/QtWidgets/QPlainTextEdit/qplaintextedit_wrap.h index b9ff9c6ce..a451c8318 100644 --- a/src/cpp/QtWidgets/QPlainTextEdit/qplaintextedit_wrap.h +++ b/src/cpp/QtWidgets/QPlainTextEdit/qplaintextedit_wrap.h @@ -20,8 +20,13 @@ class QPlainTextEditWrap : public Napi::ObjectWrap{ QABSTRACTSCROLLAREA_WRAPPED_METHODS_DECLARATION Napi::Value setPlainText(const Napi::CallbackInfo& info); + Napi::Value setPlaceholderText(const Napi::CallbackInfo& info); Napi::Value toPlainText(const Napi::CallbackInfo &info); Napi::Value setReadOnly(const Napi::CallbackInfo &info); Napi::Value clear(const Napi::CallbackInfo &info); + Napi::Value setWordWrapMode(const Napi::CallbackInfo &info); + Napi::Value wordWrapMode(const Napi::CallbackInfo &info); + Napi::Value setLineWrapMode(const Napi::CallbackInfo &info); + Napi::Value lineWrapMode(const Napi::CallbackInfo &info); }; diff --git a/src/cpp/QtWidgets/QScrollArea/qscrollarea_wrap.cpp b/src/cpp/QtWidgets/QScrollArea/qscrollarea_wrap.cpp index 5e7d330ca..2f6e5f910 100644 --- a/src/cpp/QtWidgets/QScrollArea/qscrollarea_wrap.cpp +++ b/src/cpp/QtWidgets/QScrollArea/qscrollarea_wrap.cpp @@ -46,4 +46,12 @@ Napi::Value QScrollAreaWrap::setWidget(const Napi::CallbackInfo& info) { QWidgetWrap* contentWidgetWrap = Napi::ObjectWrap::Unwrap(contentWidget); this->instance->setWidget(contentWidgetWrap->getInternalInstance()); return env.Null(); +} + +Napi::Value QScrollAreaWrap::takeWidget(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + this->instance->takeWidget(); + // We will not return the value here since we are doing it in js side anyway + return env.Null(); } \ No newline at end of file diff --git a/src/cpp/QtWidgets/QScrollArea/qscrollarea_wrap.h b/src/cpp/QtWidgets/QScrollArea/qscrollarea_wrap.h index a263962dc..26df980f9 100644 --- a/src/cpp/QtWidgets/QScrollArea/qscrollarea_wrap.h +++ b/src/cpp/QtWidgets/QScrollArea/qscrollarea_wrap.h @@ -17,6 +17,7 @@ class QScrollAreaWrap : public Napi::ObjectWrap{ static Napi::FunctionReference constructor; //wrapped methods Napi::Value setWidget(const Napi::CallbackInfo &info); + Napi::Value takeWidget(const Napi::CallbackInfo &info); QABSTRACTSCROLLAREA_WRAPPED_METHODS_DECLARATION }; diff --git a/src/cpp/QtWidgets/QWidget/qwidget_macro.h b/src/cpp/QtWidgets/QWidget/qwidget_macro.h index f0a8a3729..7e3ae6d7b 100644 --- a/src/cpp/QtWidgets/QWidget/qwidget_macro.h +++ b/src/cpp/QtWidgets/QWidget/qwidget_macro.h @@ -154,6 +154,17 @@ Napi::Value setGeometry(const Napi::CallbackInfo& info){ \ this->instance->setGeometry(x, y, width, height); \ return env.Null(); \ } \ +Napi::Value geometry(const Napi::CallbackInfo& info){ \ + Napi::Env env = info.Env(); \ + Napi::HandleScope scope(env); \ + QRect geometry = this->instance->geometry(); \ + Napi::Object geometryObj = Napi::Object::New(env); \ + geometryObj.Set("width", geometry.width()); \ + geometryObj.Set("height", geometry.height()); \ + geometryObj.Set("x", geometry.x()); \ + geometryObj.Set("y", geometry.y()); \ + return geometryObj; \ +} \ Napi::Value setMaximumSize(const Napi::CallbackInfo& info){ \ Napi::Env env = info.Env(); \ Napi::HandleScope scope(env); \ @@ -263,6 +274,7 @@ Napi::Value setWindowFlag(const Napi::CallbackInfo& info){ \ InstanceMethod("setEnabled",&WidgetWrapName::setEnabled), \ InstanceMethod("setFixedSize",&WidgetWrapName::setFixedSize), \ InstanceMethod("setGeometry",&WidgetWrapName::setGeometry), \ + InstanceMethod("geometry",&WidgetWrapName::geometry), \ InstanceMethod("setMaximumSize",&WidgetWrapName::setMaximumSize), \ InstanceMethod("setMinimumSize",&WidgetWrapName::setMinimumSize), \ InstanceMethod("repaint",&WidgetWrapName::repaint), \ diff --git a/src/cpp/core/FlexLayout/flexlayout.cpp b/src/cpp/core/FlexLayout/flexlayout.cpp index ad87c0ded..e2fd8363e 100644 --- a/src/cpp/core/FlexLayout/flexlayout.cpp +++ b/src/cpp/core/FlexLayout/flexlayout.cpp @@ -4,7 +4,7 @@ #include "spdlog/spdlog.h" #include "src/cpp/core/YogaWidget/yogawidget.h" -FlexLayout::NodeContext *FlexLayout::getNodeContext(YGNodeRef node) +FlexLayout::NodeContext* FlexLayout::getNodeContext(YGNodeRef node) { if(!node){ return nullptr; @@ -16,7 +16,6 @@ FlexLayout::NodeContext *FlexLayout::getNodeContext(YGNodeRef node) FlexLayout::FlexLayout(QWidget *parentWidget, YGNodeRef parentNode): QLayout(parentWidget) { - // spdlog::set_level(spdlog::level::off); this->node = parentNode; } @@ -58,7 +57,6 @@ QLayoutItem *FlexLayout::itemAt(int index) const YGNodeRef childNode = YGNodeGetChild(this->node, static_cast(index)); NodeContext *ctx = getNodeContext(childNode); if(!ctx){ - // spdlog::info("flexlayout: itemAt null context {}",index); return nullptr; } return ctx->item; @@ -91,14 +89,13 @@ void FlexLayout::addWidget(QWidget* childWidget, YGNodeRef childNode) spdlog::warn("Flex layout's parent yoga node not set yet. Set it using setFlexNode. Child widget will not be added to Flex Layout"); return; } - // spdlog::info("flexlayout: addWidget Object: {}",childWidget->metaObject()->className()); - uint count = YGNodeGetChildCount(this->node); YGNodeInsertChild(this->node,childNode, count); QLayoutItem* layoutItem = new QWidgetItem(childWidget); NodeContext* childContext = new NodeContext(layoutItem); YGNodeSetContext(childNode, static_cast(childContext)); QLayout::addWidget(childWidget); + this->invalidate(); } void FlexLayout::removeWidget(QWidget* childWidget, YGNodeRef childNode) @@ -114,6 +111,7 @@ void FlexLayout::removeWidget(QWidget* childWidget, YGNodeRef childNode) } YGNodeRemoveChild(this->node, childNode); QLayout::removeWidget(childWidget); + this->invalidate(); } void FlexLayout::insertChildBefore(QWidget* childWidget, YGNodeRef beforeChildNode, YGNodeRef childNode) @@ -135,14 +133,30 @@ void FlexLayout::insertChildBefore(QWidget* childWidget, YGNodeRef beforeChildNo NodeContext* childContext = new NodeContext(layoutItem); YGNodeSetContext(childNode, static_cast(childContext)); QLayout::addWidget(childWidget); + this->invalidate(); } + +YGNodeRef FlexLayout::getRootNode(YGNodeRef node){ + YGNodeRef parent = node->getOwner(); + if(!parent){ + return node; + }else { + return getRootNode(parent); + } +} + + void FlexLayout::setGeometry(const QRect &rect) { if(!this->node){ return; } - + YGNodeRef rootNode = getRootNode(this->node); + QWidget* parentWidget = this->parentWidget(); + QWidget* window = parentWidget->window(); + YGDirection direction = YGNodeStyleGetDirection(rootNode); + YGNodeCalculateLayout(rootNode,window->width(),window->height(),direction); uint count = YGNodeGetChildCount(this->node); for (uint i = 0; i < count; ++i) { diff --git a/src/cpp/core/FlexLayout/flexlayout.h b/src/cpp/core/FlexLayout/flexlayout.h index ab35ef896..bdcaf46ae 100644 --- a/src/cpp/core/FlexLayout/flexlayout.h +++ b/src/cpp/core/FlexLayout/flexlayout.h @@ -24,6 +24,7 @@ class FlexLayout: public QLayout { private: YGNodeRef node; + YGNodeRef getRootNode(YGNodeRef node); public: struct NodeContext { diff --git a/src/cpp/main.cpp b/src/cpp/main.cpp index e892eeca3..2fc36e65d 100644 --- a/src/cpp/main.cpp +++ b/src/cpp/main.cpp @@ -1,4 +1,5 @@ #include "src/cpp/QtGui/QApplication/qapplication_wrap.h" +#include "src/cpp/QtGui/QClipboard/qclipboard_wrap.h" #include "src/cpp/QtWidgets/QWidget/qwidget_wrap.h" #include "src/cpp/QtGui/QPixmap/qpixmap_wrap.h" #include "src/cpp/QtGui/QIcon/qicon_wrap.h" @@ -28,6 +29,7 @@ void InitPrivateHelpers(Napi::Env env){ Napi::Object Main(Napi::Env env, Napi::Object exports) { InitPrivateHelpers(env); QApplicationWrap::init(env, exports); + QClipboardWrap::init(env, exports); QWidgetWrap::init(env, exports); QPixmapWrap::init(env, exports); QIconWrap::init(env, exports); diff --git a/src/demo.ts b/src/demo.ts index 0aa725caa..ff0bbe855 100644 --- a/src/demo.ts +++ b/src/demo.ts @@ -12,11 +12,17 @@ import { QDial, QPlainTextEdit, QTabWidget, - QGridLayout + QGridLayout, + QScrollArea, + QPixmap, + CursorShape, + WindowState, + QTextOptionWrapMode, + QApplication, + QClipboardMode, + QCheckBoxEvents } from "./index"; -import { QScrollArea } from "./lib/QtWidgets/QScrollArea"; -import { QPixmap } from "./lib/QtGui/QPixmap"; -import { CursorShape, WindowState } from "./lib/QtEnums" + import { QTabWidgetEvents } from "./lib/QtWidgets/QTabWidget"; const path = require("path"); @@ -37,6 +43,9 @@ const checkbox = new QCheckBox(); checkbox.setText("Check me out?"); checkbox.setObjectName("check"); checkbox.setChecked(true); +checkbox.addEventListener(QCheckBoxEvents.toggled, () => { + console.log('checkbox was toggled!'); +}) const dial = new QDial(); checkbox.setObjectName("dial"); @@ -49,6 +58,15 @@ const button = new QPushButton(); button.setText("Push Push Push!"); button.setObjectName("btn"); button.setFlat(true); +button.addEventListener("clicked", () => { + const clipboard = QApplication.clipboard(); + console.log("clipboard: ", clipboard.text(QClipboardMode.Clipboard)); + clipboard.setText("yooooo", QClipboardMode.Clipboard); + if (rootView.layout) { + (rootView.layout as FlexLayout).removeWidget(dial); + } + label.setInlineStyle("color:blue;"); +}); const nodeguiLogo = new QIcon( path.resolve(__dirname, "../extras/assets/nodegui.png") @@ -88,6 +106,7 @@ 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%';"); @@ -123,8 +142,7 @@ win.setStyleSheet(` win.setWindowIcon(nodeguiLogo); win.setWindowTitle("NodeGUI Demo"); - -win.resize(400, 500); +win.resize(400, 700); win.show(); win.setWindowState(WindowState.WindowActive); diff --git a/src/index.ts b/src/index.ts index 9e90c35d7..96c04202f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,10 +1,14 @@ +// bootstrap +import "./lib/core/bootstrap"; // Enums: export * from "./lib/QtEnums"; // Gui: export { QApplication } from "./lib/QtGui/QApplication"; export { QPixmap } from "./lib/QtGui/QPixmap"; -export { QIcon } from "./lib/QtGui/QIcon"; +export { QIcon, QIconMode, QIconState } from "./lib/QtGui/QIcon"; export { QCursor } from "./lib/QtGui/QCursor"; +export { QTextOptionWrapMode } from "./lib/QtGui/QTextOption"; +export { QClipboard, QClipboardMode } from "./lib/QtGui/QClipboard"; // Events: Maybe a separate module ? export { QKeyEvent } from "./lib/QtGui/QEvent/QKeyEvent"; export { NativeEvent } from "./lib/core/EventWidget"; @@ -27,7 +31,8 @@ export { QRadioButton, QRadioButtonEvents } from "./lib/QtWidgets/QRadioButton"; export { QTabWidget, QTabWidgetEvents } from "./lib/QtWidgets/QTabWidget"; export { QPlainTextEdit, - QPlainTextEditEvents + QPlainTextEditEvents, + LineWrapMode } from "./lib/QtWidgets/QPlainTextEdit"; export { QScrollArea, QScrollAreaEvents } from "./lib/QtWidgets/QScrollArea"; // Layouts: diff --git a/src/lib/QtGui/QApplication/index.ts b/src/lib/QtGui/QApplication/index.ts index 67ed6a616..cca01ed5c 100644 --- a/src/lib/QtGui/QApplication/index.ts +++ b/src/lib/QtGui/QApplication/index.ts @@ -1,6 +1,7 @@ import addon from "../../core/addon"; import { Component, NativeElement } from "../../core/Component"; import { checkIfNativeElement } from "../../utils"; +import { QClipboard } from "../QClipboard"; type arg = NativeElement; export class QApplication extends Component { @@ -13,6 +14,9 @@ export class QApplication extends Component { this.native = new addon.QApplication(); } } + static clipboard = (): QClipboard => { + return new QClipboard(addon.QApplication.clipboard()); + }; processEvents = () => { this.native.processEvents(); }; diff --git a/src/lib/QtGui/QClipboard/index.ts b/src/lib/QtGui/QClipboard/index.ts new file mode 100644 index 000000000..e2a871325 --- /dev/null +++ b/src/lib/QtGui/QClipboard/index.ts @@ -0,0 +1,31 @@ +import { Component, NativeElement } from "../../core/Component"; +import { checkIfNativeElement } from "../../utils"; + +export class QClipboard extends Component { + native: NativeElement; + constructor(native: NativeElement) { + super(); + if (checkIfNativeElement(native)) { + this.native = native; + } else { + throw new Error( + "QClipboard cannot be initialised this way. Use QApplication::clipboard()" + ); + } + } + clear = (mode: QClipboardMode) => { + this.native.clear(mode); + }; + setText = (text: string, mode: QClipboardMode) => { + this.native.setText(text, mode); + }; + text = (mode: QClipboardMode): string => { + return this.native.text(mode); + }; +} + +export enum QClipboardMode { + Clipboard, + Selection, + FindBuffer +} diff --git a/src/lib/QtGui/QIcon/index.ts b/src/lib/QtGui/QIcon/index.ts index 70c1de6a2..b35fe6351 100644 --- a/src/lib/QtGui/QIcon/index.ts +++ b/src/lib/QtGui/QIcon/index.ts @@ -1,5 +1,17 @@ import addon from "../../core/addon"; import { Component, NativeElement } from "../../core/Component"; +import { QPixmap } from "../../QtGui/QPixmap"; + +export enum QIconMode { + Normal, + Disabled, + Active, + Selected +} +export enum QIconState { + Off, + On +} type arg = string | NativeElement; export class QIcon extends Component { @@ -13,4 +25,20 @@ export class QIcon extends Component { this.native = new addon.QIcon(); } } + pixmap = ( + width: number, + height: number, + mode?: QIconMode, + state?: QIconState + ): QPixmap => { + let nativePixmap; + if (mode && state) { + nativePixmap = this.native.pixmap(width, height, mode, state); + } else if (mode) { + nativePixmap = this.native.pixmap(width, height, mode); + } else { + nativePixmap = this.native.pixmap(width, height); + } + return new QPixmap(nativePixmap); + }; } diff --git a/src/lib/QtGui/QTextOption/index.ts b/src/lib/QtGui/QTextOption/index.ts new file mode 100644 index 000000000..184df95fd --- /dev/null +++ b/src/lib/QtGui/QTextOption/index.ts @@ -0,0 +1,7 @@ +export enum QTextOptionWrapMode { + NoWrap, + WordWrap, + ManualWrap, + WrapAnywhere, + WrapAtWordBoundaryOrAnywhere +} diff --git a/src/lib/QtWidgets/QCheckBox/index.ts b/src/lib/QtWidgets/QCheckBox/index.ts index 7d739c06d..29b6b8127 100644 --- a/src/lib/QtWidgets/QCheckBox/index.ts +++ b/src/lib/QtWidgets/QCheckBox/index.ts @@ -4,7 +4,8 @@ import { BaseWidgetEvents } from "../../core/EventWidget"; import { NativeElement } from "../../core/Component"; export const QCheckBoxEvents = Object.freeze({ - ...BaseWidgetEvents + ...BaseWidgetEvents, + toggled: "toggled" }); export class QCheckBox extends NodeWidget { native: NativeElement; diff --git a/src/lib/QtWidgets/QMainWindow/index.ts b/src/lib/QtWidgets/QMainWindow/index.ts index fdc9b889f..34e36d51f 100644 --- a/src/lib/QtWidgets/QMainWindow/index.ts +++ b/src/lib/QtWidgets/QMainWindow/index.ts @@ -32,6 +32,7 @@ export class QMainWindow extends NodeWidget { }; } setCentralWidget(widget: NodeWidget) { + // react:βœ“ this.native.setCentralWidget(widget.native, widget.getFlexNode()); this.centralWidget = widget; } diff --git a/src/lib/QtWidgets/QPlainTextEdit/index.ts b/src/lib/QtWidgets/QPlainTextEdit/index.ts index b87994b01..65f2d9e57 100644 --- a/src/lib/QtWidgets/QPlainTextEdit/index.ts +++ b/src/lib/QtWidgets/QPlainTextEdit/index.ts @@ -3,14 +3,27 @@ import { NodeWidget } from "../QWidget"; import { BaseWidgetEvents } from "../../core/EventWidget"; import { NativeElement } from "../../core/Component"; import { QAbstractScrollArea } from "../QAbstractScrollArea"; +import { QTextOptionWrapMode } from "../../QtGui/QTextOption"; export const QPlainTextEditEvents = Object.freeze({ ...BaseWidgetEvents, - textChanged: "textChanged" + textChanged: "textChanged", + blockCountChanged: "blockCountChanged", + copyAvailable: "copyAvailable", + cursorPositionChanged: "cursorPositionChanged", + modificationChanged: "modificationChanged", + redoAvailable: "redoAvailable", + selectionChanged: "selectionChanged", + undoAvailable: "undoAvailable" }); +export enum LineWrapMode { + NoWrap, + WidgetWidth +} export class QPlainTextEdit extends QAbstractScrollArea { native: NativeElement; + placeholderText?: string; constructor(parent?: NodeWidget) { let native; if (parent) { @@ -23,15 +36,25 @@ export class QPlainTextEdit extends QAbstractScrollArea { this.parent = parent; // bind member functions this.setPlainText.bind(this); + this.setPlaceholderText.bind(this); this.toPlainText.bind(this); this.setReadOnly.bind(this); this.clear.bind(this); + this.setWordWrapMode.bind(this); + this.wordWrapMode.bind(this); + this.setLineWrapMode.bind(this); + this.lineWrapMode.bind(this); } setPlainText(text: string | number) { // react:βœ“ this.native.setPlainText(`${text}`); } - toPlainText() { + setPlaceholderText(text: string) { + // react:βœ“, //TODO:getter + this.placeholderText = text; + this.native.setPlaceholderText(text); + } + toPlainText(): string { // react:βœ“ return this.native.toPlainText(); } @@ -43,4 +66,16 @@ export class QPlainTextEdit extends QAbstractScrollArea { // react:βœ“ this.native.clear(); } + setWordWrapMode(mode: QTextOptionWrapMode) { + this.native.setWordWrapMode(mode); + } + wordWrapMode(): QTextOptionWrapMode { + return this.native.wordWrapMode(); + } + setLineWrapMode(mode: LineWrapMode) { + this.native.setLineWrapMode(mode); + } + lineWrapMode(): LineWrapMode { + return this.native.lineWrapMode(); + } } diff --git a/src/lib/QtWidgets/QPushButton/index.ts b/src/lib/QtWidgets/QPushButton/index.ts index 4145c70d9..926aeeabd 100644 --- a/src/lib/QtWidgets/QPushButton/index.ts +++ b/src/lib/QtWidgets/QPushButton/index.ts @@ -28,16 +28,16 @@ export class QPushButton extends NodeWidget { this.setText.bind(this); this.setFlat.bind(this); } - setText(text: string | number) { + // react:βœ“, //TODO:getter this.native.setText(`${text}`); } - setFlat(isFlat: boolean) { + // react:βœ“, //TODO:getter this.native.setFlat(isFlat); } - setIcon(icon: QIcon) { + // react:βœ“, //TODO:getter this.native.setIcon(icon.native); } } diff --git a/src/lib/QtWidgets/QScrollArea/index.ts b/src/lib/QtWidgets/QScrollArea/index.ts index 1324f1caf..0859b366c 100644 --- a/src/lib/QtWidgets/QScrollArea/index.ts +++ b/src/lib/QtWidgets/QScrollArea/index.ts @@ -9,7 +9,7 @@ export const QScrollAreaEvents = Object.freeze({ }); export class QScrollArea extends QAbstractScrollArea { native: NativeElement; - contentWidget?: NodeWidget; + contentWidget?: NodeWidget | null; constructor(parent?: NodeWidget) { let native; if (parent) { @@ -21,9 +21,22 @@ export class QScrollArea extends QAbstractScrollArea { this.native = native; this.parent = parent; // bind member functions + this.setWidget.bind(this); + this.takeWidget.bind(this); } setWidget(widget: NodeWidget) { + // react:βœ“, //TODO:getter this.contentWidget = widget; this.native.setWidget(widget.native); } + takeWidget(): NodeWidget | null { + // react:βœ“ + const contentWidget = this.contentWidget; + this.contentWidget = null; + if (contentWidget) { + this.native.takeWidget(); + return contentWidget; + } + return null; + } } diff --git a/src/lib/QtWidgets/QWidget/index.ts b/src/lib/QtWidgets/QWidget/index.ts index 7349cea40..48caf7163 100644 --- a/src/lib/QtWidgets/QWidget/index.ts +++ b/src/lib/QtWidgets/QWidget/index.ts @@ -45,9 +45,13 @@ export abstract class NodeWidget extends EventWidget { await applyStyleSheet(this, preparedSheet); }; setGeometry = (x: number, y: number, w: number, h: number) => { - // react:βœ“, //TODO:getter + // react:βœ“ this.native.setGeometry(x, y, w, h); }; + geometry = (): Rect => { + // react:βœ“ + return this.native.geometry(); + }; setObjectName = (objectName: string) => { // react:βœ“ this.native.setObjectName(objectName); @@ -148,6 +152,12 @@ export abstract class NodeWidget extends EventWidget { }; } +type Rect = { + x: number; + y: number; + width: number; + height: number; +}; type arg = NodeWidget | NativeElement; export class QWidget extends NodeWidget { diff --git a/src/lib/core/Style/StyleSheet.ts b/src/lib/core/Style/StyleSheet.ts index 17ee60168..af070849e 100644 --- a/src/lib/core/Style/StyleSheet.ts +++ b/src/lib/core/Style/StyleSheet.ts @@ -19,7 +19,7 @@ export const prepareInlineStyleSheet = async ( rawStyle: string ) => { const inlineStyle = await StyleSheet.create(rawStyle); - // Make sure to not calculate ObjectName is the same pass of event loop as other props (incase of react) since the order will matter in that case + // Make sure to not calculate ObjectName in the same pass of event loop as other props (incase of react) since the order will matter in that case // So doing it in multiple passes of event loop allows objectName to be set before using it. The above await solves it. let cssId = widget.objectName(); if (!cssId) { @@ -38,7 +38,5 @@ export const applyStyleSheet = async ( styleSheet: string ) => { widget.native.setStyleSheet(styleSheet); - setTimeout(() => { - widget.layout ? widget.layout.update() : widget.update(); - }, 20); + widget.layout ? widget.layout.update() : widget.update(); }; diff --git a/src/lib/core/bootstrap.ts b/src/lib/core/bootstrap.ts new file mode 100644 index 000000000..6ce6a9637 --- /dev/null +++ b/src/lib/core/bootstrap.ts @@ -0,0 +1,26 @@ +/* + From: https://github.com/yue/yode/blob/master/src/bootstrap.js + setImmediate and process.nextTick makes use of uv_check and uv_prepare to + run the callbacks, however since we only run uv loop on requests, the + callbacks wouldn't be called until something else activated the uv loop, + which would delay the callbacks for arbitrary long time. So we should + initiatively activate the uv loop once setImmediate and process.nextTick is + called. + This is required inorder to make the timers work nicely due to merger of event loops +*/ +function wrapWithActivateUvLoop(func: any) { + return function() { + (process as any).activateUvLoop(); + //@ts-ignore + return func.apply(this, arguments); + }; +} + +const main = () => { + process.nextTick = wrapWithActivateUvLoop(process.nextTick); + global.setImmediate = wrapWithActivateUvLoop(global.setImmediate); + global.setTimeout = wrapWithActivateUvLoop(global.setTimeout); + global.setInterval = wrapWithActivateUvLoop(global.setInterval); +}; + +main();