diff --git a/examples/calculator/README.md b/examples/calculator/README.md new file mode 100644 index 000000000..3c67c6d0d --- /dev/null +++ b/examples/calculator/README.md @@ -0,0 +1,7 @@ +# Calculator app + +This example showcases how to build a basic mac calculator clone. + +The app should look like this: + +![demo](https://github.com/master-atul/node-native-ui/raw/master/examples/calculator/calculator.png "Calculator screenshot") diff --git a/examples/calculator/calculator.png b/examples/calculator/calculator.png new file mode 100644 index 000000000..c3e146d89 Binary files /dev/null and b/examples/calculator/calculator.png differ diff --git a/examples/calculator/index.ts b/examples/calculator/index.ts new file mode 100644 index 000000000..042a859c4 --- /dev/null +++ b/examples/calculator/index.ts @@ -0,0 +1,119 @@ +import { QMainWindow } from "../../src/lib/QtWidgets/QMainWindow"; +import { QWidget } from "../../src/lib/QtGui/QWidget"; +import { FlexLayout } from "../../src/lib/core/FlexLayout"; +import { QPushButton } from "../../src/lib/QtWidgets/QPushButton"; +import { QLabel } from "../../src/lib/QtWidgets/QLabel"; + +const globals = global as any; + +// Main Window +const win = new QMainWindow(); + +win.resize(400, 600); + +const getButton = ( + label: string, + value: number | string, + type: "value" | "command" +) => { + const button = new QPushButton(); + button.setText(label); + return { + ui: button, + value, + type + }; +}; + +// Top Row +const row0 = new QWidget(); +win.setCentralWidget(row0); + +row0.setStyleSheet(` + qproperty-flex: 1; +`); +const btnClear = getButton("AC", "AC", "command"); +const resultText = new QLabel(); +resultText.setText(0); + +const row0Layout = new FlexLayout(); +row0Layout.setFlexNode(row0.getFlexNode()); + +row0Layout.addWidget(btnClear.ui, btnClear.ui.getFlexNode()); +row0Layout.addWidget(resultText, resultText.getFlexNode()); +row0.setLayout(row0Layout); + +// // First Row +// const btn7 = getButton("7", 7, "value"); +// const btn8 = getButton("8", 8, "value"); +// const btn9 = getButton("9", 9, "value"); +// const btnDivide = getButton("/", "/", "command"); +// const row1 = new QWidget(); +// const row1Layout = new FlexLayout(); +// row1.setLayout(row1Layout); +// row1Layout.setFlexNode(row1.getFlexNode()); +// row1Layout.addWidget(btn7.ui, btn7.ui.getFlexNode()); +// row1Layout.addWidget(btn8.ui, btn8.ui.getFlexNode()); +// row1Layout.addWidget(btn9.ui, btn9.ui.getFlexNode()); +// row1Layout.addWidget(btnDivide.ui, btnDivide.ui.getFlexNode()); + +// // Second Row +// const btn4 = getButton("4", 4, "value"); +// const btn5 = getButton("5", 5, "value"); +// const btn6 = getButton("6", 6, "value"); +// const btnMultiply = getButton("x", "*", "command"); +// const row2 = new QWidget(); +// const row2Layout = new FlexLayout(); +// row2.setLayout(row2Layout); +// row2Layout.setFlexNode(row2.getFlexNode()); +// row2Layout.addWidget(btn4.ui, btn4.ui.getFlexNode()); +// row2Layout.addWidget(btn5.ui, btn5.ui.getFlexNode()); +// row2Layout.addWidget(btn6.ui, btn6.ui.getFlexNode()); +// row2Layout.addWidget(btnMultiply.ui, btnMultiply.ui.getFlexNode()); + +// // Third Row +// const btn1 = getButton("1", 1, "value"); +// const btn2 = getButton("2", 2, "value"); +// const btn3 = getButton("3", 3, "value"); +// const btnMinus = getButton("-", "-", "command"); +// const row3 = new QWidget(); +// const row3Layout = new FlexLayout(); +// row3.setLayout(row3Layout); +// row3Layout.setFlexNode(row3.getFlexNode()); +// row3Layout.addWidget(btn1.ui, btn1.ui.getFlexNode()); +// row3Layout.addWidget(btn2.ui, btn2.ui.getFlexNode()); +// row3Layout.addWidget(btn3.ui, btn3.ui.getFlexNode()); +// row3Layout.addWidget(btnMinus.ui, btnMinus.ui.getFlexNode()); + +// // Fourth Row +// const btn0 = getButton("0", 0, "value"); +// const btnDot = getButton(".", ".", "command"); +// const btnEquals = getButton("=", "=", "command"); +// const btnPlus = getButton("+", "+", "command"); +// const row4 = new QWidget(); +// const row4Layout = new FlexLayout(); +// row4.setLayout(row4Layout); +// row4Layout.setFlexNode(row4.getFlexNode()); +// row4Layout.addWidget(btn0.ui, btn0.ui.getFlexNode()); +// row4Layout.addWidget(btnDot.ui, btnDot.ui.getFlexNode()); +// row4Layout.addWidget(btnEquals.ui, btnEquals.ui.getFlexNode()); +// row4Layout.addWidget(btnPlus.ui, btnPlus.ui.getFlexNode()); + +// Root view +// const rootView = new QWidget(); +// rootView.setStyleSheet( +// ` +// qproperty-flex: 1; +// ` +// ); +// const rootViewFlexLayout = new FlexLayout(); +// rootViewFlexLayout.setFlexNode(rootView.getFlexNode()); +// rootView.setLayout(rootViewFlexLayout); +// rootViewFlexLayout.addWidget(row0, row0.getFlexNode()); +// rootViewFlexLayout.addWidget(row1, row1.getFlexNode()); +// rootViewFlexLayout.addWidget(row2, row2.getFlexNode()); +// rootViewFlexLayout.addWidget(row3, row3.getFlexNode()); + +win.show(); + +globals.win = win; //to keep gc from collecting ui widgets diff --git a/package.json b/package.json index e48562c33..56556a8d8 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "typescript": "^3.4.5" }, "scripts": { + "build": "npm run build:addon&&npm run build:lib", "build:lib": "tsc", "build:addon": "node-gyp -j 8 build", "rebuild:addon": "node-gyp -j 8 rebuild", diff --git a/src/cpp/Extras/Utils/utils.cpp b/src/cpp/Extras/Utils/utils.cpp index 492e68344..a724f14ac 100644 --- a/src/cpp/Extras/Utils/utils.cpp +++ b/src/cpp/Extras/Utils/utils.cpp @@ -1,8 +1,26 @@ #include "utils.h" -#include +#include +#include +#include "deps/spdlog/spdlog.h" void extrautils::noop(){} void extrautils::throwTypeError(Napi::Env env, std::string errorMessage){ Napi::TypeError::New(env, errorMessage.c_str()).ThrowAsJavaScriptException(); } + +YGSize extrautils::measureQtWidget (YGNodeRef node, float width, YGMeasureMode widthMode, float height, YGMeasureMode heightMode){ + FlexLayout::NodeContext *ctx = FlexLayout::getNodeContext(node); + if(ctx){ + QLayoutItem* childLayoutItem = ctx->item; + QWidget* widget = childLayoutItem->widget(); + if(widget){ + QSize size = widget->sizeHint(); + return YGSize{ + .height = static_cast(size.height()), + .width = static_cast(size.width()) + }; + } + } + return YGSize{ .height = 0, .width = 0 }; +} \ No newline at end of file diff --git a/src/cpp/Extras/Utils/utils.h b/src/cpp/Extras/Utils/utils.h index 554a5a10f..f11be68be 100644 --- a/src/cpp/Extras/Utils/utils.h +++ b/src/cpp/Extras/Utils/utils.h @@ -1,11 +1,11 @@ -#ifndef EXTRAUTILS_WRAP_H -#define EXTRAUTILS_WRAP_H +#pragma once #include +#include "src/cpp/core/FlexLayout/flexlayout.h" namespace extrautils { void noop(); void throwTypeError(Napi::Env env, std::string errorMessage); + YGSize measureQtWidget (YGNodeRef node, float width, YGMeasureMode widthMode, float height, YGMeasureMode heightMode); } -#endif \ No newline at end of file diff --git a/src/cpp/QtWidgets/QLabel/qlabel_wrap.cpp b/src/cpp/QtWidgets/QLabel/qlabel_wrap.cpp index 173e763c6..4f121e021 100644 --- a/src/cpp/QtWidgets/QLabel/qlabel_wrap.cpp +++ b/src/cpp/QtWidgets/QLabel/qlabel_wrap.cpp @@ -39,6 +39,8 @@ QLabelWrap::QLabelWrap(const Napi::CallbackInfo& info): Napi::ObjectWrapinstance->getFlexNode(), &extrautils::measureQtWidget); } QLabelWrap::~QLabelWrap() { diff --git a/src/cpp/QtWidgets/QLabel/qlabel_wrap.h b/src/cpp/QtWidgets/QLabel/qlabel_wrap.h index 93387ca89..c0b6af3a8 100644 --- a/src/cpp/QtWidgets/QLabel/qlabel_wrap.h +++ b/src/cpp/QtWidgets/QLabel/qlabel_wrap.h @@ -21,7 +21,7 @@ class QLabelWrap : public Napi::ObjectWrap{ Napi::Value text(const Napi::CallbackInfo &info); QWIDGET_WRAPPED_METHODS_DECLARATION -YOGAWIDGET_WRAPPED_METHODS_DECLARATION + YOGAWIDGET_WRAPPED_METHODS_DECLARATION }; diff --git a/src/cpp/QtWidgets/QPushButton/qpushbutton_wrap.cpp b/src/cpp/QtWidgets/QPushButton/qpushbutton_wrap.cpp index 316bb0b75..35b1152e7 100644 --- a/src/cpp/QtWidgets/QPushButton/qpushbutton_wrap.cpp +++ b/src/cpp/QtWidgets/QPushButton/qpushbutton_wrap.cpp @@ -35,6 +35,8 @@ QPushButtonWrap::QPushButtonWrap(const Napi::CallbackInfo& info): Napi::ObjectWr }else { extrautils::throwTypeError(env, "Wrong number of arguments"); } + // Adds measure function on yoga node so that widget size is calculated based on its text also. + YGNodeSetMeasureFunc(this->instance->getFlexNode(), &extrautils::measureQtWidget); } QPushButtonWrap::~QPushButtonWrap() { diff --git a/src/cpp/QtWidgets/QPushButton/qpushbutton_wrap.h b/src/cpp/QtWidgets/QPushButton/qpushbutton_wrap.h index d0b21541f..da77bcee8 100644 --- a/src/cpp/QtWidgets/QPushButton/qpushbutton_wrap.h +++ b/src/cpp/QtWidgets/QPushButton/qpushbutton_wrap.h @@ -5,6 +5,7 @@ #include "npushbutton.h" #include "src/cpp/QtGui/QWidget/qwidget_macro.h" #include "src/cpp/core/YogaWidget/yogawidget_macro.h" +#include "src/cpp/Extras/Utils/utils.h" class QPushButtonWrap : public Napi::ObjectWrap { private: diff --git a/src/cpp/core/FlexLayout/flexlayout.cpp b/src/cpp/core/FlexLayout/flexlayout.cpp index 46587db04..c95f1dff9 100644 --- a/src/cpp/core/FlexLayout/flexlayout.cpp +++ b/src/cpp/core/FlexLayout/flexlayout.cpp @@ -3,7 +3,7 @@ #include #include "spdlog/spdlog.h" -FlexLayout::NodeContext *FlexLayout::getNodeContext(YGNodeRef node) const +FlexLayout::NodeContext *FlexLayout::getNodeContext(YGNodeRef node) { if(!node){ return nullptr; @@ -122,6 +122,7 @@ void FlexLayout::setGeometry(const QRect &rect) QWidget* childWidget = childLayoutItem->widget(); if(childWidget){ childWidget->setGeometry(childRect); + spdlog::info("Object: {}, rect: w:{}, h:{}, l:{}, t:{}",childWidget->metaObject()->className(),width,height,left,top); }else { childLayoutItem->setGeometry(childRect); } diff --git a/src/cpp/core/FlexLayout/flexlayout.h b/src/cpp/core/FlexLayout/flexlayout.h index ee42aab8f..4ca12923c 100644 --- a/src/cpp/core/FlexLayout/flexlayout.h +++ b/src/cpp/core/FlexLayout/flexlayout.h @@ -1,5 +1,4 @@ -#ifndef FLEXLAYOUT_H -#define FLEXLAYOUT_H +#pragma once #include "deps/yoga/YGNode.h" #include @@ -25,6 +24,7 @@ class FlexLayout: public QLayout { private: YGNodeRef node; +public: struct NodeContext { NodeContext(QLayoutItem *i) { @@ -32,9 +32,6 @@ private: } QLayoutItem *item; }; - NodeContext* getNodeContext(YGNodeRef node) const; - -public: FlexLayout(QWidget* parentWidget=nullptr, YGNodeRef parentNode=nullptr); ~FlexLayout() override; QSize sizeHint() const override; @@ -45,6 +42,6 @@ public: void addWidget(QWidget* childWidget, YGNodeRef childNode); void setGeometry(const QRect &rect) override; void setFlexNode(YGNodeRef parentNode); + static NodeContext* getNodeContext(YGNodeRef node); }; -#endif // FLEXLAYOUT_H