diff --git a/deps/yoga/doc.md b/deps/yoga/doc.md new file mode 100644 index 000000000..b577159c3 --- /dev/null +++ b/deps/yoga/doc.md @@ -0,0 +1,278 @@ +## Copy of old doc + +This doc was originally found at: `https://github.com/facebook/yoga/blob/d66239bea8dc74526343d414a4923237081d7b5c/docs/_docs/api/c.md` + +`YGNodeRef` is the main object you will be interfacing with when using Yoga in C. `YGNodeRef` is a pointer to an internal `YGNode` struct. + +### Lifecycle + +The following functions control the lifecycle of a `YGNodeRef`. + +```c +YGNodeRef YGNodeNew(); +void YGNodeReset(YGNodeRef node); +void YGNodeFree(YGNodeRef node); +void YGNodeFreeRecursive(YGNodeRef node); +``` + +- `YGNodeReset` will reset a node to its initial state so it can be re-used without needing to re-allocate a new node. +- `YGNodeFreeRecursive` is mostly used for testing and will free not only the node itself but also its children. + +### Children + +The following functions help manage the children of a node. + +```c +void YGNodeInsertChild(YGNodeRef node, YGNodeRef child, uint32_t index); +void YGNodeRemoveChild(YGNodeRef node, YGNodeRef child); +YGNodeRef YGNodeGetChild(YGNodeRef node, uint32_t index); +uint32_t YGNodeGetChildCount(YGNodeRef node); +``` + +### Style getters & setters + +The large part of Yoga's API consists of setters and getters for styles. These all follow the same general structure. Bellow are the function and enums used to control the various styles. For an in depth guide to how each style works see the getting started guide. + +```c +typedef enum YGDirection { + YGDirectionInherit, + YGDirectionLTR, + YGDirectionRTL, +} YGDirection; + +void YGNodeStyleSetDirection(YGNodeRef node, YGDirection direction); +YGDirection YGNodeStyleGetDirection(YGNodeRef node); + +typedef enum YGFlexDirection { + YGFlexDirectionColumn, + YGFlexDirectionColumnReverse, + YGFlexDirectionRow, + YGFlexDirectionRowReverse, +} YGFlexDirection; + +void YGNodeStyleSetFlexDirection(YGNodeRef node, + YGFlexDirection flexDirection); +YGFlexDirection YGNodeStyleGetFlexDirection(YGNodeRef node); + +typedef enum YGJustify { + YGJustifyFlexStart, + YGJustifyCenter, + YGJustifyFlexEnd, + YGJustifySpaceBetween, + YGJustifySpaceAround, +} YGJustify; + +void YGNodeStyleSetJustifyContent(YGNodeRef node, + YGJustify justifyContent); +YGJustify YGNodeStyleGetJustifyContent(YGNodeRef node); + +typedef enum YGAlign { + YGAlignAuto, + YGAlignFlexStart, + YGAlignCenter, + YGAlignFlexEnd, + YGAlignStretch, +} YGAlign; + +void YGNodeStyleSetAlignContent(YGNodeRef node, YGAlign alignContent); +YGAlign YGNodeStyleGetAlignContent(YGNodeRef node); + +void YGNodeStyleSetAlignItems(YGNodeRef node, YGAlign alignItems); +YGAlign YGNodeStyleGetAlignItems(YGNodeRef node); + +void YGNodeStyleSetAlignSelf(YGNodeRef node, YGAlign alignSelf); +YGAlign YGNodeStyleGetAlignSelf(YGNodeRef node); + +typedef enum YGPositionType { + YGPositionTypeRelative, + YGPositionTypeAbsolute, +} YGPositionType; + +void YGNodeStyleSetPositionType(YGNodeRef node, + YGPositionType positionType); +YGPositionType YGNodeStyleGetPositionType(YGNodeRef node); + +typedef enum YGWrap { + YGWrapNoWrap, + YGWrapWrap, +} YGWrap; + +void YGNodeStyleSetFlexWrap(YGNodeRef node, YGWrap flexWrap); +YGWrap YGNodeStyleGetFlexWrap(YGNodeRef node); + +typedef enum YGOverflow { + YGOverflowVisible, + YGOverflowHidden, + YGOverflowScroll, +} YGOverflow; + +void YGNodeStyleSetOverflow(YGNodeRef node, YGOverflow overflow); +YGOverflow YGNodeStyleGetOverflow(YGNodeRef node); + +void YGNodeStyleSetFlex(YGNodeRef node, float flex); + +void YGNodeStyleSetFlexGrow(YGNodeRef node, float flexGrow); +float YGNodeStyleGetFlexGrow(YGNodeRef node); + +void YGNodeStyleSetFlexShrink(YGNodeRef node, float flexShrink); +float YGNodeStyleGetFlexShrink(YGNodeRef node); + +void YGNodeStyleSetFlexBasis(YGNodeRef node, float flexBasis); +float YGNodeStyleGetFlexBasis(YGNodeRef node); + +typedef enum YGEdge { + YGEdgeLeft, + YGEdgeTop, + YGEdgeRight, + YGEdgeBottom, + YGEdgeStart, + YGEdgeEnd, + YGEdgeHorizontal, + YGEdgeVertical, + YGEdgeAll, +} YGEdge; + +void YGNodeStyleSetPosition(YGNodeRef node, YGEdge edge, float position); +float YGNodeStyleGetPosition(YGNodeRef node, YGEdge edge); + +void YGNodeStyleSetMargin(YGNodeRef node, YGEdge edge, float margin); +float YGNodeStyleGetMargin(YGNodeRef node, YGEdge edge); + +void YGNodeStyleSetPadding(YGNodeRef node, YGEdge edge, float padding); +float YGNodeStyleGetPadding(YGNodeRef node, YGEdge edge); + +void YGNodeStyleSetBorder(YGNodeRef node, YGEdge edge, float border); +float YGNodeStyleGetBorder(YGNodeRef node, YGEdge edge); + +void YGNodeStyleSetWidth(YGNodeRef node, float width); +float YGNodeStyleGetWidth(YGNodeRef node); + +void YGNodeStyleSetHeight(YGNodeRef node, float height); +float YGNodeStyleGetHeight(YGNodeRef node); + +void YGNodeStyleSetMinWidth(YGNodeRef node, float minWidth); +float YGNodeStyleGetMinWidth(YGNodeRef node); + +void YGNodeStyleSetMinHeight(YGNodeRef node, float minHeight); +float YGNodeStyleGetMinHeight(YGNodeRef node); + +void YGNodeStyleSetMaxWidth(YGNodeRef node, float maxWidth); +float YGNodeStyleGetMaxWidth(YGNodeRef node); + +void YGNodeStyleSetMaxHeight(YGNodeRef node, float maxHeight); +float YGNodeStyleGetMaxHeight(YGNodeRef node); + +void YGNodeStyleSetAspectRatio(YGNodeRef node, float aspectRatio); +float YGNodeStyleGetAspectRatio(YGNodeRef node); +``` + +### Layout results + +Once you have set up a tree of nodes with styles you will want to get the result of a layout calculation. Call `YGNodeCalculateLayout` with the desired width and height or `YGUndefined` to perform the layout calculation. Once this function returns the results of the layout calculation is stored on each node. Traverse the tree and retrieve the values from each node. + +```c +void YGNodeCalculateLayout(YGNodeRef node, + float availableWidth, + float availableHeight, + YGDirection parentDirection); +float YGNodeLayoutGetLeft(YGNodeRef node); +float YGNodeLayoutGetTop(YGNodeRef node); +float YGNodeLayoutGetRight(YGNodeRef node); +float YGNodeLayoutGetBottom(YGNodeRef node); +float YGNodeLayoutGetWidth(YGNodeRef node); +float YGNodeLayoutGetHeight(YGNodeRef node); +YGDirection YGNodeLayoutGetDirection(YGNodeRef node); +``` + +### Custom measurements + +Certain nodes need the ability to measure themselves, the most common example is nodes which represent text. Text has an intrinsic size and requires measuring itself to determine that size. This is not something Yoga can do as it requires relying on the host system's text rendering engine. + +- Call `YGNodeMarkDirty` if a node with a custom text measurement function needs to be re-measured during the next layout pass. + +> A measure function can only be attached to a leaf node in the hierarchy. + +```c +typedef enum YGMeasureMode { + YGMeasureModeUndefined, + YGMeasureModeExactly, + YGMeasureModeAtMost, +} YGMeasureMode; + +typedef struct YGSize { + float width; + float height; +} YGSize; + +typedef YGSize (*YGMeasureFunc)(YGNodeRef node, + float width, + YGMeasureMode widthMode, + float height, + YGMeasureMode heightMode); + +void YGNodeSetMeasureFunc(YGNodeRef node, YGMeasureFunc measureFunc); +YGMeasureFunc YGNodeGetMeasureFunc(YGNodeRef node); + +void YGNodeMarkDirty(YGNodeRef node); +bool YGNodeIsDirty(YGNodeRef node); +``` + +### Context + +Context is important when integrating Yoga into another layout system. Context allows you to associate another object with a `YGNodeRef`. This context can then be retrieved from a `YGNodeRef` when for example its measure function is called. This is what enables Yoga to rely on the Android and iOS system implementations of text measurement in React Native. + +```c +void YGNodeSetContext(YGNodeRef node, void *context); +void *YGNodeGetContext(YGNodeRef node); +``` + +### Logging + +Yoga will by default log to stdout and stderr. You may however customize this to instead log to your own logger. + +```c +typedef enum YGLogLevel { + YGLogLevelError, + YGLogLevelWarn, + YGLogLevelInfo, + YGLogLevelDebug, + YGLogLevelVerbose, +} YGLogLevel; + +typedef int (*YGLogger)(YGLogLevel level, char *format, va_list args); +void YGSetLogger(YGLogger logger); +void YGLog(YGLogLevel level, char *message, ...); +``` + +### Experiments + +Yoga has the concept of experiments. An experiment is a feature which is not yet stable. To enable a feature use the following functions. Once a feature has been tested and is ready to be released as a stable API we will remove its feature flag. + +```c +typedef enum YGExperimentalFeature { + // Current experiments +} YGExperimentalFeature; + +void YGSetExperimentalFeatureEnabled(YGExperimentalFeature feature, + bool enabled); +bool YGIsExperimentalFeatureEnabled(YGExperimentalFeature feature); +``` + +### Printing + +Yoga has some hooks to print debug information while calculating layout. With the printing APIs you can add additional information to a node when it is printed. + +```c +typedef enum YGPrintOptions { + YGPrintOptionsLayout = 1, + YGPrintOptionsStyle = 2, + YGPrintOptionsChildren = 4, +} YGPrintOptions; + +typedef void (*YGPrintFunc)(YGNodeRef node); + +void YGNodeSetPrintFunc(YGNodeRef node, YGPrintFunc printFunc); +YGPrintFunc YGNodeGetPrintFunc(YGNodeRef node); + +void YGNodePrint(YGNodeRef node, YGPrintOptions options); +``` diff --git a/devdocs/setting_up.md b/devdocs/setting_up.md index 119fb6eb8..26ddb2647 100644 --- a/devdocs/setting_up.md +++ b/devdocs/setting_up.md @@ -159,7 +159,7 @@ The idea is : 2. Then we will use NLabel and wrap it using NAPI and export it to JS side. This is what qlabel_wrap does. **NLabel**: Since NLabel has inherited from QLabel we can treat is as QLabel with extra methods and properties. Primary reason to extend QLabel to create NLabel is to add support for Event listeners and CSS styling using Flex. -So if you take a look at NLabel you will see, it inherits from QLabel and YogaWidget. It would in future inherit from more classes that implement event listeners etc. YogaWidget is a class that contains the magic that enables a regular Qt Widget to have Yoga node. A Yoga node is an instance used by yoga library to calculate a widgets position on the screen. Yoga is a library that will layout the widget on the screen. To do so we will specify the flex properties like alignitems, justify content, margin, paddings etc on the Yoga node of the widget. Apart from adding yoga node, YogaWidget adds support for specifying those yoga properties via Qt's stylesheet. (This is done by using Q_PROPERTY). To make this work we need to use something called as Q_OBJECT inside the class which is a C++ macro. Q_OBJECT will be expanded to relevant code by the compiler. In Qt whenever we add Q_OBJECT to a header file, we need to use a pre compiler called Qt MOC (Meta Object Compiler). The way we use it is +So if you take a look at NLabel you will see, it inherits from QLabel and NodeWidget. NodeWidget inturn inherits from YogaWidget and EventWidget. Event widget adds event handling support. YogaWidget is a class that contains the magic that enables a regular Qt Widget to have Yoga node. A Yoga node is an instance used by yoga library to calculate a widgets position on the screen. Yoga is a library that will layout the widget on the screen. To do so we will specify the flex properties like alignitems, justify content, margin, paddings etc on the Yoga node of the widget. Apart from adding yoga node, YogaWidget adds support for specifying those yoga properties via Qt's stylesheet. (This is done by using Q_PROPERTY). To make this work we need to use something called as Q_OBJECT inside the class which is a C++ macro. Q_OBJECT will be expanded to relevant code by the compiler. In Qt whenever we add Q_OBJECT to a header file, we need to use a pre compiler called Qt MOC (Meta Object Compiler). The way we use it is ``` moc headername.h -o headername_moc.cpp --include // example : ../../core/YogaWidget/yogawidget.h diff --git a/devdocs/styling.md b/devdocs/styling.md index a3e0311e0..da878b781 100644 --- a/devdocs/styling.md +++ b/devdocs/styling.md @@ -1 +1,101 @@ # How styling works? + +There are two parts to styling. + +1. Layout +2. 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: + +```cpp + // 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: + +```js +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 + +1. Every widget that wants to use flex layout should extend from `flexItem` found at `src/cpp/core/FlexLayout/flexitem.h`. + For example, see `nlabel.h` at `src/cpp/QtWidgets/QLabel/nlabel.h` + + NLabel inherits from `NodeWidget` which inherits from `YogaWidget` which inturn inherits from `FlexItem` + + - `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 via `YogaWidget` and event handling support via `EventWidget` + +#### 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: + + ```js + 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` + +#### 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.