├── CMakeLists.txt
├── src
├── CMakeLists.txt
├── FlexLayout.h
└── FlexLayout.c
├── example
├── CMakeLists.txt
└── demo.c
├── javascript
├── webpack.config.js
├── demo.js
├── package.json
├── demo.html
├── final-flags.gypi
├── binding.gyp
├── entry-node.js
├── entry-browser.js
├── binding.cpp
└── package-lock.json
├── .gitignore
├── LICENSE
└── README.md
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | project(FlexLayout)
2 |
3 | add_subdirectory(src)
4 | add_subdirectory(example)
5 |
--------------------------------------------------------------------------------
/src/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | project(lib)
2 |
3 | add_library(FlexLayout FlexLayout.h FlexLayout.c)
4 |
5 | set(LIBRARY_OUTPUT_PATH ${FlexLayout_SOURCE_DIR}/bin)
6 |
--------------------------------------------------------------------------------
/example/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | project(demo)
2 |
3 | add_executable(demo demo.c)
4 |
5 | include_directories(${FlexLayout_SOURCE_DIR}/src)
6 |
7 | add_dependencies(demo FlexLayout)
8 | target_link_libraries(demo FlexLayout)
9 |
10 | set(EXECUTABLE_OUTPUT_PATH ${FlexLayout_SOURCE_DIR}/bin)
11 |
--------------------------------------------------------------------------------
/javascript/webpack.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | entry: "./entry-browser",
3 | output: {
4 | library: "FlexLayout",
5 | libraryTarget: "umd",
6 | path: require("path").resolve(__dirname, "out"),
7 | filename: "FlexLayout.js"
8 | },
9 | node: {
10 | fs: "empty",
11 | module: "empty"
12 | },
13 | performance: {
14 | hints: false
15 | }
16 | };
17 |
--------------------------------------------------------------------------------
/javascript/demo.js:
--------------------------------------------------------------------------------
1 | var flex = require('./entry-node');
2 |
3 | function demo() {
4 | var root = new flex.Node();
5 | root.direction = flex.Vertical;
6 |
7 | var child1 = new flex.Node();
8 | child1.width = new flex.Length(50);
9 | child1.height = new flex.Length(50);
10 | root.add(child1);
11 |
12 | var child2 = new flex.Node();
13 | child2.setMeasure(function(size){return new flex.Size(10, 10);});
14 | root.add(child2);
15 |
16 | root.layout(NaN, NaN);
17 | root.print();
18 | }
19 |
20 | demo();
21 |
--------------------------------------------------------------------------------
/javascript/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "flex_layout",
3 | "version": "0.1.0",
4 | "scripts": {
5 | "autogypi": "autogypi",
6 | "node-gyp": "node-gyp",
7 | "emcc-path": "emcc-path",
8 | "copyasm": "copyasm",
9 | "ndts": "ndts",
10 | "install": "node-gyp configure build",
11 | "browser": "node-gyp configure build --asmjs=1 && webpack && ndts . > out/FlexLayout.d.ts",
12 | "test": "node demo.js"
13 | },
14 | "dependencies": {
15 | "nbind": "^0.3.6",
16 | "node-gyp": "^3.3.1",
17 | "webpack": "^2.2.0-rc.2"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/example/demo.c:
--------------------------------------------------------------------------------
1 |
2 | #include "FlexLayout.h"
3 |
4 | int main(int argc, char const *argv[])
5 | {
6 | FlexNodeRef root = Flex_newNode();
7 |
8 | FlexNodeRef child1 = Flex_newNode();
9 | Flex_setWidth(child1, 50);
10 | Flex_setHeight(child1, 50);
11 | Flex_addChild(root, child1);
12 |
13 | FlexNodeRef child2 = Flex_newNode();
14 | Flex_setWidth(child2, 50);
15 | Flex_setHeight(child2, 50);
16 | Flex_addChild(root, child2);
17 |
18 | Flex_layout(root, FlexUndefined, FlexUndefined, 1);
19 | Flex_print(root, FlexPrintDefault);
20 |
21 | Flex_freeNodeRecursive(root);
22 |
23 | return 0;
24 | }
25 |
--------------------------------------------------------------------------------
/javascript/demo.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
27 |
28 |
--------------------------------------------------------------------------------
/javascript/final-flags.gypi:
--------------------------------------------------------------------------------
1 | {
2 | "conditions": [
3 |
4 | [ "1==1", {
5 |
6 | "cflags_cc": [
7 | "-std=c++14",
8 | "-DNBIND_DUPLICATE_POINTERS"
9 | ],
10 |
11 | "xcode_settings": {
12 | "OTHER_CPLUSPLUSFLAGS": [ "<@(_cflags_cc)" ]
13 | }
14 |
15 | } ],
16 |
17 | [ "asmjs==1", {
18 |
19 | "ldflags": [
20 | "--memory-init-file", "0",
21 | "-s", "PRECISE_F32=1",
22 | "-s", "TOTAL_MEMORY=134217728"
23 | ],
24 |
25 | "xcode_settings": {
26 | "OTHER_LDFLAGS": [ "<@(_ldflags)" ]
27 | }
28 |
29 | } ]
30 |
31 | ]
32 | }
33 |
--------------------------------------------------------------------------------
/javascript/binding.gyp:
--------------------------------------------------------------------------------
1 | {
2 | "targets": [
3 | {
4 | "include_dirs": [
5 | "node_modules/nan"
6 | ],
7 | "includes": [
8 | "node_modules/nbind/src/nbind.gypi"
9 | ],
10 | "conditions": [
11 | [
12 | "asmjs==1",
13 | {
14 | "ldflags": [
15 | "--memory-init-file",
16 | "0",
17 | "-s",
18 | "PRECISE_F32=1",
19 | "-s",
20 | "TOTAL_MEMORY=134217728"
21 | ],
22 | "xcode_settings": {
23 | "OTHER_LDFLAGS": [
24 | "<@(_ldflags)"
25 | ]
26 | }
27 | }
28 | ]
29 | ],
30 | "sources": [
31 | "binding.cpp"
32 | ]
33 | }
34 | ],
35 | "includes": [
36 | "node_modules/nbind/src/nbind-common.gypi"
37 | ]
38 | }
--------------------------------------------------------------------------------
/javascript/entry-node.js:
--------------------------------------------------------------------------------
1 | var nbind = require('nbind').init();
2 |
3 | var FlexDirection = {
4 | Horizontal: 0,
5 | Vertical: 1,
6 | HorizontalReverse: 2,
7 | VerticalReverse: 3,
8 | };
9 |
10 | var FlexWrapMode = {
11 | NoWrap: 0,
12 | Wrap: 1,
13 | WrapReverse: 2,
14 | };
15 |
16 | var FlexAlign = {
17 | Inherit: 0,
18 | Stretch: 1,
19 | Start: 2,
20 | Center: 3,
21 | End: 4,
22 | SpaceBetween: 5,
23 | SpaceAround: 6,
24 | Baseline: 7,
25 | };
26 |
27 | var FlexLengthType = {
28 | LengthTypeUndefined: 0,
29 | LengthTypePoint: 1,
30 | LengthTypePercent: 2,
31 | LengthTypeAuto: 3,
32 | LengthTypeContent: 4,
33 | };
34 |
35 | module.exports = Object.assign(nbind.lib, {
36 | Undefined: NaN,
37 | }, FlexDirection, FlexWrapMode, FlexAlign, FlexLengthType);
38 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Windows image file caches
2 | Thumbs.db
3 | ehthumbs.db
4 |
5 | # Folder config file
6 | Desktop.ini
7 |
8 | # Recycle Bin used on file shares
9 | $RECYCLE.BIN/
10 |
11 | # Windows Installer files
12 | *.cab
13 | *.msi
14 | *.msm
15 | *.msp
16 |
17 | # build directories
18 | build*/
19 |
20 | # binary directory
21 | bin/
22 |
23 | # Windows shortcuts
24 | *.lnk
25 |
26 | # =========================
27 | # Operating System Files
28 | # =========================
29 |
30 | # OSX
31 | # =========================
32 |
33 | .DS_Store
34 | .AppleDouble
35 | .LSOverride
36 |
37 | # Thumbnails
38 | ._*
39 |
40 | # Files that might appear on external disk
41 | .Spotlight-V100
42 | .Trashes
43 |
44 | # Directories potentially created on remote AFP share
45 | .AppleDB
46 | .AppleDesktop
47 | Network Trash Folder
48 | Temporary Items
49 | .apdisk
50 |
51 | node_modules/
52 | out/
53 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Sleen
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/javascript/entry-browser.js:
--------------------------------------------------------------------------------
1 | var nbind = require('./build/Release/nbind.js');
2 |
3 | var ran = false;
4 | var ret = null;
5 |
6 | nbind({}, function (err, result) {
7 |
8 | if (ran)
9 | return;
10 |
11 | ran = true;
12 |
13 | if (err)
14 | throw err;
15 |
16 | ret = result;
17 |
18 | });
19 |
20 | if (!ran)
21 | throw new Error('Failed to load the yoga module - it needed to be loaded synchronously, but didn\'t');
22 |
23 | var FlexDirection = {
24 | Horizontal: 0,
25 | Vertical: 1,
26 | HorizontalReverse: 2,
27 | VerticalReverse: 3,
28 | };
29 |
30 | var FlexWrapMode = {
31 | NoWrap: 0,
32 | Wrap: 1,
33 | WrapReverse: 2,
34 | };
35 |
36 | var FlexAlign = {
37 | Inherit: 0,
38 | Stretch: 1,
39 | Start: 2,
40 | Center: 3,
41 | End: 4,
42 | SpaceBetween: 5,
43 | SpaceAround: 6,
44 | Baseline: 7,
45 | };
46 |
47 | var FlexLengthType = {
48 | LengthTypeUndefined: 0,
49 | LengthTypePoint: 1,
50 | LengthTypePercent: 2,
51 | LengthTypeAuto: 3,
52 | LengthTypeContent: 4,
53 | };
54 |
55 | module.exports = Object.assign(ret.lib, {
56 | Undefined: NaN,
57 | }, FlexDirection, FlexWrapMode, FlexAlign, FlexLengthType);
58 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # FlexLayout
2 |
3 | [中文介绍](#中文介绍)
4 |
5 | `FlexLayout` is an `C` implementation of `Flexible Box` layout. It follows the standard [algorithm](https://www.w3.org/TR/css-flexbox-1/#layout-algorithm) and supports almost all features.
6 |
7 | `FlexLayout` is battle tested. Please feel free to use.
8 |
9 | ### Usage
10 |
11 | #### C/C++
12 | ```C
13 | FlexNodeRef root = Flex_newNode();
14 |
15 | FlexNodeRef child1 = Flex_newNode();
16 | Flex_setWidth(child1, 50);
17 | Flex_setHeight(child1, 50);
18 | Flex_addChild(root, child1);
19 |
20 | FlexNodeRef child2 = Flex_newNode();
21 | Flex_setWidth(child2, 50);
22 | Flex_setHeight(child2, 50);
23 | Flex_addChild(root, child2);
24 |
25 | Flex_layout(root, FlexUndefined, FlexUndefined, 1);
26 | Flex_print(root, FlexPrintDefault);
27 |
28 | Flex_freeNodeRecursive(root);
29 | ```
30 |
31 | #### Javascript
32 |
33 | `FlexLayout` has ported to javascript by using [`nbind`](https://github.com/charto/nbind).
34 |
35 | ```js
36 | var root = new flex.Node();
37 | root.direction = flex.Vertical;
38 |
39 | var child1 = new flex.Node();
40 | child1.width = new flex.Length(50);
41 | child1.height = new flex.Length(50);
42 | root.add(child1);
43 |
44 | var child2 = new flex.Node();
45 | child2.setMeasure(function(size){return new flex.Size(10, 10);});
46 | root.add(child2);
47 |
48 | root.layout(NaN, NaN);
49 | root.print();
50 | ```
51 |
52 | ### Features
53 |
54 | - margin/padding
55 | - min/max size
56 | - flex-direction
57 | - wrap
58 | - align-items
59 | - align-self
60 | - align-content
61 | - justify-content
62 | - flex-basis
63 | - flex-grow
64 | - flex-shrink
65 | - percentage
66 |
67 | ### Additions
68 |
69 | `FlexLayout` added some useful properties:
70 |
71 | - `spacing / line-spacing` spacing between items/lines
72 | - `fixed` do not participate the flex layout,like `position: absolute`
73 | - `lines` maximum number of lines
74 | - `items-per-line` maximum number of items in each line
75 |
76 | ### Known Issues
77 |
78 | - `min-width`/`min-height` do not support [`auto`](https://www.w3.org/TR/css-flexbox-1/#min-size-auto)
79 | - `order` is not supported
80 |
81 | ### License
82 |
83 | Licensed under the MIT License.
84 |
85 | ---
86 |
87 | ## 中文介绍
88 |
89 | `FlexLayout` 是 `Flexible Box` 布局算法的 `C` 语言实现。按照标准[算法](https://www.w3.org/TR/css-flexbox-1/#layout-algorithm)实现,几乎支持所有特性。
90 |
91 | `FlexLayout` 经过充分测试,请放心食用。
92 |
93 | ### 使用
94 |
95 | #### C/C++
96 | ```C
97 | FlexNodeRef root = Flex_newNode();
98 |
99 | FlexNodeRef child1 = Flex_newNode();
100 | Flex_setWidth(child1, 50);
101 | Flex_setHeight(child1, 50);
102 | Flex_addChild(root, child1);
103 |
104 | FlexNodeRef child2 = Flex_newNode();
105 | Flex_setWidth(child2, 50);
106 | Flex_setHeight(child2, 50);
107 | Flex_addChild(root, child2);
108 |
109 | Flex_layout(root, FlexUndefined, FlexUndefined, 1);
110 | Flex_print(root, FlexPrintDefault);
111 |
112 | Flex_freeNodeRecursive(root);
113 | ```
114 |
115 | #### Javascript
116 |
117 | `FlexLayout` 使用 [`nbind`](https://github.com/charto/nbind) 移植了 javascript 版本。
118 |
119 | ```js
120 | var root = new flex.Node();
121 | root.direction = flex.Vertical;
122 |
123 | var child1 = new flex.Node();
124 | child1.width = new flex.Length(50);
125 | child1.height = new flex.Length(50);
126 | root.add(child1);
127 |
128 | var child2 = new flex.Node();
129 | child2.setMeasure(function(size){return new flex.Size(10, 10);});
130 | root.add(child2);
131 |
132 | root.layout(NaN, NaN);
133 | root.print();
134 | ```
135 |
136 | ### 支持的特性
137 |
138 | - margin/padding
139 | - min/max size
140 | - flex-direction
141 | - wrap
142 | - align-items
143 | - align-self
144 | - align-content
145 | - justify-content
146 | - flex-basis
147 | - flex-grow
148 | - flex-shrink
149 | - 百分比
150 |
151 | ### 扩展属性
152 |
153 | `FlexLayout` 增加了以下扩展属性:
154 |
155 | - `spacing / line-spacing` 为每两个元素/行之间增加间距
156 | - `fixed` 元素不参与 flex 布局,类似于 `position: absolute`
157 | - `lines` 最大行数
158 | - `items-per-line` 每行最大元素个数
159 |
160 | ### 已知问题
161 |
162 | - `min-width`/`min-height` 不支持设置为 [`auto`](https://www.w3.org/TR/css-flexbox-1/#min-size-auto)
163 | - 不支持 `order` 属性
164 |
165 | ### 协议
166 |
167 | 遵循 MIT 协议。
168 |
--------------------------------------------------------------------------------
/javascript/binding.cpp:
--------------------------------------------------------------------------------
1 | extern "C" {
2 | #include "../src/FlexLayout.c"
3 | }
4 |
5 | #include
6 | #include
7 | #include
8 |
9 | template struct _WrapType { using type = T; };
10 | template struct _WrapType { using type = typename std::underlying_type::type; };
11 | template struct WrapType {
12 | using type = typename _WrapType::value>::type;
13 | };
14 |
15 | typedef struct Size {
16 | float width;
17 | float height;
18 |
19 | Size(float width, float height) {
20 | this->width = width;
21 | this->height = height;
22 | }
23 |
24 | Size(): Size(0, 0) {}
25 |
26 | float getWidth() { return width; }
27 | void setWidth(float width) { this->width = width; }
28 |
29 | float getHeight() { return height; }
30 | void setHeight(float height) { this->height = height; }
31 |
32 | } Size;
33 |
34 | struct Length {
35 | using LengthType = typename WrapType::type;
36 |
37 | float value;
38 | LengthType type;
39 |
40 | Length(float value, LengthType type) {
41 | this->value = value;
42 | this->type = type;
43 | }
44 |
45 | Length(float value): Length(value, (LengthType)FlexLengthTypePoint) {}
46 |
47 | Length(): Length(0, (LengthType)FlexLengthTypePoint) {}
48 |
49 | float getValue() { return value; }
50 | void setValue(float value) { this->value = value; }
51 |
52 | LengthType getType() { return type; }
53 | void setType(LengthType type) { this->type = type; }
54 | };
55 |
56 | class Node {
57 | private:
58 | FlexNodeRef _node;
59 |
60 | std::vector> children;
61 |
62 | typedef std::function MeasureCallback;
63 | typedef std::function BaselineCallback;
64 |
65 | MeasureCallback measure;
66 | BaselineCallback baseline;
67 |
68 | static FlexSize static_measure(void* context, FlexSize constrainedSize) {
69 | Node* node = (Node*)context;
70 | Size size = node->measure(Size{constrainedSize.width, constrainedSize.height});
71 | return FlexSize{size.width, size.height};
72 | }
73 |
74 | static float static_baseline(void* context, FlexSize constrainedSize) {
75 | Node* node = (Node*)context;
76 | return node->baseline(Size{constrainedSize.width, constrainedSize.height});
77 | }
78 |
79 | public:
80 | Node() {
81 | // printf("%x\n", this);
82 | _node = Flex_newNode();
83 | Flex_setContext(_node, this);
84 | }
85 |
86 | Node(const Node& node) {
87 | // printf("%x -> %x\n", &node, this);
88 | _node = node._node;
89 | Flex_setContext(_node, this);
90 | children = node.children;
91 | measure = node.measure;
92 | baseline = node.baseline;
93 | }
94 |
95 | ~Node() {
96 | // printf("%x free\n", this);
97 | Flex_freeNode(_node);
98 | }
99 |
100 | void setMeasure(MeasureCallback measure) {
101 | this->measure = measure;
102 | Flex_setMeasureFunc(_node, measure ? Node::static_measure : NULL);
103 | }
104 |
105 | void setBaseline(BaselineCallback baseline) {
106 | this->baseline = baseline;
107 | Flex_setBaselineFunc(_node, baseline ? Node::static_baseline : NULL);
108 | }
109 |
110 | #define FLEX_GETTER(TYPE, Name, field) WrapType::type get##Name() { return (WrapType::type)Flex_get##Name(_node); }
111 | #define FLEX_SETTER(TYPE, Name, field) void set##Name(WrapType::type Name) { Flex_set##Name(_node, (TYPE)Name); }
112 | #define FLEX_LENGTH_PROPERTY(Name, field) \
113 | Length get##Name() { FlexLength ret = Flex_get##Name(_node); return *(Length*)&ret; } \
114 | void set##Name(Length Name) { Flex_set##Name##_Length(_node, *(FlexLength*)&Name); }
115 | #define FLEX_SETTER_LENGTH_VALUE(Name, field, Type)
116 | #define FLEX_SETTER_LENGTH_TYPE(Name, field, Type)
117 |
118 | FLEX_PROPERTYES()
119 | FLEX_EXT_PROPERTYES()
120 | FLEX_RESULT_PROPERTYES()
121 |
122 | #undef FLEX_GETTER
123 | #undef FLEX_SETTER
124 | #undef FLEX_LENGTH_PROPERTY
125 | #undef FLEX_SETTER_LENGTH_VALUE
126 | #undef FLEX_SETTER_LENGTH_TYPE
127 |
128 | void layout(float constrainedWidth, float constrainedHeight, float scale) {
129 | Flex_layout(_node, constrainedWidth, constrainedHeight, scale);
130 | }
131 |
132 | void layout(float constrainedWidth, float constrainedHeight) {
133 | layout(constrainedWidth, constrainedHeight, 1);
134 | }
135 |
136 | void print() {
137 | Flex_print(_node, (FlexPrintOptions)0);
138 | }
139 |
140 | void add(std::shared_ptr node) {
141 | children.push_back(node);
142 | Flex_addChild(_node, node->_node);
143 | }
144 |
145 | void insert(std::shared_ptr node, size_t index) {
146 | children.insert(children.begin() + index, node);
147 | Flex_insertChild(_node, node->_node, index);
148 | }
149 |
150 | void remove(std::shared_ptr node) {
151 | for (auto it = children.begin(); it != children.end(); it++) {
152 | if ((*it)->_node == node->_node) {
153 | children.erase(it);
154 | Flex_removeChild(_node, node->_node);
155 | return;
156 | }
157 | }
158 | }
159 |
160 | std::shared_ptr childAt(size_t index) {
161 | return children[index];
162 | }
163 |
164 | size_t getChildrenCount() {
165 | return children.size();
166 | }
167 | };
168 |
169 | #include "nbind/nbind.h"
170 |
171 | NBIND_CLASS(Size) {
172 | construct<>();
173 | construct();
174 | getset(getWidth, setWidth);
175 | getset(getHeight, setHeight);
176 | }
177 |
178 | NBIND_CLASS(Length) {
179 | construct<>();
180 | construct();
181 | construct();
182 | getset(getValue, setValue);
183 | getset(getType, setType);
184 | }
185 |
186 | NBIND_CLASS(Node) {
187 |
188 | #define FLEX_PROPERTY(type, Name, field) getset(get##Name, set##Name)
189 | #define FLEX_LENGTH_PROPERTY(Name, field) getset(get##Name, set##Name)
190 | #define FLEX_RESULT_PROPERTY(Name, field) getter(getResult##Name)
191 | #define FLEX_SETTER_LENGTH_VALUE(Name, field, Type)
192 | #define FLEX_SETTER_LENGTH_TYPE(Name, field, Type)
193 |
194 | FLEX_PROPERTYES()
195 | FLEX_EXT_PROPERTYES()
196 | FLEX_RESULT_PROPERTYES()
197 |
198 | #undef FLEX_PROPERTY
199 | #undef FLEX_LENGTH_PROPERTY
200 | #undef FLEX_RESULT_PROPERTY
201 | #undef FLEX_SETTER_LENGTH_VALUE
202 | #undef FLEX_SETTER_LENGTH_TYPE
203 |
204 | construct<>();
205 | method(setMeasure);
206 | method(setBaseline);
207 | multimethod(layout, args(float, float));
208 | multimethod(layout, args(float, float, float), "layoutWithScale");
209 | method(add);
210 | method(insert);
211 | method(remove);
212 | method(childAt);
213 | getter(getChildrenCount);
214 | method(print);
215 | }
216 |
--------------------------------------------------------------------------------
/src/FlexLayout.h:
--------------------------------------------------------------------------------
1 | //
2 | // FlexLayout.h
3 | // flex_layout
4 | //
5 | // Created by Sleen on 16/1/25.
6 | // Copyright © 2016年 Sleen. All rights reserved.
7 | //
8 |
9 | #pragma once
10 |
11 | #ifdef __cplusplus
12 | extern "C"{
13 | #endif
14 |
15 | #include
16 | #include
17 | #ifndef __cplusplus
18 | # include
19 | #endif
20 |
21 | typedef enum {
22 | FlexHorizontal,
23 | FlexVertical,
24 | FlexHorizontalReverse,
25 | FlexVerticalReverse
26 | } FlexDirection;
27 |
28 | typedef enum {
29 | FlexNoWrap,
30 | FlexWrap,
31 | FlexWrapReverse,
32 | } FlexWrapMode;
33 |
34 | typedef enum {
35 | FlexInherit,
36 | FlexStretch,
37 | FlexStart,
38 | FlexCenter,
39 | FlexEnd,
40 | FlexSpaceBetween,
41 | FlexSpaceAround,
42 | FlexBaseline,
43 | } FlexAlign;
44 |
45 | typedef enum {
46 | FlexLengthTypeUndefined,
47 | FlexLengthTypePoint,
48 | FlexLengthTypePercent,
49 | FlexLengthTypeAuto,
50 | FlexLengthTypeContent,
51 | } FlexLengthType;
52 |
53 |
54 | typedef struct {
55 | float value;
56 | FlexLengthType type;
57 | } FlexLength;
58 |
59 |
60 | #define FlexUndefined NAN
61 | #define FlexIsUndefined(n) isnan(n)
62 |
63 | #define FlexLengthZero (FlexLength){ 0, FlexLengthTypePoint }
64 | #define FlexLengthAuto (FlexLength){ NAN, FlexLengthTypeAuto }
65 | #define FlexLengthContent (FlexLength){ NAN, FlexLengthTypeContent }
66 | #define FlexLengthUndefined (FlexLength){ NAN, FlexLengthTypeUndefined }
67 |
68 |
69 | typedef union {
70 | struct {
71 | float width;
72 | float height;
73 | };
74 | float size[2];
75 | } FlexSize;
76 |
77 | typedef enum {
78 | FlexPrintDefault = 0,
79 | FlexPrintStyle = 1 << 0,
80 | FlexPrintResult = 1 << 1,
81 | FlexPrintChildren = 1 << 2,
82 | FlexPrintHideUnspecified = 1 << 3,
83 | } FlexPrintOptions;
84 |
85 | typedef struct FlexNode * FlexNodeRef;
86 |
87 | typedef FlexSize (*FlexMeasureFunc )(void* context, FlexSize constrainedSize);
88 | typedef float (*FlexBaselineFunc)(void* context, FlexSize constrainedSize);
89 |
90 |
91 | #define FLEX_PROPERTY(type, Name, field) \
92 | FLEX_GETTER(type, Name, field) \
93 | FLEX_SETTER(type, Name, field)
94 | #define FLEX_FLOAT_PROPERTY(Name, field) FLEX_PROPERTY(float, Name, field)
95 | #define FLEX_LENGTH_PROPERTY(Name, field) \
96 | FLEX_GETTER(FlexLength, Name, field) \
97 | FLEX_SETTER_LENGTH(Name##_Length, field) \
98 | FLEX_SETTER_LENGTH_VALUE(Name, field, Point) \
99 | FLEX_SETTER_LENGTH_VALUE(Name##Percent, field, Percent)
100 | #define FLEX_LENGTH_PROPERTY_AUTO(Name, field) \
101 | FLEX_LENGTH_PROPERTY(Name, field) \
102 | FLEX_SETTER_LENGTH_TYPE(Name, field, Auto)
103 | #define FLEX_LENGTH_PROPERTY_AUTO_CONTENT(Name, field) \
104 | FLEX_LENGTH_PROPERTY_AUTO(Name, field) \
105 | FLEX_SETTER_LENGTH_TYPE(Name, field, Content)
106 | #define FLEX_RESULT_PROPERTY(Name, field) FLEX_GETTER(float, Result##Name, field)
107 |
108 | #define FLEX_PROPERTYES() \
109 | FLEX_PROPERTY ( FlexWrapMode, Wrap, wrap ); \
110 | FLEX_PROPERTY ( FlexDirection, Direction, direction ); \
111 | FLEX_PROPERTY ( FlexAlign, AlignItems, alignItems ); \
112 | FLEX_PROPERTY ( FlexAlign, AlignSelf, alignSelf ); \
113 | FLEX_PROPERTY ( FlexAlign, AlignContent, alignContent ); \
114 | FLEX_PROPERTY ( FlexAlign, JustifyContent, justifyContent ); \
115 | FLEX_LENGTH_PROPERTY_AUTO_CONTENT ( FlexBasis, flexBasis ); \
116 | FLEX_FLOAT_PROPERTY ( FlexGrow, flexGrow ); \
117 | FLEX_FLOAT_PROPERTY ( FlexShrink, flexShrink ); \
118 | FLEX_LENGTH_PROPERTY_AUTO ( Width, size[FLEX_WIDTH] ); \
119 | FLEX_LENGTH_PROPERTY_AUTO ( Height, size[FLEX_HEIGHT] ); \
120 | FLEX_LENGTH_PROPERTY ( MinWidth, minSize[FLEX_WIDTH] ); \
121 | FLEX_LENGTH_PROPERTY ( MinHeight, minSize[FLEX_HEIGHT] ); \
122 | FLEX_LENGTH_PROPERTY ( MaxWidth, maxSize[FLEX_WIDTH] ); \
123 | FLEX_LENGTH_PROPERTY ( MaxHeight, maxSize[FLEX_HEIGHT] ); \
124 | FLEX_LENGTH_PROPERTY_AUTO ( MarginLeft, margin[FLEX_LEFT] ); \
125 | FLEX_LENGTH_PROPERTY_AUTO ( MarginTop, margin[FLEX_TOP] ); \
126 | FLEX_LENGTH_PROPERTY_AUTO ( MarginBottom, margin[FLEX_BOTTOM] ); \
127 | FLEX_LENGTH_PROPERTY_AUTO ( MarginRight, margin[FLEX_RIGHT] ); \
128 | FLEX_LENGTH_PROPERTY_AUTO ( MarginStart, margin[FLEX_START] ); \
129 | FLEX_LENGTH_PROPERTY_AUTO ( MarginEnd, margin[FLEX_END] ); \
130 | FLEX_LENGTH_PROPERTY ( PaddingLeft, padding[FLEX_LEFT] ); \
131 | FLEX_LENGTH_PROPERTY ( PaddingTop, padding[FLEX_TOP] ); \
132 | FLEX_LENGTH_PROPERTY ( PaddingBottom, padding[FLEX_BOTTOM] ); \
133 | FLEX_LENGTH_PROPERTY ( PaddingRight, padding[FLEX_RIGHT] ); \
134 | FLEX_LENGTH_PROPERTY ( PaddingStart, padding[FLEX_START] ); \
135 | FLEX_LENGTH_PROPERTY ( PaddingEnd, padding[FLEX_END] ); \
136 | FLEX_FLOAT_PROPERTY ( BorderLeft, border[FLEX_LEFT] ); \
137 | FLEX_FLOAT_PROPERTY ( BorderTop, border[FLEX_TOP] ); \
138 | FLEX_FLOAT_PROPERTY ( BorderBottom, border[FLEX_BOTTOM] ); \
139 | FLEX_FLOAT_PROPERTY ( BorderRight, border[FLEX_RIGHT] ); \
140 | FLEX_FLOAT_PROPERTY ( BorderStart, border[FLEX_START] ); \
141 | FLEX_FLOAT_PROPERTY ( BorderEnd, border[FLEX_END] ); \
142 | FLEX_PROPERTY ( void*, Context, context ); \
143 |
144 | #define FLEX_CALLBACK_PROPERTIES() \
145 | FLEX_PROPERTY ( FlexMeasureFunc, MeasureFunc, measure ); \
146 | FLEX_PROPERTY ( FlexBaselineFunc, BaselineFunc, baseline ); \
147 |
148 | #define FLEX_EXT_PROPERTYES() \
149 | FLEX_PROPERTY ( bool, Fixed, fixed ); \
150 | FLEX_LENGTH_PROPERTY ( Spacing, spacing ); \
151 | FLEX_LENGTH_PROPERTY ( LineSpacing, lineSpacing ); \
152 | FLEX_PROPERTY ( unsigned int, Lines, lines ); \
153 | FLEX_PROPERTY ( unsigned int, ItemsPerLine, itemsPerLine ); \
154 |
155 | #define FLEX_RESULT_PROPERTYES() \
156 | FLEX_RESULT_PROPERTY ( Width, result.size[FLEX_WIDTH] ); \
157 | FLEX_RESULT_PROPERTY ( Height, result.size[FLEX_HEIGHT] ); \
158 | FLEX_RESULT_PROPERTY ( Left, result.position[FLEX_LEFT] ); \
159 | FLEX_RESULT_PROPERTY ( Top, result.position[FLEX_TOP] ); \
160 | FLEX_RESULT_PROPERTY ( MarginLeft, result.margin[FLEX_LEFT] ); \
161 | FLEX_RESULT_PROPERTY ( MarginRight, result.margin[FLEX_RIGHT] ); \
162 | FLEX_RESULT_PROPERTY ( MarginTop, result.margin[FLEX_TOP] ); \
163 | FLEX_RESULT_PROPERTY ( MarginBottom, result.margin[FLEX_BOTTOM] ); \
164 | FLEX_RESULT_PROPERTY ( PaddingLeft, result.padding[FLEX_LEFT] ); \
165 | FLEX_RESULT_PROPERTY ( PaddingRight, result.padding[FLEX_RIGHT] ); \
166 | FLEX_RESULT_PROPERTY ( PaddingTop, result.padding[FLEX_TOP] ); \
167 | FLEX_RESULT_PROPERTY ( PaddingBottom, result.padding[FLEX_BOTTOM] ); \
168 |
169 |
170 | // declaration of getters and setters
171 | #define FLEX_GETTER(type, Name, field) type Flex_get##Name(FlexNodeRef node);
172 | #define FLEX_SETTER(type, Name, field) void Flex_set##Name(FlexNodeRef node, type Name);
173 | #define FLEX_SETTER_LENGTH(Name, field) FLEX_SETTER(FlexLength, Name, field)
174 | #define FLEX_SETTER_LENGTH_VALUE(Name, field, Type) void Flex_set##Name(FlexNodeRef node, float Name);
175 | #define FLEX_SETTER_LENGTH_TYPE(Name, field, Type) void Flex_set##Name##Type(FlexNodeRef node);
176 |
177 | FLEX_PROPERTYES()
178 | FLEX_CALLBACK_PROPERTIES()
179 | FLEX_EXT_PROPERTYES()
180 | FLEX_RESULT_PROPERTYES()
181 |
182 | #undef FLEX_GETTER
183 | #undef FLEX_SETTER
184 | #undef FLEX_SETTER_LENGTH
185 | #undef FLEX_SETTER_LENGTH_VALUE
186 | #undef FLEX_SETTER_LENGTH_TYPE
187 |
188 |
189 | FlexNodeRef Flex_newNode();
190 | void Flex_freeNode (FlexNodeRef node);
191 | void Flex_freeNodeRecursive (FlexNodeRef node);
192 | void Flex_insertChild (FlexNodeRef node, FlexNodeRef child, size_t index);
193 | void Flex_addChild (FlexNodeRef node, FlexNodeRef child);
194 | void Flex_removeChild (FlexNodeRef node, FlexNodeRef child);
195 | FlexNodeRef Flex_getChild (FlexNodeRef node, size_t index);
196 | size_t Flex_getChildrenCount (FlexNodeRef node);
197 | void Flex_layout (FlexNodeRef node, float constrainedWidth, float constrainedHeight, float scale);
198 | void Flex_print (FlexNodeRef node, FlexPrintOptions options);
199 |
200 |
201 | #ifdef __cplusplus
202 | }
203 | #endif
204 |
--------------------------------------------------------------------------------
/src/FlexLayout.c:
--------------------------------------------------------------------------------
1 | //
2 | // FlexLayout.m
3 | // flex_layout
4 | //
5 | // Created by Sleen on 16/1/25.
6 | // Copyright © 2016年 Sleen. All rights reserved.
7 | //
8 |
9 | #include "FlexLayout.h"
10 |
11 | #include
12 | #include
13 | #include
14 | #include
15 |
16 |
17 | #if DEBUG
18 | # define flex_assert(e) assert(e)
19 | #else
20 | # define flex_assert(e) ((void)0)
21 | #endif
22 |
23 |
24 | #define FlexTreatNanAsInf(n) (isnan(n) ? INFINITY : n)
25 | #define FlexFloatEquals(a, b) ((isnan(a) && isnan(b)) || a == b)
26 | #define FlexLengthEquals(a, b) (FlexFloatEquals(a.value, b.value) && a.type == b.type)
27 | #define FlexPixelRound(value, scale) (roundf((value) * (scale)) / (scale))
28 | #define FlexIsResolved(n) !FlexIsUndefined(n)
29 | #define FlexCacheSizeUndefined (FlexSize){ -1000, -1000 }
30 |
31 | #define FlexVector(type) FlexVector_##type
32 | #define FlexVectorRef(type) FlexVector(type)*
33 | #define FlexVector_new(type, initialCapacity) _FlexVector_new_##type(initialCapacity)
34 | #define FlexVector_free(type, vector) _FlexVector_free_##type(vector)
35 | #define FlexVector_insert(type, vector, value, index) _FlexVector_insert_##type(vector, value, index)
36 | #define FlexVector_add(type, vector, value) _FlexVector_add_##type(vector, value)
37 | #define FlexVector_removeAt(type, vector, index) _FlexVector_removeAt_##type(vector, index)
38 | #define FlexVector_remove(type, vector, value) _FlexVector_remove_##type(vector, value)
39 | #define FlexVector_size(type, vector) _FlexVector_size_##type(vector)
40 |
41 | #define FLEX_VECTOR_INIT_WITH_EQUALS(type, equals) \
42 | typedef struct { \
43 | size_t size; \
44 | size_t capacity; \
45 | type *data; \
46 | } FlexVector(type); \
47 | \
48 | FlexVectorRef(type) _FlexVector_new_##type(size_t initialCapacity) { \
49 | flex_assert(initialCapacity > 0); \
50 | FlexVectorRef(type) vector = (FlexVectorRef(type))malloc(sizeof(FlexVector(type))); \
51 | vector->size = 0; \
52 | vector->capacity = initialCapacity; \
53 | vector->data = (type *)malloc(sizeof(type) * vector->capacity); \
54 | return vector; \
55 | } \
56 | \
57 | void _FlexVector_free_##type(FlexVectorRef(type) vector) { \
58 | free(vector->data); \
59 | free(vector); \
60 | } \
61 | \
62 | void _FlexVector_insert_##type(FlexVectorRef(type) vector, type value, size_t index) { \
63 | if (vector->size == vector->capacity) { \
64 | vector->capacity *= 2; \
65 | vector->data = (type *)realloc(vector->data, sizeof(type) * vector->capacity); \
66 | } \
67 | for (size_t i = index; i < vector->size; i++) { \
68 | vector->data[i + 1] = vector->data[i]; \
69 | } \
70 | vector->data[index] = value; \
71 | vector->size++; \
72 | } \
73 | \
74 | void _FlexVector_add_##type(FlexVectorRef(type) vector, type value) { \
75 | _FlexVector_insert_##type(vector, value, vector->size); \
76 | } \
77 | \
78 | void _FlexVector_removeAt_##type(FlexVectorRef(type) vector, size_t index) { \
79 | for (size_t i = index + 1; i < vector->size; i++) { \
80 | vector->data[i - 1] = vector->data[i]; \
81 | } \
82 | vector->size--; \
83 | } \
84 | \
85 | void _FlexVector_remove_##type(FlexVectorRef(type) vector, type value) { \
86 | for (size_t i = 0; i < vector->size; i++) { \
87 | if (equals(vector->data[i], value)) { \
88 | _FlexVector_removeAt_##type(vector, i); \
89 | return; \
90 | } \
91 | } \
92 | } \
93 | \
94 | size_t _FlexVector_size_##type(FlexVectorRef(type) vector) { \
95 | return vector ? vector->size : 0; \
96 | } \
97 |
98 | #define FLEX_VECTOR_EQUALS_FUNC(a, b) (a == b)
99 | #define FLEX_VECTOR_INIT(type) FLEX_VECTOR_INIT_WITH_EQUALS(type, FLEX_VECTOR_EQUALS_FUNC)
100 |
101 |
102 | static const FlexDirection FLEX_WIDTH = FlexHorizontal;
103 | static const FlexDirection FLEX_HEIGHT = FlexVertical;
104 |
105 | typedef enum {
106 | FLEX_LEFT = 0,
107 | FLEX_TOP,
108 | FLEX_RIGHT,
109 | FLEX_BOTTOM,
110 | FLEX_START,
111 | FLEX_END
112 | } FlexPositionIndex;
113 |
114 | static FlexPositionIndex flex_start[4] = { FLEX_LEFT, FLEX_TOP, FLEX_RIGHT, FLEX_BOTTOM };
115 | static FlexPositionIndex flex_end[4] = { FLEX_RIGHT, FLEX_BOTTOM, FLEX_LEFT, FLEX_TOP };
116 | static FlexDirection flex_dim[4] = { FLEX_WIDTH, FLEX_HEIGHT, FLEX_WIDTH, FLEX_HEIGHT };
117 |
118 |
119 | typedef struct {
120 | float scale;
121 | } FlexLayoutContext;
122 |
123 | typedef enum {
124 | FlexLayoutFlagMeasureWidth = 1 << 0, // only measure width
125 | FlexLayoutFlagMeasureHeight = 1 << 1, // only measure height
126 | FlexLayoutFlagLayout = 1 << 2, // measure and layout
127 | } FlexLayoutFlags;
128 |
129 | typedef struct {
130 | float itemsSize;
131 | size_t itemsCount;
132 | float ascender;
133 | } FlexLine;
134 |
135 | typedef struct {
136 | float position[2];
137 | float size[2];
138 | float margin[4];
139 | float padding[4];
140 | } FlexResult;
141 |
142 | typedef struct {
143 | FlexSize availableSize;
144 | FlexSize measuredSize;
145 | } FlexMeasureCache;
146 |
147 | FLEX_VECTOR_INIT(FlexNodeRef)
148 | #define FlexSizeEquals(a, b) (FlexFloatEquals(a.width, b.width) && FlexFloatEquals(a.height, b.height))
149 | #define FlexMeasureCacheEquals(a, b) (FlexSizeEquals(a.availableSize, b.availableSize) && FlexSizeEquals(a.measuredSize, b.measuredSize))
150 | FLEX_VECTOR_INIT_WITH_EQUALS(FlexMeasureCache, FlexMeasureCacheEquals)
151 |
152 | typedef struct FlexNode {
153 | FlexWrapMode wrap;
154 | FlexDirection direction;
155 | FlexAlign alignItems;
156 | FlexAlign alignSelf;
157 | FlexAlign alignContent;
158 | FlexAlign justifyContent;
159 | FlexLength flexBasis; // length, percentage(relative to the flex container's inner main size), auto, content
160 | float flexGrow;
161 | float flexShrink;
162 | FlexLength size[2]; // length, percentage(relative to the flex container's inner size), auto
163 | FlexLength minSize[2]; // length, percentage(relative to the flex container's inner size)
164 | FlexLength maxSize[2]; // length, percentage(relative to the flex container's inner size), none
165 | FlexLength margin[6]; // length, percentage(relative to the flex container's inner width), auto
166 | FlexLength padding[6]; // length, percentage(relative to the flex container's inner width)
167 | float border[6]; // length
168 |
169 | // extension
170 | bool fixed;
171 | FlexLength spacing; // the spacing between each two items. length, percentage(relative to its inner main size)
172 | FlexLength lineSpacing; // the spacing between each two lines. length, percentage(relative to its inner cross size)
173 | unsigned int lines; // the maximum number of lines, 0 means no limit
174 | unsigned int itemsPerLine; // the maximum number of items per line, 0 means no limit
175 |
176 | FlexResult result;
177 |
178 | void* context;
179 | FlexMeasureFunc measure;
180 | FlexBaselineFunc baseline;
181 |
182 | FlexVectorRef(FlexNodeRef) children;
183 | FlexNodeRef parent;
184 |
185 | // internal fields
186 | float flexBaseSize;
187 | float resolvedMargin[4];
188 | float ascender;
189 | FlexLength calculatedFlexBasis;
190 | FlexAlign calculatedAlignSelf;
191 |
192 | // measure cache
193 | FlexVectorRef(FlexMeasureCache) measuredSizeCache;
194 |
195 | // layout cache
196 | FlexSize lastConstrainedSize;
197 | FlexLength lastSize[2];
198 |
199 | } FlexNode;
200 |
201 |
202 | static const FlexNode defaultFlexNode = {
203 | .wrap = FlexNoWrap,
204 | .direction = FlexHorizontal,
205 | .alignItems = FlexStretch,
206 | .alignSelf = FlexInherit,
207 | .alignContent = FlexStretch,
208 | .justifyContent = FlexStart,
209 | .flexBasis = FlexLengthAuto,
210 | .flexGrow = 0,
211 | .flexShrink = 1,
212 | .size = { FlexLengthAuto, FlexLengthAuto },
213 | .minSize = { FlexLengthZero, FlexLengthZero },
214 | .maxSize = { FlexLengthUndefined, FlexLengthUndefined },
215 | .margin = { FlexLengthZero, FlexLengthZero, FlexLengthZero, FlexLengthZero, FlexLengthUndefined, FlexLengthUndefined },
216 | .padding = { FlexLengthZero, FlexLengthZero, FlexLengthZero, FlexLengthZero, FlexLengthUndefined, FlexLengthUndefined },
217 | .border = { 0, 0, 0, 0, FlexUndefined, FlexUndefined },
218 |
219 | .fixed = false,
220 | .spacing = FlexLengthZero,
221 | .lineSpacing = FlexLengthZero,
222 | .lines = 0,
223 | .itemsPerLine = 0,
224 |
225 | .result = {
226 | .position = { 0, 0 },
227 | .size = { 0, 0 },
228 | .margin = { 0, 0, 0, 0 },
229 | .padding = { 0, 0, 0, 0 },
230 | },
231 |
232 | .context = NULL,
233 | .measure = NULL,
234 | .baseline = NULL,
235 |
236 | .children = NULL,
237 | .parent = NULL,
238 |
239 | .measuredSizeCache = NULL,
240 | .lastConstrainedSize = FlexCacheSizeUndefined,
241 | };
242 |
243 |
244 | void flex_markDirty(FlexNodeRef node) {
245 | node->lastConstrainedSize = FlexCacheSizeUndefined;
246 | if (node->parent) {
247 | flex_markDirty(node->parent);
248 | }
249 | }
250 |
251 |
252 | // implementation of getters and setters
253 | #define FLEX_GETTER(type, Name, field) \
254 | type Flex_get##Name(FlexNodeRef node) { \
255 | return node->field; \
256 | }
257 | #define FLEX_SETTER(type, Name, field) \
258 | void Flex_set##Name(FlexNodeRef node, type Name) { \
259 | if (node->field != Name) { \
260 | node->field = Name; \
261 | flex_markDirty(node); \
262 | } \
263 | }
264 | #define FLEX_SETTER_LENGTH(Name, field) \
265 | void Flex_set##Name(FlexNodeRef node, FlexLength Name) { \
266 | if (!FlexLengthEquals(node->field, Name)) { \
267 | node->field = Name; \
268 | flex_markDirty(node); \
269 | } \
270 | }
271 | #define FLEX_SETTER_LENGTH_VALUE(Name, field, Type) \
272 | void Flex_set##Name(FlexNodeRef node, float Name) { \
273 | FlexLength value = {Name, FlexLengthType##Type}; \
274 | if (!FlexLengthEquals(node->field, value)) { \
275 | node->field = value; \
276 | flex_markDirty(node); \
277 | } \
278 | }
279 | #define FLEX_SETTER_LENGTH_TYPE(Name, field, Type) \
280 | void Flex_set##Name##Type(FlexNodeRef node) { \
281 | if (node->field.type != FlexLengthType##Type) { \
282 | node->field.type = FlexLengthType##Type; \
283 | flex_markDirty(node); \
284 | } \
285 | }
286 |
287 | FLEX_PROPERTYES()
288 | FLEX_CALLBACK_PROPERTIES()
289 | FLEX_EXT_PROPERTYES()
290 | FLEX_RESULT_PROPERTYES()
291 |
292 | #undef FLEX_GETTER
293 | #undef FLEX_SETTER
294 | #undef FLEX_SETTER_LENGTH
295 | #undef FLEX_SETTER_LENGTH_VALUE
296 | #undef FLEX_SETTER_LENGTH_TYPE
297 |
298 |
299 | // for internal use
300 | #define FLEX_GETTER(type, Name, field) \
301 | void Flex_set##Name(FlexNodeRef node, type Name) { \
302 | node->field = Name; \
303 | }
304 | FLEX_RESULT_PROPERTYES()
305 | #undef FLEX_GETTER
306 |
307 |
308 | static inline bool flex_isAbsolute(FlexLength length) {
309 | return length.type == FlexLengthTypePoint;
310 | }
311 |
312 | static inline float flex_absoluteValue(FlexLength length, FlexLayoutContext *context) {
313 | flex_assert(flex_isAbsolute(length)); // absolute value is requested
314 | return length.value;
315 | }
316 |
317 | static inline float flex_resolve(FlexLength length, FlexLayoutContext *context, float relativeTo) {
318 | if (flex_isAbsolute(length)) {
319 | return flex_absoluteValue(length, context);
320 | }
321 | else if (length.type == FlexLengthTypePercent && FlexIsResolved(relativeTo)) {
322 | return length.value / 100 * relativeTo;
323 | }
324 |
325 | return NAN;
326 | }
327 |
328 | static inline float flex_auto_to_0(float value) {
329 | return FlexIsResolved(value) ? value : 0;
330 | }
331 |
332 | static inline float flex_clamp(float value, float minValue, float maxValue) {
333 | if (FlexIsUndefined(value)) {
334 | return value;
335 | }
336 |
337 | if (FlexIsUndefined(maxValue)) {
338 | return fmaxf(value, minValue);
339 | } else {
340 | return fmaxf(fminf(value, maxValue), minValue);
341 | }
342 | }
343 |
344 | static inline float flex_clampMax(float value, float maxValue) {
345 | if (FlexIsUndefined(value)) {
346 | return value;
347 | }
348 |
349 | if (FlexIsUndefined(maxValue)) {
350 | return value;
351 | } else {
352 | return fminf(value, maxValue);
353 | }
354 | }
355 |
356 | static inline float flex_inset(float *inset, FlexDirection direction) {
357 | float inset_start = inset[flex_start[direction]];
358 | float inset_end = inset[flex_end[direction]];
359 | return (!FlexIsUndefined(inset_start) ? inset_start : 0) + (!FlexIsUndefined(inset_end) ? inset_end : 0);
360 | }
361 |
362 | static inline bool flex_hasAutoMargin(FlexNodeRef node, FlexDirection direction) {
363 | return FlexIsUndefined(node->resolvedMargin[flex_start[direction]]) || FlexIsUndefined(node->resolvedMargin[flex_end[direction]]);
364 | }
365 |
366 | void flex_resolveMarginAndPadding(FlexNodeRef node, FlexLayoutContext *context, float widthOfContainingBlock, FlexDirection parentDirection) {
367 | bool isMainHorizontal = parentDirection == FlexHorizontal || parentDirection == FlexHorizontalReverse;
368 | FlexDirection crossAxis = isMainHorizontal ? FlexVertical : FlexHorizontal;
369 | node->resolvedMargin[flex_start[parentDirection]] = flex_resolve(!node->fixed && isMainHorizontal && node->margin[FLEX_START].type != FlexLengthTypeUndefined ? node->margin[FLEX_START] : node->margin[flex_start[parentDirection]], context, widthOfContainingBlock);
370 | node->resolvedMargin[flex_end[parentDirection]] = flex_resolve(!node->fixed && isMainHorizontal && node->margin[FLEX_END].type != FlexLengthTypeUndefined ? node->margin[FLEX_END] : node->margin[flex_end[parentDirection]], context, widthOfContainingBlock);
371 | node->resolvedMargin[flex_start[crossAxis]] = flex_resolve(!node->fixed && !isMainHorizontal && node->margin[FLEX_START].type != FlexLengthTypeUndefined ? node->margin[FLEX_START] : node->margin[flex_start[crossAxis]], context, widthOfContainingBlock);
372 | node->resolvedMargin[flex_end[crossAxis]] = flex_resolve(!node->fixed && !isMainHorizontal && node->margin[FLEX_END].type != FlexLengthTypeUndefined ? node->margin[FLEX_END] : node->margin[flex_end[crossAxis]], context, widthOfContainingBlock);
373 | float border[4];
374 | border[flex_start[parentDirection]] = isMainHorizontal && !FlexIsUndefined(node->border[FLEX_START]) ? node->border[FLEX_START] : node->border[flex_start[parentDirection]];
375 | border[flex_end[parentDirection]] = isMainHorizontal && !FlexIsUndefined(node->border[FLEX_END]) ? node->border[FLEX_END] : node->border[flex_end[parentDirection]];
376 | border[flex_start[crossAxis]] = !isMainHorizontal && !FlexIsUndefined(node->border[FLEX_START]) ? node->border[FLEX_START] : node->border[flex_start[crossAxis]];
377 | border[flex_end[crossAxis]] = !isMainHorizontal && !FlexIsUndefined(node->border[FLEX_END]) ? node->border[FLEX_END] : node->border[flex_end[crossAxis]];
378 | node->result.padding[flex_start[parentDirection]] = flex_auto_to_0(flex_resolve(isMainHorizontal && node->padding[FLEX_START].type != FlexLengthTypeUndefined ? node->padding[FLEX_START] : node->padding[flex_start[parentDirection]], context, widthOfContainingBlock) + border[flex_start[parentDirection]]);
379 | node->result.padding[flex_end[parentDirection]] = flex_auto_to_0(flex_resolve(isMainHorizontal && node->padding[FLEX_END].type != FlexLengthTypeUndefined ? node->padding[FLEX_END] : node->padding[flex_end[parentDirection]], context, widthOfContainingBlock) + border[flex_end[parentDirection]]);
380 | node->result.padding[flex_start[crossAxis]] = flex_auto_to_0(flex_resolve(!isMainHorizontal && node->padding[FLEX_START].type != FlexLengthTypeUndefined ? node->padding[FLEX_START] : node->padding[flex_start[crossAxis]], context, widthOfContainingBlock) + border[flex_start[crossAxis]]);
381 | node->result.padding[flex_end[crossAxis]] = flex_auto_to_0(flex_resolve(!isMainHorizontal && node->padding[FLEX_END].type != FlexLengthTypeUndefined ? node->padding[FLEX_END] : node->padding[flex_end[crossAxis]], context, widthOfContainingBlock) + border[flex_end[crossAxis]]);
382 | node->result.margin[0] = node->resolvedMargin[0];
383 | node->result.margin[1] = node->resolvedMargin[1];
384 | node->result.margin[2] = node->resolvedMargin[2];
385 | node->result.margin[3] = node->resolvedMargin[3];
386 | }
387 |
388 | void flex_baseline(FlexNodeRef node, float *ascender, float *descender) {
389 | FlexDirection crossAxis = FlexVertical;
390 | float _ascender, _descender;
391 |
392 | if (!FlexIsUndefined(node->ascender)) {
393 | _ascender = node->ascender;
394 | _descender = node->result.size[crossAxis] - _ascender;
395 | }
396 | else if (node->baseline) {
397 | FlexSize size;
398 | size.width = node->result.size[FLEX_WIDTH] - flex_inset(node->result.padding, FLEX_WIDTH);
399 | size.height = node->result.size[FLEX_HEIGHT] - flex_inset(node->result.padding, FLEX_HEIGHT);
400 | _descender = node->baseline(node->context, size) + node->result.padding[flex_end[crossAxis]];
401 | _ascender = node->result.size[crossAxis] - _descender;
402 | }
403 | else {
404 | _ascender = node->result.size[crossAxis];
405 | for (int i=0;ifixed) {
408 | flex_baseline(child, NULL, NULL);
409 | _ascender = child->ascender + (isnan(child->result.position[flex_start[crossAxis]]) ? 0 : child->result.position[flex_start[crossAxis]]);
410 | break;
411 | }
412 | }
413 | _descender = node->result.size[crossAxis] - _ascender;
414 | }
415 |
416 | node->ascender = _ascender;
417 | _ascender += FlexIsUndefined(node->resolvedMargin[flex_start[crossAxis]]) ? 0 : node->resolvedMargin[flex_start[crossAxis]];
418 | _descender += FlexIsUndefined(node->resolvedMargin[flex_end[crossAxis]]) ? 0 : node->resolvedMargin[flex_end[crossAxis]];
419 |
420 | if (ascender) *ascender = _ascender;
421 | if (descender) *descender = _descender;
422 | }
423 |
424 | FlexSize flex_measureNode(FlexNodeRef node, FlexSize availableSize) {
425 | if (node->measure) {
426 | for (int i = (int)FlexVector_size(FlexMeasureCache, node->measuredSizeCache) - 1; i >= 0; i--) {
427 | FlexSize key = node->measuredSizeCache->data[i].availableSize;
428 | FlexSize value = node->measuredSizeCache->data[i].measuredSize;
429 | if (FlexSizeEquals(availableSize, key)
430 | || (FlexTreatNanAsInf(availableSize.width) <= FlexTreatNanAsInf(key.width)
431 | && FlexTreatNanAsInf(availableSize.height) <= FlexTreatNanAsInf(key.height)
432 | && FlexTreatNanAsInf(availableSize.width) >= value.width
433 | && FlexTreatNanAsInf(availableSize.height) >= value.height)) {
434 | return value;
435 | }
436 | }
437 | FlexSize measuredSize = node->measure(node->context, availableSize);
438 | if (!node->measuredSizeCache) {
439 | node->measuredSizeCache = FlexVector_new(FlexMeasureCache, 2);
440 | }
441 | FlexVector_add(FlexMeasureCache, node->measuredSizeCache, ((FlexMeasureCache){availableSize, measuredSize}));
442 | return measuredSize;
443 | }
444 |
445 | return (FlexSize){0,0};
446 | }
447 |
448 | bool flex_canUseCache(FlexNodeRef node, FlexSize constrainedSize) {
449 | return FlexFloatEquals(node->lastConstrainedSize.width, constrainedSize.width)
450 | && FlexFloatEquals(node->lastConstrainedSize.height, constrainedSize.height)
451 | && FlexLengthEquals(node->size[FLEX_WIDTH], node->lastSize[FLEX_WIDTH])
452 | && FlexLengthEquals(node->size[FLEX_HEIGHT], node->lastSize[FLEX_HEIGHT]);
453 | }
454 |
455 | void flex_layoutInternal(FlexNodeRef node, FlexLayoutContext *context, FlexSize constrainedSize, FlexLayoutFlags flags, bool isRoot) {
456 | node->ascender = NAN;
457 |
458 | if (constrainedSize.width < 0) constrainedSize.width = 0;
459 | if (constrainedSize.height < 0) constrainedSize.height = 0;
460 | float constrainedWidth = constrainedSize.width;
461 | float constrainedHeight = constrainedSize.height;
462 | float resolvedWidth = flex_resolve(node->size[FLEX_WIDTH], context, constrainedWidth);
463 | float resolvedHeight = flex_resolve(node->size[FLEX_HEIGHT], context, constrainedHeight);
464 | if (isRoot && !FlexIsResolved(resolvedWidth) && !FlexIsUndefined(constrainedWidth)) resolvedWidth = constrainedWidth;
465 | if (isRoot && !FlexIsResolved(resolvedHeight) && !FlexIsUndefined(constrainedHeight)) resolvedHeight = constrainedHeight;
466 | resolvedWidth = flex_clamp(resolvedWidth, flex_resolve(node->minSize[FLEX_WIDTH], context, constrainedWidth), flex_resolve(node->maxSize[FLEX_WIDTH], context, constrainedWidth));
467 | resolvedHeight = flex_clamp(resolvedHeight, flex_resolve(node->minSize[FLEX_HEIGHT], context, constrainedHeight), flex_resolve(node->maxSize[FLEX_HEIGHT], context, constrainedHeight));
468 | FlexSize resolvedSize = {resolvedWidth, resolvedHeight};
469 | FlexSize resolvedInnerSize = {
470 | FlexIsResolved(resolvedWidth) ? resolvedWidth - flex_inset(node->result.padding, FLEX_WIDTH) : NAN,
471 | FlexIsResolved(resolvedHeight) ? resolvedHeight - flex_inset(node->result.padding, FLEX_HEIGHT) : NAN
472 | };
473 |
474 | if (FlexIsResolved(resolvedWidth)) node->result.size[FLEX_WIDTH] = resolvedWidth;
475 | if (FlexIsResolved(resolvedHeight)) node->result.size[FLEX_HEIGHT] = resolvedHeight;
476 |
477 | if (flags == FlexLayoutFlagMeasureWidth && FlexIsResolved(resolvedWidth)) {
478 | node->result.size[FLEX_WIDTH] = resolvedWidth;
479 | return;
480 | }
481 | if (flags == FlexLayoutFlagMeasureHeight && FlexIsResolved(resolvedHeight)) {
482 | node->result.size[FLEX_HEIGHT] = resolvedHeight;
483 | return;
484 | }
485 |
486 | float resolvedMarginWidth = flex_inset(node->resolvedMargin, FLEX_WIDTH);
487 | float resolvedMarginHeight = flex_inset(node->resolvedMargin, FLEX_HEIGHT);
488 | float resolvedPaddingWidth = flex_inset(node->result.padding, FLEX_WIDTH);
489 | float resolvedPaddingHeight = flex_inset(node->result.padding, FLEX_HEIGHT);
490 | FlexSize availableSize;
491 | availableSize.width = FlexIsResolved(resolvedWidth) ? resolvedWidth - resolvedPaddingWidth : FlexIsUndefined(constrainedWidth) ? NAN : (constrainedWidth - resolvedMarginWidth - resolvedPaddingWidth);
492 | availableSize.height = FlexIsResolved(resolvedHeight) ? resolvedHeight - resolvedPaddingHeight : FlexIsUndefined(constrainedHeight) ? NAN : (constrainedHeight - resolvedMarginHeight - resolvedPaddingHeight);
493 |
494 | if (flex_canUseCache(node, availableSize)) {
495 | return;
496 | }
497 | else if (flags == FlexLayoutFlagLayout) {
498 | node->lastConstrainedSize = availableSize;
499 | }
500 | else {
501 | node->lastConstrainedSize = FlexCacheSizeUndefined;
502 | }
503 | node->lastSize[FLEX_WIDTH] = node->size[FLEX_WIDTH];
504 | node->lastSize[FLEX_HEIGHT] = node->size[FLEX_HEIGHT];
505 |
506 | // measure non-container element
507 | if (Flex_getChildrenCount(node) == 0) {
508 | if (!FlexIsResolved(resolvedWidth) || !FlexIsResolved(resolvedHeight)) {
509 | FlexSize measuredSize = flex_measureNode(node, availableSize);
510 |
511 | if (!FlexIsResolved(resolvedWidth)) {
512 | node->result.size[FLEX_WIDTH] = flex_clamp(measuredSize.width + flex_inset(node->result.padding, FLEX_WIDTH), flex_resolve(node->minSize[FLEX_WIDTH], context, constrainedWidth), flex_resolve(node->maxSize[FLEX_WIDTH], context, constrainedWidth));
513 | }
514 | if (!FlexIsResolved(resolvedHeight)) {
515 | node->result.size[FLEX_HEIGHT] = flex_clamp(measuredSize.height + flex_inset(node->result.padding, FLEX_HEIGHT), flex_resolve(node->minSize[FLEX_HEIGHT], context, constrainedHeight), flex_resolve(node->maxSize[FLEX_HEIGHT], context, constrainedHeight));
516 | }
517 | }
518 | return;
519 | }
520 |
521 | // layout flex container
522 |
523 | bool isReverse = node->direction == FlexHorizontalReverse || node->direction == FlexVerticalReverse;
524 | bool isWrapReverse = node->wrap == FlexWrapReverse;
525 | FlexDirection mainAxis = flex_dim[node->direction];
526 | FlexDirection crossAxis = mainAxis == FlexHorizontal ? FlexVertical : FlexHorizontal;
527 |
528 | size_t i, j;
529 |
530 | // [flex items ----------->|<----------- fixed items]
531 | FlexNodeRef* items = (FlexNodeRef*)malloc(sizeof(FlexNode) * Flex_getChildrenCount(node));
532 | int flexItemsCount = 0;
533 | int fixedItemsCount = 0;
534 |
535 | for (i=0;idirection);
539 | if (item->fixed) {
540 | items[Flex_getChildrenCount(node) - ++fixedItemsCount] = item;
541 | }
542 | else {
543 | items[flexItemsCount++] = item;
544 | }
545 | }
546 |
547 | // reference: http://www.w3.org/TR/css3-flexbox/#layout-algorithm
548 |
549 | ///////////////////////////////////////////////////////////////////////////
550 | // Line Length Determination
551 |
552 | // 2. Determine the available main and cross space for the flex items. For each dimension, if that dimension of the flex container’s content box is a definite size, use that; otherwise, subtract the flex container’s margin, border, and padding from the space available to the flex container in that dimension and use that value. This might result in an infinite value.
553 |
554 | float resolvedMinCrossSize = flex_resolve(node->minSize[crossAxis], context, constrainedSize.size[crossAxis]);
555 | float resolvedMaxCrossSize = flex_resolve(node->maxSize[crossAxis], context, constrainedSize.size[crossAxis]);
556 | float resolvedMinMainSize = flex_resolve(node->minSize[mainAxis], context, constrainedSize.size[mainAxis]);
557 | float resolvedMaxMainSize = flex_resolve(node->maxSize[mainAxis], context, constrainedSize.size[mainAxis]);
558 | FlexSize resolvedMarginSize = {resolvedMarginWidth, resolvedMarginHeight};
559 | FlexSize resolvedPaddingSize = {resolvedPaddingWidth, resolvedPaddingHeight};
560 |
561 | FlexLine* lines = (FlexLine*)malloc(sizeof(FlexLine) * (flexItemsCount > 0 ? flexItemsCount : 1));
562 | lines[0].itemsCount = 0;
563 | lines[0].itemsSize = 0;
564 | size_t linesCount = 1;
565 |
566 | float spacing = flex_auto_to_0(flex_resolve(node->spacing, context, resolvedInnerSize.size[mainAxis]));
567 | float lineSpacing = flex_auto_to_0(flex_resolve(node->lineSpacing, context, resolvedInnerSize.size[crossAxis]));
568 |
569 | // 3. Determine the flex base size and hypothetical main size of each item:
570 | for (i=0;icalculatedFlexBasis, context, resolvedInnerSize.size[mainAxis]);
575 |
576 | // A. If the item has a definite used flex basis, that’s the flex base size.
577 | if (FlexIsResolved(resolvedFlexBasis)) {
578 | item->flexBaseSize = resolvedFlexBasis;
579 | }
580 |
581 | // B. If the flex item has ...
582 | // * an intrinsic aspect ratio,
583 | // * a used flex basis of content, and
584 | // * a definite cross size
585 | // then the flex base size is calculated from its inner cross size and the flex item’s intrinsic aspect ratio.
586 | // TODO supports aspect ratio ?
587 |
588 | // E. Otherwise, size the item into the available space using its used flex basis in place of its main size, treating a value of content as max-content. If a cross size is needed to determine the main size (e.g. when the flex item’s main size is in its block axis) and the flex item’s cross size is auto and not definite, in this calculation use fit-content as the flex item’s cross size. The flex base size is the item’s resulting main size.
589 | else {
590 | FlexLength oldMainSize = item->size[mainAxis];
591 | item->size[mainAxis] = item->calculatedFlexBasis;
592 | flex_layoutInternal(item, context, availableSize, mainAxis == FlexHorizontal ? FlexLayoutFlagMeasureWidth : FlexLayoutFlagMeasureHeight, false);
593 | item->size[mainAxis] = oldMainSize;
594 | item->flexBaseSize = item->result.size[mainAxis];
595 | }
596 |
597 | // When determining the flex base size, the item’s min and max main size properties are ignored (no clamping occurs).
598 | // The hypothetical main size is the item’s flex base size clamped according to its min and max main size properties.
599 | itemHypotheticalMainSize = flex_clamp(item->flexBaseSize, flex_resolve(item->minSize[mainAxis], context, resolvedInnerSize.size[mainAxis]), flex_resolve(item->maxSize[mainAxis], context, resolvedInnerSize.size[mainAxis]));
600 | item->result.size[mainAxis] = itemHypotheticalMainSize;
601 | float outerItemHypotheticalMainSize = itemHypotheticalMainSize + flex_inset(item->resolvedMargin, mainAxis);
602 |
603 | ///////////////////////////////////////////////////////////////////////////
604 | // Main Size Determination
605 |
606 | // 5. Collect flex items into flex lines:
607 | // * If the flex container is single-line, collect all the flex items into a single flex line.
608 | if (!node->wrap) {
609 | if (lines[linesCount - 1].itemsCount > 0) lines[linesCount - 1].itemsSize += spacing;
610 | lines[linesCount - 1].itemsSize += outerItemHypotheticalMainSize;
611 | lines[linesCount - 1].itemsCount++;
612 | }
613 | // * Otherwise, starting from the first uncollected item, collect consecutive items one by one until the first time that the next collected item would not fit into the flex container’s inner main size (or until a forced break is encountered, see §10 Fragmenting Flex Layout). If the very first uncollected item wouldn’t fit, collect just it into the line.
614 | /* line break not supported yet */
615 | // For this step, the size of a flex item is its outer hypothetical main size.
616 | // Repeat until all flex items have been collected into flex lines.
617 | // > Note that the "collect as many" line will collect zero-sized flex items onto the end of the previous line even if the last non-zero item exactly "filled up" the line.
618 | else {
619 | if (lines[linesCount - 1].itemsCount > 0) outerItemHypotheticalMainSize += spacing;
620 | // line break
621 | if ((node->itemsPerLine > 0 && lines[linesCount - 1].itemsCount >= node->itemsPerLine) ||
622 | lines[linesCount - 1].itemsSize + outerItemHypotheticalMainSize > FlexTreatNanAsInf(availableSize.size[mainAxis]) + 0.001) {
623 | if (node->lines > 0 && linesCount >= node->lines) {
624 | for (int j=i;jresult.position[0] = 0;
627 | item->result.position[1] = 0;
628 | item->result.size[0] = -1;
629 | item->result.size[1] = -1;
630 | }
631 | break;
632 | }
633 |
634 | if (lines[linesCount - 1].itemsCount == 0) {
635 | lines[linesCount - 1].itemsCount = 1;
636 | lines[linesCount - 1].itemsSize = outerItemHypotheticalMainSize;
637 | }
638 | else {
639 | if (lines[linesCount - 1].itemsCount > 0) outerItemHypotheticalMainSize -= spacing;
640 | lines[linesCount].itemsCount = 1;
641 | lines[linesCount].itemsSize = outerItemHypotheticalMainSize;
642 | linesCount++;
643 | }
644 | }
645 | else {
646 | lines[linesCount - 1].itemsCount++;
647 | lines[linesCount - 1].itemsSize += outerItemHypotheticalMainSize;
648 | }
649 | }
650 | }
651 |
652 | flex_assert(node->wrap || linesCount == 1);
653 |
654 | float itemsMainSize = 0;
655 | for (i=0;i itemsMainSize) {
657 | // lineSize means the item's main size, and it will be used to store the lines' cross size below
658 | itemsMainSize = lines[i].itemsSize;
659 | }
660 | }
661 |
662 | // 4. Determine the main size of the flex container using the rules of the formatting context in which it participates. For this computation, auto margins on flex items are treated as 0.
663 | node->result.size[mainAxis] = FlexIsResolved(resolvedSize.size[mainAxis]) ? resolvedSize.size[mainAxis] : flex_clampMax(itemsMainSize + resolvedPaddingSize.size[mainAxis], !FlexIsUndefined(constrainedSize.size[mainAxis]) ? constrainedSize.size[mainAxis] - resolvedMarginSize.size[mainAxis] : NAN);
664 | node->result.size[mainAxis] = flex_clamp(node->result.size[mainAxis], resolvedMinMainSize, resolvedMaxMainSize);
665 | itemsMainSize = node->result.size[mainAxis] - resolvedPaddingSize.size[mainAxis];
666 |
667 | if ((flags == FlexLayoutFlagMeasureWidth && mainAxis == FLEX_WIDTH)
668 | || (flags == FlexLayoutFlagMeasureHeight && mainAxis == FLEX_HEIGHT)) {
669 | free(lines);
670 | free(items);
671 | return;
672 | }
673 |
674 | float innerMainSize = node->result.size[mainAxis] - resolvedPaddingSize.size[mainAxis];
675 |
676 | // 6. Resolve the flexible lengths of all the flex items to find their used main size (see section 9.7.).
677 | bool* isFrozen = (bool*)malloc(sizeof(bool) * flexItemsCount);
678 | char* violations = (char*)malloc(sizeof(char) * flexItemsCount);
679 | size_t lineStart = 0;
680 | for (i=0;iresult.size[mainAxis] + flex_inset(item->resolvedMargin, mainAxis);
690 | }
691 | float itemsSpacing = (count - 1) * spacing;
692 | lineMainSize += itemsSpacing;
693 | bool usingFlexGrowFactor = lineMainSize < FlexTreatNanAsInf(availableSize.size[mainAxis]);
694 | float initialFreeSpace = innerMainSize - itemsSpacing;
695 |
696 | for (j=lineStart;jflexGrow : item->flexShrink * item->flexBaseSize) == 0
704 | || (usingFlexGrowFactor && item->flexBaseSize > item->result.size[mainAxis])
705 | || (!usingFlexGrowFactor && item->flexBaseSize < item->result.size[mainAxis])) {
706 | isFrozen[j] = true;
707 | unfrozenCount--;
708 | initialFreeSpace -= item->result.size[mainAxis] + flex_inset(item->resolvedMargin, mainAxis);
709 | } else {
710 | isFrozen[j] = false;
711 | initialFreeSpace -= item->flexBaseSize + flex_inset(item->resolvedMargin, mainAxis);
712 | }
713 | }
714 |
715 | // 6.4 Loop:
716 | while (true) {
717 | // a. Check for flexible items. If all the flex items on the line are frozen, free space has been distributed; exit this loop.
718 | if (unfrozenCount == 0) {
719 | break;
720 | }
721 |
722 | // b. Calculate the remaining free space as for initial free space, above. If the sum of the unfrozen flex items’ flex factors is less than one, multiply the initial free space by this sum. If the magnitude of this value is less than the magnitude of the remaining free space, use this as the remaining free space.
723 | float remainingFreeSpace = fmax(0.f, innerMainSize - itemsSpacing);
724 | float unFrozenFlexFactors = 0;
725 |
726 | for (j=lineStart;jresult.size[mainAxis] + flex_inset(item->resolvedMargin, mainAxis);
730 | }
731 | else {
732 | remainingFreeSpace -= item->flexBaseSize + flex_inset(item->resolvedMargin, mainAxis);
733 | unFrozenFlexFactors += usingFlexGrowFactor ? item->flexGrow : item->flexShrink;
734 | }
735 | }
736 |
737 | if (unFrozenFlexFactors < 1) {
738 | float value = initialFreeSpace * unFrozenFlexFactors;
739 | if (value < remainingFreeSpace) {
740 | remainingFreeSpace = value;
741 | }
742 | }
743 |
744 | // c. Distribute free space proportional to the flex factors.
745 | // If the remaining free space is zero
746 | // Do nothing.
747 | if (remainingFreeSpace == 0) {
748 |
749 | }
750 | // If using the flex grow factor
751 | // Find the ratio of the item’s flex grow factor to the sum of the flex grow factors of all unfrozen items on the line. Set the item’s target main size to its flex base size plus a fraction of the remaining free space proportional to the ratio.
752 | else if (usingFlexGrowFactor && unFrozenFlexFactors > 0) {
753 | for (j=lineStart;jflexGrow / unFrozenFlexFactors;
757 | item->result.size[mainAxis] = item->flexBaseSize + remainingFreeSpace * ratio;
758 | }
759 | }
760 | }
761 | // If using the flex shrink factor
762 | // For every unfrozen item on the line, multiply its flex shrink factor by its inner flex base size, and note this as its scaled flex shrink factor. Find the ratio of the item’s scaled flex shrink factor to the sum of the scaled flex shrink factors of all unfrozen items on the line. Set the item’s target main size to its flex base size minus a fraction of the absolute value of the remaining free space proportional to the ratio. Note this may result in a negative inner main size; it will be corrected in the next step.
763 | else if (!usingFlexGrowFactor && unFrozenFlexFactors > 0) {
764 | float unFrozenScaledFlexFactors = 0;
765 | for (j=lineStart;jflexShrink * item->flexBaseSize;
769 | }
770 | }
771 | for (j=lineStart;jflexShrink * item->flexBaseSize / unFrozenScaledFlexFactors;
775 | item->result.size[mainAxis] = item->flexBaseSize + remainingFreeSpace * ratio;
776 | }
777 | }
778 | }
779 | // Otherwise
780 | // Do nothing.
781 |
782 | // d. Fix min/max violations. Clamp each non-frozen item’s target main size by its min and max main size properties. If the item’s target main size was made smaller by this, it’s a max violation. If the item’s target main size was made larger by this, it’s a min violation.
783 | float totalViolation = 0;
784 | for (j=lineStart;jresult.size[mainAxis];
788 | float clampedSize = flex_clamp(targetMainSize, flex_resolve(item->minSize[mainAxis], context, resolvedInnerSize.size[mainAxis]), flex_resolve(item->maxSize[mainAxis], context, resolvedInnerSize.size[mainAxis]));
789 | violations[j] = clampedSize == targetMainSize ? 0 : clampedSize < targetMainSize ? 1 : -1; // 1/-1 means max/min
790 | totalViolation += clampedSize - targetMainSize;
791 | item->result.size[mainAxis] = clampedSize;
792 | }
793 | }
794 |
795 | // e. Freeze over-flexed items. The total violation is the sum of the adjustments from the previous step ∑(clamped size - unclamped size). If the total violation is:
796 | // Zero
797 | // Freeze all items.
798 | if (totalViolation == 0) {
799 | unfrozenCount = 0;
800 | }
801 | // Positive
802 | // Freeze all the items with min violations.
803 | else if (totalViolation > 0) {
804 | for (j=lineStart;j 0) {
819 | isFrozen[j] = true;
820 | unfrozenCount--;
821 | }
822 | }
823 | }
824 | }
825 |
826 | // f. Return to the start of this loop.
827 | }
828 | lineStart = lineEnd;
829 | }
830 | free(isFrozen);
831 | free(violations);
832 |
833 | ///////////////////////////////////////////////////////////////////////////
834 | // Cross Size Determination
835 |
836 | // 7. Determine the hypothetical cross size of each item by performing layout with the used main size and the available space, treating auto as fit-content.
837 | for (i=0;isize[mainAxis];
840 | item->size[mainAxis] = (FlexLength){item->result.size[mainAxis], FlexLengthTypePoint};
841 | flex_layoutInternal(item, context, availableSize, flags, false);
842 | item->size[mainAxis] = oldMainSize;
843 | }
844 |
845 | // 8. Calculate the cross size of each flex line.
846 | // If the flex container is single-line and has a definite cross size, the cross size of the flex line is the flex container’s inner cross size.
847 | float itemsCrossSize = 0;
848 | float resolvedInnerCrossSize = FlexIsResolved(resolvedSize.size[crossAxis]) ? resolvedSize.size[crossAxis] - flex_inset(node->result.padding, crossAxis) : NAN;
849 | if (!node->wrap && FlexIsResolved(resolvedSize.size[crossAxis])) {
850 | itemsCrossSize = resolvedInnerCrossSize;
851 | lines[0].itemsSize = itemsCrossSize;
852 |
853 | // prepare for baseline alignment
854 | float maxAscender = 0;
855 | for (j=0;jcalculatedAlignSelf == FlexBaseline && !flex_hasAutoMargin(item, crossAxis)) {
858 | float ascender;
859 | flex_baseline(item, &ascender, NULL);
860 |
861 | if (ascender > maxAscender) {
862 | maxAscender = ascender;
863 | }
864 | }
865 | }
866 | lines[0].ascender = maxAscender;
867 | }
868 | //Otherwise, for each flex line:
869 | else {
870 | lineStart = 0;
871 | for (i=0;icalculatedAlignSelf == FlexBaseline && !flex_hasAutoMargin(item, crossAxis)) {
883 | float ascender, descender;
884 | flex_baseline(item, &ascender, &descender);
885 |
886 | if (ascender > maxAscender) maxAscender = ascender;
887 | if (descender > maxDescender) maxDescender = descender;
888 | }
889 | // 2. Among all the items not collected by the previous step, find the largest outer hypothetical cross size.
890 | else {
891 | float itemOuterHypotheticalCrossSize = item->result.size[crossAxis] + flex_inset(item->resolvedMargin, crossAxis);
892 | if (itemOuterHypotheticalCrossSize > maxOuterHypotheticalCrossSize) {
893 | maxOuterHypotheticalCrossSize = itemOuterHypotheticalCrossSize;
894 | }
895 | }
896 | }
897 |
898 | lines[i].ascender = maxAscender;
899 |
900 | // 3. The used cross-size of the flex line is the largest of the numbers found in the previous two steps and zero.
901 | float usedCrossSize = fmax(0.f, fmax(maxOuterHypotheticalCrossSize, maxAscender + maxDescender));
902 |
903 | // If the flex container is single-line, then clamp the line’s cross-size to be within the container’s computed min and max cross-size properties. Note that if CSS 2.1’s definition of min/max-width/height applied more generally, this behavior would fall out automatically.
904 | if (!node->wrap) {
905 | usedCrossSize = flex_clamp(usedCrossSize, resolvedMinCrossSize, resolvedMaxCrossSize);
906 | }
907 | if (i > 0) itemsCrossSize += lineSpacing;
908 | itemsCrossSize += usedCrossSize;
909 | lines[i].itemsSize = usedCrossSize;
910 |
911 | lineStart = lineEnd;
912 | }
913 | }
914 |
915 | node->ascender = lines[0].ascender ? lines[0].ascender + node->result.padding[flex_start[crossAxis]] : NAN;
916 |
917 | if (flags == FlexLayoutFlagLayout) {
918 | // 9. Handle 'align-content: stretch'. If the flex container has a definite cross size, align-content is stretch, and the sum of the flex lines' cross sizes is less than the flex container’s inner cross size, increase the cross size of each flex line by equal amounts such that the sum of their cross sizes exactly equals the flex container’s inner cross size.
919 | if (FlexIsResolved(resolvedSize.size[crossAxis]) && node->alignContent == FlexStretch && itemsCrossSize < FlexTreatNanAsInf(resolvedInnerCrossSize)) {
920 | float increment = (resolvedInnerCrossSize - itemsCrossSize) / linesCount;
921 | for (i=0;i Note that this step does not affect the main size of the flex item, even if it has an intrinsic aspect ratio.
934 | lineStart = 0;
935 | for (i=0;isize[crossAxis];
942 | if (item->calculatedAlignSelf == FlexStretch && item->size[crossAxis].type == FlexLengthTypeAuto && !flex_hasAutoMargin(item, crossAxis)) {
943 | item->size[crossAxis].type = FlexLengthTypePoint;
944 | item->size[crossAxis].value = item->result.size[crossAxis] = lines[i].itemsSize - flex_inset(item->resolvedMargin, crossAxis);
945 | }
946 |
947 | item->result.size[crossAxis] = flex_clamp(item->result.size[crossAxis], flex_resolve(item->minSize[crossAxis], context, resolvedInnerSize.size[crossAxis]), flex_resolve(item->maxSize[crossAxis], context, resolvedInnerSize.size[crossAxis]));
948 |
949 | if (item->calculatedAlignSelf == FlexStretch) {
950 | FlexLength oldMainSize = item->size[mainAxis];
951 | item->size[crossAxis] = (FlexLength){item->result.size[crossAxis], FlexLengthTypePoint};
952 | item->size[mainAxis] = (FlexLength){item->result.size[mainAxis], FlexLengthTypePoint};
953 | flex_layoutInternal(item, context, availableSize, flags, false);
954 | item->size[mainAxis] = oldMainSize;
955 | }
956 | item->size[crossAxis] = oldCrossSize;
957 | }
958 |
959 | lineStart = lineEnd;
960 | }
961 |
962 | ///////////////////////////////////////////////////////////////////////////
963 | // Main-Axis Alignment
964 |
965 | // 12. Distribute any remaining free space. For each flex line:
966 | lineStart = 0;
967 | for (i=0;iresult.size[mainAxis] + flex_inset(item->resolvedMargin, mainAxis);
974 | }
975 | lineMainSize += (lines[i].itemsCount - 1) * spacing;
976 |
977 | float remainMainSize = innerMainSize - lineMainSize;
978 | // 1. If the remaining free space is positive and at least one main-axis margin on this line is auto, distribute the free space equally among these margins.
979 | if (remainMainSize > 0) {
980 | size_t count = 0;
981 | for (j=lineStart;jresolvedMargin[flex_start[node->direction]])) count++;
984 | if (FlexIsUndefined(item->resolvedMargin[flex_end[node->direction]])) count++;
985 | }
986 | if (count > 0) {
987 | float autoMargin = remainMainSize / count;
988 | remainMainSize = 0;
989 | for (j=lineStart;jresolvedMargin[flex_start[node->direction]])) item->result.margin[flex_start[node->direction]] = autoMargin;
992 | if (FlexIsUndefined(item->resolvedMargin[flex_end[node->direction]])) item->result.margin[flex_end[node->direction]] = autoMargin;
993 | }
994 | }
995 | }
996 | // Otherwise, set all auto margins to zero.
997 | else {
998 | for (j=lineStart;jresolvedMargin[flex_start[node->direction]])) item->result.margin[flex_start[node->direction]] = 0;
1001 | if (FlexIsUndefined(item->resolvedMargin[flex_end[node->direction]])) item->result.margin[flex_end[node->direction]] = 0;
1002 | }
1003 | }
1004 |
1005 | // 2. Align the items along the main-axis per justify-content.
1006 | float offsetStart = node->result.padding[flex_start[node->direction]];
1007 | float offsetStep = 0;
1008 | switch (node->justifyContent) {
1009 | case FlexStart:
1010 | break;
1011 | case FlexCenter:
1012 | offsetStart += remainMainSize / 2;
1013 | break;
1014 | case FlexEnd:
1015 | offsetStart += remainMainSize;
1016 | break;
1017 | case FlexSpaceBetween:
1018 | offsetStep = remainMainSize / (lines[i].itemsCount - 1);
1019 | break;
1020 | case FlexSpaceAround:
1021 | offsetStep = remainMainSize / lines[i].itemsCount;
1022 | offsetStart += offsetStep / 2;
1023 | break;
1024 | default:
1025 | flex_assert(false);
1026 | }
1027 |
1028 | for (j=lineStart;jresult.margin[flex_start[node->direction]];
1031 | item->result.position[flex_start[mainAxis]] = isReverse ? node->result.size[mainAxis] - offsetStart - item->result.size[mainAxis] : offsetStart;
1032 | offsetStart += item->result.size[mainAxis] + item->result.margin[flex_end[node->direction]] + offsetStep + spacing;
1033 | }
1034 |
1035 | lineStart = lineEnd;
1036 | }
1037 |
1038 | ///////////////////////////////////////////////////////////////////////////
1039 | // Cross-Axis Alignment
1040 |
1041 | // 13. Resolve cross-axis auto margins. If a flex item has auto cross-axis margins:
1042 | // * If its outer cross size (treating those auto margins as zero) is less than the cross size of its flex line, distribute the difference in those sizes equally to the auto margins.
1043 | lineStart = 0;
1044 | for (i=0;iresult.size[crossAxis] + flex_inset(item->resolvedMargin, crossAxis));
1050 | if (remindCrossSize > 0) {
1051 | if (FlexIsUndefined(item->resolvedMargin[flex_start[crossAxis]]) && FlexIsUndefined(item->resolvedMargin[flex_end[crossAxis]])) {
1052 | item->result.margin[flex_start[crossAxis]] = item->result.margin[flex_end[crossAxis]] = remindCrossSize / 2;
1053 | } else if (FlexIsUndefined(item->resolvedMargin[flex_start[crossAxis]])) {
1054 | item->result.margin[flex_start[crossAxis]] = remindCrossSize;
1055 | } else if (FlexIsUndefined(item->resolvedMargin[flex_end[crossAxis]])) {
1056 | item->result.margin[flex_end[crossAxis]] = remindCrossSize;
1057 | }
1058 | }
1059 | // * Otherwise, if the block-start or inline-start margin (whichever is in the cross axis) is auto, set it to zero. Set the opposite margin so that the outer cross size of the item equals the cross size of its flex line.
1060 | else {
1061 | if (FlexIsUndefined(item->resolvedMargin[flex_start[crossAxis]])) {
1062 | item->result.margin[flex_start[crossAxis]] = 0;
1063 | }
1064 | // item->result.margin[flex_end[crossAxis]] = elementsSizeInLine[i] - (item->result.size[crossAxis] + item->result.margin[flex_start[crossAxis]]);
1065 | }
1066 |
1067 | // 14. Align all flex items along the cross-axis per align-self, if neither of the item’s cross-axis margins are auto.
1068 | remindCrossSize = lines[i].itemsSize - (item->result.size[crossAxis] + flex_inset(item->result.margin, crossAxis));
1069 | float itemResultCrossPosition = item->result.margin[flex_start[crossAxis]];
1070 | switch (item->calculatedAlignSelf) {
1071 | case FlexStart:
1072 | break;
1073 | case FlexCenter:
1074 | itemResultCrossPosition += remindCrossSize / 2;
1075 | break;
1076 | case FlexEnd:
1077 | itemResultCrossPosition += remindCrossSize;
1078 | break;
1079 | case FlexStretch:
1080 | break;
1081 | case FlexBaseline:
1082 | if (mainAxis == FlexHorizontal && !flex_hasAutoMargin(item, crossAxis)) {
1083 | itemResultCrossPosition = lines[i].ascender - item->ascender;
1084 | }
1085 | break;
1086 | default:
1087 | flex_assert(false);
1088 | break;
1089 | }
1090 | item->result.position[flex_start[crossAxis]] = isWrapReverse ? lines[i].itemsSize - itemResultCrossPosition - item->result.size[crossAxis] : itemResultCrossPosition;
1091 | }
1092 |
1093 | lineStart = lineEnd;
1094 | }
1095 | }
1096 |
1097 | // 15. Determine the flex container’s used cross size:
1098 | // * If the cross size property is a definite size, use that, clamped by the min and max cross size properties of the flex container.
1099 | if (FlexIsResolved(resolvedSize.size[crossAxis])) {
1100 | node->result.size[crossAxis] = flex_clamp(resolvedSize.size[crossAxis], resolvedMinCrossSize, resolvedMaxCrossSize);
1101 | }
1102 | // * Otherwise, use the sum of the flex lines' cross sizes, clamped by the min and max cross size properties of the flex container.
1103 | else {
1104 | node->result.size[crossAxis] = flex_clamp(itemsCrossSize + resolvedPaddingSize.size[crossAxis], resolvedMinCrossSize, resolvedMaxCrossSize);
1105 | }
1106 |
1107 | if (flags != FlexLayoutFlagLayout) {
1108 | free(lines);
1109 | free(items);
1110 | return;
1111 | }
1112 |
1113 | float innerCrossSize = node->result.size[crossAxis] - resolvedPaddingSize.size[crossAxis];
1114 |
1115 | // 16. Align all flex lines per align-content.
1116 | lineStart = 0;
1117 | float offsetStart = node->result.padding[flex_start[crossAxis]];
1118 | float offsetStep = 0;
1119 | float remindCrossSize = innerCrossSize - itemsCrossSize;
1120 | switch (node->alignContent) {
1121 | case FlexStretch:
1122 | case FlexStart:
1123 | break;
1124 | case FlexCenter:
1125 | offsetStart += remindCrossSize / 2;
1126 | break;
1127 | case FlexEnd:
1128 | offsetStart += remindCrossSize;
1129 | break;
1130 | case FlexSpaceBetween:
1131 | offsetStep = remindCrossSize / (linesCount - 1);
1132 | break;
1133 | case FlexSpaceAround:
1134 | offsetStep = remindCrossSize / linesCount;
1135 | offsetStart += offsetStep / 2;
1136 | break;
1137 | default:
1138 | flex_assert(false);
1139 | }
1140 | float lineCrossPositionStart = offsetStart;
1141 | for (i=0;iresult.position[flex_start[crossAxis]] += isWrapReverse ? node->result.size[crossAxis] - lines[i].itemsSize - lineCrossPositionStart : lineCrossPositionStart;
1146 | }
1147 | lineCrossPositionStart += offsetStep + lines[i].itemsSize + lineSpacing;
1148 | lineStart = lineEnd;
1149 | }
1150 |
1151 | // layout fixed items
1152 | for (i=0;iresult.size[FLEX_WIDTH];
1157 | childConstrainedSize.height = node->result.size[FLEX_HEIGHT];
1158 |
1159 | FlexLength oldWidth = item->size[FLEX_WIDTH];
1160 | FlexLength oldHeight = item->size[FLEX_HEIGHT];
1161 | if (item->size[FLEX_WIDTH].type == FlexLengthTypeAuto && !flex_hasAutoMargin(item, FLEX_WIDTH)) {
1162 | item->size[FLEX_WIDTH] = (FlexLength){node->result.size[FLEX_WIDTH] - flex_inset(item->resolvedMargin, FLEX_WIDTH), FlexLengthTypePoint};
1163 | }
1164 | if (item->size[FLEX_HEIGHT].type == FlexLengthTypeAuto && !flex_hasAutoMargin(item, FLEX_HEIGHT)) {
1165 | item->size[FLEX_HEIGHT] = (FlexLength){node->result.size[FLEX_HEIGHT] - flex_inset(item->resolvedMargin, FLEX_HEIGHT), FlexLengthTypePoint};
1166 | }
1167 | flex_layoutInternal(item, context, childConstrainedSize, flags, false);
1168 | item->size[FLEX_WIDTH] = oldWidth;
1169 | item->size[FLEX_HEIGHT] = oldHeight;
1170 |
1171 | float remindWidth = node->result.size[FLEX_WIDTH] - item->result.size[FLEX_WIDTH] - flex_inset(item->resolvedMargin, FLEX_WIDTH);
1172 | float remindHeight = node->result.size[FLEX_HEIGHT] - item->result.size[FLEX_HEIGHT] - flex_inset(item->resolvedMargin, FLEX_HEIGHT);
1173 | if (remindWidth > 0) {
1174 | if (FlexIsUndefined(item->resolvedMargin[flex_start[FLEX_WIDTH]]) && FlexIsUndefined(item->resolvedMargin[flex_end[FLEX_WIDTH]])) {
1175 | item->result.margin[flex_start[FLEX_WIDTH]] = item->result.margin[flex_end[FLEX_WIDTH]] = remindWidth / 2;
1176 | } else if (FlexIsUndefined(item->resolvedMargin[flex_start[FLEX_WIDTH]])) {
1177 | item->result.margin[flex_start[FLEX_WIDTH]] = remindWidth;
1178 | } else if (FlexIsUndefined(item->resolvedMargin[flex_end[FLEX_WIDTH]])) {
1179 | item->result.margin[flex_end[FLEX_WIDTH]] = remindWidth;
1180 | }
1181 | } else {
1182 | if (FlexIsUndefined(item->resolvedMargin[flex_start[FLEX_WIDTH]])) item->result.margin[flex_start[FLEX_WIDTH]] = 0;
1183 | if (FlexIsUndefined(item->resolvedMargin[flex_end[FLEX_WIDTH]])) item->result.margin[flex_end[FLEX_WIDTH]] = 0;
1184 | }
1185 |
1186 | flex_assert(FlexIsResolved(item->result.margin[0]));
1187 |
1188 | if (remindHeight > 0) {
1189 | if (FlexIsUndefined(item->resolvedMargin[flex_start[FLEX_HEIGHT]]) && FlexIsUndefined(item->resolvedMargin[flex_end[FLEX_HEIGHT]])) {
1190 | item->result.margin[flex_start[FLEX_HEIGHT]] = item->result.margin[flex_end[FLEX_HEIGHT]] = remindHeight / 2;
1191 | } else if (FlexIsUndefined(item->resolvedMargin[flex_start[FLEX_HEIGHT]])) {
1192 | item->result.margin[flex_start[FLEX_HEIGHT]] = remindHeight;
1193 | } else if (FlexIsUndefined(item->resolvedMargin[flex_end[FLEX_HEIGHT]])) {
1194 | item->result.margin[flex_end[FLEX_HEIGHT]] = remindHeight;
1195 | }
1196 | } else {
1197 | if (FlexIsUndefined(item->resolvedMargin[flex_start[FLEX_HEIGHT]])) item->result.margin[flex_start[FLEX_HEIGHT]] = 0;
1198 | if (FlexIsUndefined(item->resolvedMargin[flex_end[FLEX_HEIGHT]])) item->result.margin[flex_end[FLEX_HEIGHT]] = 0;
1199 | }
1200 |
1201 | item->result.position[flex_start[FLEX_WIDTH]] = item->result.margin[flex_start[FLEX_WIDTH]];
1202 | item->result.position[flex_start[FLEX_HEIGHT]] = item->result.margin[flex_start[FLEX_HEIGHT]];
1203 | }
1204 |
1205 | // pixel snapping
1206 | for (i=0;iresult.position[FLEX_LEFT] + item->result.size[FLEX_WIDTH];
1209 | float bottom = item->result.position[FLEX_TOP] + item->result.size[FLEX_HEIGHT];
1210 | item->result.position[FLEX_LEFT] = FlexPixelRound(item->result.position[FLEX_LEFT], context->scale);
1211 | item->result.position[FLEX_TOP] = FlexPixelRound(item->result.position[FLEX_TOP], context->scale);
1212 | item->result.size[FLEX_WIDTH] = FlexPixelRound(item->result.size[FLEX_WIDTH], context->scale);//FlexPixelRound(right - item->result.position[FLEX_LEFT], context->scale);
1213 | item->result.size[FLEX_HEIGHT] = FlexPixelRound(item->result.size[FLEX_HEIGHT], context->scale);//FlexPixelRound(bottom - item->result.position[FLEX_TOP], context->scale);
1214 | }
1215 |
1216 | free(items);
1217 | free(lines);
1218 | }
1219 |
1220 | void flex_setupProperties(FlexNodeRef node) {
1221 | // flex_assert(node->size[FLEX_WIDTH].value >= 0);
1222 | // flex_assert(node->size[FLEX_HEIGHT].value >= 0);
1223 | // flex_assert(node->minSize[FLEX_WIDTH].value >= 0);
1224 | // flex_assert(node->minSize[FLEX_HEIGHT].value >= 0);
1225 | // flex_assert(node->maxSize[FLEX_WIDTH].value >= 0);
1226 | // flex_assert(node->maxSize[FLEX_HEIGHT].value >= 0);
1227 | // flex_assert(node->flexBasis.value >= 0);
1228 | flex_assert(node->alignSelf == FlexInherit || node->alignSelf == FlexStretch || node->alignSelf == FlexStart || node->alignSelf == FlexCenter || node->alignSelf == FlexEnd || node->alignSelf == FlexBaseline);
1229 | flex_assert(node->alignItems == FlexStretch || node->alignItems == FlexStart || node->alignItems == FlexCenter || node->alignItems == FlexEnd || node->alignItems == FlexBaseline);
1230 | flex_assert(node->justifyContent == FlexStart || node->justifyContent == FlexCenter || node->justifyContent == FlexEnd || node->justifyContent == FlexSpaceBetween || node->justifyContent == FlexSpaceAround);
1231 | flex_assert(node->alignContent == FlexStretch || node->alignContent == FlexStart || node->alignContent == FlexCenter || node->alignContent == FlexEnd || node->alignContent == FlexSpaceBetween || node->alignContent == FlexSpaceAround);
1232 |
1233 | #if DEBUG
1234 | node->result.position[FLEX_LEFT] = NAN;
1235 | node->result.position[FLEX_TOP] = NAN;
1236 | node->result.size[FLEX_WIDTH] = NAN;
1237 | node->result.size[FLEX_HEIGHT] = NAN;
1238 | #endif
1239 |
1240 | for (size_t i=0;iflexBasis.type == FlexLengthTypeAuto) {
1244 | item->calculatedFlexBasis = item->size[flex_dim[node->direction]];
1245 | }
1246 | else if (item->flexBasis.type == FlexLengthTypeContent) {
1247 | item->calculatedFlexBasis = FlexLengthAuto;
1248 | }
1249 | else {
1250 | item->calculatedFlexBasis = item->flexBasis;
1251 | }
1252 |
1253 | if (item->alignSelf == FlexInherit) {
1254 | item->calculatedAlignSelf = node->alignItems;
1255 | }
1256 | else {
1257 | item->calculatedAlignSelf = item->alignSelf;
1258 | }
1259 |
1260 | flex_setupProperties(item);
1261 | }
1262 | }
1263 |
1264 | void Flex_layout(FlexNodeRef node, float constrainedWidth, float constrainedHeight, float scale) {
1265 | FlexLayoutContext context;
1266 | context.scale = scale;
1267 |
1268 | flex_setupProperties(node);
1269 | flex_resolveMarginAndPadding(node, &context, constrainedWidth, FlexVertical);
1270 | node->result.margin[FLEX_LEFT] = FlexIsResolved(node->resolvedMargin[FLEX_LEFT]) ? FlexPixelRound(node->resolvedMargin[FLEX_LEFT], scale) : 0;
1271 | node->result.margin[FLEX_TOP] = FlexIsResolved(node->resolvedMargin[FLEX_TOP]) ? FlexPixelRound(node->resolvedMargin[FLEX_TOP], scale) : 0;
1272 | node->result.margin[FLEX_RIGHT] = FlexIsResolved(node->resolvedMargin[FLEX_RIGHT]) ? FlexPixelRound(node->resolvedMargin[FLEX_RIGHT], scale) : 0;
1273 | node->result.margin[FLEX_BOTTOM] = FlexIsResolved(node->resolvedMargin[FLEX_BOTTOM]) ? FlexPixelRound(node->resolvedMargin[FLEX_BOTTOM], scale) : 0;
1274 | FlexSize constrainedSize;
1275 | constrainedSize.width = FlexIsUndefined(constrainedWidth) ? NAN : (constrainedWidth - flex_inset(node->resolvedMargin, FLEX_WIDTH));
1276 | constrainedSize.height = FlexIsUndefined(constrainedHeight) ? NAN : (constrainedHeight - flex_inset(node->resolvedMargin, FLEX_HEIGHT));
1277 | flex_layoutInternal(node, &context, constrainedSize, FlexLayoutFlagLayout, true);
1278 | node->result.position[FLEX_LEFT] = node->result.margin[FLEX_LEFT];
1279 | node->result.position[FLEX_TOP] = node->result.margin[FLEX_TOP];
1280 | node->result.size[FLEX_WIDTH] = FlexPixelRound(node->result.size[FLEX_WIDTH], scale);
1281 | node->result.size[FLEX_HEIGHT] = FlexPixelRound(node->result.size[FLEX_HEIGHT], scale);
1282 | }
1283 |
1284 | FlexNodeRef Flex_newNode() {
1285 | FlexNodeRef flexNode = (FlexNodeRef)malloc(sizeof(FlexNode));
1286 | memcpy(flexNode, &defaultFlexNode, sizeof(FlexNode));
1287 | return flexNode;
1288 | }
1289 |
1290 | void Flex_freeNode(FlexNodeRef node) {
1291 | if (node->measuredSizeCache) {
1292 | FlexVector_free(FlexMeasureCache, node->measuredSizeCache);
1293 | }
1294 | if (node->children) {
1295 | FlexVector_free(FlexNodeRef, node->children);
1296 | }
1297 | free(node);
1298 | }
1299 |
1300 | void Flex_freeNodeRecursive(FlexNodeRef node) {
1301 | for (size_t i = 0; i < Flex_getChildrenCount(node); i++) {
1302 | Flex_freeNodeRecursive(Flex_getChild(node, i));
1303 | }
1304 | Flex_freeNode(node);
1305 | }
1306 |
1307 | void Flex_insertChild(FlexNodeRef node, FlexNodeRef child, size_t index) {
1308 | if (!node->children) {
1309 | node->children = FlexVector_new(FlexNodeRef, 4);
1310 | }
1311 | flex_markDirty(node);
1312 | FlexVector_insert(FlexNodeRef, node->children, child, index);
1313 | }
1314 |
1315 | void Flex_addChild(FlexNodeRef node, FlexNodeRef child) {
1316 | if (!node->children) {
1317 | node->children = FlexVector_new(FlexNodeRef, 4);
1318 | }
1319 | flex_markDirty(node);
1320 | FlexVector_add(FlexNodeRef, node->children, child);
1321 | }
1322 |
1323 | void Flex_removeChild(FlexNodeRef node, FlexNodeRef child) {
1324 | FlexVector_remove(FlexNodeRef, node->children, child);
1325 | flex_markDirty(node);
1326 | }
1327 |
1328 | FlexNodeRef Flex_getChild(FlexNodeRef node, size_t index) {
1329 | return node->children->data[index];
1330 | }
1331 |
1332 | size_t Flex_getChildrenCount(FlexNodeRef node) {
1333 | return FlexVector_size(FlexNodeRef, node->children);
1334 | }
1335 |
1336 |
1337 | void flex_print_float(float value) {
1338 | printf("%.1f", value);
1339 | }
1340 |
1341 | void flex_print_FlexLength(FlexLength value) {
1342 | switch (value.type) {
1343 | case FlexLengthTypePoint:
1344 | printf("%.1f", value.value);
1345 | break;
1346 | case FlexLengthTypePercent:
1347 | printf("%.1f%%", value.value);
1348 | break;
1349 | case FlexLengthTypeAuto:
1350 | printf("auto");
1351 | break;
1352 | case FlexLengthTypeContent:
1353 | printf("content");
1354 | break;
1355 | case FlexLengthTypeUndefined:
1356 | printf("undefined");
1357 | break;
1358 | }
1359 | }
1360 |
1361 | void flex_print_FlexWrapMode(FlexWrapMode value) {
1362 | switch (value) {
1363 | case FlexWrap:
1364 | printf("wrap");
1365 | break;
1366 | case FlexNoWrap:
1367 | printf("nowrap");
1368 | break;
1369 | case FlexWrapReverse:
1370 | printf("wrap-reverse");
1371 | break;
1372 | }
1373 | }
1374 |
1375 | void flex_print_FlexDirection(FlexDirection value) {
1376 | switch (value) {
1377 | case FlexVertical:
1378 | printf("vertical");
1379 | break;
1380 | case FlexHorizontal:
1381 | printf("horizontal");
1382 | break;
1383 | case FlexVerticalReverse:
1384 | printf("vertical-reverse");
1385 | break;
1386 | case FlexHorizontalReverse:
1387 | printf("horizontal-reverse");
1388 | break;
1389 | }
1390 | }
1391 |
1392 | void flex_print_FlexAlign(FlexAlign value) {
1393 | switch (value) {
1394 | case FlexEnd:
1395 | printf("end");
1396 | break;
1397 | case FlexStart:
1398 | printf("start");
1399 | break;
1400 | case FlexCenter:
1401 | printf("center");
1402 | break;
1403 | case FlexInherit:
1404 | printf("auto");
1405 | break;
1406 | case FlexStretch:
1407 | printf("stretch");
1408 | break;
1409 | case FlexBaseline:
1410 | printf("baseline");
1411 | break;
1412 | case FlexSpaceAround:
1413 | printf("space-around");
1414 | break;
1415 | case FlexSpaceBetween:
1416 | printf("space-between");
1417 | break;
1418 | }
1419 | }
1420 |
1421 | void flex_print_bool(bool value) {
1422 | printf("%s", value ? "true" : "false");
1423 | }
1424 |
1425 | void flex_print_int(int value) {
1426 | printf("%d", value);
1427 | }
1428 |
1429 | void flex_printIndent(int indent) {
1430 | for (int i=0;ifield, &defaultFlexNode.field, sizeof(node->field))){ \
1438 | flex_printIndent(indent); \
1439 | printf(name ": "); \
1440 | flex_print_##type(node->field); \
1441 | printf("\n"); \
1442 | }
1443 | #define FLEX_PRINT_INSET(type, name, field) \
1444 | if (showUnspecified || memcmp(&node->field, &defaultFlexNode.field, sizeof(node->field))){ \
1445 | flex_printIndent(indent); \
1446 | printf(name ": "); \
1447 | flex_print_##type(node->field[FLEX_TOP]); \
1448 | printf(", "); \
1449 | flex_print_##type(node->field[FLEX_LEFT]); \
1450 | printf(", "); \
1451 | flex_print_##type(node->field[FLEX_BOTTOM]); \
1452 | printf(", "); \
1453 | flex_print_##type(node->field[FLEX_RIGHT]); \
1454 | printf("\n"); \
1455 | }
1456 |
1457 | if (options == FlexPrintDefault) {
1458 | options = (FlexPrintOptions)0xffffffff;
1459 | }
1460 |
1461 | bool showUnspecified = !(options & FlexPrintHideUnspecified);
1462 |
1463 | flex_printIndent(indent); printf("{\n");
1464 | indent++;
1465 | if (options & FlexPrintStyle) {
1466 | FLEX_PRINT(FlexWrapMode, "wrap", wrap);
1467 | FLEX_PRINT(FlexDirection, "direction", direction);
1468 | FLEX_PRINT(FlexAlign, "align-items", alignItems);
1469 | FLEX_PRINT(FlexAlign, "align-self", alignSelf);
1470 | FLEX_PRINT(FlexAlign, "align-content", alignContent);
1471 | FLEX_PRINT(FlexAlign, "justify-content", justifyContent);
1472 | FLEX_PRINT(FlexLength, "flex-basis", flexBasis);
1473 | FLEX_PRINT(float, "flex-grow", flexGrow);
1474 | FLEX_PRINT(float, "flex-shrink", flexShrink);
1475 | FLEX_PRINT(FlexLength, "width", size[FLEX_WIDTH]);
1476 | FLEX_PRINT(FlexLength, "height", size[FLEX_HEIGHT]);
1477 | FLEX_PRINT(FlexLength, "min-width", minSize[FLEX_WIDTH]);
1478 | FLEX_PRINT(FlexLength, "min-height", minSize[FLEX_HEIGHT]);
1479 | FLEX_PRINT(FlexLength, "max-width", maxSize[FLEX_WIDTH]);
1480 | FLEX_PRINT(FlexLength, "max-height", maxSize[FLEX_HEIGHT]);
1481 | FLEX_PRINT_INSET(FlexLength, "margin", margin);
1482 | FLEX_PRINT_INSET(FlexLength, "padding", padding);
1483 | FLEX_PRINT_INSET(float, "border", border);
1484 | FLEX_PRINT(bool, "fixed", fixed);
1485 | FLEX_PRINT(FlexLength, "spacing", spacing);
1486 | FLEX_PRINT(FlexLength, "line-spacing", lineSpacing);
1487 | FLEX_PRINT(int, "lines", lines);
1488 | FLEX_PRINT(int, "items-per-line", itemsPerLine);
1489 | FLEX_PRINT(bool, "has-measure-func", measure);
1490 | FLEX_PRINT(bool, "has-baseline-func", baseline);
1491 | }
1492 | if (options & FlexPrintResult) {
1493 | FLEX_PRINT(float, "result-x", result.position[FLEX_LEFT]);
1494 | FLEX_PRINT(float, "result-y", result.position[FLEX_TOP]);
1495 | FLEX_PRINT(float, "result-width", result.size[FLEX_WIDTH]);
1496 | FLEX_PRINT(float, "result-height", result.size[FLEX_HEIGHT]);
1497 | FLEX_PRINT_INSET(float, "result-margin", result.margin);
1498 | FLEX_PRINT_INSET(float, "result-padding", result.padding);
1499 | }
1500 | if ((options & FlexPrintChildren) && Flex_getChildrenCount(node) > 0) {
1501 | flex_printIndent(indent); printf("children: [\n");
1502 | for (size_t i=0;i