Adds dynamic content support for flex layout. (#199)

* layout performance fix

* adjust size, webdefaults

* Add flexlayout owner to the context

* revert

* reorders setlayout and setflexnode

* add defaults

* fix mainwindow rootNode

* properties to methods

* Adds defaultsAdds flexutils  measurewidget

* introduce configure for all types of nodes.

* lint fix

* Revams flexlayout to handle dynamic content

* Adds dynamic layout support for flex layout.

* lint fix

* fix few code updates
This commit is contained in:
Atul R 2019-11-18 00:52:29 +01:00 committed by GitHub
parent 1c1cea7a3b
commit edcdbb510e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
53 changed files with 390 additions and 315 deletions

View File

@ -15,6 +15,7 @@ add_library(${CORE_WIDGETS_ADDON} SHARED
"${PROJECT_SOURCE_DIR}/src/cpp/main.cpp"
# core internals
"${PROJECT_SOURCE_DIR}/src/cpp/lib/Extras/Utils/nutils.cpp"
"${PROJECT_SOURCE_DIR}/src/cpp/lib/core/FlexLayout/flexutils.cpp"
"${PROJECT_SOURCE_DIR}/src/cpp/lib/core/FlexLayout/flexlayout.cpp"
"${PROJECT_SOURCE_DIR}/src/cpp/lib/core/FlexLayout/flexitem.cpp"
"${PROJECT_SOURCE_DIR}/src/cpp/lib/core/YogaWidget/nodestyle.cpp"

View File

@ -6,16 +6,16 @@
#include <QVariant>
#include "core/FlexLayout/flexlayout.h"
#include "core/FlexLayout/flexutils.h"
namespace extrautils {
YGSize measureQtWidget(YGNodeRef node, float width, YGMeasureMode widthMode,
float height, YGMeasureMode heightMode);
QVariant* convertToQVariant(Napi::Env& env, Napi::Value& value);
bool isNapiValueInt(Napi::Env& env, Napi::Value& num);
std::string getNapiObjectClassName(Napi::Object& object);
void* configureQWidget(QWidget* widget, YGNodeRef node,
bool isLeafNode = false);
void* configureQObject(QObject* object);
void* configureComponent(void* component);
template <typename T>
void safeDelete(QPointer<T>& component) {

View File

@ -25,6 +25,5 @@ class QMouseEventWrap : public Napi::ObjectWrap<QMouseEventWrap> {
Napi::Value globalX(const Napi::CallbackInfo& info);
Napi::Value globalY(const Napi::CallbackInfo& info);
COMPONENT_WRAPPED_METHODS_DECLARATION
};

View File

@ -1,7 +1,6 @@
#pragma once
#include <napi.h>
#include <stdlib.h>
#include <QPointer>
@ -13,6 +12,7 @@ class QLabelWrap : public Napi::ObjectWrap<QLabelWrap> {
QPointer<NLabel> instance;
public:
QWIDGET_WRAPPED_METHODS_DECLARATION
static Napi::Object init(Napi::Env env, Napi::Object exports);
QLabelWrap(const Napi::CallbackInfo& info);
~QLabelWrap();
@ -26,6 +26,4 @@ class QLabelWrap : public Napi::ObjectWrap<QLabelWrap> {
Napi::Value text(const Napi::CallbackInfo& info);
Napi::Value setPixmap(const Napi::CallbackInfo& info);
Napi::Value setOpenExternalLinks(const Napi::CallbackInfo& info);
QWIDGET_WRAPPED_METHODS_DECLARATION
};

View File

@ -11,6 +11,7 @@
class QScrollAreaWrap : public Napi::ObjectWrap<QScrollAreaWrap> {
private:
QPointer<NScrollArea> instance;
YGNodeRef scrollNode;
public:
static Napi::Object init(Napi::Env env, Napi::Object exports);

View File

@ -262,6 +262,12 @@
this->instance->setWindowFlag(static_cast<Qt::WindowType>(windowType), \
switchOn); \
return env.Null(); \
} \
Napi::Value adjustSize(const Napi::CallbackInfo& info) { \
Napi::Env env = info.Env(); \
Napi::HandleScope scope(env); \
this->instance->adjustSize(); \
return env.Null(); \
}
#endif // QWIDGET_WRAPPED_METHODS_DECLARATION
@ -302,6 +308,7 @@
InstanceMethod("testAttribute", &WidgetWrapName::testAttribute), \
InstanceMethod("setWindowOpacity", &WidgetWrapName::setWindowOpacity), \
InstanceMethod("windowOpacity", &WidgetWrapName::windowOpacity), \
InstanceMethod("setWindowFlag", &WidgetWrapName::setWindowFlag),
InstanceMethod("setWindowFlag", &WidgetWrapName::setWindowFlag), \
InstanceMethod("adjustSize", &WidgetWrapName::adjustSize),
#endif // QWIDGET_WRAPPED_METHODS_EXPORT_DEFINE

View File

@ -21,13 +21,9 @@ YGNodeNew(); FlexLayout * flayout = new FlexLayout(container,root);
class FlexLayout : public QLayout, public EventWidget {
private:
YGNodeRef node;
YGNodeRef getRootNode(YGNodeRef node);
YGNodeRef getRootNode(YGNodeRef node) const;
public:
struct NodeContext {
NodeContext(QLayoutItem *i) { item = i; }
QLayoutItem *item;
};
FlexLayout(QWidget *parentWidget = nullptr, YGNodeRef parentNode = nullptr);
~FlexLayout() override;
QSize sizeHint() const override;
@ -41,7 +37,6 @@ class FlexLayout : public QLayout, public EventWidget {
void removeWidget(QWidget *childWidget, YGNodeRef childNode);
void setGeometry(const QRect &rect) override;
void setFlexNode(YGNodeRef parentNode);
static NodeContext *getNodeContext(YGNodeRef node);
EVENTWIDGET_IMPLEMENTATIONS(FlexLayout)
};

View File

@ -0,0 +1,27 @@
#pragma once
#include <QLayoutItem>
#include <QWidget>
#include "deps/yoga/YGNode.h"
class FlexNodeContext {
void* _widget;
QLayoutItem* _layoutItem;
public:
FlexNodeContext(void* widget);
QWidget* widget();
QLayoutItem* layoutItem();
void setLayoutItem(QLayoutItem* item);
};
namespace flexutils {
YGSize measureQtWidget(YGNodeRef node, float width, YGMeasureMode widthMode,
float height, YGMeasureMode heightMode);
const QRect getFlexNodeGeometry(YGNodeRef node);
void setFlexNodeGeometry(YGNodeRef node, const QRect& geometry);
FlexNodeContext* getFlexNodeContext(YGNodeRef node);
void configureFlexNode(QWidget* widget, YGNodeRef node,
bool isLeafNode = false);
} // namespace flexutils

View File

@ -5,28 +5,7 @@
#include <string>
#include "core/Component/component_wrap.h"
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();
float width = 0.0;
float height = 0.0;
if (widget) {
QSize size = widget->sizeHint();
width = static_cast<float>(size.width());
height = static_cast<float>(size.height());
return YGSize{
width,
height,
};
}
}
return YGSize{width, height};
}
#include "core/FlexLayout/flexutils.h"
bool extrautils::isNapiValueInt(Napi::Env& env, Napi::Value& num) {
return env.Global()
@ -96,3 +75,13 @@ QVariant* extrautils::convertToQVariant(Napi::Env& env, Napi::Value& value) {
return new QVariant();
}
}
void* extrautils::configureComponent(void* component) { return component; }
void* extrautils::configureQObject(QObject* object) {
return configureComponent(object);
}
void* extrautils::configureQWidget(QWidget* widget, YGNodeRef node,
bool isLeafNode) {
flexutils::configureFlexNode(widget, node, isLeafNode);
return configureQObject(widget);
}

View File

@ -37,5 +37,5 @@ QObjectWrap::QObjectWrap(const Napi::CallbackInfo& info)
Napi::TypeError::New(env, "Wrong number of arguments")
.ThrowAsJavaScriptException();
}
this->rawData = this->getInternalInstance();
this->rawData = extrautils::configureQObject(this->getInternalInstance());
}

View File

@ -39,7 +39,7 @@ QSizeWrap::QSizeWrap(const Napi::CallbackInfo& info)
Napi::TypeError::New(env, "Wrong number of arguments")
.ThrowAsJavaScriptException();
}
this->rawData = this->getInternalInstance();
this->rawData = extrautils::configureComponent(this->getInternalInstance());
}
QSizeWrap::~QSizeWrap() { this->instance.reset(); }

View File

@ -32,7 +32,7 @@ QVariantWrap::QVariantWrap(const Napi::CallbackInfo& info)
} else {
this->instance = QSharedPointer<QVariant>(new QVariant());
}
this->rawData = this->getInternalInstance();
this->rawData = extrautils::configureComponent(this->getInternalInstance());
}
Napi::Value QVariantWrap::toString(const Napi::CallbackInfo& info) {

View File

@ -41,7 +41,7 @@ QApplicationWrap::QApplicationWrap(const Napi::CallbackInfo& info)
Napi::TypeError::New(env, "Wrong number of arguments")
.ThrowAsJavaScriptException();
}
this->rawData = this->getInternalInstance();
this->rawData = extrautils::configureComponent(this->getInternalInstance());
}
QApplicationWrap::~QApplicationWrap() {
if (this->_wasManuallyCreated) {

View File

@ -29,7 +29,7 @@ QClipboardWrap::QClipboardWrap(const Napi::CallbackInfo& info)
Napi::TypeError::New(env, "Incorrect initialization of QClipboardWrap")
.ThrowAsJavaScriptException();
}
this->rawData = this->getInternalInstance();
this->rawData = extrautils::configureComponent(this->getInternalInstance());
}
QClipboard* QClipboardWrap::getInternalInstance() { return this->instance; }

View File

@ -33,7 +33,7 @@ QCursorWrap::QCursorWrap(const Napi::CallbackInfo& info)
Napi::TypeError::New(env, "Wrong number of arguments")
.ThrowAsJavaScriptException();
}
this->rawData = this->getInternalInstance();
this->rawData = extrautils::configureComponent(this->getInternalInstance());
}
QCursorWrap::~QCursorWrap() { this->instance.reset(); }

View File

@ -36,7 +36,7 @@ QKeyEventWrap::QKeyEventWrap(const Napi::CallbackInfo& info)
Napi::TypeError::New(env, "Wrong number of arguments")
.ThrowAsJavaScriptException();
}
this->rawData = this->getInternalInstance();
this->rawData = extrautils::configureComponent(this->getInternalInstance());
}
QKeyEventWrap::~QKeyEventWrap() {

View File

@ -9,15 +9,15 @@ Napi::FunctionReference QMouseEventWrap::constructor;
Napi::Object QMouseEventWrap::init(Napi::Env env, Napi::Object exports) {
Napi::HandleScope scope(env);
char CLASSNAME[] = "QMouseEvent";
Napi::Function func = DefineClass(
env, CLASSNAME,
{InstanceMethod("button", &QMouseEventWrap::button),
InstanceMethod("x", &QMouseEventWrap::x),
InstanceMethod("y", &QMouseEventWrap::y),
InstanceMethod("globalX", &QMouseEventWrap::globalX),
InstanceMethod("globalY", &QMouseEventWrap::globalY),
Napi::Function func =
DefineClass(env, CLASSNAME,
{InstanceMethod("button", &QMouseEventWrap::button),
InstanceMethod("x", &QMouseEventWrap::x),
InstanceMethod("y", &QMouseEventWrap::y),
InstanceMethod("globalX", &QMouseEventWrap::globalX),
InstanceMethod("globalY", &QMouseEventWrap::globalY),
COMPONENT_WRAPPED_METHODS_EXPORT_DEFINE});
COMPONENT_WRAPPED_METHODS_EXPORT_DEFINE});
constructor = Napi::Persistent(func);
exports.Set(CLASSNAME, func);
return exports;
@ -37,7 +37,7 @@ QMouseEventWrap::QMouseEventWrap(const Napi::CallbackInfo& info)
Napi::TypeError::New(env, "Wrong number of arguments")
.ThrowAsJavaScriptException();
}
this->rawData = this->getInternalInstance();
this->rawData = extrautils::configureComponent(this->getInternalInstance());
}
QMouseEventWrap::~QMouseEventWrap() {

View File

@ -34,7 +34,7 @@ QIconWrap::QIconWrap(const Napi::CallbackInfo& info)
Napi::TypeError::New(env, "Wrong number of arguments")
.ThrowAsJavaScriptException();
}
this->rawData = this->getInternalInstance();
this->rawData = extrautils::configureComponent(this->getInternalInstance());
}
QIconWrap::~QIconWrap() { this->instance.reset(); }

View File

@ -32,7 +32,7 @@ QKeySequenceWrap::QKeySequenceWrap(const Napi::CallbackInfo &info)
Napi::TypeError::New(env, "Wrong number of arguments")
.ThrowAsJavaScriptException();
}
this->rawData = this->getInternalInstance();
this->rawData = extrautils::configureComponent(this->getInternalInstance());
}
QKeySequenceWrap::~QKeySequenceWrap() { this->instance.reset(); }

View File

@ -41,7 +41,7 @@ QPixmapWrap::QPixmapWrap(const Napi::CallbackInfo& info)
Napi::TypeError::New(env, "Wrong number of arguments")
.ThrowAsJavaScriptException();
}
this->rawData = this->getInternalInstance();
this->rawData = extrautils::configureComponent(this->getInternalInstance());
}
QPixmapWrap::~QPixmapWrap() { this->instance.reset(); }

View File

@ -50,7 +50,7 @@ QActionWrap::QActionWrap(const Napi::CallbackInfo& info)
Napi::TypeError::New(env, "Wrong number of arguments")
.ThrowAsJavaScriptException();
}
this->rawData = this->getInternalInstance();
this->rawData = extrautils::configureQObject(this->getInternalInstance());
}
QActionWrap::~QActionWrap() { extrautils::safeDelete(this->instance); }

View File

@ -39,11 +39,9 @@ QCheckBoxWrap::QCheckBoxWrap(const Napi::CallbackInfo& info)
Napi::TypeError::New(env, "Wrong number of arguments")
.ThrowAsJavaScriptException();
}
this->rawData = this->getInternalInstance();
// Adds measure function on yoga node so that widget size is calculated based
// on its text also.
YGNodeSetMeasureFunc(this->instance->getFlexNode(),
&extrautils::measureQtWidget);
this->rawData = extrautils::configureQWidget(
this->getInternalInstance(), this->getInternalInstance()->getFlexNode(),
true);
}
QCheckBoxWrap::~QCheckBoxWrap() { extrautils::safeDelete(this->instance); }

View File

@ -43,11 +43,10 @@ QDialWrap::QDialWrap(const Napi::CallbackInfo& info)
Napi::TypeError::New(env, "Wrong number of arguments")
.ThrowAsJavaScriptException();
}
this->rawData = this->getInternalInstance();
// Adds measure function on yoga node so that widget size is calculated based
// on its own size.
YGNodeSetMeasureFunc(this->instance->getFlexNode(),
&extrautils::measureQtWidget);
this->rawData = extrautils::configureQWidget(
this->getInternalInstance(), this->getInternalInstance()->getFlexNode(),
true);
}
QDialWrap::~QDialWrap() { extrautils::safeDelete(this->instance); }

View File

@ -37,7 +37,7 @@ QGridLayoutWrap::QGridLayoutWrap(const Napi::CallbackInfo& info)
Napi::TypeError::New(env, "Wrong number of arguments")
.ThrowAsJavaScriptException();
}
this->rawData = this->getInternalInstance();
this->rawData = extrautils::configureQObject(this->getInternalInstance());
}
Napi::Value QGridLayoutWrap::addWidget(const Napi::CallbackInfo& info) {

View File

@ -45,11 +45,10 @@ QLabelWrap::QLabelWrap(const Napi::CallbackInfo& info)
Napi::TypeError::New(env, "Wrong number of arguments")
.ThrowAsJavaScriptException();
}
this->rawData = this->getInternalInstance();
// Adds measure function on yoga node so that widget size is calculated based
// on its text also.
YGNodeSetMeasureFunc(this->instance->getFlexNode(),
&extrautils::measureQtWidget);
auto flexNode = this->getInternalInstance()->getFlexNode();
YGNodeSetNodeType(flexNode, YGNodeType::YGNodeTypeText);
this->rawData =
extrautils::configureQWidget(this->getInternalInstance(), flexNode, true);
}
Napi::Value QLabelWrap::setWordWrap(const Napi::CallbackInfo& info) {

View File

@ -43,11 +43,9 @@ QLineEditWrap::QLineEditWrap(const Napi::CallbackInfo& info)
Napi::TypeError::New(env, "Wrong number of arguments")
.ThrowAsJavaScriptException();
}
this->rawData = this->getInternalInstance();
// Adds measure function on yoga node so that widget size is calculated based
// on its text also.
YGNodeSetMeasureFunc(this->instance->getFlexNode(),
&extrautils::measureQtWidget);
this->rawData = extrautils::configureQWidget(
this->getInternalInstance(), this->getInternalInstance()->getFlexNode(),
true);
}
QLineEditWrap::~QLineEditWrap() { extrautils::safeDelete(this->instance); }

View File

@ -46,7 +46,8 @@ QMainWindowWrap::QMainWindowWrap(const Napi::CallbackInfo& info)
Napi::TypeError::New(env, "Wrong number of arguments")
.ThrowAsJavaScriptException();
}
this->rawData = this->getInternalInstance();
this->rawData = extrautils::configureQWidget(
this->getInternalInstance(), this->getInternalInstance()->getFlexNode());
}
Napi::Value QMainWindowWrap::setCentralWidget(const Napi::CallbackInfo& info) {
@ -54,16 +55,13 @@ Napi::Value QMainWindowWrap::setCentralWidget(const Napi::CallbackInfo& info) {
Napi::HandleScope scope(env);
Napi::Object widgetObject = info[0].As<Napi::Object>();
Napi::External<YGNode> centralWidgetFlexNode =
info[1].As<Napi::External<YGNode>>();
QWidgetWrap* centralWidget =
Napi::ObjectWrap<QWidgetWrap>::Unwrap(widgetObject);
Napi::External<YGNode> flexNodeObject = info[1].As<Napi::External<YGNode>>();
YGNodeRef flexNodeRef = flexNodeObject.Data();
YGNodeRef node = this->instance->getFlexNode();
YGNodeInsertChild(node, flexNodeRef, 0);
YGNodeInsertChild(this->instance->getFlexNode(), centralWidgetFlexNode.Data(),
0);
this->instance->setCentralWidget(centralWidget->getInternalInstance());
return env.Null();
}

View File

@ -40,11 +40,9 @@ QMenuWrap::QMenuWrap(const Napi::CallbackInfo& info)
Napi::TypeError::New(env, "Wrong number of arguments")
.ThrowAsJavaScriptException();
}
this->rawData = this->getInternalInstance();
// Adds measure function on yoga node so that widget size is calculated based
// on its text also.
YGNodeSetMeasureFunc(this->instance->getFlexNode(),
&extrautils::measureQtWidget);
this->rawData = extrautils::configureQWidget(
this->getInternalInstance(), this->getInternalInstance()->getFlexNode(),
true);
}
QMenuWrap::~QMenuWrap() { extrautils::safeDelete(this->instance); }

View File

@ -45,11 +45,8 @@ QMenuBarWrap::QMenuBarWrap(const Napi::CallbackInfo& info)
Napi::TypeError::New(env, "Wrong number of arguments")
.ThrowAsJavaScriptException();
}
this->rawData = this->getInternalInstance();
// Adds measure function on yoga node so that widget size is calculated based
// on its text also.
YGNodeSetMeasureFunc(this->instance->getFlexNode(),
&extrautils::measureQtWidget);
this->rawData = extrautils::configureQWidget(
this->getInternalInstance(), this->getInternalInstance()->getFlexNode());
}
QMenuBarWrap::~QMenuBarWrap() { extrautils::safeDelete(this->instance); }

View File

@ -52,11 +52,9 @@ QPlainTextEditWrap::QPlainTextEditWrap(const Napi::CallbackInfo &info)
Napi::TypeError::New(env, "Wrong number of arguments")
.ThrowAsJavaScriptException();
}
this->rawData = this->getInternalInstance();
// Adds measure function on yoga node so that widget size is calculated based
// on its text also.
YGNodeSetMeasureFunc(this->instance->getFlexNode(),
&extrautils::measureQtWidget);
this->rawData = extrautils::configureQWidget(
this->getInternalInstance(), this->getInternalInstance()->getFlexNode(),
true);
}
QPlainTextEditWrap::~QPlainTextEditWrap() {

View File

@ -42,11 +42,9 @@ QProgressBarWrap::QProgressBarWrap(const Napi::CallbackInfo& info)
Napi::TypeError::New(env, "Wrong number of arguments")
.ThrowAsJavaScriptException();
}
this->rawData = this->getInternalInstance();
// Adds measure function on yoga node so that widget size is calculated based
// on its own size.
YGNodeSetMeasureFunc(this->instance->getFlexNode(),
&extrautils::measureQtWidget);
this->rawData = extrautils::configureQWidget(
this->getInternalInstance(), this->getInternalInstance()->getFlexNode(),
true);
}
QProgressBarWrap::~QProgressBarWrap() {

View File

@ -38,10 +38,9 @@ QPushButtonWrap::QPushButtonWrap(const Napi::CallbackInfo& info)
Napi::TypeError::New(env, "Wrong number of arguments")
.ThrowAsJavaScriptException();
}
// Adds measure function on yoga node so that widget size is calculated based
// on its text also.
YGNodeSetMeasureFunc(this->instance->getFlexNode(),
&extrautils::measureQtWidget);
this->rawData = extrautils::configureQWidget(
this->getInternalInstance(), this->getInternalInstance()->getFlexNode(),
true);
}
QPushButtonWrap::~QPushButtonWrap() { extrautils::safeDelete(this->instance); }

View File

@ -38,11 +38,9 @@ QRadioButtonWrap::QRadioButtonWrap(const Napi::CallbackInfo& info)
Napi::TypeError::New(env, "Wrong number of arguments")
.ThrowAsJavaScriptException();
}
this->rawData = this->getInternalInstance();
// Adds measure function on yoga node so that widget size is calculated based
// on its own size.
YGNodeSetMeasureFunc(this->instance->getFlexNode(),
&extrautils::measureQtWidget);
this->rawData = extrautils::configureQWidget(
this->getInternalInstance(), this->getInternalInstance()->getFlexNode(),
true);
}
QRadioButtonWrap::~QRadioButtonWrap() {

View File

@ -40,11 +40,14 @@ QScrollAreaWrap::QScrollAreaWrap(const Napi::CallbackInfo& info)
Napi::TypeError::New(env, "Wrong number of arguments")
.ThrowAsJavaScriptException();
}
this->rawData = this->getInternalInstance();
// Adds measure function on yoga node so that widget size is calculated based
// on its own size.
YGNodeSetMeasureFunc(this->instance->getFlexNode(),
&extrautils::measureQtWidget);
this->scrollNode = YGNodeNew();
YGConfigSetUseWebDefaults(this->scrollNode->getConfig(), true);
FlexNodeContext* scrollNodeCtx = new FlexNodeContext(this->instance);
YGNodeSetContext(this->scrollNode, scrollNodeCtx);
this->rawData = extrautils::configureQWidget(
this->getInternalInstance(), this->getInternalInstance()->getFlexNode(),
true);
}
QScrollAreaWrap::~QScrollAreaWrap() { extrautils::safeDelete(this->instance); }
@ -53,8 +56,11 @@ Napi::Value QScrollAreaWrap::setWidget(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
Napi::Object contentWidget = info[0].As<Napi::Object>();
Napi::External<YGNode> centralWidgetFlexNode =
info[1].As<Napi::External<YGNode>>();
QWidgetWrap* contentWidgetWrap =
Napi::ObjectWrap<QWidgetWrap>::Unwrap(contentWidget);
YGNodeInsertChild(this->scrollNode, centralWidgetFlexNode.Data(), 0);
this->instance->setWidget(contentWidgetWrap->getInternalInstance());
return env.Null();
}

View File

@ -40,7 +40,7 @@ QShortcutWrap::QShortcutWrap(const Napi::CallbackInfo& info)
Napi::TypeError::New(env, "Wrong number of arguments")
.ThrowAsJavaScriptException();
}
this->rawData = this->getInternalInstance();
this->rawData = extrautils::configureQObject(this->getInternalInstance());
}
QShortcutWrap::~QShortcutWrap() { extrautils::safeDelete(this->instance); }

View File

@ -45,11 +45,9 @@ QSpinBoxWrap::QSpinBoxWrap(const Napi::CallbackInfo& info)
Napi::TypeError::New(env, "Wrong number of arguments")
.ThrowAsJavaScriptException();
}
this->rawData = this->getInternalInstance();
// Adds measure function on yoga node so that widget size is calculated based
// on its text also.
YGNodeSetMeasureFunc(this->instance->getFlexNode(),
&extrautils::measureQtWidget);
this->rawData = extrautils::configureQWidget(
this->getInternalInstance(), this->getInternalInstance()->getFlexNode(),
true);
}
QSpinBoxWrap::~QSpinBoxWrap() { extrautils::safeDelete(this->instance); }

View File

@ -48,7 +48,7 @@ QSystemTrayIconWrap::QSystemTrayIconWrap(const Napi::CallbackInfo& info)
Napi::TypeError::New(env, "Wrong number of arguments")
.ThrowAsJavaScriptException();
}
this->rawData = this->getInternalInstance();
this->rawData = extrautils::configureQObject(this->getInternalInstance());
}
QSystemTrayIconWrap::~QSystemTrayIconWrap() {

View File

@ -47,11 +47,9 @@ QTabWidgetWrap::QTabWidgetWrap(const Napi::CallbackInfo& info)
Napi::TypeError::New(env, "Wrong number of arguments")
.ThrowAsJavaScriptException();
}
this->rawData = this->getInternalInstance();
// Adds measure function on yoga node so that widget size is calculated based
// on its text also.
YGNodeSetMeasureFunc(this->instance->getFlexNode(),
&extrautils::measureQtWidget);
this->rawData = extrautils::configureQWidget(
this->getInternalInstance(), this->getInternalInstance()->getFlexNode(),
true);
}
Napi::Value QTabWidgetWrap::addTab(const Napi::CallbackInfo& info) {

View File

@ -39,5 +39,7 @@ QWidgetWrap::QWidgetWrap(const Napi::CallbackInfo& info)
Napi::TypeError::New(env, "Wrong number of arguments")
.ThrowAsJavaScriptException();
}
this->rawData = this->getInternalInstance();
this->rawData = extrautils::configureQWidget(
this->getInternalInstance(), this->getInternalInstance()->getFlexNode(),
false);
}

View File

@ -1,10 +1,20 @@
#include "core/FlexLayout/flexitem.h"
#include <QWidget>
#include "core/FlexLayout/flexutils.h"
FlexItem::FlexItem() {
this->node = YGNodeNew();
// YGConfigSetUseWebDefaults(this->node->getConfig(),true);
YGConfigSetUseWebDefaults(this->node->getConfig(), true);
}
YGNodeRef FlexItem::getFlexNode() const { return this->node; }
FlexItem::~FlexItem() { YGNodeFree(this->node); }
FlexItem::~FlexItem() {
FlexNodeContext* ctx = flexutils::getFlexNodeContext(this->node);
if (ctx != nullptr) {
delete ctx;
};
YGNodeFree(this->node);
}

View File

@ -1,19 +1,11 @@
#include "core/FlexLayout/flexlayout.h"
#include <QDebug>
#include <QWidget>
#include "core/FlexLayout/flexitem.h"
#include "core/FlexLayout/flexutils.h"
#include "core/YogaWidget/yogawidget.h"
#include "spdlog/spdlog.h"
FlexLayout::NodeContext* FlexLayout::getNodeContext(YGNodeRef node) {
if (!node) {
return nullptr;
}
void* childContext = YGNodeGetContext(node);
NodeContext* ctx = static_cast<NodeContext*>(
childContext); // because we are managing this at all times
return ctx;
}
FlexLayout::FlexLayout(QWidget* parentWidget, YGNodeRef parentNode)
: QLayout(parentWidget) {
@ -27,99 +19,44 @@ FlexLayout::~FlexLayout() {
const uint32_t childCount = YGNodeGetChildCount(this->node);
for (uint32_t i = 0; i < childCount; i++) {
const YGNodeRef oldChild = YGNodeGetChild(this->node, i);
NodeContext* ctx = getNodeContext(oldChild);
if (ctx) {
delete ctx->item;
FlexNodeContext* childCtx = flexutils::getFlexNodeContext(oldChild);
if (childCtx->layoutItem()) {
delete childCtx->layoutItem();
childCtx->setLayoutItem(nullptr);
}
}
YGNodeRemoveAllChildren(this->node);
}
QSize FlexLayout::sizeHint() const {
if (!this->node) {
return QSize(0, 0);
}
int width = static_cast<uint>(YGNodeLayoutGetWidth(this->node));
int height = static_cast<uint>(YGNodeLayoutGetHeight(this->node));
return QSize(width, height);
}
void FlexLayout::addItem(QLayoutItem* item) {
// Noop: We already have addWidget doing all the hard work.
}
QLayoutItem* FlexLayout::itemAt(int index) const {
if (!this->node) {
return nullptr;
}
// spdlog::info("flexlayout: itemAt {}",index);
YGNodeRef childNode = YGNodeGetChild(this->node, static_cast<uint>(index));
NodeContext* ctx = getNodeContext(childNode);
if (!ctx) {
return nullptr;
}
return ctx->item;
}
QLayoutItem* FlexLayout::takeAt(int index) {
YGNodeRef childNode = YGNodeGetChild(this->node, static_cast<uint>(index));
NodeContext* ctx = getNodeContext(childNode);
QLayoutItem* childLayoutItem = ctx->item;
YGNodeRemoveChild(this->node, childNode);
spdlog::info("flexlayout: takeAt ", index);
delete ctx;
return childLayoutItem;
}
int FlexLayout::count() const {
if (!this->node) {
return 0;
}
float childCount = YGNodeGetChildCount(this->node);
spdlog::info("flexlayout: count {}", childCount);
return static_cast<uint>(childCount);
return;
}
void FlexLayout::addWidget(QWidget* childWidget, YGNodeRef childNode) {
if (!this->node) {
spdlog::warn(
"Flex layout's parent yoga node not set yet. Set it using setFlexNode. "
"Child widget will not be added to Flex Layout");
qWarning() << "Flex layout's parent yoga node not set yet. Set it using "
"setFlexNode. \n"
<< "Child widget will not be added to Flex Layout";
return;
}
uint count = YGNodeGetChildCount(this->node);
YGNodeInsertChild(this->node, childNode, count);
QLayoutItem* layoutItem = new QWidgetItem(childWidget);
NodeContext* childContext = new NodeContext(layoutItem);
YGNodeSetContext(childNode, static_cast<void*>(childContext));
FlexNodeContext* childCtx = flexutils::getFlexNodeContext(childNode);
auto layoutitem = new QWidgetItem(childWidget);
childCtx->setLayoutItem(layoutitem);
QLayout::addWidget(childWidget);
this->invalidate();
}
void FlexLayout::removeWidget(QWidget* childWidget, YGNodeRef childNode) {
if (!this->node) {
spdlog::warn(
"Flex layout's parent yoga node not set yet. Set it using setFlexNode. "
"childwidget cant be removed");
return;
}
NodeContext* ctx = getNodeContext(childNode);
if (ctx) {
delete ctx->item;
}
YGNodeRemoveChild(this->node, childNode);
QLayout::removeWidget(childWidget);
this->invalidate();
}
void FlexLayout::insertChildBefore(QWidget* childWidget,
YGNodeRef beforeChildNode,
YGNodeRef childNode) {
if (!this->node) {
spdlog::warn(
"Flex layout's parent yoga node not set yet. Set it using setFlexNode. "
"childwidget cant be inserted");
qWarning() << "Flex layout's parent yoga node not set yet. Set it using "
"setFlexNode. \n"
<< "childwidget cant be inserted";
return;
}
uint count = YGNodeGetChildCount(this->node);
@ -131,14 +68,61 @@ void FlexLayout::insertChildBefore(QWidget* childWidget,
}
}
YGNodeInsertChild(this->node, childNode, indexToInsert);
QLayoutItem* layoutItem = new QWidgetItem(childWidget);
NodeContext* childContext = new NodeContext(layoutItem);
YGNodeSetContext(childNode, static_cast<void*>(childContext));
FlexNodeContext* ctx = flexutils::getFlexNodeContext(childNode);
ctx->setLayoutItem(new QWidgetItem(childWidget));
QLayout::addWidget(childWidget);
this->invalidate();
}
YGNodeRef FlexLayout::getRootNode(YGNodeRef node) {
QLayoutItem* FlexLayout::itemAt(int index) const {
if (!this->node ||
index >= static_cast<int>(YGNodeGetChildCount(this->node))) {
return nullptr;
}
YGNodeRef childNode = YGNodeGetChild(this->node, static_cast<uint>(index));
FlexNodeContext* childCtx = flexutils::getFlexNodeContext(childNode);
return childCtx->layoutItem();
}
QLayoutItem* FlexLayout::takeAt(int index) {
if (!this->node ||
index >= static_cast<int>(YGNodeGetChildCount(this->node))) {
return nullptr;
}
YGNodeRef childNode = YGNodeGetChild(this->node, static_cast<uint>(index));
FlexNodeContext* ctx = flexutils::getFlexNodeContext(childNode);
QLayoutItem* childLayoutItem = ctx->layoutItem();
ctx->setLayoutItem(nullptr);
YGNodeRemoveChild(this->node, childNode);
return childLayoutItem;
}
int FlexLayout::count() const {
if (!this->node) {
return 0;
}
float childCount = YGNodeGetChildCount(this->node);
return static_cast<int>(childCount);
}
void FlexLayout::removeWidget(QWidget* childWidget, YGNodeRef childNode) {
if (!this->node) {
qWarning() << "Flex layout's parent yoga node not set yet. Set it using "
"setFlexNode. "
<< "childwidget cant be removed";
return;
}
FlexNodeContext* ctx = flexutils::getFlexNodeContext(childNode);
if (ctx->layoutItem()) {
delete ctx->layoutItem();
ctx->setLayoutItem(nullptr);
}
YGNodeRemoveChild(this->node, childNode);
QLayout::removeWidget(childWidget);
this->invalidate();
}
YGNodeRef FlexLayout::getRootNode(YGNodeRef node) const {
YGNodeRef parent = node->getOwner();
if (!parent) {
return node;
@ -147,35 +131,37 @@ YGNodeRef FlexLayout::getRootNode(YGNodeRef node) {
}
}
QSize FlexLayout::sizeHint() const {
QSize totalSize;
YGNodeRef parentNode = this->node;
YGNodeRef rootNode = getRootNode(parentNode);
YGDirection rootDirection = YGNodeStyleGetDirection(rootNode);
YGNodeStyleSetMaxHeight(rootNode, QWIDGETSIZE_MAX);
YGNodeStyleSetMaxWidth(rootNode, QWIDGETSIZE_MAX);
YGNodeCalculateLayout(rootNode, 0, 0, rootDirection);
totalSize.rwidth() = YGNodeLayoutGetWidth(this->node);
totalSize.rheight() = YGNodeLayoutGetHeight(this->node);
return totalSize;
}
void FlexLayout::setGeometry(const QRect& rect) {
if (!this->node) {
return;
}
YGNodeRef rootNode = getRootNode(this->node);
QWidget* parentWidget = this->parentWidget();
QWidget* window = parentWidget->window();
YGNodeRef rootNode = FlexLayout::getRootNode(this->node);
YGDirection direction = YGNodeStyleGetDirection(rootNode);
YGNodeCalculateLayout(rootNode, window->width(), window->height(), direction);
YGNodeStyleSetMaxHeight(rootNode, QWIDGETSIZE_MAX);
YGNodeStyleSetMaxWidth(rootNode, QWIDGETSIZE_MAX);
YGNodeCalculateLayout(rootNode, 0, 0, direction);
uint count = YGNodeGetChildCount(this->node);
for (uint i = 0; i < count; ++i) {
YGNode* childNode = YGNodeGetChild(this->node, i);
int width = static_cast<uint>(YGNodeLayoutGetWidth(childNode));
int height = static_cast<uint>(YGNodeLayoutGetHeight(childNode));
int left = static_cast<uint>(YGNodeLayoutGetLeft(childNode));
int top = static_cast<uint>(YGNodeLayoutGetTop(childNode));
QRect childRect(left, top, width, height);
NodeContext* ctx = getNodeContext(childNode);
if (ctx) {
QLayoutItem* childLayoutItem = ctx->item;
QWidget* childWidget = childLayoutItem->widget();
if (childWidget) {
childWidget->setGeometry(childRect);
} else {
childLayoutItem->setGeometry(childRect);
}
}
QRect childRect = flexutils::getFlexNodeGeometry(childNode);
FlexNodeContext* ctx = flexutils::getFlexNodeContext(childNode);
QLayoutItem* childItem = ctx->layoutItem();
childItem->setGeometry(childRect);
}
QLayout::setGeometry(rect);
}

View File

@ -41,7 +41,7 @@ FlexLayoutWrap::FlexLayoutWrap(const Napi::CallbackInfo& info)
Napi::TypeError::New(env, "Wrong number of arguments")
.ThrowAsJavaScriptException();
}
this->rawData = this->getInternalInstance();
this->rawData = extrautils::configureQObject(this->getInternalInstance());
}
Napi::Value FlexLayoutWrap::addWidget(const Napi::CallbackInfo& info) {

View File

@ -0,0 +1,70 @@
#include "core/FlexLayout/flexutils.h"
FlexNodeContext::FlexNodeContext(void* widget) {
this->_widget = widget;
this->_layoutItem = nullptr;
}
QWidget* FlexNodeContext::widget() {
QWidget* flexNodeWidget = static_cast<QWidget*>(this->_widget);
return flexNodeWidget;
}
QLayoutItem* FlexNodeContext::layoutItem() { return this->_layoutItem; }
void FlexNodeContext::setLayoutItem(QLayoutItem* item) {
this->_layoutItem = item;
}
const QRect flexutils::getFlexNodeGeometry(YGNodeRef node) {
int width = static_cast<uint>(YGNodeLayoutGetWidth(node));
int height = static_cast<uint>(YGNodeLayoutGetHeight(node));
int left = static_cast<uint>(YGNodeLayoutGetLeft(node));
int top = static_cast<uint>(YGNodeLayoutGetTop(node));
const QRect geometry(left, top, width, height);
return geometry;
}
void flexutils::setFlexNodeGeometry(YGNodeRef node, const QRect& geometry) {
int width = geometry.width();
int height = geometry.height();
int left = geometry.left();
int top = geometry.top();
YGNodeStyleSetWidth(node, width);
YGNodeStyleSetHeight(node, height);
YGNodeStyleSetPosition(node, YGEdgeTop, top);
YGNodeStyleSetPosition(node, YGEdgeLeft, left);
}
FlexNodeContext* flexutils::getFlexNodeContext(YGNodeRef node) {
if (!node) {
return nullptr;
}
void* rawCtx = YGNodeGetContext(node);
FlexNodeContext* ctx = static_cast<FlexNodeContext*>(rawCtx);
return ctx;
}
YGSize flexutils::measureQtWidget(YGNodeRef node, float _width,
YGMeasureMode widthMode, float _height,
YGMeasureMode heightMode) {
FlexNodeContext* ctx = getFlexNodeContext(node);
QWidget* widget = ctx->widget();
QSize size = widget->sizeHint();
float width = static_cast<float>(size.width());
float height = static_cast<float>(size.height());
return YGSize{
width,
height,
};
}
void flexutils::configureFlexNode(QWidget* widget, YGNodeRef node,
bool isLeafNode) {
FlexNodeContext* ctx = new FlexNodeContext(widget);
YGNodeSetContext(node, ctx);
if (isLeafNode) {
YGNodeSetMeasureFunc(node, &flexutils::measureQtWidget);
}
}

View File

@ -1,44 +1,51 @@
import { QMainWindow } from './index';
import { QWidget } from './lib/QtWidgets/QWidget';
import { FlexLayout } from './lib/core/FlexLayout';
import { QLabel, QLabelEvents } from './lib/QtWidgets/QLabel';
import { AlignmentFlag } from './lib/QtEnums';
import { QPixmap } from './lib/QtGui/QPixmap';
import { QMouseEvent } from './lib/QtGui/QEvent/QMouseEvent';
import { QWidget, QScrollArea, QMainWindow, FlexLayout, QLabel } from './index';
const win = new QMainWindow();
const view = new QWidget();
view.setLayout(new FlexLayout());
view.setStyleSheet(`
flex: 1;
width: '100%';
height: '100%';
`);
const hello = new QLabel();
hello.setText('Hello');
hello.setStyleSheet(`
border: 1px solid blue;
`);
const world = new QLabel();
world.setText('World');
world.addEventListener(QLabelEvents.MouseButtonPress, e => {
const event = new QMouseEvent(e);
console.log('clicked!', event.x(), event.y());
});
world.setStyleSheet(`
border: 1px solid blue;
qproperty-alignment: AlignCenter;
`);
const pixmap = new QPixmap('/Users/atulr/Project/nodegui/nodegui/extras/assets/kitchen.png');
hello.setProperty('pixmap', pixmap);
const main = async (): Promise<void> => {
const win = new QMainWindow();
const scrollArea = new QScrollArea();
const center = new QWidget();
const label = new QLabel();
label.setText(`
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
`);
hello.setProperty('alignment', AlignmentFlag.AlignCenter);
await scrollArea.setInlineStyle('border: 1px solid yellow;');
await center.setInlineStyle(`border: 3px solid blue;`);
await label.setInlineStyle(`border: 2px solid green;padding: 10;`);
if (view.layout) {
view.layout.addWidget(hello);
view.layout.addWidget(world);
}
win.setCentralWidget(view);
win.show();
center.setLayout(new FlexLayout());
center.layout?.addWidget(label);
scrollArea.setWidget(center);
win.setCentralWidget(scrollArea);
win.show();
(global as any).win = win;
};
(global as any).win = win; // To prevent win from being garbage collected.
main();

View File

@ -7,19 +7,19 @@ export class QKeyEvent {
constructor(event: NativeEvent) {
this.native = new addon.QKeyEvent(event);
}
text = (): string => {
text(): string {
return this.native.text();
};
key = (): number => {
}
key(): number {
return this.native.key();
};
modifiers = (): number => {
}
modifiers(): number {
return this.native.modifiers();
};
count = (): number => {
}
count(): number {
return this.native.count();
};
isAutoRepeat = (): boolean => {
}
isAutoRepeat(): boolean {
return this.native.isAutoRepeat();
};
}
}

View File

@ -7,19 +7,19 @@ export class QMouseEvent {
constructor(event: NativeEvent) {
this.native = new addon.QMouseEvent(event);
}
button = (): string => {
button(): string {
return this.native.button();
};
x = (): number => {
}
x(): number {
return this.native.x();
};
y = (): number => {
}
y(): number {
return this.native.y();
};
globalX = (): number => {
}
globalX(): number {
return this.native.globalX();
};
globalY = (): number => {
}
globalY(): number {
return this.native.globalY();
};
}
}

View File

@ -25,7 +25,7 @@ export class QIcon extends Component {
this.native = new addon.QIcon();
}
}
pixmap = (width: number, height: number, mode?: QIconMode, state?: QIconState): QPixmap => {
pixmap(width: number, height: number, mode?: QIconMode, state?: QIconState): QPixmap {
let nativePixmap;
if (mode && state) {
nativePixmap = this.native.pixmap(width, height, mode, state);
@ -35,12 +35,10 @@ export class QIcon extends Component {
nativePixmap = this.native.pixmap(width, height);
}
return new QPixmap(nativePixmap);
};
}
isMask(): boolean {
return this.native.isMask();
}
setIsMask(isMask: boolean): void {
this.native.setIsMask(isMask);
}

View File

@ -5,9 +5,9 @@ import { NodeObject } from '../QtCore/QObject';
export abstract class NodeLayout extends NodeObject {
type = 'layout';
abstract addWidget(childWidget: NodeWidget, ...args: any[]): void;
activate = (): boolean => {
activate(): boolean {
return this.native.activate();
};
}
invalidate(): void {
this.native.invalidate();
}

View File

@ -24,7 +24,7 @@ export class QScrollArea extends QAbstractScrollArea {
setWidget(widget: NodeWidget): void {
// react:✓, //TODO:getter
this.contentWidget = widget;
this.native.setWidget(widget.native);
this.native.setWidget(widget.native, widget.getFlexNode());
}
takeWidget(): NodeWidget | null {
// react:✓

View File

@ -126,13 +126,16 @@ export abstract class NodeWidget extends YogaWidget {
}
setLayout(parentLayout: NodeLayout): void {
const flexLayout = parentLayout as FlexLayout;
this.native.setLayout(parentLayout.native);
if (flexLayout.setFlexNode) {
//if flex layout set the flexnode
flexLayout.setFlexNode(this.getFlexNode());
}
this.native.setLayout(parentLayout.native);
this.layout = parentLayout;
}
adjustSize(): void {
this.native.adjustSize();
}
}
type Rect = {

View File

@ -3,7 +3,7 @@ import cuid from 'cuid';
import nodeguiAutoPrefixer from 'postcss-nodegui-autoprefixer';
import { NodeWidget } from '../../QtWidgets/QWidget';
export class StyleSheet {
static create = async (cssString: string): Promise<string> => {
static async create(cssString: string): Promise<string> {
const { css } = await postcss([nodeguiAutoPrefixer()])
.process(cssString, { from: undefined })
.catch(err => {
@ -11,7 +11,7 @@ export class StyleSheet {
return { css: cssString };
});
return css;
};
}
}
export async function prepareInlineStyleSheet(widget: NodeWidget, rawStyle: string): Promise<string> {

View File

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

View File

@ -3,5 +3,5 @@
1. **Segmentation fault:** Segmentation fault occurs when you access a Pointer that is pointing to an invalid memory address. One major reason for this can be that JS garbage collector would have garbage collected the addon generated value and you try accessing it after a while. This is mostly the case if you see seg fault happening randomly after some time of startup.
2. **Widget not visible in Flex layout** Widget might have gotten zero height/width. This can occur if yoga was not able to get the default height/width of the widget. Make sure you have implemented
`YGNodeSetMeasureFunc(this->instance->getFlexNode(), &extrautils::measureQtWidget);`
`YGNodeSetMeasureFunc(this->instance->getFlexNode(), &flexutils::measureQtWidget);`
if its a leaf node widget(doesnt contain any children).