├── 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