How styling works?
There are two parts to styling.
- Layout
- Painting : Colors, text color, etc
Painting
The regular styles such as text color, font-size, font weight etc are achieved using Qt's stylesheet. We just call Qt's setStyleSheet method on the native widget and pass in the styles as a string.
This method is implemented as part of QWIDGET_WRAPPED_METHODS_DECLARATION
in qwidget_macro.h
.
So all widgets using this macro will get the setStyleSheet method.
Layout
Layouting is basically positioning widgets on the screen. It takes into account everything from margins, paddings, positions etc. Our main focus will be Flex layouting. For flex layout we are using yoga library from facebook. This is the same library used by React Native. Before looking at flaxlayout in this libarary I recommend browsing Yoga's C API doc here: deps/yoga/doc.md
In case nodegui
. I have implemented a custom Qt layout by extending QLayout
, hence Qt is able to take over automagically when window is resized or any other layouting event occurs.
You can find the implementation at src/cpp/core/FlexLayout/flexlayout.h
.
The c++ api provided by this custom layout looks like this:
// FlexLayout is a custom Layout built for QT. This layout will be used to layout qt widgets using facebook's yoga library.
// Thus giving ability to layout Qt Widgets using Flexbox.
// Usage:
QWidget *container = new QWidget();
YGNodeRef root = YGNodeNew();
YGNodeRef child1 = YGNodeNew();
YGNodeRef child2 = YGNodeNew();
FlexLayout * flayout = new FlexLayout(container,root);
// or FlexLayout * flayout = new FlexLayout(container);
// or FlexLayout *flayout = new FlexLayout();
flayout->addWidget(btn1, child1);
flayout->addWidget(btn2, child2);
This layout is exported to Javascript side via src/cpp/core/FlexLayout/flexlayout_wrap.h
The JS Api looks like this:
const view = new QWidget(rootView);
const flayout = new FlexLayout(); // Create layout
flayout.setFlexNode(view.getFlexNode()); // Set widget's flex as layout's flex node.
view.setLayout(flayout); // set layout as view's layout
const label = new QLabel(view);
label.setText("Hello12321");
const label2 = new QLabel(view);
label2.setText("SECOND LABEL");
flayout.addWidget(label2, label2.getFlexNode()); // Add child to layout
flayout.addWidget(label, label.getFlexNode()); // Add child to layout
Implementation
-
Every widget that wants to use flex layout should extend from
flexItem
found atsrc/cpp/core/FlexLayout/flexitem.h
. For example, seenlabel.h
atsrc/cpp/QtWidgets/QLabel/nlabel.h
NLabel inherits from
NodeWidget
which inherits fromYogaWidget
which inturn inherits fromFlexItem
FlexItem
adds a YogaNode to every widget.YogaWidget
adds Yoga specific q-properties to the widget, which is useful to assign yoga properties via qstylesheet. More on this below.NodeWidget
adds layout support viaYogaWidget
and event handling support viaEventWidget
FlexItem
FlexItem : src/cpp/core/FlexLayout/flexitem.h
add flexnode to each widget.
FlexItem adds methods like getFlexNode.
YogaWidget
Qt StyleSheet allows you to specify style properties just like in web. You could specify font-size, margin, padding, etc. Qt StyleSheet also allows custom style properties via Qt's q-property system.
So in order to enable yoga based properties like alignItems, justifyContent, flex, etc via qt's stylesheet we declare and define q properties for each of those custom properties we want. This allows us to use something like:
view.setStyleSheet(`
background-color:green;
qproperty-flex: 1;
qproperty-alignItems: 'center';
`);
Notice qproperty-
prefix? These are the custom q-properties we defined in YogaWidget.h
. We do not need to prefix qproperty-
if a stylehsheet string is passed through StyleSheet.create()
. StyleSheet.create has an autoprefixer which will do the right thing.
NodeWidget
Every widget we implement should inherit from NodeWidget. This helps us add all the properties we want in the widgets via a single class. NodeWidget is the class that contains properties and methods shared by all widgets. This class allows us to add features to all widgets easily.